diff --git a/.readthedocs-environment.yml b/.readthedocs-environment.yml new file mode 100644 index 00000000..8b70d329 --- /dev/null +++ b/.readthedocs-environment.yml @@ -0,0 +1,8 @@ +name: solaris +dependencies: + - python + - pip + - gdal + - pip: + - sphinx_bootstrap_theme + - nbsphinx diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 00000000..ac3c539c --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,17 @@ +version: 2 +formats: + - htmlzip + +python: + version: 3.6 + install: + - method: pip + path: . + - requirements: .rtfd-requirements.txt + + +conda: + environment: .readthedocs-environment.yml + +build: + image: latest diff --git a/.rtfd-requirements.txt b/.rtfd-requirements.txt new file mode 100644 index 00000000..59aa86cc --- /dev/null +++ b/.rtfd-requirements.txt @@ -0,0 +1 @@ +sphinx_bootstrap_theme diff --git a/README.md b/README.md index 3bd0c390..ef15e090 100644 --- a/README.md +++ b/README.md @@ -13,30 +13,58 @@

-## This package is under active development. Check back soon for updates! +## This is a beta version of Solaris which may continue to develop. Please report any bugs through issues! +- [Documentation](#documentation) - [Installation Instructions](#installation-instructions) - [Dependencies](#dependencies) - [License](#license) --- + This repository provides the source code for the CosmiQ Works `solaris` project, which provides software tools for: - Tiling large-format overhead images and vector labels - Converting between geospatial raster and vector formats and machine learning-compatible formats - Performing semantic and instance segmentation, object detection, and related tasks using deep learning models designed specifically for overhead image analysis - Evaluating performance of deep learning model predictions +## Documentation +The full documentation for `solaris` can be found at https://solaris.readthedocs.io, and includes: +- A summary of `solaris` +- Installation instructions +- API Documentation +- Tutorials for common uses + +The documentation is still being improved, so if a tutorial you need isn't there yet, check back soon or post an issue! + ## Installation Instructions + +_coming soon_: One-command installation from conda-forge. + We recommend creating a `conda` environment with the dependencies defined in [environment.yml](./environment.yml) before installing `solaris`. After cloning the repository: ``` cd solaris +``` + +If you're installing on a system with GPU access: +``` +conda env create -n solaris -f environment-gpu.yml +``` +Otherwise: +If you're installing on a system with GPU access: +``` conda env create -n solaris -f environment.yml +``` + +Finally, regardless of your installation environment: +``` conda activate solaris pip install . ``` #### pip -The package also exists on[ PyPI](https://pypi.org), but note that some of the dependencies, specifically [rtree](https://github.com/Toblerity/) and [gdal](https://www.gdal.org), are challenging to install without anaconda. We therefore recommend installing at least those dependencies using `conda` before installing from PyPI. + +The package also exists on[ PyPI](https://pypi.org), but note that some of the dependencies, specifically [rtree](https://github.com/Toblerity/rtree) and [gdal](https://www.gdal.org), are challenging to install without anaconda. We therefore recommend installing at least those dependency using `conda` before installing from PyPI. ``` conda install -c conda-forge rtree gdal=2.4.1 @@ -60,7 +88,7 @@ docker pull cosmiqworks/solaris See the [readthedocs](https://cw-eval.readthedocs.io/) page. --> ## Dependencies -All dependencies can be found in the docker file [Dockerfile](./Dockerfile) or +All dependencies can be found in the requirements file [./requirements.txt](requirements.txt) or [environment.yml](./environment.yml) ## License diff --git a/docker/cpu/Dockerfile b/docker/cpu/Dockerfile new file mode 100644 index 00000000..9669e1e8 --- /dev/null +++ b/docker/cpu/Dockerfile @@ -0,0 +1,79 @@ +FROM nvidia/cuda:9.2-devel-ubuntu16.04 +LABEL maintainer="nweir " + +ARG solaris_branch='master' + + +# prep apt-get and cudnn +RUN apt-get update && apt-get install -y --no-install-recommends \ + apt-utils \ + rm -rf /var/lib/apt/lists/* + +# install requirements +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + bc \ + bzip2 \ + ca-certificates \ + curl \ + git \ + libgdal-dev \ + libssl-dev \ + libffi-dev \ + libncurses-dev \ + libgl1 \ + jq \ + nfs-common \ + parallel \ + python-dev \ + python-pip \ + python-wheel \ + python-setuptools \ + unzip \ + vim \ + wget \ + build-essential \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +SHELL ["/bin/bash", "-c"] +ENV PATH /opt/conda/bin:$PATH + +# install anaconda +RUN wget --quiet https://repo.anaconda.com/miniconda/Miniconda3-4.5.4-Linux-x86_64.sh -O ~/miniconda.sh && \ + /bin/bash ~/miniconda.sh -b -p /opt/conda && \ + rm ~/miniconda.sh && \ + /opt/conda/bin/conda clean -tipsy && \ + ln -s /opt/conda/etc/profile.d/conda.sh /etc/profile.d/conda.sh && \ + echo ". /opt/conda/etc/profile.d/conda.sh" >> ~/.bashrc && \ + echo "conda activate base" >> ~/.bashrc + +# prepend pytorch and conda-forge before default channel +RUN conda update conda && \ + conda config --prepend channels conda-forge && \ + conda config --prepend channels pytorch + +# get dev version of solaris and create conda environment based on its env file +WORKDIR /tmp/ +RUN git clone https://github.com/cosmiq/solaris.git && \ + cd solaris && \ + git checkout ${solaris_branch} && \ + conda env create -f environment.yml +ENV PATH /opt/conda/envs/solaris/bin:$PATH + +RUN cd solaris && pip install . + +# install various conda dependencies into the space_base environment +RUN conda install -n solaris \ + jupyter \ + jupyterlab \ + ipykernel + +# add a jupyter kernel for the conda environment in case it's wanted +RUN source activate solaris && python -m ipykernel.kernelspec \ + --name solaris --display-name solaris + +# open ports for jupyterlab and tensorboard +EXPOSE 8888 6006 + +RUN ["/bin/bash"] diff --git a/docker/Dockerfile b/docker/gpu/Dockerfile similarity index 98% rename from docker/Dockerfile rename to docker/gpu/Dockerfile index 522f557f..ed3dc5b7 100644 --- a/docker/Dockerfile +++ b/docker/gpu/Dockerfile @@ -63,7 +63,7 @@ WORKDIR /tmp/ RUN git clone https://github.com/cosmiq/solaris.git && \ cd solaris && \ git checkout ${solaris_branch} && \ - conda env create -f environment.yml + conda env create -f environment-gpu.yml ENV PATH /opt/conda/envs/solaris/bin:$PATH RUN cd solaris && pip install . diff --git a/docs/_static/custom_styles.css b/docs/_static/custom_styles.css new file mode 100644 index 00000000..ff858af8 --- /dev/null +++ b/docs/_static/custom_styles.css @@ -0,0 +1,15 @@ +p { + font-size: 1.1em; +} + +h1 { + font-size: 2.5em; +} + +h2 { + font-size: 2.2em; +} + +h3 { + font-size: 1.75em; +} diff --git a/docs/_static/solaris_logo.png b/docs/_static/solaris_logo.png new file mode 100644 index 00000000..abf94841 Binary files /dev/null and b/docs/_static/solaris_logo.png differ diff --git a/docs/_static/solaris_logo_tiny_black.png b/docs/_static/solaris_logo_tiny_black.png new file mode 100644 index 00000000..ba8fc46a Binary files /dev/null and b/docs/_static/solaris_logo_tiny_black.png differ diff --git a/docs/_templates/autosummary/base.rst b/docs/_templates/autosummary/base.rst new file mode 100644 index 00000000..b7556ebf --- /dev/null +++ b/docs/_templates/autosummary/base.rst @@ -0,0 +1,5 @@ +{{ fullname | escape | underline}} + +.. currentmodule:: {{ module }} + +.. auto{{ objtype }}:: {{ objname }} diff --git a/docs/_templates/autosummary/class.rst b/docs/_templates/autosummary/class.rst new file mode 100644 index 00000000..8861b79a --- /dev/null +++ b/docs/_templates/autosummary/class.rst @@ -0,0 +1,29 @@ +{{ fullname | escape | underline}} + +.. currentmodule:: {{ module }} + +.. autoclass:: {{ objname }} + + {% block methods %} + .. automethod:: __init__ + + {% if methods %} + .. rubric:: Methods + + .. autosummary:: + {% for item in methods %} + ~{{ name }}.{{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block attributes %} + {% if attributes %} + .. rubric:: Attributes + + .. autosummary:: + {% for item in attributes %} + ~{{ name }}.{{ item }} + {%- endfor %} + {% endif %} + {% endblock %} diff --git a/docs/_templates/autosummary/module.rst b/docs/_templates/autosummary/module.rst new file mode 100644 index 00000000..6ec89e05 --- /dev/null +++ b/docs/_templates/autosummary/module.rst @@ -0,0 +1,36 @@ +{{ fullname | escape | underline}} + +.. automodule:: {{ fullname }} + + {% block functions %} + {% if functions %} + .. rubric:: Functions + + .. autosummary:: + {% for item in functions %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block classes %} + {% if classes %} + .. rubric:: Classes + + .. autosummary:: + {% for item in classes %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block exceptions %} + {% if exceptions %} + .. rubric:: Exceptions + + .. autosummary:: + {% for item in exceptions %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} diff --git a/docs/_templates/base/base.rst b/docs/_templates/base/base.rst new file mode 100644 index 00000000..45c8dd7f --- /dev/null +++ b/docs/_templates/base/base.rst @@ -0,0 +1,7 @@ +.. {{ obj.type }}:: {{ obj.name }} + + {% if summary %} + + {{ obj.summary }} + + {% endif %} diff --git a/docs/_templates/dotnet/base_detail.rst b/docs/_templates/dotnet/base_detail.rst new file mode 100644 index 00000000..a197d68b --- /dev/null +++ b/docs/_templates/dotnet/base_detail.rst @@ -0,0 +1,88 @@ +{% block title %} + +{{ obj.short_name }} {{ obj.type.title()}} +{{ "=" * (obj.short_name|length + obj.type|length + 1) }} + +{% endblock %} + +{% block summary %} + {% if obj.summary %} + +{{ obj.summary }} + + {% endif %} +{% endblock %} + +{% if obj.namespace %} +Namespace + :dn:ns:`{{ obj.namespace }}` + +{% endif %} +{% if obj.assemblies %} +Assemblies + {% for assembly in obj.assemblies %} + * {{ assembly }} + {% endfor %} +{% endif %} + +---- + +.. contents:: + :local: + +{% block inheritance %} + +{% if obj.inheritance %} + +Inheritance Hierarchy +--------------------- + +{% for item in obj.inheritance %} +* :dn:{{ item.ref_directive }}:`{{ item.ref_name }}` + {% endfor %} +* :dn:{{ obj.ref_directive }}:`{{ obj.ref_name }}` +{% endif %} + +{% endblock %} + +{% block syntax %} + +{% if obj.example %} + +Syntax +------ + +.. code-block:: csharp + + {{ obj.example|indent(4) }} + +{% endif %} + +{% endblock %} + + +{% block content %} + +.. dn:{{ obj.ref_type }}:: {{ obj.definition }} + :hidden: + +.. dn:{{ obj.ref_type }}:: {{ obj.name }} + +{% for item_type in obj.item_map.keys() %} +{% if item_type in obj.item_map %} + +{{ item_type.title() }} +{{ "-" * item_type|length }} + +.. dn:{{ obj.ref_type }}:: {{ obj.name }} + :noindex: + :hidden: + + {% for obj_item in obj.item_map.get(item_type, []) %} + {{ obj_item.render()|indent(4) }} + {% endfor %} + +{% endif %} +{% endfor %} + +{% endblock %} diff --git a/docs/_templates/dotnet/base_embed.rst b/docs/_templates/dotnet/base_embed.rst new file mode 100644 index 00000000..62ca04a0 --- /dev/null +++ b/docs/_templates/dotnet/base_embed.rst @@ -0,0 +1,29 @@ +.. dn:{{ obj.ref_type }}:: {{ obj.name }} + + {% if obj.summary %} + {{ obj.summary|indent(4) }} + + {% endif %} + + {% for param in obj.parameters %} + + {% if param.desc %} + :param {{ param.name }}: {{ param.desc|indent(8) }} + {% endif %} + {% if param.type %} + :type {{ param.name }}: {{ param.type|indent(8) }} + {% endif %} + {% endfor %} + + {% if obj.returns.type %} + :rtype: {{ obj.returns.type|indent(8) }} + {% endif %} + {% if obj.returns.description %} + :return: {{ obj.returns.description|indent(8) }} + {% endif %} + + {% if obj.example %} + .. code-block:: csharp + + {{ obj.example|indent(8) }} + {% endif %} diff --git a/docs/_templates/dotnet/base_list.rst b/docs/_templates/dotnet/base_list.rst new file mode 100644 index 00000000..f1635272 --- /dev/null +++ b/docs/_templates/dotnet/base_list.rst @@ -0,0 +1,66 @@ +{% block title %} + +{{ obj.name }} {{ obj.type.title() }} +{{ "=" * (obj.name|length + obj.type|length + 1) }} + +{% endblock %} + +{% block toc %} + +{% if obj.children %} + +.. toctree:: + :hidden: + :maxdepth: 2 + + {% for item in obj.children|sort %} + {% if item.type != 'namespace' %} + {{ item.include_path }} + {% endif %} + {% endfor %} + + +{% endif %} + +{% if obj.references %} + +.. toctree:: + :hidden: + :maxdepth: 2 + + {% for item in obj.references|sort %} + {% if item.type != 'namespace' %} + {{ item.include_path }} + {% endif %} + {% endfor %} + +{% endif %} + +{% endblock %} + + +{% block content %} + +{% macro display_type(item_type) %} + + .. rubric:: {{ item_type.title() }} + +{% for obj_item in obj.item_map.get(item_type, []) %} +{% macro render() %}{{ obj_item.summary }}{% endmacro %} + + {{ obj_item.type }} :dn:{{ obj_item.ref_directive }}:`{{ obj_item.ref_short_name }}` + .. object: type={{ obj_item.type }} name={{ obj_item.ref_name }} + + {{ render()|indent(8) }} + +{% endfor %} +{% endmacro %} + +.. dn:{{ obj.ref_type }}:: {{ obj.name }} + +{% for item_type in obj.item_map.keys() %} +{{ display_type(item_type) }} +{% endfor %} + + +{% endblock %} diff --git a/docs/_templates/dotnet/class.rst b/docs/_templates/dotnet/class.rst new file mode 100644 index 00000000..556f99b0 --- /dev/null +++ b/docs/_templates/dotnet/class.rst @@ -0,0 +1 @@ +{% extends "dotnet/base_detail.rst" %} \ No newline at end of file diff --git a/docs/_templates/dotnet/constructor.rst b/docs/_templates/dotnet/constructor.rst new file mode 100644 index 00000000..c9188602 --- /dev/null +++ b/docs/_templates/dotnet/constructor.rst @@ -0,0 +1 @@ +{% extends "dotnet/base_embed.rst" %} \ No newline at end of file diff --git a/docs/_templates/dotnet/delegate.rst b/docs/_templates/dotnet/delegate.rst new file mode 100644 index 00000000..556f99b0 --- /dev/null +++ b/docs/_templates/dotnet/delegate.rst @@ -0,0 +1 @@ +{% extends "dotnet/base_detail.rst" %} \ No newline at end of file diff --git a/docs/_templates/dotnet/enum.rst b/docs/_templates/dotnet/enum.rst new file mode 100644 index 00000000..556f99b0 --- /dev/null +++ b/docs/_templates/dotnet/enum.rst @@ -0,0 +1 @@ +{% extends "dotnet/base_detail.rst" %} \ No newline at end of file diff --git a/docs/_templates/dotnet/event.rst b/docs/_templates/dotnet/event.rst new file mode 100644 index 00000000..c9188602 --- /dev/null +++ b/docs/_templates/dotnet/event.rst @@ -0,0 +1 @@ +{% extends "dotnet/base_embed.rst" %} \ No newline at end of file diff --git a/docs/_templates/dotnet/field.rst b/docs/_templates/dotnet/field.rst new file mode 100644 index 00000000..c9188602 --- /dev/null +++ b/docs/_templates/dotnet/field.rst @@ -0,0 +1 @@ +{% extends "dotnet/base_embed.rst" %} \ No newline at end of file diff --git a/docs/_templates/dotnet/interface.rst b/docs/_templates/dotnet/interface.rst new file mode 100644 index 00000000..556f99b0 --- /dev/null +++ b/docs/_templates/dotnet/interface.rst @@ -0,0 +1 @@ +{% extends "dotnet/base_detail.rst" %} \ No newline at end of file diff --git a/docs/_templates/dotnet/method.rst b/docs/_templates/dotnet/method.rst new file mode 100644 index 00000000..c9188602 --- /dev/null +++ b/docs/_templates/dotnet/method.rst @@ -0,0 +1 @@ +{% extends "dotnet/base_embed.rst" %} \ No newline at end of file diff --git a/docs/_templates/dotnet/namespace.rst b/docs/_templates/dotnet/namespace.rst new file mode 100644 index 00000000..fa9ffc27 --- /dev/null +++ b/docs/_templates/dotnet/namespace.rst @@ -0,0 +1 @@ +{% extends "dotnet/base_list.rst" %} \ No newline at end of file diff --git a/docs/_templates/dotnet/operator.rst b/docs/_templates/dotnet/operator.rst new file mode 100644 index 00000000..c9188602 --- /dev/null +++ b/docs/_templates/dotnet/operator.rst @@ -0,0 +1 @@ +{% extends "dotnet/base_embed.rst" %} \ No newline at end of file diff --git a/docs/_templates/dotnet/property.rst b/docs/_templates/dotnet/property.rst new file mode 100644 index 00000000..c9188602 --- /dev/null +++ b/docs/_templates/dotnet/property.rst @@ -0,0 +1 @@ +{% extends "dotnet/base_embed.rst" %} \ No newline at end of file diff --git a/docs/_templates/dotnet/struct.rst b/docs/_templates/dotnet/struct.rst new file mode 100644 index 00000000..556f99b0 --- /dev/null +++ b/docs/_templates/dotnet/struct.rst @@ -0,0 +1 @@ +{% extends "dotnet/base_detail.rst" %} \ No newline at end of file diff --git a/docs/_templates/go/base_member.rst b/docs/_templates/go/base_member.rst new file mode 100644 index 00000000..ad6e5b68 --- /dev/null +++ b/docs/_templates/go/base_member.rst @@ -0,0 +1,26 @@ +.. go:{{ obj.ref_type }}:: {{ obj.name }} +{% if obj.type == 'func' %} + {% set argjoin = joiner(', ') %} + ({% for param in obj.parameters %} + {{ argjoin() }}{{ param.name }} {{ param.type }} + {% endfor %}) +{% endif %} + + {% macro render() %}{{ obj.docstring }}{% endmacro %} + {{ render()|indent(4) }} + + {# Don't define parameter description here, that can be done in the block + above #} + {% for param in obj.parameters %} + :type {{ param.name }}: {{ param.type }} + {% endfor %} + {% if obj.returns %} + :rtype: {{ obj.returns.type }} + {% endif %} + + {% if obj.children %} + {% for child in obj.children|sort %} + {% macro render_child() %}{{ child.render() }}{% endmacro %} + {{ render_child()|indent(4) }} + {% endfor %} + {% endif %} diff --git a/docs/_templates/go/const.rst b/docs/_templates/go/const.rst new file mode 100644 index 00000000..04663550 --- /dev/null +++ b/docs/_templates/go/const.rst @@ -0,0 +1 @@ +{% extends "go/base_member.rst" %} diff --git a/docs/_templates/go/func.rst b/docs/_templates/go/func.rst new file mode 100644 index 00000000..04663550 --- /dev/null +++ b/docs/_templates/go/func.rst @@ -0,0 +1 @@ +{% extends "go/base_member.rst" %} diff --git a/docs/_templates/go/method.rst b/docs/_templates/go/method.rst new file mode 100644 index 00000000..04663550 --- /dev/null +++ b/docs/_templates/go/method.rst @@ -0,0 +1 @@ +{% extends "go/base_member.rst" %} diff --git a/docs/_templates/go/package.rst b/docs/_templates/go/package.rst new file mode 100644 index 00000000..85cf0579 --- /dev/null +++ b/docs/_templates/go/package.rst @@ -0,0 +1,32 @@ +.. go:package:: {{ obj.name }} + +{{ obj.name }} +{{ "=" * obj.name|length }} + +{% block toc %} + {% if obj.children %} + +{# TODO Make this work +.. toctree:: + :maxdepth: 4 + + {% for item in obj.children|sort %} + /autoapi/{{ item.id.split('.')|join('/') }}/index + {% endfor %} +#} + + {% endif %} +{% endblock %} + +{% if obj.docstring %} +{{ obj.docstring }} +{% endif %} + +{% block content %} + {% for obj_item in obj.children|sort %} + +{% macro render() %}{{ obj_item.render() }}{% endmacro %} +{{ render()|indent(0) }} + + {% endfor %} +{% endblock %} diff --git a/docs/_templates/go/type.rst b/docs/_templates/go/type.rst new file mode 100644 index 00000000..04663550 --- /dev/null +++ b/docs/_templates/go/type.rst @@ -0,0 +1 @@ +{% extends "go/base_member.rst" %} diff --git a/docs/_templates/go/var.rst b/docs/_templates/go/var.rst new file mode 100644 index 00000000..04663550 --- /dev/null +++ b/docs/_templates/go/var.rst @@ -0,0 +1 @@ +{% extends "go/base_member.rst" %} diff --git a/docs/_templates/index.rst b/docs/_templates/index.rst new file mode 100644 index 00000000..f8c78f0a --- /dev/null +++ b/docs/_templates/index.rst @@ -0,0 +1,15 @@ +API Reference +============= + +This page contains auto-generated API reference documentation [#f1]_. + +.. toctree:: + :titlesonly: + + {% for page in pages %} + {% if page.top_level_object and page.display %} + {{ page.include_path }} + {% endif %} + {% endfor %} + +.. [#f1] Created with `sphinx-autoapi `_ diff --git a/docs/_templates/javascript/class.rst b/docs/_templates/javascript/class.rst new file mode 100644 index 00000000..7a44aab0 --- /dev/null +++ b/docs/_templates/javascript/class.rst @@ -0,0 +1,20 @@ +.. js:class:: {{ obj.name }}{% if obj.args %}({{ obj.args|join(',') }}){% endif %} + + {% if obj.docstring %} + + .. rubric:: Summary + + {{ obj.docstring|indent(3) }} + + {% endif %} + + {% if obj.methods %} + + {% for method in obj.methods %} + + {% macro render() %}{{ method.render() }}{% endmacro %} + {{ render()|indent(3) }} + + {%- endfor %} + + {% endif %} diff --git a/docs/_templates/javascript/function.rst b/docs/_templates/javascript/function.rst new file mode 100644 index 00000000..88835ab2 --- /dev/null +++ b/docs/_templates/javascript/function.rst @@ -0,0 +1,14 @@ +{# Identention in this file is important #} + +{% if is_method %} +{# Slice self off #} +.. method:: {{ obj.name.split('.')[-1] }}({{ args[1:]|join(',') }}) +{% else %} +.. function:: {{ obj.name.split('.')[-1] }}({{ args|join(',') }}) +{% endif %} + + {% if obj.docstring %} + {{ obj.docstring|indent(3) }} + {% endif %} + + diff --git a/docs/_templates/javascript/member.rst b/docs/_templates/javascript/member.rst new file mode 100644 index 00000000..8e2d86a6 --- /dev/null +++ b/docs/_templates/javascript/member.rst @@ -0,0 +1,7 @@ +{# Identention in this file is important #} + +.. {{ obj.type }}:: {{ obj.name }} + + {{ obj.docstring|indent(3) }} + + diff --git a/docs/_templates/javascript/module.rst b/docs/_templates/javascript/module.rst new file mode 100644 index 00000000..7f344ab4 --- /dev/null +++ b/docs/_templates/javascript/module.rst @@ -0,0 +1,52 @@ +{{ obj.name }} +{{ "-" * obj.name|length }} + +{% block toc %} + +{% if obj.children %} + +.. toctree:: + :maxdepth: 4 + + {% for item in obj.children|sort %} + /autoapi/{{ item.pathname }}/index + {%- endfor %} + +{% endif %} + +{% endblock %} + +{% if obj.docstring %} + +.. rubric:: Summary + +{{ obj.docstring }} + +{% endif %} + +.. js:module:: {{ obj.name }} + + + +{% block content %} + +{%- macro display_type(item_type) %} + +{{ item_type.title() }} +{{ "*" * item_type|length }} + +{%- for obj_item in obj.item_map.get(item_type, []) %} +{% macro render() %}{{ obj_item.render() }}{% endmacro %} + + {{ render()|indent(4) }} + +{%- endfor %} +{%- endmacro %} + +{%- for item_type in obj.item_map.keys() %} +{% if item_type.lower() != 'module' %} +{{ display_type(item_type) }} +{% endif %} +{%- endfor %} + +{% endblock %} diff --git a/docs/_templates/python/attribute.rst b/docs/_templates/python/attribute.rst new file mode 100644 index 00000000..ebaba555 --- /dev/null +++ b/docs/_templates/python/attribute.rst @@ -0,0 +1 @@ +{% extends "python/data.rst" %} diff --git a/docs/_templates/python/class.rst b/docs/_templates/python/class.rst new file mode 100644 index 00000000..1c082dd3 --- /dev/null +++ b/docs/_templates/python/class.rst @@ -0,0 +1,25 @@ +{% if obj.display %} +.. py:{{ obj.type }}:: {{ obj.short_name }}{% if obj.args %}({{ obj.args }}){% endif %} + + + {% if obj.bases %} + Bases: {% for base in obj.bases %}:class:`{{ base }}`{% if not loop.last %}, {% endif %}{% endfor %} + + + {% endif %} + {% if obj.docstring %} + {{ obj.docstring|prepare_docstring|indent(3) }} + {% endif %} + {% set visible_classes = obj.classes|selectattr("display")|list %} + {% for klass in visible_classes %} + {{ klass.rendered|indent(3) }} + {% endfor %} + {% set visible_attributes = obj.attributes|selectattr("display")|list %} + {% for attribute in visible_attributes %} + {{ attribute.rendered|indent(3) }} + {% endfor %} + {% set visible_methods = obj.methods|selectattr("display")|list %} + {% for method in visible_methods %} + {{ method.rendered|indent(3) }} + {% endfor %} +{% endif %} diff --git a/docs/_templates/python/data.rst b/docs/_templates/python/data.rst new file mode 100644 index 00000000..2ce3e1ef --- /dev/null +++ b/docs/_templates/python/data.rst @@ -0,0 +1,7 @@ +{% if obj.display %} +.. {{ obj.type }}:: {{ obj.name }} + {%+ if obj.value is not none or obj.annotation is not none %}:annotation:{% if obj.annotation %} :{{ obj.annotation }}{% endif %}{% if obj.value is not none %} = {{ obj.value }}{% endif %}{% endif %} + + + {{ obj.docstring|prepare_docstring|indent(3) }} +{% endif %} diff --git a/docs/_templates/python/exception.rst b/docs/_templates/python/exception.rst new file mode 100644 index 00000000..92f3d38f --- /dev/null +++ b/docs/_templates/python/exception.rst @@ -0,0 +1 @@ +{% extends "python/class.rst" %} diff --git a/docs/_templates/python/function.rst b/docs/_templates/python/function.rst new file mode 100644 index 00000000..af1d122d --- /dev/null +++ b/docs/_templates/python/function.rst @@ -0,0 +1,8 @@ +{% if obj.display %} +.. function:: {{ obj.short_name }}({{ obj.args }}){% if obj.return_annotation is not none %} -> {{ obj.return_annotation }}{% endif %} + + {% if obj.docstring %} + + {{ obj.docstring|prepare_docstring|indent(3) }} + {% endif %} +{% endif %} diff --git a/docs/_templates/python/method.rst b/docs/_templates/python/method.rst new file mode 100644 index 00000000..be336195 --- /dev/null +++ b/docs/_templates/python/method.rst @@ -0,0 +1,9 @@ +{%- if obj.display %} + +.. {{ obj.method_type }}:: {{ obj.short_name }}({{ obj.args }}) + + {% if obj.docstring %} + {{ obj.docstring|prepare_docstring|indent(3) }} + {% endif %} + +{% endif %} diff --git a/docs/_templates/python/module.rst b/docs/_templates/python/module.rst new file mode 100644 index 00000000..55df3f71 --- /dev/null +++ b/docs/_templates/python/module.rst @@ -0,0 +1,94 @@ +{% if not obj.display %} +:orphan: + +{% endif %} +:mod:`{{ obj.name }}` +======={{ "=" * obj.name|length }} + +.. py:module:: {{ obj.name }} + +{% if obj.docstring %} +.. autoapi-nested-parse:: + + {{ obj.docstring|prepare_docstring|indent(3) }} + +{% endif %} + +{% block subpackages %} +{% set visible_subpackages = obj.subpackages|selectattr("display")|list %} +{% if visible_subpackages %} +Subpackages +----------- +.. toctree:: + :titlesonly: + :maxdepth: 3 + +{% for subpackage in visible_subpackages %} + {{ subpackage.short_name }}/index.rst +{% endfor %} + + +{% endif %} +{% endblock %} +{% block submodules %} +{% set visible_submodules = obj.submodules|selectattr("display")|list %} +{% if visible_submodules %} +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + +{% for submodule in visible_submodules %} + {{ submodule.short_name }}/index.rst +{% endfor %} + + +{% endif %} +{% endblock %} +{% block content %} +{% set visible_children = obj.children|selectattr("display")|list %} +{% if visible_children %} +{{ obj.type|title }} Contents +{{ "-" * obj.type|length }}--------- + +{% set visible_classes = visible_children|selectattr("type", "equalto", "class")|list %} +{% set visible_functions = visible_children|selectattr("type", "equalto", "function")|list %} +{% if include_summaries and (visible_classes or visible_functions) %} +{% block classes %} +{% if visible_classes %} +Classes +~~~~~~~ + +.. autoapisummary:: + +{% for klass in visible_classes %} + {{ klass.id }} +{% endfor %} + + +{% endif %} +{% endblock %} + +{% block functions %} +{% if visible_functions %} +Functions +~~~~~~~~~ + +.. autoapisummary:: + +{% for function in visible_functions %} + {{ function.id }} +{% endfor %} + + +{% endif %} +{% endblock %} +{% endif %} +{% for obj_item in visible_children %} +{% if obj.all is none or obj_item.short_name in obj.all %} +{{ obj_item.rendered|indent(0) }} +{% endif %} +{% endfor %} +{% endif %} +{% endblock %} diff --git a/docs/_templates/python/package.rst b/docs/_templates/python/package.rst new file mode 100644 index 00000000..fb9a6496 --- /dev/null +++ b/docs/_templates/python/package.rst @@ -0,0 +1 @@ +{% extends "python/module.rst" %} diff --git a/docs/api.rst b/docs/api.rst deleted file mode 100644 index 580dbc28..00000000 --- a/docs/api.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. title:: API reference - -CosmiQ Works Evaluation API reference -===================================== - -Core functionality ------------------- -.. automodule:: cw_eval.baseeval - :members: - -.. automodule:: cw_eval.evalfunctions - :members: - -SpaceNet Challenge eval code ----------------------------- -.. automodule:: cw_eval.challenge_eval.off_nadir_dataset - :members: diff --git a/docs/api/eval.rst b/docs/api/eval.rst new file mode 100644 index 00000000..b86edda3 --- /dev/null +++ b/docs/api/eval.rst @@ -0,0 +1,32 @@ +.. title:: solaris.eval API reference + +``solaris.eval`` API reference +============================== + +.. contents:: + + +``solaris.eval.base`` Base evaluator class +---------------------------------------------- + +.. automodule:: solaris.eval.base + :members: + +``solaris.eval.pixel`` Pixel-wise scoring functions +--------------------------------------------------- + +.. automodule:: solaris.eval.pixel + :members: + + +``solaris.eval.iou`` IoU scoring functions +------------------------------------------ + +.. automodule:: solaris.eval.iou + :members: + +``solaris.eval.challenges`` SpaceNet Challenge scoring functionality +-------------------------------------------------------------------- + +.. automodule:: solaris.eval.challenges + :members: diff --git a/docs/api/index.rst b/docs/api/index.rst new file mode 100644 index 00000000..fea8972b --- /dev/null +++ b/docs/api/index.rst @@ -0,0 +1,32 @@ +.. title:: API reference contents + +################### +Solaris API summary +################### + +Complete submodule documentation +================================ +* `solaris.tile `_: Tiling functionality for imagery and vector labels +* `solaris.raster `_: Raster (imagery) coordinate management and formatting +* `solaris.vector `_: Vector (label) management and format interconversion +* `solaris.nets `_: Deep learning model ingestion, creation, training, and inference +* `solaris.eval `_: Deep learning model performance evaluation +* `solaris.utils `_: Utility functions for the above toolsets + + +Submodule summaries +=================== + +.. toctree:: + :maxdepth: 3 + + tile + raster + vector + nets + eval + utils + +CLI commands +============ +See `Tutorials <../tutorials/index.html>`_. diff --git a/docs/api/nets.rst b/docs/api/nets.rst new file mode 100644 index 00000000..9c1052e5 --- /dev/null +++ b/docs/api/nets.rst @@ -0,0 +1,76 @@ +.. title:: solaris.nets API reference + +``solaris.nets`` API reference +============================== + +.. contents:: + + +``solaris.nets.callbacks`` Keras-like callbacks +----------------------------------------------- + +.. automodule:: solaris.nets.callbacks + :members: + +.. automodule:: solaris.nets.torch_callbacks + :members: + +``solaris.nets.losses`` Loss functions for Geo CV model training +---------------------------------------------------------------- + +.. automodule:: solaris.nets.losses + :members: + +.. automodule:: solaris.nets._keras_losses + :members: + +.. automodule:: solaris.nets._torch_losses + :members: + +``solaris.nets.transform`` Augmentation pipeline prep for Geo imagery +--------------------------------------------------------------------- + +.. automodule:: solaris.nets.transform + :members: + +``solaris.nets.optimizers`` Model training optimizer management +--------------------------------------------------------------- + +.. automodule:: solaris.nets.optimizers + :members: + +``solaris.nets.model_io`` Model I/O and model weight management +--------------------------------------------------------------- + +.. automodule:: solaris.nets.model_io + :members: + +``solaris.nets.datagen`` Data generators for model training +----------------------------------------------------------- + +.. automodule:: solaris.nets.datagen + :members: + +``solaris.nets.metrics`` Metrics for evaluating model performance +----------------------------------------------------------------- + +.. automodule:: solaris.nets.metrics + :members: + +``solaris.nets.zoo`` Model definitions for geospatial image analysis +-------------------------------------------------------------------- + +.. automodule:: solaris.nets.zoo + :members: + +``solaris.nets.train`` Model training functionality +--------------------------------------------------- + +.. automodule:: solaris.nets.train + :members: + +``solaris.nets.infer`` Prediction with Geo CV models +---------------------------------------------------- + +.. automodule:: solaris.nets.infer + :members: diff --git a/docs/api/raster.rst b/docs/api/raster.rst new file mode 100644 index 00000000..c51e2ba6 --- /dev/null +++ b/docs/api/raster.rst @@ -0,0 +1,13 @@ +.. title:: solaris.raster API reference + +``solaris.raster`` API reference +================================ + +.. contents:: + + +``solaris.raster.image`` Image pre- and post-processing +------------------------------------------------------- + +.. automodule:: solaris.raster.image + :members: diff --git a/docs/api/tile.rst b/docs/api/tile.rst new file mode 100644 index 00000000..219679ad --- /dev/null +++ b/docs/api/tile.rst @@ -0,0 +1,19 @@ +.. title:: solaris.tile API reference + +``solaris.tile`` API reference +============================== + +.. contents:: + + +``solaris.tile.raster_tile`` Raster image tiling functionality +-------------------------------------------------------------- + +.. automodule:: solaris.tile.raster_tile + :members: + +``solaris.tile.vector_tile`` Vector tiling functionality +--------------------------------------------------------- + +.. automodule:: solaris.tile.vector_tile + :members: diff --git a/docs/api/utils.rst b/docs/api/utils.rst new file mode 100644 index 00000000..afc6e2b6 --- /dev/null +++ b/docs/api/utils.rst @@ -0,0 +1,42 @@ +.. title:: solaris.utils API reference + +``solaris.utils`` API reference +=============================== + +.. contents:: + +``solaris.utils.core`` Core utilities +------------------------------------- + +.. automodule:: solaris.utils.core + :members: + +``solaris.utils.config`` Configuration file utilities +----------------------------------------------------- + +.. automodule:: solaris.utils.config + :members: + +``solaris.utils.io`` Imagery and vector I/O utilities +----------------------------------------------------- + +.. automodule:: solaris.utils.io + :members: + +``solaris.utils.geo`` Geographic coordinate system management utilities +----------------------------------------------------------------------- + +.. automodule:: solaris.utils.geo + :members: + +``solaris.utils.tile`` Tiling utilities +--------------------------------------- + +.. automodule:: solaris.utils.tile + :members: + +``solaris.utils.raster`` Raster image and array management utilities +-------------------------------------------------------------------- + +.. automodule:: solaris.utils.raster + :members: diff --git a/docs/api/vector.rst b/docs/api/vector.rst new file mode 100644 index 00000000..a4ff2aba --- /dev/null +++ b/docs/api/vector.rst @@ -0,0 +1,24 @@ +.. title:: solaris.vector API reference + +``solaris.vector`` API reference +================================ + +.. contents:: + +``solaris.vector.polygon`` vector polygon management +---------------------------------------------------- + +.. automodule:: solaris.vector.polygon + :members: + +``solaris.vector.graph`` graph and road network analysis +-------------------------------------------------------- + +.. automodule:: solaris.vector.graph + :members: + +``solaris.vector.mask`` vector <-> training mask interconversion +---------------------------------------------------------------- + +.. automodule:: solaris.vector.mask + :members: diff --git a/docs/conf.py b/docs/conf.py index 54031232..b9f05384 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,9 +1,7 @@ -# -*- coding: utf-8 -*- -# # Configuration file for the Sphinx documentation builder. # -# This file does only contain a selection of the most common options. For a -# full list see the documentation: +# This file only contains a selection of the most common options. For a full +# list see the documentation: # http://www.sphinx-doc.org/en/master/config # -- Path setup -------------------------------------------------------------- @@ -15,43 +13,56 @@ import os import sys sys.path.insert(0, os.path.abspath('..')) - - +import sphinx_bootstrap_theme # -- Project information ----------------------------------------------------- -project = 'cw-eval' -copyright = '2018, CosmiQ Works' -author = 'David Lindenbaum and Nick Weir' +project = 'solaris' +copyright = '2019, CosmiQ Works' +author = 'CosmiQ Works' license = 'Apache 2.0' +import time +copyright = u'2018-{}, CosmiQ Works: an IQT Lab'.format(time.strftime("%Y")) -# The short X.Y version -version = '1.0' # The full version, including alpha/beta/rc tags -release = '1.0.0' - +release = '0.1.0' +version = '0.1.0' # -- 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.napoleon', 'sphinx.ext.autodoc', + 'sphinx.ext.napoleon', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', + 'sphinx.ext.autosummary', 'sphinx.ext.coverage', - 'sphinx.ext.mathjax', + 'sphinx.ext.imgmath', 'sphinx.ext.viewcode', 'sphinx.ext.githubpages', + # 'autoapi.extension', + 'sphinx.ext.autosectionlabel', 'nbsphinx' ] +# autoapi_type = 'python' +# autoapi_template_dir = '_templates/' +# autoapi_dirs = ['../solaris'] +# autoapi_options = ['members', 'undoc-members', 'special-members'] +# autoapi_ignore = ['*data*', +# '*bin*', +# '*migrations*'] +# autoapi_root = 'api' + +autodoc_mock_imports = ['shapely', 'fiona', 'pandas', 'geopandas', 'cv2', + 'numpy', 'gdal', 'tqdm', 'rtree', 'networkx', + 'rasterio', 'scipy', 'skimage', 'tensorflow', 'torch', + 'torchvision', 'yaml', 'affine', 'albumentations', + 'rio_tiler', 'PIL', 'matplotlib', 'rio_cogeo'] + # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -74,7 +85,7 @@ # 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 = ['_build', 'Thumbs.db', '.DS_Store', '**.ipynb_checkpoints'] +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = None @@ -85,18 +96,29 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'sphinx_rtd_theme' +html_theme = 'bootstrap' +html_theme_path = sphinx_bootstrap_theme.get_html_theme_path() + +html_theme_options = { + 'source_link_position': "footer", + 'bootswatch_theme': "paper", + 'bootstrap_version': "3", + 'navbar_links': [ + ("Tutorials", "tutorials/index"), + ("API", "api/index") + ], + + } -# 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 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'] +html_logo = 'solaris_logo_tiny_black.png' + +def setup(app): + app.add_stylesheet("custom_styles.css") # Custom sidebar templates, must be a dictionary that maps document names # to template names. @@ -106,42 +128,12 @@ # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', # 'searchbox.html']``. # -# html_sidebars = {} - +html_sidebars = {'**': ['localtoc.html', 'sourcelink.html', 'searchbox.html']} # -- Options for HTMLHelp output --------------------------------------------- # Output file base name for HTML help builder. -htmlhelp_basename = 'cw-evaldoc' - - -# -- 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, 'cw-eval.tex', 'cw-eval Documentation', - 'David Lindenbaum and Nick Weir', 'manual'), -] +htmlhelp_basename = 'solarisdoc' # -- Options for manual page output ------------------------------------------ @@ -149,7 +141,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, 'cw-eval', 'cw-eval Documentation', + (master_doc, 'solaris', 'solaris Documentation', [author], 1) ] @@ -160,10 +152,8 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'cw-eval', 'cw-eval Documentation', - author, 'cw-eval', - 'CosmiQ Works evaluation package for SpaceNet Challenges.', - 'Miscellaneous'), + (master_doc, 'solaris', 'solaris Documentation', + author, 'solaris'), ] @@ -196,10 +186,8 @@ "pandas": ('http://pandas.pydata.org/pandas-docs/stable/', None), "geopandas": ('http://geopandas.org/', None), "rtree": ('http://toblerity.org/rtree/', None), - "shapely": ('https://shapely.readthedocs.io/en/stable/', None) + "shapely": ('https://shapely.readthedocs.io/en/stable/', None), + 'numpy': ('http://docs.scipy.org/doc/numpy/', None), + 'scipy': ('http://docs.scipy.org/doc/scipy/reference/', None), + 'PyTorch': ('http://pytorch.org/docs/master/', None) } - -# -- Options for todo extension ---------------------------------------------- - -# If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = True diff --git a/docs/cookbook/Competition_Eval.ipynb b/docs/cookbook/Competition_Eval.ipynb deleted file mode 100644 index 0aab8f6f..00000000 --- a/docs/cookbook/Competition_Eval.ipynb +++ /dev/null @@ -1,761 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Score an entire competition (or a whole AOI) using `cw-eval`\n", - "This recipe describes how to run evaluation of a proposal CSV for an entire competition against a ground truth CSV.\n", - "\n", - "## Things to understand before starting\n", - "When we score entire competitions, we want to ensure that competitors provide submissions for the entire area of interest (AOI), not just the subset that competitors provide scores for, in case they leave out chips that they can't predict well. Therefore, proposal files scored using this pipeline should contain predictions for every chip in the ground truth CSV. The score outputs also provide chip-by-chip results which can be used to remove non-predicted chips if needed.\n", - "\n", - "When CosmiQ Works runs competitions in partnership with TopCoder, we set some cutoffs for scoring buildings:\n", - "\n", - "- An IoU score of > 0.5 is required to ID a building as correctly identified.\n", - "\n", - "- Ground truth buildings fewer than 20 pixels in extent are ignored. However, it is up to competitors to filter out their own small footprint predictions.\n", - "\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## Imports \n", - "\n", - "For this test case we will only need `cw_eval` installed - [Installation instructions for cw_eval](https://github.com/cosmiq/cw-eval/#installation-instructions)" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# imports\n", - "import os\n", - "import cw_eval\n", - "from cw_eval.challenge_eval.off_nadir_dataset import eval_off_nadir # runs eval\n", - "from cw_eval.data import data_dir # get the path to the sample eval data\n", - "import pandas as pd # just for visualizing the outputs in this recipe\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "---\n", - "\n", - "## Ground truth CSV format\n", - "\n", - "The following shows a sample ground truth CSV and the elements it must contain." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
ImageIdBuildingIdPolygonWKT_PixPolygonWKT_Geo
0Atlanta_nadir8_catid_10300100023BC100_743501_3...0POLYGON ((476.88 884.61, 485.59 877.64, 490.50...1
1Atlanta_nadir8_catid_10300100023BC100_743501_3...1POLYGON ((459.45 858.97, 467.41 853.09, 463.37...1
2Atlanta_nadir8_catid_10300100023BC100_743501_3...2POLYGON ((407.34 754.17, 434.90 780.55, 420.27...1
3Atlanta_nadir8_catid_10300100023BC100_743501_3...3POLYGON ((311.00 760.22, 318.38 746.78, 341.02...1
4Atlanta_nadir8_catid_10300100023BC100_743501_3...4POLYGON ((490.49 742.67, 509.81 731.14, 534.12...1
5Atlanta_nadir8_catid_10300100023BC100_743501_3...5POLYGON ((319.28 723.07, 339.97 698.22, 354.29...1
6Atlanta_nadir8_catid_10300100023BC100_743501_3...6POLYGON ((466.49 709.69, 484.26 696.45, 502.59...1
7Atlanta_nadir8_catid_10300100023BC100_743501_3...7POLYGON ((433.84 673.34, 443.90 663.96, 448.70...1
8Atlanta_nadir8_catid_10300100023BC100_743501_3...8POLYGON ((459.24 649.03, 467.38 641.90, 472.84...1
9Atlanta_nadir8_catid_10300100023BC100_743501_3...9POLYGON ((403.55 643.50, 416.98 630.51, 440.36...1
\n", - "
" - ], - "text/plain": [ - " ImageId BuildingId \\\n", - "0 Atlanta_nadir8_catid_10300100023BC100_743501_3... 0 \n", - "1 Atlanta_nadir8_catid_10300100023BC100_743501_3... 1 \n", - "2 Atlanta_nadir8_catid_10300100023BC100_743501_3... 2 \n", - "3 Atlanta_nadir8_catid_10300100023BC100_743501_3... 3 \n", - "4 Atlanta_nadir8_catid_10300100023BC100_743501_3... 4 \n", - "5 Atlanta_nadir8_catid_10300100023BC100_743501_3... 5 \n", - "6 Atlanta_nadir8_catid_10300100023BC100_743501_3... 6 \n", - "7 Atlanta_nadir8_catid_10300100023BC100_743501_3... 7 \n", - "8 Atlanta_nadir8_catid_10300100023BC100_743501_3... 8 \n", - "9 Atlanta_nadir8_catid_10300100023BC100_743501_3... 9 \n", - "\n", - " PolygonWKT_Pix PolygonWKT_Geo \n", - "0 POLYGON ((476.88 884.61, 485.59 877.64, 490.50... 1 \n", - "1 POLYGON ((459.45 858.97, 467.41 853.09, 463.37... 1 \n", - "2 POLYGON ((407.34 754.17, 434.90 780.55, 420.27... 1 \n", - "3 POLYGON ((311.00 760.22, 318.38 746.78, 341.02... 1 \n", - "4 POLYGON ((490.49 742.67, 509.81 731.14, 534.12... 1 \n", - "5 POLYGON ((319.28 723.07, 339.97 698.22, 354.29... 1 \n", - "6 POLYGON ((466.49 709.69, 484.26 696.45, 502.59... 1 \n", - "7 POLYGON ((433.84 673.34, 443.90 663.96, 448.70... 1 \n", - "8 POLYGON ((459.24 649.03, 467.38 641.90, 472.84... 1 \n", - "9 POLYGON ((403.55 643.50, 416.98 630.51, 440.36... 1 " - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ground_truth_path = os.path.join(data_dir, 'sample_truth_competition.csv')\n", - "\n", - "pd.read_csv(ground_truth_path).head(10)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "__Important points about the CSV format:__\n", - "\n", - "- The column denoting the chip ID for a given geospatial location must be titled `ImageId`.\n", - "\n", - "- The column containing geometries must be in [WKT format](https://en.wikipedia.org/wiki/Well-known_text) and should be titled `PolygonWKT_Pix`.\n", - "\n", - "- The `BuildingId` column provides a numeric identifier sequentially numbering each building _within each chip_. Order doesn't matter.\n", - "\n", - "- __For chips with no buildings__, a single row should be provided with `BuildingID=-1` and `PolygonWKT_Pix=\"POLYGON EMPTY\"`.\n", - "\n", - "## Proposal CSV format" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
ImageIdBuildingIdPolygonWKT_PixConfidence
0Atlanta_nadir8_catid_10300100023BC100_743501_3...0POLYGON ((0.00 712.83, 158.37 710.28, 160.59 6...1
1Atlanta_nadir8_catid_10300100023BC100_743501_3...1POLYGON ((665.82 0.00, 676.56 1.50, 591.36 603...1
2Atlanta_nadir8_catid_10300100023BC100_743501_3...0POLYGON ((182.62 324.15, 194.25 323.52, 197.97...1
3Atlanta_nadir8_catid_10300100023BC100_743501_3...1POLYGON ((92.99 96.94, 117.20 99.64, 114.72 12...1
4Atlanta_nadir8_catid_10300100023BC100_743501_3...2POLYGON ((0.82 29.96, 3.48 40.71, 2.80 51.00, ...1
5Atlanta_nadir8_catid_10300100023BC100_743501_3...0POLYGON ((476.88 884.61, 485.59 877.64, 490.50...1
6Atlanta_nadir8_catid_10300100023BC100_743501_3...1POLYGON ((459.45 858.97, 467.41 853.09, 463.37...1
7Atlanta_nadir8_catid_10300100023BC100_743501_3...2POLYGON ((407.34 754.17, 434.90 780.55, 420.27...1
8Atlanta_nadir8_catid_10300100023BC100_743501_3...3POLYGON ((311.00 760.22, 318.38 746.78, 341.02...1
9Atlanta_nadir8_catid_10300100023BC100_743501_3...4POLYGON ((490.49 742.67, 509.81 731.14, 534.12...1
\n", - "
" - ], - "text/plain": [ - " ImageId BuildingId \\\n", - "0 Atlanta_nadir8_catid_10300100023BC100_743501_3... 0 \n", - "1 Atlanta_nadir8_catid_10300100023BC100_743501_3... 1 \n", - "2 Atlanta_nadir8_catid_10300100023BC100_743501_3... 0 \n", - "3 Atlanta_nadir8_catid_10300100023BC100_743501_3... 1 \n", - "4 Atlanta_nadir8_catid_10300100023BC100_743501_3... 2 \n", - "5 Atlanta_nadir8_catid_10300100023BC100_743501_3... 0 \n", - "6 Atlanta_nadir8_catid_10300100023BC100_743501_3... 1 \n", - "7 Atlanta_nadir8_catid_10300100023BC100_743501_3... 2 \n", - "8 Atlanta_nadir8_catid_10300100023BC100_743501_3... 3 \n", - "9 Atlanta_nadir8_catid_10300100023BC100_743501_3... 4 \n", - "\n", - " PolygonWKT_Pix Confidence \n", - "0 POLYGON ((0.00 712.83, 158.37 710.28, 160.59 6... 1 \n", - "1 POLYGON ((665.82 0.00, 676.56 1.50, 591.36 603... 1 \n", - "2 POLYGON ((182.62 324.15, 194.25 323.52, 197.97... 1 \n", - "3 POLYGON ((92.99 96.94, 117.20 99.64, 114.72 12... 1 \n", - "4 POLYGON ((0.82 29.96, 3.48 40.71, 2.80 51.00, ... 1 \n", - "5 POLYGON ((476.88 884.61, 485.59 877.64, 490.50... 1 \n", - "6 POLYGON ((459.45 858.97, 467.41 853.09, 463.37... 1 \n", - "7 POLYGON ((407.34 754.17, 434.90 780.55, 420.27... 1 \n", - "8 POLYGON ((311.00 760.22, 318.38 746.78, 341.02... 1 \n", - "9 POLYGON ((490.49 742.67, 509.81 731.14, 534.12... 1 " - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "proposals_path = os.path.join(data_dir, 'sample_preds_competition.csv')\n", - "pd.read_csv(proposals_path).head(10)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The only difference between the ground truth CSV format and the prediction CSV format is the `Confidence` column, which can be used to provide prediction confidence for a polygon. Alternatively, it can be set to 1 for all polygons to indicate equal confidence.\n", - "\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## Running eval on the Off-Nadir challenge: Python API\n", - "\n", - "`cw-eval` currently contains code for scoring proposals from the [Off-Nadir Building Detection challenge](https://topcoder.com/spacenet). There are two ways to run scoring: using the [Python API](https://cw-eval.readthedocs.io/en/latest/api.html#cw_eval.challenge_eval.off_nadir_dataset.eval_off_nadir) or using the CLI (see later in this recipe). The below provides an example using the Python API.\n", - "\n", - "If you provide proposals and ground truth formatted as described earlier, no additional arguments are required unless you would like to alter the default scoring settings. If so, see the API docs linked above.\n", - "\n", - "The scoring function provides two outputs:\n", - "\n", - "- `results_DF`, a summary Pandas DataFrame with scores for the entire AOI split into the nadir/off-nadir/very off-nadir bins\n", - "\n", - "- `results_DF_Full`, a DataFrame with chip-by-chip score outputs for detailed analysis. For large AOIs this function takes a fair amount of time to run." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 33/33 [00:14<00:00, 2.11it/s]\n" - ] - } - ], - "source": [ - "results_DF, results_DF_Full = eval_off_nadir(proposals_path, ground_truth_path)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
F1ScoreFalseNegFalsePosPrecisionRecallTruePos
nadir-category
Nadir1.0001.01.02319
\n", - "
" - ], - "text/plain": [ - " F1Score FalseNeg FalsePos Precision Recall TruePos\n", - "nadir-category \n", - "Nadir 1.0 0 0 1.0 1.0 2319" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results_DF" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "(This ground truth dataset only contained nadir imagery, hence the absence of the other bins)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
F1ScoreFalseNegFalsePosPrecisionRecallTruePosimageIDiou_fieldnadir-category
01.0001.01.096Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
11.0001.01.03Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
21.0001.01.043Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
31.0001.01.067Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
41.0001.01.03Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
51.0001.01.091Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
61.0001.01.080Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
71.0001.01.096Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
81.0001.01.0112Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
91.0001.01.078Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
\n", - "
" - ], - "text/plain": [ - " F1Score FalseNeg FalsePos Precision Recall TruePos \\\n", - "0 1.0 0 0 1.0 1.0 96 \n", - "1 1.0 0 0 1.0 1.0 3 \n", - "2 1.0 0 0 1.0 1.0 43 \n", - "3 1.0 0 0 1.0 1.0 67 \n", - "4 1.0 0 0 1.0 1.0 3 \n", - "5 1.0 0 0 1.0 1.0 91 \n", - "6 1.0 0 0 1.0 1.0 80 \n", - "7 1.0 0 0 1.0 1.0 96 \n", - "8 1.0 0 0 1.0 1.0 112 \n", - "9 1.0 0 0 1.0 1.0 78 \n", - "\n", - " imageID iou_field nadir-category \n", - "0 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score Nadir \n", - "1 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score Nadir \n", - "2 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score Nadir \n", - "3 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score Nadir \n", - "4 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score Nadir \n", - "5 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score Nadir \n", - "6 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score Nadir \n", - "7 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score Nadir \n", - "8 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score Nadir \n", - "9 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score Nadir " - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "results_DF_Full.head(10)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "---\n", - "\n", - "# Running eval on the Off-Nadir Challenge using the CLI\n", - "\n", - "The `cw-eval` CLI allows competition scoring without even needing to open a Python shell. Its usage is as follows:\n", - "\n", - "```\n", - "\n", - "$ spacenet_eval --proposal_csv [proposal_csv_path] --truth_csv [truth_csv_path] --output_file [output_csv_path]\n", - "\n", - "```\n", - "\n", - "__Argument details:__\n", - "\n", - "- `--proposal_csv`, `-p`: Path to the proposal CSV. Required argument. See the API usage details above for CSV specifications.\n", - "\n", - "- `--truth_csv`, `-t`: Path to the ground truth CSV. Required argument. See the API usage details above for CSV specifications.\n", - "\n", - "- `--output_file`, `-o`: Path to save the output CSVs to. This script will produce two CSV outputs: `[output_file].csv`, which is the summary DataFrame described above, and `[output_file]_full.csv`, which contains the chip-by-chip scoring results.\n", - "\n", - "_Not implemented yet:_ The CLI also provides a `--challenge` command, which is not yet implemented, but will be available in future versions to enable scoring of other SpaceNet challenges.\n", - "\n", - "__Example:__" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " F1Score FalseNeg FalsePos Precision Recall TruePos\n", - "nadir-category \n", - "Nadir 1.0 0 0 1.0 1.0 2319\n", - "Writing summary results to result.csv\n", - "Writing full results to result_full.csv\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 33/33 [00:17<00:00, 1.16it/s]\n" - ] - } - ], - "source": [ - "%%bash -s \"$proposals_path\" \"$ground_truth_path\" # ignore this line - magic line to run bash shell command\n", - "spacenet_eval --proposal_csv $1 --truth_csv $2 --output_file results # argument values taken from magic line above" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python [conda env:spacenetutilities]", - "language": "python", - "name": "conda-env-spacenetutilities-py" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.7" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/cookbook/index.rst b/docs/cookbook/index.rst deleted file mode 100644 index 651672cf..00000000 --- a/docs/cookbook/index.rst +++ /dev/null @@ -1,9 +0,0 @@ -.. title:: cw-eval cookbook - -CosmiQ Works Evaluation Cookbook -================================ - -.. toctree:: - - Chip_Eval.ipynb - Competition_Eval.ipynb diff --git a/docs/index.rst b/docs/index.rst index 42493256..b7d95d8c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,23 +1,53 @@ -CosmiQ Works Evaluation (`cw-eval `__) Documentation -============================================================================================= -:Author: `CosmiQ Works `__ -:Release: |release| -:Copyright: 2018, CosmiQ Works -:License: This work is licensed under an `Apache 2.0 License`__. +| +| + +.. image:: _static/solaris_logo.png + :width: 500px + :align: center + :alt: solaris_logo + +############################################################################################################ +`solaris `__ |release| by `CosmiQ Works `__ +############################################################################################################ + +*************************************************************** +An open source machine learning pipeline for geospatial imagery +*************************************************************** -.. __: https://www.apache.org/licenses/LICENSE-2.0 .. toctree:: - :name: mastertoc - :maxdepth: 3 - :glob: + :name: mastertoc + :maxdepth: 3 + :glob: + :hidden: + + installation + intro + api/index + tutorials/index + - api - cookbook/index +========== -Indices and tables ------------------- +User Guide +========== +* `What is solaris? `_ +* `Installation `_ +* `Tutorials and recipes `_ +Reference +========= +* `API reference `_ + +Index +===== * :ref:`genindex` * :ref:`modindex` -* :ref:`search` + + +**License:** `Apache 2.0`__. + +.. __: https://github.com/CosmiQ/solaris/blob/master/LICENSE.txt + +Follow us at our blog `The DownlinQ `_ or +`on Twitter `_ for updates! diff --git a/docs/installation.rst b/docs/installation.rst new file mode 100644 index 00000000..5ba2c785 --- /dev/null +++ b/docs/installation.rst @@ -0,0 +1,91 @@ +###################### +Installing ``solaris`` +###################### + +There are several methods available for installing `solaris `_: + +* :ref:`conda-forge` **(recommended)** +* :ref:`github-install` +* :ref:`pip-only` *use at your own risk!* + +---------- + +Prerequisites +============= + +Regardless of installation method, you'll need Python version 3.6 or greater. +More details on installing Python can be found +`here `_. Additionally, if you +plan to use the SpaceNet dataset with ``solaris`` (it features prominently in +many of the tutorials), you'll need `a free Amazon Web Services account `_ +and the AWS CLI `installed `_ +and `configured `_. +If you're just going to work with your own data, you can skip these steps. + +-------------- + +.. _conda-forge: + +Installing from ``conda-forge`` +=============================== +**This is the recommended installation method.** + +If you have `anaconda`_ installed, +you can create a new ``conda`` environment and install ``solaris`` there with ease:: + + conda create solaris python=3.6 solaris -c conda-forge + +We recommend installing ``solaris`` in a new environment to avoid conflicts with +existing installations of packages (``GDAL`` incompatibility being the usual problem), +but installing ``solaris`` in an existing environment can work in some cases. + +---------- + +.. _github-install: + +Installing from GitHub using a ``conda`` environment and ``pip`` +================================================================ +If you wish to install a bleeding-edge version of ``solaris`` that isn't available +on conda-forge yet, you can install from GitHub. You'll need +`anaconda`_ for this installation as well. + +From a terminal, run:: + + git clone https://github.com/cosmiq/solaris.git + cd solaris + git checkout [branch_name] # for example, git checkout dev for bleeding-edge + +If you have access to a GPU where you're installing ``solaris``, use the following:: + + conda env create -f environment-gpu.yml + +If you don't have access to a GPU:: + + conda env create -f environment.yml + +Finally, run the last two lines (for installs both with or without GPU):: + + conda activate solaris + pip install . + +The above installation will create a new conda environment called ``solaris`` +containing your desired version of solaris and all of its dependencies. + +---------- + +.. _pip-only: + +Installing with only ``pip`` +============================ +*Use this method at your own risk!* + +If you have already installed the dependencies with underlying binaries that +don't install well with ``pip`` (i.e. ``GDAL`` and ``rtree``), you can easily +``pip install`` the rest:: + + pip install solaris + +Note that this will raise an error if you don't already have ``GDAL`` installed. + + +.. _anaconda: https://docs.anaconda.com/anaconda/install/ diff --git a/docs/intro.rst b/docs/intro.rst new file mode 100644 index 00000000..d595cafc --- /dev/null +++ b/docs/intro.rst @@ -0,0 +1,96 @@ + +############################## +An introduction to ``solaris`` +############################## + +-------------- + +What is ``solaris``? +==================== + +``solaris`` is a Python library with two main purposes: + +#. Run existing geospatial computer vision models on any overhead imagery with + a single line of code + +#. Accelerate research in the geospatial computer vision domain by providing + efficient implementations of common utility functions: + + * Imagery and vector-formatted label tiling + * Interconversion between geospatial and machine learning data formats + * Loss functions common in geospatial computer vision applications + * Standardized evaluation of model performance on geospatial analysis tasks + * *And more!* + +-------------- + +Why should I use ``solaris``? +============================= +Most geospatial machine learning researchers discover early that they need to +write custom code to massage their data into a machine learning-compatible +format. This poses three major problems: + +#. It is very challenging to evaluate models developed elsewhere or using different + data, precluding deployment of geospatial ML solutions. + +#. Researchers must have deep expertise in both GIS concepts and computer vision + to advance the field, meaning less research gets done, slowing progress. + +#. Every geospatial ML practitioner uses different data formats, + imagery normalization methods, and machine learning frameworks during algorithm + development. This makes comparison between models and application to new data + time-consuming, if not impossible. + +``solaris`` aims to overcome these obstacles by providing a single, centralized, +open source tool suite that can: + +#. Accommodate any geospatial imagery and label formats, + +#. prepare data for use in machine learning in a standardized fashion, + +#. train computer vision models and generate predictions on geospatial imagery + data using common deep learning frameworks, and + +#. score model performance using domain-relevant metrics in a reproducible + manner. + +-------------- + +How do I use ``solaris``? +========================= +After `installing solaris `_, there are two usage +modes: + +Command line: train or test models performance with a single command +-------------------------------------------------------------------- +``solaris`` will provide a command line interface (CLI) tool to run an entire +geospatial imagery analysis pipeline from raw, un-chipped imagery, through model +training (if applicable) and prediction, to vector-formatted outputs. If you +provide ground truth labels over your prediction area, ``solaris`` can generate +quality metrics for the predictions. See +`an introduction to the solaris CLI `_ for more. + + +Python API: Use ``solaris`` to accelerate model development +----------------------------------------------------------- +Alongside the simple CLI, all of ``solaris``'s functionality is accessible via +the Python API. The entirely open source codebase provides classes and functions +to: + +* Tile imagery and labels +* Convert geospatial raster and vector data to formats compatible with machine + learning frameworks +* Train deep learning models using PyTorch and Tensorflow (Keras) - more + frameworks coming soon! +* Generate predictions on any geospatial imagery using your own models or + existing pre-trained models from past `SpaceNet `_ + challenges +* Convert model outputs to geospatial raster or vector formats +* Score model performance using standardized, geospatial-specific metrics + +The ``solaris`` Python API documentation can be found `here `_, and +`we have provided tutorials for common use cases `_. +The open source codebase is available `on GitHub `_. + +Follow us at our blog `The DownlinQ `_ or +`on Twitter `_ for updates! diff --git a/docs/tutorials/cli_eval.rst b/docs/tutorials/cli_eval.rst new file mode 100644 index 00000000..8af957e0 --- /dev/null +++ b/docs/tutorials/cli_eval.rst @@ -0,0 +1,13 @@ +Evaluating prediction quality on SpaceNet data with the ``solaris`` CLI +======================================================================= + +*Coming soon!* + + +------------- + + +Follow us at our blog `The DownlinQ `_ or +`on Twitter `_ for updates! + +`Click here to view solaris on GitHub `_ diff --git a/docs/tutorials/index.rst b/docs/tutorials/index.rst new file mode 100644 index 00000000..a8e89a22 --- /dev/null +++ b/docs/tutorials/index.rst @@ -0,0 +1,83 @@ +############################## +Solaris Tutorials and Cookbook +############################## + +.. toctree:: + :maxdepth: 3 + :glob: + :hidden: + + cli* + notebooks/* + + +There are two different ways to use ``solaris``: + +* :ref:`The Command Line Interface` (Simple use with existing models) +* :ref:`The Python API` (Python users who wish to develop their own models) + +Here we provide a brief introduction to these two approaches, links to tutorials, +and usage recipes to complete common tasks. If there's a common use case not +covered here, `submit an issue on the GitHub repo to request its inclusion. `_ + +--------------- + +The command line interface +========================== +The command line interface (CLI) is the simplest way to use Solaris. Using the CLI, +you can run training and/or prediction on overhead imagery using `SpaceNet `_ models +without writing a single line of python code. + +After `installing Solaris <../installation.html>`_, you can run simple commands from a +terminal or command prompt for standard operations, from creating training masks +using vector labels to running an entire deep learning pipeline through +evaluating model performance. Instead of having to write code to help ``solaris`` +find your data, you just make basic edits to a configuration file template, then +``solaris`` does all the work to make your analysis pipeline fit together. Tutorials +on creating configuration files and running the CLI can be found below. + +* `Creating the .yml config file `_ +* `Creating reference files to help solaris find your imagery `_ +* `Creating training masks with the solaris CLI `_ +* `Running a full deep learning pipeline using the solaris CLI `_ +* `Evaluating prediction quality on SpaceNet data with the solaris CLI `_ + +If these relatively narrow use cases don't cover your needs, the ``solaris`` python +API can help! + +The Python API +============== +The ``solaris`` Python API provides every functionality needed to perform deep learning +analysis of overhead imagery data: + +* Customizable imagery and vector label tiling, with different size and coordinate system options. +* Training mask creation functions, with the option to create custom width edge masks, building footprint masks, road network masks, multi-class masks, and even masks which label narrow spaces between objects. +* All required deep learning functionality, from augmentation (including >3 channel imagery tools!) to data ingestion to model training and inference to evaluation during training. These functions are currently implemented with both PyTorch and TensorFlow backends. +* The ability to use pre-trained or freshly initialized `SpaceNet `_ models, as well as your own custom models +* Model performance evaluation tools for the SpaceNet IoU metric (APLS coming soon!) + +The `Python API Reference <../api/index.html>`_ provides full documentation of +everything described above and more. For usage examples to get you started, see +the tutorials below. + +* `Tiling imagery `_ +* `Creating training masks `_ +* `Training a SpaceNet model `_ +* `Inference with a pre-trained SpaceNet model `_ +* `Training a custom model `_ +* `Converting pixel masks to vector labels `_ +* `Scoring your model's performance with the solaris Python API `_ + + +Reference +========= +* `API reference <../api/index>`_ + +Index +===== +* :ref:`genindex` +* :ref:`modindex` + + +Check back here and `follow us on Twitter `_ or +on our blog, `The DownlinQ `_ for updates! diff --git a/docs/cookbook/Chip_Eval.ipynb b/docs/tutorials/notebooks/api_evaluation_tutorial.ipynb similarity index 87% rename from docs/cookbook/Chip_Eval.ipynb rename to docs/tutorials/notebooks/api_evaluation_tutorial.ipynb index 68df70ec..7ca7a23e 100644 --- a/docs/cookbook/Chip_Eval.ipynb +++ b/docs/tutorials/notebooks/api_evaluation_tutorial.ipynb @@ -4,9 +4,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Evaluate a proposal for one chip against ground truth\n", + "# Scoring model performance with the `solaris` python API\n", "\n", - "This recipe describes how to run evaluation of a proposal (CSV or .geojson) for a single chip against a ground truth (CSV or .geojson) for the same chip.\n", + "This tutorial describes how to run evaluation of a proposal (CSV or .geojson) for a single chip against a ground truth (CSV or .geojson) for the same chip.\n", "\n", "---" ] @@ -42,10 +42,9 @@ "source": [ "# imports\n", "import os\n", - "import cw_eval\n", - "from cw_eval.baseeval import EvalBase # class used for evaluation\n", - "from cw_eval.data import data_dir # get the path to the sample eval data\n", - "import pandas as pd # just for visualizing the outputs in this recipe\n" + "import solaris as sol\n", + "from solaris.data import data_dir\n", + "import pandas as pd # just for visualizing the outputs\n" ] }, { @@ -62,16 +61,16 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "EvalBase sample_truth.csv" + "Evaluator sample_truth.csv" ] }, - "execution_count": 2, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" } @@ -79,15 +78,15 @@ "source": [ "ground_truth_path = os.path.join(data_dir, 'sample_truth.csv')\n", "\n", - "eval_object = EvalBase(ground_truth_path)\n", - "eval_object" + "evaluator = sol.eval.base.Evaluator(ground_truth_path)\n", + "evaluator" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "At this point, `eval_object` has the following attributes:\n", + "At this point, `evaluator` has the following attributes:\n", "\n", "- `ground_truth_fname`: the filename corresponding to the ground truth data. This is simply `'GeoDataFrame'` if a GDF was passed during instantiation.\n", "\n", @@ -114,12 +113,12 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "proposals_path = os.path.join(data_dir, 'sample_preds.csv')\n", - "eval_object.load_proposal(proposals_path, proposalCSV=True, conf_field_list=[])" + "evaluator.load_proposal(proposals_path, proposalCSV=True, conf_field_list=[])" ] }, { @@ -156,14 +155,14 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "151it [00:01, 110.15it/s]\n" + "151it [00:00, 153.44it/s]\n" ] }, { @@ -179,13 +178,13 @@ " 'F1Score': 1.0}]" ] }, - "execution_count": 4, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "eval_object.eval_iou(calculate_class_scores=False)" + "evaluator.eval_iou(calculate_class_scores=False)" ] }, { @@ -208,14 +207,14 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "28it [00:00, 85.50it/s]\n" + "28it [00:00, 76.20it/s]\n" ] }, { @@ -231,7 +230,7 @@ " 'F1Score': 0.2857142857142857}]" ] }, - "execution_count": 5, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -240,9 +239,9 @@ "ground_truth_geojson = os.path.join(data_dir, 'gt.geojson')\n", "proposal_geojson = os.path.join(data_dir, 'pred.geojson')\n", "\n", - "eval_object = EvalBase(ground_truth_geojson)\n", - "eval_object.load_proposal(proposal_geojson, proposalCSV=False, conf_field_list=[])\n", - "eval_object.eval_iou(calculate_class_scores=False)" + "evaluator = sol.eval.base.Evaluator(ground_truth_geojson)\n", + "evaluator.load_proposal(proposal_geojson, proposalCSV=False, conf_field_list=[])\n", + "evaluator.eval_iou(calculate_class_scores=False)" ] }, { @@ -255,9 +254,9 @@ ], "metadata": { "kernelspec": { - "display_name": "Python [conda env:spacenetutilities]", + "display_name": "solaris", "language": "python", - "name": "conda-env-spacenetutilities-py" + "name": "solaris" }, "language_info": { "codemirror_mode": { diff --git a/docs/tutorials/notebooks/api_inference_spacenet.rst b/docs/tutorials/notebooks/api_inference_spacenet.rst new file mode 100644 index 00000000..4e620d84 --- /dev/null +++ b/docs/tutorials/notebooks/api_inference_spacenet.rst @@ -0,0 +1,14 @@ +Performing inference with a pre-trained model on SpaceNet data +============================================================== + +*Tutorial coming soon!* + +In the meantime, you can check out our `tutorial on running a model using the CLI `_ + +------------- + + +Follow us at our blog `The DownlinQ `_ or +`on Twitter `_ for updates! + +`Click here to view solaris on GitHub `_ diff --git a/docs/tutorials/notebooks/api_mask_to_vector.rst b/docs/tutorials/notebooks/api_mask_to_vector.rst new file mode 100644 index 00000000..0e74061b --- /dev/null +++ b/docs/tutorials/notebooks/api_mask_to_vector.rst @@ -0,0 +1,12 @@ +Creating vector labels from a predicted mask +============================================ + +*Tutorial coming soon!* + +------------- + + +Follow us at our blog `The DownlinQ `_ or +`on Twitter `_ for updates! + +`Click here to view solaris on GitHub `_ diff --git a/docs/tutorials/notebooks/api_masks_tutorial.ipynb b/docs/tutorials/notebooks/api_masks_tutorial.ipynb new file mode 100644 index 00000000..27bb340f --- /dev/null +++ b/docs/tutorials/notebooks/api_masks_tutorial.ipynb @@ -0,0 +1,421 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Creating training masks with the `solaris` python API\n", + "\n", + "You can create training masks from geojson-formatted labels with a single `solaris` command. Here, we'll go through creating masks using a sample image and geojson provided with `solaris`.\n", + "\n", + "Solaris enables creation of four types of masks:\n", + "\n", + "- [Polygon footprints](#polygon-footprints), for buildings or other objects\n", + "- [Polygon outlines](#polygon-edges), for outlines of buildings or other target objects\n", + "- [Polygon contact points](#polygon-contact-points), for places where polygons are closely apposed to one another (e.g. buildings in suburbs)\n", + "- [Road network masks](#road-network-masks), from linestring-formatted road networks\n", + "\n", + "The first three options here can also be combined to make multi-channel training targets, [as many of the SpaceNet 4 competitors did](https://medium.com/the-downlinq/a-deep-dive-into-the-spacenet-4-winning-algorithms-8d611a5dfe25).\n", + "\n", + "Let's start with creating building footprints." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Polygon footprints\n", + "\n", + "The [solaris.vector.mask.footprint_mask()](../../api/vector.rst#solaris.vector.mask.footprint_mask) function creates footprints from polygons, with 0s on the outside of the polygon and _burn\\_value_ on the outside. The function's arguments:\n", + "\n", + "- `df`: A `pandas.DataFrame` or `geopandas.GeoDataFrame` containing polygons in one column.\n", + "- `out_file`: An optional argument to specify a filepath to save outputs to.\n", + "- `reference_im`: A georegistered image covering the same geography as `df`. This is optional, but if it's not provided and you wish to convert the polygons from a geospatial CRS to pixel coordinates, you must provide `affine_obj`.\n", + "- `geom_col`: An optional argument specifying which column holds polygons in `df`. This defaults to `\"geometry\"`, the default geometry column in GeoDataFrames.\n", + "- `do_transform`: Should polygons be transformed from a geospatial CRS to pixel coordinates? `solaris` will try to infer whether or not this is necessary, but you can force behavior by providing `True` or `False` with this argument.\n", + "- `affine_obj`: An [affine](https://pypi.org/project/affine/) object specifying a transform to convert polygons from a geospatial CRS to pixel coordinates. This optional (unless `do_transform=True` and you don't provide `reference_im`), and is superceded by `reference_im` if that argument is also provided.\n", + "- `shape`: Optionally, the `(X, Y)` bounds of the output image. If `reference_im` is provided, the shape of that image supercedes this argument.\n", + "- `out_type`: Either `int` or `float`, the dtype of the output. Defaults to `int`.\n", + "- `burn_value`: The value that pixels falling within the polygon will be set to. Defaults to `255`.\n", + "- `burn_field`: Optionally, a column within `df` that specifies the value to set for each individual polygon. This can be used if you have multiple classes.\n", + "\n", + "Let's make a mask! We'll use `sample_geotiff.tif` from the sample data and `geotiff_labels.geojson` for polygons. First, we'll open those two objects up so you can see what we're working with." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import solaris as sol\n", + "from solaris.data import data_dir\n", + "import os\n", + "import skimage\n", + "import geopandas as gpd\n", + "from matplotlib import pyplot as plt\n", + "from shapely.ops import cascaded_union\n", + "\n", + "image = skimage.io.imread(os.path.join(data_dir, 'sample_geotiff.tif'))\n", + "f, axarr = plt.subplots(figsize=(10, 10))\n", + "plt.imshow(image, cmap='gray')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is a panchromatic image of an area in Atlanta. Can you pick out the buildings?" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gdf = gpd.read_file(os.path.join(data_dir, 'geotiff_labels.geojson'))\n", + "cascaded_union(gdf.geometry.values)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is a convenient visualization built into Jupyter notebooks, but what are the objects it's showing?" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "POLYGON ((733633.9175634939 3724917.327059259, 733644.0265766426 3724916.940842033, 733643.0617814267 3724892.157892002, 733632.9527424234 3724892.544111664, 733633.9175634939 3724917.327059259))\n", + "POLYGON ((733653.1326928001 3724949.324520395, 733648.855268121 3724922.585283833, 733642.8083045555 3724925.412150491, 733636.7858552651 3724926.852379398, 733638.0802132279 3724931.256500166, 733642.4909367257 3724931.19752501, 733643.0288657807 3724937.691814653, 733641.6848001335 3724942.564334524, 733641.2768538269 3724944.829464028, 733643.2673519406 3724949.61678283, 733653.1326928001 3724949.324520395))\n", + "POLYGON ((733614.6924076959 3725025.91770485, 733618.2329309537 3725025.759830723, 733618.3612589325 3725028.493043821, 733623.4073750104 3725028.260883178, 733623.2266384158 3725024.250133027, 733629.2206437828 3725023.974487836, 733628.4996830962 3725008.231178387, 733624.4670389239 3725005.347326851, 733613.7709592135 3725005.83023022, 733614.6924076959 3725025.91770485))\n" + ] + } + ], + "source": [ + "for geom in gdf.geometry[0:3]:\n", + " print(geom)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A bunch of Well-Known-Text (WKT)-formatted strings. Let's convert these into something a deep learning model can use." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "fp_mask = sol.vector.mask.footprint_mask(df=os.path.join(data_dir, 'geotiff_labels.geojson'),\n", + " reference_im=os.path.join(data_dir, 'sample_geotiff.tif'))\n", + "f, ax = plt.subplots(figsize=(10, 10))\n", + "plt.imshow(fp_mask, cmap='gray')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is a `numpy` array of shape `(900, 900)` with `0` at non-building pixels and `255` at building pixels." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Polygon outlines\n", + "\n", + "What if we want to find all of the edges of these buildings? In that case, we can use the [solaris.vector.mask.boundary_mask()](../../api/vector.rst#solaris.vector.mask.boundary_mask) function. There are a few arguments to this function:\n", + "\n", + "- `footprint_msk`: Instead of providing `df` as you did for [footprint_mask()](../../api/vector.rst#solaris.vector.mask.footprint_mask), for this function you can provide the footprint output. (Don't worry, if you didn't make one yet, you can still just provide the `df` argument and other [footprint_mask()](../../api/vector.rst#solaris.vector.mask.footprint_mask) arguments - `solaris` will create it behind the scenes)\n", + "- `out_file`: Same as earlier.\n", + "- `reference_im`: Same as earlier.\n", + "- `boundary_width`: The width, in pixel units, of the outline around the polygon. Defaults to `3`.\n", + "- `boundary_type`: Should the boundary be inside the polygon (`\"inner\"`, the default value) or outside (`\"outer\"`)?\n", + "- `burn_value`: Same as earlier, still defaults to `255`.\n", + "\n", + "You can also provide additional keyword arguments to pass to [footprint_mask()](../../api/vector.rst#solaris.vector.mask.footprint_mask) if that needs to be made first." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "b_mask = sol.vector.mask.boundary_mask(fp_mask, boundary_width=5)\n", + "f, ax = plt.subplots(figsize=(10, 10))\n", + "ax.imshow(b_mask, cmap='gray')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Some of the SpaceNet competitors have found this type of mask useful for helping separate nearby objects.\n", + "\n", + "## Polygon contact points\n", + "\n", + "What about training a model to specifically find places where buildings are near each other? This can be helpful for the same purpose as the edges. The [solaris.vector.mask.contact_mask()](../../api/vector.rst#solaris.vector.mask.contact_mask) function creates this.\n", + "\n", + "Its arguments:\n", + "\n", + "\n", + "- `contact_spacing`: An `int` specifying how close objects have to be for contact points to be labeled. Default value is 10.\n", + "- `meters`: A boolean argument indicating whether or not the `contact_spacing` argument is in meters. Defaults to `False`, in which case it's in pixels.\n", + "\n", + "This function also takes `df` (__required__), `out_file`, `reference_im`, `geom_col`, `do_transform`, `affine_obj`, `shape`, `out_type`, and `burn_value` as arguments, which have the same meanings as in [solaris.vector.mask.footprint_mask()](../../api/vector.rst#solaris.vector.mask.footprint_mask).\n", + "\n", + "Let's create some contact points!" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkcAAAJCCAYAAADKjmNEAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjAsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+17YcXAAAdEUlEQVR4nO3df4xlZ33f8c+33hiCk2KbpJaz69aOsEAoKsZZEaOgKIEktWmE/QeiRqlYIVf7T9pAEik1rdQKqX8UKYoDSmXVCklMlPIjBGoLRUlcg9SqEg67QMBgCEsCeFf+QcCYJkhJnDz9Y56FL8vi+bH3zp2583pJoznnuefOPI93Zv3ec86dqTFGAADY8I9WPQEAgL1EHAEANOIIAKARRwAAjTgCAGjEEQBAs5Q4qqobq+ozVXWqqm5fxucAAFiGWvTPOaqqi5L8WZKfSnI6yYeTvGaM8amFfiIAgCVYxpmjFyc5Ncb48zHG3yZ5Z5Kbl/B5AAAW7tASPubhJA+3/dNJfuTpnlBVfkw3ALDb/nKM8f3nDi4jjrakqo4nOb6qzw8AHHhfON/gMuLoTJKr2v6ROfYtxhh3JbkrceYIANg7lnHP0YeTXFtV11TVxUluTXLvEj4PAMDCLfzM0Rjjqar6t0n+KMlFSX5zjPHJRX8eAIBlWPhL+Xc0CZfVAIDdd3KMcfTcQT8hGwCgEUcAAI04AgBoxBEAQCOOAAAacQQA0IgjAIBGHAEANOIIAKARRwAAjTgCAGjEEQBAI44AABpxBADQiCMAgObQqicAsBVjjG/Zr6oVzQRYd+II2BfOxtDZSDo3ls53LMBOiCNgXzk3ks6nPyaUgO0SR8C+1KNnK6EkkoCtckM2sO9V1abx83QBBdCJI2BtODsELILLasBa2erlNoDvRBwBa8uZJGAnxBEAsKmD9CpQcQQAJNn6peh1DyVxBAAH2IXem7eOPy7Dq9UAABpxBAAH2Dqd8VkUl9UA4IDbyq/lebrnrRtxBAAkWd/Y2S6X1QAAGnEEANCIIwCARhwBADTiCACgEUcAAI04AgBoxBEAQCOOAAAacQQA0IgjAIBGHAEANOIIAKARRwAAjTgCAGjEEQBAI44AABpxBADQiCMAgEYcAQA04ggAoBFHAADNpnFUVb9ZVY9X1YNt7PKquq+qPjvfXzbHq6reWlWnqurjVXX9MicPALBoWzlz9NtJbjxn7PYk948xrk1y/9xPkpuSXDvfjie5czHTBADYHZvG0Rjjfyf5yjnDNye5e27fneSWNv72seFDSS6tqisXNVkAgGXb6T1HV4wxHpnbjya5Ym4fTvJwO+70HPs2VXW8qk5U1YkdzgEAYOEOXegHGGOMqho7eN5dSe5Kkp08HwBgGXZ65uixs5fL5vvH5/iZJFe1447MMYAdG8O/n4Dds9M4ujfJsbl9LMk9bfy181VrNyR5sl1+A9iRqlr1FIADZNPLalX1jiQ/nuT7qup0kv+c5L8meXdV3ZbkC0lePQ//gySvSHIqydeTvG4JcwYAWJraC6er3XMEAKzAyTHG0XMH/YRsAIBGHAEANOIIAKARRwAAjTgCAGjEEQBAI44AABpxBADQiCMAgEYcAQA04ggAoBFHAACNOAIAaMQRAEAjjgAAGnEEANCIIwCARhwBADTiCACgEUcAAI04AgBoxBEAQCOOAAAacQQA0IgjAIBGHAEANOIIAKA5tOoJAOtrjHFBz6+qBc0EYOucOQKW5kLjZoxxwYEFsF3iCACgEUfAUlXVBZ1BcmkN2G3iCACgEUfArtjuGaALPeMEsFNerQbsGrED7AfOHAEANOIIAKARRwAAjTgCAGjEEQBAI44AABpxBADQiCMAgEYcAQA04ggAoBFHAACNOAIAaMQRAEBzaNUTADgIxhhJkqr6xvZmqmqZUwK+A3EEsERbDaFOFMFquawGsCTnC6OdxBKwu5w5AtgjnDGCvWHTM0dVdVVVfbCqPlVVn6yq18/xy6vqvqr67Hx/2RyvqnprVZ2qqo9X1fXLXgTAfieMYO/YymW1p5L80hjjBUluSPJzVfWCJLcnuX+McW2S++d+ktyU5Nr5djzJnQufNQDAkmwaR2OMR8YYH5nb/y/JQ0kOJ7k5yd3zsLuT3DK3b07y9rHhQ0kuraorFz5zgH2oqs77Buwd27ohu6quTvKiJA8kuWKM8ch86NEkV8ztw0kebk87PcfO/VjHq+pEVZ3Y5pwB9gXhA/vTlm/IrqrvSfL7Sd4wxvha/4YfY4yq2tZLMMYYdyW5a35sL98A1pZAgv1lS2eOquq7shFGvzvGeO8cfuzs5bL5/vE5fibJVe3pR+YYAMCet5VXq1WStyV5aIzxq+2he5Mcm9vHktzTxl87X7V2Q5In2+U3AIA9rTb7gWRV9dIk/yfJJ5L8wxz+D9m47+jdSf5pki8kefUY4yszpn49yY1Jvp7kdWOMp72vyGU1AGAFTo4xjp47uGkc7QZxBACswHnjyK8PAQBoxBEAQCOOAAAav3gWWKhz72P0M36A/UYcAQvxnV7ccaEv+hBXwG5zWQ3Y08YYFxxYANshjgAAGnEELMQyf8mqS2vAbhJHwEItOmSEEbDb3JANLNx2gubp7icSRsAqiCNgpQQQsNe4rAYA0IgjAIBGHAEANOIIAKARRwAAjTgCAGjEEQBAI44AABpxBADQiCMAgEYcAQA04ggAoBFHAACNOAIAaA6tegIAB8kYY8fPraoFzgT4Tpw5AtglFxJGZ59/oR8D2Jw4AtgFogb2D3EEsGSLDCOX1mD53HMEsGQ9aNxzBHufOALYRQIH9j6X1QAAGnEEANCIIwCARhwBADTiCACgEUcAAI04AgBoxBEAQCOOAAAacQQA0IgjAIBGHAEANOIIAKARRwAAjTgC9oQxxqqnAJBEHAF7RFWtegoAScQRAMC3EEcAAI04AgBoxBEAQLNpHFXVM6vqT6rqT6vqk1X1pjl+TVU9UFWnqupdVXXxHH/G3D81H796uUsAAFicrZw5+pskLxtjvDDJdUlurKobkrw5yR1jjOcmeSLJbfP425I8McfvmMcBAOwLm8bR2PBXc/e75ttI8rIk75njdye5ZW7fPPczH395eY0uALBPbOmeo6q6qKo+luTxJPcl+VySr44xnpqHnE5yeG4fTvJwkszHn0zynEVOGgBgWbYUR2OMvx9jXJfkSJIXJ3n+hX7iqjpeVSeq6sSFfiwAgEXZ1qvVxhhfTfLBJC9JcmlVHZoPHUlyZm6fSXJVkszHn53ky+f5WHeNMY6OMY7ucO4AAAu3lVerfX9VXTq3vzvJTyV5KBuR9Kp52LEk98zte+d+5uMfGH5pEgCwTxza/JBcmeTuqrooGzH17jHG+6vqU0neWVX/JclHk7xtHv+2JL9TVaeSfCXJrUuYNwDAUtReOKlTVaufBABw0Jw83+09fkI2AEAjjgAAGnEEANCIIwCARhwBADTiCACgEUcAAI04AgBoxBEAQCOOAAAacQQA0IgjAIBGHAEANOIIAKARRwAAjTgCAGjEEQBAI44AABpxBADQiCMAgEYcAQA04ggAoBFHAACNOAIAaMQRAEAjjgAAGnEEANCIIwCARhwBADTiCACgEUcAAI04AgBoxBEAQCOOAAAacQQA0IgjAIDm0KonsE7GGDt6XlUteCYAwE45c7RAO42cMcaOwwoAWCxnjhasB9J2g+fs8c4kAcDqOHO0RFUldABgnxFHu+BsJG01lFxiA4DVcVltl50bSEIIAPYWZ45WzKU3ANhbnDnaIwQSkGycTfb3AayWM0cAAI04AgBoxBHAHuEFGrA3iCOAPcT9RrB64ggAoBFHAACNOAIAaMQRwB7hfiPYG7YcR1V1UVV9tKreP/evqaoHqupUVb2rqi6e48+Y+6fm41cvZ+oAAIu3nTNHr0/yUNt/c5I7xhjPTfJEktvm+G1Jnpjjd8zjAAD2hS3FUVUdSfIvk/zG3K8kL0vynnnI3Ulumds3z/3Mx19ezhUDAPvEVs8c/VqSX07yD3P/OUm+OsZ4au6fTnJ4bh9O8nCSzMefnMd/i6o6XlUnqurEDucOALBwm8ZRVf1MksfHGCcX+YnHGHeNMY6OMY4u8uMCAFyIQ1s45keTvLKqXpHkmUn+cZK3JLm0qg7Ns0NHkpyZx59JclWS01V1KMmzk3x54TMHAFiCTc8cjTHeOMY4Msa4OsmtST4wxvjZJB9M8qp52LEk98zte+d+5uMfGH5hEACwT1zIzzn690l+sapOZeOeorfN8bclec4c/8Ukt1/YFAEAdk/thZM6VbX6SQAAB83J89377CdkAwA04ggAoBFHAACNOAIAaMQRAEAjjgAAGnEEANCIIwCARhwBADTiCACgEUcAAI04AgBoxBEAQCOOAACaQ6uewH4zxtjysVW1xJkAAMvgzNE2VdWWo2c7IQUA7A3OHO3Q+QLpfDF0dsxZJADYH5w5WqDtnFUCAPYmcbQEIgkA9i9xtEQ9ktx/BAD7g3uOdoGzSACwfzhzBADQiCMAgEYcAQA04ggAoBFHAACNOAIAaMQRAEAjjgAAGnEEANCIIwCARhwBADTiCACgEUcAAI04AgBoxBEAQCOOAAAacQQA0IgjAIBGHAEANOIIAKARRwAAjTgCAGjEEQBAI44AABpxBADQiCMAgEYcAQA04ggAoBFHAADNoVVPYK8bY2x6TFV9x+P7YwDA3relM0dV9fmq+kRVfayqTsyxy6vqvqr67Hx/2RyvqnprVZ2qqo9X1fXLXMCyjDG2FEb92PMdv52PAwCs3nYuq/3EGOO6McbRuX97kvvHGNcmuX/uJ8lNSa6db8eT3Lmoye6WRceMs0cAsH9cyD1HNye5e27fneSWNv72seFDSS6tqisv4PMAAOyarcbRSPLHVXWyqo7PsSvGGI/M7UeTXDG3Dyd5uD339Bw7kJw1AoD9Zas3ZL90jHGmqv5Jkvuq6tP9wTHGqKptXYuakXV80wNXoKrOe2lts9BxMzYA7H9bOnM0xjgz3z+e5H1JXpzksbOXy+b7x+fhZ5Jc1Z5+ZI6d+zHvGmMcbfcw7SlV9W1v230OALD/bBpHVXVJVX3v2e0kP53kwST3Jjk2DzuW5J65fW+S185Xrd2Q5Ml2+Q0AYE/bymW1K5K8b54JOZTkf4wx/rCqPpzk3VV1W5IvJHn1PP4PkrwiyakkX0/yuoXPGgBgSWov/Aye7d6vBACwACfPd3uPXx8CANCIIwCARhwBADTiCACgEUcAAI04AgBoxBEAQCOOAAAacQQA0IgjAIBGHAEANOIIAKARRwAAjTgCAGjEEQBAI44AABpxBADQiCMAgEYcAQA04ggAoBFHAACNOAIAaMQRAEAjjgAAGnEEANCIIwCA5tCqJ7AfjDEu+GNU1QJmAgAsmzNHm1hEGJ39OIv6WADA8ogjAIBGHD2NZZzpcfYIAPY29xw9japaWMy45wgA9gdxtARCCAD2L3G0CaEDAAeLe44AABpxBADQiCMAgEYcAQA04ggAoBFHAACNOAIAaMQRAEAjjgAAGnEEANCIIwCARhwBADTiCACgEUcAAI04AgBoxBEAQCOOAAAacQQA0IgjAIBmS3FUVZdW1Xuq6tNV9VBVvaSqLq+q+6rqs/P9ZfPYqqq3VtWpqvp4VV2/3CUAACzOVs8cvSXJH44xnp/khUkeSnJ7kvvHGNcmuX/uJ8lNSa6db8eT3LnQGQMALNGmcVRVz07yY0neliRjjL8dY3w1yc1J7p6H3Z3klrl9c5K3jw0fSnJpVV258JkDACzBVs4cXZPkS0l+q6o+WlW/UVWXJLlijPHIPObRJFfM7cNJHm7PPz3HvkVVHa+qE1V1YufTBwBYrK3E0aEk1ye5c4zxoiR/nW9eQkuSjDFGkrGdTzzGuGuMcXSMcXQ7zwMAWKatxNHpJKfHGA/M/fdkI5YeO3u5bL5/fD5+JslV7flH5hgAwJ63aRyNMR5N8nBVPW8OvTzJp5Lcm+TYHDuW5J65fW+S185Xrd2Q5Ml2+Q0AYE87tMXj/l2S362qi5P8eZLXZSOs3l1VtyX5QpJXz2P/IMkrkpxK8vV5LADAvlAbtwuteBJVq58EAHDQnDzfvc9+QjYAQCOOAAAacQQA0IgjAIBGHAEANOIIAKARRwAAjTgCAGjEEQBAI44AABpxBADQiCMAgEYcAQA04ggAoBFHAACNOAIAaMQRAEAjjgAAGnEEANCIIwCARhwBADTiCACgEUcAAI04AgBoxBEAQCOOAAAacQQA0IgjAIBGHAEANOIIAKARRwAAjTgCAGjEEQBAI44AABpxBADQiCMAgEYcAQA04ggAoBFHAACNOAIAaMQRAEAjjgAAGnEEANCIIwCARhwBADTiCACgEUcAAI04AgBoxBEAQCOOAAAacQQA0GwaR1X1vKr6WHv7WlW9oaour6r7quqz8/1l8/iqqrdW1amq+nhVXb/8ZQAALMamcTTG+MwY47oxxnVJfjjJ15O8L8ntSe4fY1yb5P65nyQ3Jbl2vh1PcucyJg4AsAzbvaz28iSfG2N8IcnNSe6e43cnuWVu35zk7WPDh5JcWlVXLmS2AABLtt04ujXJO+b2FWOMR+b2o0mumNuHkzzcnnN6jn2LqjpeVSeq6sQ25wAAsDRbjqOqujjJK5P83rmPjTFGkrGdTzzGuGuMcXSMcXQ7zwMAWKbtnDm6KclHxhiPzf3Hzl4um+8fn+NnklzVnndkjgEA7HnbiaPX5JuX1JLk3iTH5vaxJPe08dfOV63dkOTJdvkNAGBPq40rYpscVHVJki8m+cExxpNz7DlJ3p3knyb5QpJXjzG+UlWV5NeT3JiNV7a9bozxtPcVVdW2LskBACzAyfPd3rOlOFo2cQQArMB548hPyAYAaMQRAEAjjgAAGnEEANCIIwCARhwBADTiCACgEUcAAI04AgBoxBEAQCOOAAAacQQA0IgjAIBGHAEANOIIAKARRwAAjTgCAGjEEQBAI44AABpxBADQiCMAgEYcAQA04ggAoBFHAACNOAIAaMQRAEAjjgAAGnEEANCIIwCARhwBADTiCACgEUcAAI04AgBoxBEAQCOOAAAacQQA0IgjAIBGHAEANOIIAKARRwAAjTgCAGjEEQBAI44AABpxBADQiCMAgEYcAQA04ggAoBFHAACNOAIAaMQRAEAjjgCATY0xMsZY9TR2xZbiqKp+oao+WVUPVtU7quqZVXVNVT1QVaeq6l1VdfE89hlz/9R8/OplLgAA2D0HIZA2jaOqOpzk55McHWP8UJKLktya5M1J7hhjPDfJE0lum0+5LckTc/yOeRwAsE8dhCDqtnpZ7VCS766qQ0meleSRJC9L8p75+N1JbpnbN8/9zMdfXlW1mOkCACzXpnE0xjiT5FeSfDEbUfRkkpNJvjrGeGoedjrJ4bl9OMnD87lPzeOfs9hpAwAsx1Yuq12WjbNB1yT5gSSXJLnxQj9xVR2vqhNVdeJCPxYAsHvW/TLbVi6r/WSSvxhjfGmM8XdJ3pvkR5NcOi+zJcmRJGfm9pkkVyXJfPzZSb587gcdY9w1xjg6xjh6gWsAAHbZOgfSVuLoi0luqKpnzXuHXp7kU0k+mORV85hjSe6Z2/fO/czHPzDW+b8gALBWaivdUlVvSvKvkjyV5KNJ/k027i16Z5LL59i/HmP8TVU9M8nvJHlRkq8kuXWM8eebfHzxBAB71HdqhTV4vdXJ813B2lIcLZs4AoC966DFkZ+QDQDQiCMA4GmtwRmibRFHAACNOAIAaMQRALBt63ypTRwBANuyzmGUiCMAYAvWPYg6cQQAbMlBCSRxBABs2UEIJHEEANCIIwCARhwBADTiCACgEUcAAI04AgBoxBEAQCOOAAAacQQA0IgjAIBGHAEANOIIAKARRwAAjTgCAGjEEQBAI44AABpxBADQiCMAgEYcAQA04ggAoBFHAACNOAIAaMQRAEAjjgAAGnEEANCIIwCARhwBADTiCACgEUcAAI04AgBoxBEAQCOOAAAacQQA0IgjAIBGHAEANOIIAKARRwAAjTgCAGjEEQBAI44AABpxBADQiCMAgEYcAQA04ggAoBFHAACNOAIAaA6tegLTXyX5zKonscu+L8lfrnoSu+igrTc5eGu23vV30NZ80NabHLw1/7PzDe6VOPrMGOPoqiexm6rqxEFa80Fbb3Lw1my96++grfmgrTc5mGs+H5fVAAAacQQA0OyVOLpr1RNYgYO25oO23uTgrdl6199BW/NBW29yMNf8bWqMseo5AADsGXvlzBEAwJ6w8jiqqhur6jNVdaqqbl/1fBahqn6zqh6vqgfb2OVVdV9VfXa+v2yOV1W9da7/41V1/epmvjNVdVVVfbCqPlVVn6yq18/xdV7zM6vqT6rqT+ea3zTHr6mqB+ba3lVVF8/xZ8z9U/Pxq1c5/52qqouq6qNV9f65v+7r/XxVfaKqPlZVJ+bYOn9dX1pV76mqT1fVQ1X1kjVf7/Pmn+3Zt69V1RvWfM2/MP/OerCq3jH/Llvr7+OdWGkcVdVFSf5bkpuSvCDJa6rqBauc04L8dpIbzxm7Pcn9Y4xrk9w/95ONtV87344nuXOX5rhITyX5pTHGC5LckOTn5p/jOq/5b5K8bIzxwiTXJbmxqm5I8uYkd4wxnpvkiSS3zeNvS/LEHL9jHrcfvT7JQ21/3debJD8xxriuvbx5nb+u35LkD8cYz0/ywmz8Wa/tescYn5l/ttcl+eEkX0/yvqzpmqvqcJKfT3J0jPFDSS5KcmsOxvfx9owxVvaW5CVJ/qjtvzHJG1c5pwWu7eokD7b9zyS5cm5fmY2f7ZQk/z3Ja8533H59S3JPkp86KGtO8qwkH0nyI9n44WmH5vg3vr6T/FGSl8ztQ/O4WvXct7nOI9n4H8XLkrw/Sa3zeufcP5/k+84ZW8uv6yTPTvIX5/45ret6z7P+n07yf9d5zUkOJ3k4yeXz+/L9Sf7Fun8f7+Rt1ZfVzv5BnXV6jq2jK8YYj8ztR5NcMbfX6r/BPO36oiQPZM3XPC8xfSzJ40nuS/K5JF8dYzw1D+nr+saa5+NPJnnO7s74gv1akl9O8g9z/zlZ7/UmyUjyx1V1sqqOz7F1/bq+JsmXkvzWvHT6G1V1SdZ3vee6Nck75vZarnmMcSbJryT5YpJHsvF9eTLr/328bauOowNpbGT42r1MsKq+J8nvJ3nDGONr/bF1XPMY4+/Hxun4I0lenOT5K57S0lTVzyR5fIxxctVz2WUvHWNcn43LKT9XVT/WH1yzr+tDSa5PcucY40VJ/jrfvJyUZO3W+w3zHptXJvm9cx9bpzXPe6duzkYI/0CSS/Ltt4CQ1cfRmSRXtf0jc2wdPVZVVybJfP/4HF+L/wZV9V3ZCKPfHWO8dw6v9ZrPGmN8NckHs3E6+tKqOvtrefq6vrHm+fizk3x5l6d6IX40ySur6vNJ3pmNS2tvyfquN8k3/qWdMcbj2bgX5cVZ36/r00lOjzEemPvvyUYsret6u5uSfGSM8djcX9c1/2SSvxhjfGmM8XdJ3puN7+21/j7eiVXH0YeTXDvvlL84G6c1713xnJbl3iTH5vaxbNyXc3b8tfNVEDckebKdzt0XqqqSvC3JQ2OMX20PrfOav7+qLp3b352Ne6weykYkvWoedu6az/63eFWSD8x/ke4LY4w3jjGOjDGuzsb36QfGGD+bNV1vklTVJVX1vWe3s3FPyoNZ06/rMcajSR6uqufNoZcn+VTWdL3neE2+eUktWd81fzHJDVX1rPn39tk/47X9Pt6xVd/0lOQVSf4sG/dr/MdVz2dBa3pHNq7n/l02/jV2Wzau096f5LNJ/leSy+exlY1X7H0uySey8SqCla9hm+t9aTZOO388ycfm2yvWfM3/PMlH55ofTPKf5vgPJvmTJKeycYr+GXP8mXP/1Hz8B1e9hgtY+48nef+6r3eu7U/n2yfP/v205l/X1yU5Mb+u/2eSy9Z5vXMdl2TjbMiz29jarjnJm5J8ev699TtJnrHO38c7ffMTsgEAmlVfVgMA2FPEEQBAI44AABpxBADQiCMAgEYcAQA04ggAoBFHAADN/wc5pT/0jV6PQQAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "c_mask = sol.vector.mask.contact_mask(df=os.path.join(data_dir, 'geotiff_labels.geojson'),\n", + " reference_im=os.path.join(data_dir, 'sample_geotiff.tif'),\n", + " contact_spacing=10, meters=True)\n", + "f, ax = plt.subplots(figsize=(10, 10))\n", + "ax.imshow(c_mask, cmap='gray')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, let's create a three-channel mask containing all three of these layers! The [solaris.vector.mask.df_to_px_mask()](../../api/vector.rst#solaris.vector.mask.df_to_px_mask) function takes all of the same arguments as above, plus `channels`, a list specifying which of the three types of masks should be included. We'll make one that has them all!" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "fbc_mask = sol.vector.mask.df_to_px_mask(df=os.path.join(data_dir, 'geotiff_labels.geojson'),\n", + " channels=['footprint', 'boundary', 'contact'],\n", + " reference_im=os.path.join(data_dir, 'sample_geotiff.tif'),\n", + " boundary_width=5, contact_spacing=10, meters=True)\n", + "f, ax = plt.subplots(figsize=(10, 10))\n", + "ax.imshow(fbc_mask)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "That mask looks ready to train a solid model!\n", + "\n", + "## Road network masks\n", + "\n", + "Finally, we can use linestrings to create a road network. Here we're going to use a different sample input image and geojson alongside the [solaris.vector.mask.road_mask()](../../api/vector.rst#solaris.vector.mask.road_mask). This function takes many of the same arguments as the earlier functions: `df` (__required__), `meters`, `out_file`, `reference_im`, `geom_col`, `do_transform`, `affine_obj`, `shape`, `out_type`, `burn_value`, and `burn_field`.\n", + "\n", + "This function also takes `width`, which specifies the width of the road network (in pixels unless `meters=True`)." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "road_mask = sol.vector.mask.road_mask(os.path.join(data_dir, 'sample_roads_for_masking.geojson'),\n", + " reference_im=os.path.join(data_dir, 'road_mask_input.tif'),\n", + " width=4, meters=True)\n", + "\n", + "f, ax = plt.subplots(figsize=(10, 10))\n", + "ax.imshow(road_mask, cmap='gray')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And there we go! Enjoy creating masks!! If you want to do this in batch without running python code, [the make_masks CLI function](cli_mask_creation.ipynb) allows you to do so, with the option of parallelizing some aspects." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "solaris", + "language": "python", + "name": "solaris" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/tutorials/notebooks/api_tiling_tutorial.ipynb b/docs/tutorials/notebooks/api_tiling_tutorial.ipynb new file mode 100644 index 00000000..7e4a56ee --- /dev/null +++ b/docs/tutorials/notebooks/api_tiling_tutorial.ipynb @@ -0,0 +1,230 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Tiling imagery and labels using the `solaris` Python API\n", + "\n", + "This tutorial will walk you through an example case of using the `solaris` Python API to tile one of the SpaceNet cities - in this case, Rio de Janeiro. We'll assume that you have already [installed solaris](https://solaris.readthedocs.io/en/master/installation.html).\n", + "\n", + "__First__, downloaded and extracted two files from the `spacenet-dataset` AWS S3 bucket:\n", + "\n", + "1. Imagery: https://s3.amazonaws.com/spacenet-dataset/AOI_1_Rio/srcData/rasterData/3-Band.tar.gz\n", + "2. Vector labels: https://s3.amazonaws.com/spacenet-dataset/AOI_1_Rio/vectorData/Rio_BuildingLabels.tar.gz\n", + "\n", + "Extract both of those tarballs to your working directory. The imagery tarball consists of several images - we're just going to tile one of those, `013022223133.tif`, and its associated building footprints.\n", + "\n", + "As you're getting started, your directory should have the following in it:\n", + "\n", + "- A directory named 3-band which contains the imagery files\n", + "- A directory named Rio_BuildingLabels which contains two files: Rio_Buildings_Public_AOI_v2.geojson and Rio_OUTLINE_Public_AOI.geojson (we only need the first of those two).\n", + "\n", + "Feel free to open up the imagery/vector labels in QGIS or another browser and explore to see what you're looking at." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Tiling the imagery\n", + "\n", + "For this working example we'll tile into 500-by-500-pixel chips beginning at the top left corner. Note that you can also tile based on the metric units covered by an image - for example, we could specify 250 meter-by-250 meter chips (which is the same size in this case). See the documentation for `sol.tile.raster_tile.RasterTiler()` for more details.\n", + "\n", + "Initialize the `RasterTiler` object:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Initializing Tiler...\n", + "Tiler initialized.\n", + "dest_dir: rio_chips\n", + "dest_crs will be inferred from source data.\n", + "src_tile_size: (500, 500)\n", + "tile size units metric: False\n" + ] + } + ], + "source": [ + "import solaris as sol\n", + "import os\n", + "\n", + "raster_tiler = sol.tile.raster_tile.RasterTiler(dest_dir='rio_chips', # the directory to save images to\n", + " src_tile_size=(500, 500), # the size of the output chips\n", + " verbose=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This object can be re-used with the same parameters for multiple images if desired. This way, you can tile multiple images collected over the same geography with the same settings. There are additional arguments that you can provide (for example, the destination coordinate reference system).\n", + "\n", + "To tile the imagery, pass the image file to the tiler's `tile()` method:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "0it [00:00, ?it/s]The following warnings were found:\n", + "- The file is greater than 512xH or 512xW, it is recommended to include internal overviews\n", + "\n", + "The following errors were found:\n", + "- The offset of the main IFD should be 8 for ClassicTIFF or 16 for BigTIFF. It is 93844024 instead\n", + "- The offset of the first block of the image should be after its IFD\n", + "2it [00:00, 15.22it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Beginning tiling...\n", + "Checking input data...\n", + "COG: False\n", + "[1, 2, 3]\n", + "Source CRS: EPSG:4326\n", + "Destination CRS: EPSG:4326\n", + "Inputs OK.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "1600it [02:02, 13.08it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Tiling complete. Cleaning up...\n", + "Done.\n" + ] + } + ], + "source": [ + "raster_tiler.tile('3-band/013022223133.tif')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This should throw a few warnings/errors about the input file, which you can ignore. You'll create 1600 files in your \"rio_chips\" subdirectory, one for each 500x500 tile. The filenames are in the format `[src-filename]\\_[longitude]\\_[latitude].tif`. Reprojection takes a while, so be patient.\n", + "\n", + "Once that process finishes, we'll use these auto-generated tile boundaries, which are stored in `raster_tiler`, to create vector tiles." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "These bounds are in the format `[left, bottom, right, top]` in the input file CRS. The following line prints the first set of bounds (there are 1600 in the list):" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(-43.681640625, -22.939453125, -43.67939668543198, -22.937209185431986)\n" + ] + } + ], + "source": [ + "print(raster_tiler.tile_bounds[0])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`raster_tiler.tile_bounds` is passed as an argument into the `VectorTiler` instance." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "0it [00:00, ?it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Preparing the tiler...\n", + "Initialization done.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "1600it [02:28, 10.77it/s]\n" + ] + } + ], + "source": [ + "vector_tiler = sol.tile.vector_tile.VectorTiler(dest_dir='rio_labels',\n", + " verbose=True)\n", + "vector_tiler.tile('Rio_BuildingLabels/Rio_Buildings_Public_AOI_v2.geojson',\n", + " tile_bounds=raster_tiler.tile_bounds)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "...And you're done! Simple as that. For more details, check out the [tiling API docs](https://solaris.readthedocs.io/en/master/api.html)." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "solaris", + "language": "python", + "name": "solaris" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/tutorials/notebooks/api_training_custom.rst b/docs/tutorials/notebooks/api_training_custom.rst new file mode 100644 index 00000000..dbee7221 --- /dev/null +++ b/docs/tutorials/notebooks/api_training_custom.rst @@ -0,0 +1,14 @@ +Training a custom model with the `solaris` python API +===================================================== + +*Tutorial coming soon!* + +In the meantime, you can check out our `tutorial on running a model using the CLI `_ + +------------- + + +Follow us at our blog `The DownlinQ `_ or +`on Twitter `_ for updates! + +`Click here to view solaris on GitHub `_ diff --git a/docs/tutorials/notebooks/api_training_spacenet.rst b/docs/tutorials/notebooks/api_training_spacenet.rst new file mode 100644 index 00000000..a0619206 --- /dev/null +++ b/docs/tutorials/notebooks/api_training_spacenet.rst @@ -0,0 +1,14 @@ +Training a model with SpaceNet data +=================================== + +*Tutorial coming soon!* + +In the meantime, you can check out our `tutorial on running a model using the CLI `_ + +------------- + + +Follow us at our blog `The DownlinQ `_ or +`on Twitter `_ for updates! + +`Click here to view solaris on GitHub `_ diff --git a/docs/tutorials/notebooks/cli_evaluation_tutorial.rst b/docs/tutorials/notebooks/cli_evaluation_tutorial.rst new file mode 100644 index 00000000..43a589da --- /dev/null +++ b/docs/tutorials/notebooks/cli_evaluation_tutorial.rst @@ -0,0 +1,14 @@ +Evaluating model performance with the `solaris` CLI +=================================================== + +*Coming soon!* + +In the meantime, you can check out our `tutorial on evaluating model performance using the Python API `_ + +------------- + + +Follow us at our blog `The DownlinQ `_ or +`on Twitter `_ for updates! + +`Click here to view solaris on GitHub `_ diff --git a/docs/tutorials/notebooks/cli_mask_creation.ipynb b/docs/tutorials/notebooks/cli_mask_creation.ipynb new file mode 100644 index 00000000..bbb817d2 --- /dev/null +++ b/docs/tutorials/notebooks/cli_mask_creation.ipynb @@ -0,0 +1,119 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Using the `solaris` CLI to make training masks\n", + "\n", + "Geospatial data labels are rarely in the form of pixel masks; however, such masks are essential for training neural networks for segmentation tasks. We've provided functions and a CLI to standardize training mask creation so that users can convert their geospatial-format vector labels into ML-compatible training masks. If you'd prefer the Python API implementation, [there's a tutorial here](api_masks_tutorial.ipynb).\n", + "\n", + "There are two ways to create these masks with the CLI: one file at a time, or in batch based on a reference file. Each is described below. We'll start with describing the simple single-file mask creation case, then describe how to complete batch processing. If you have any questions about what the different types of masks look like, [check out the Python API tutorial for mask creation](api_masks_tutorial.ipynb).\n", + "\n", + "## Single mask creation with the CLI\n", + "\n", + "Once you have [installed solaris](../../installation.html), you will have access to the `make_masks` command in your command line prompt. This command has a number of possible arguments to control mask creation, described below. If you need a refresher on these within your command line, you can always run `make_masks -h` for usage instructions.\n", + "\n", + "### `make_masks` arguments\n", + "\n", + "- __--source\\_file__, __-s__: \\[str\\] The full path to a vector file file to create a mask from.\n", + "- __--reference\\_image__, __-r__, \\[str\\] The full path to a georegistered image in the same coordinate system (for conversion to pixels) or in the target coordinate system (for conversion to a geographic coordinate reference system)\n", + "- __--output\\_path__, __-o__: \\[str\\] The full path to the output file for the generated mask image.\n", + "- __--geometry\\_column__, __-g__: \\[str\\] (default: `'geometry'`) The column containing footprint polygons to transform.\n", + "- __--transform__, __-t__: Use this flag if the geometries are in a georeferenced coordinate system and need to be converted to pixel coordinates.\n", + "- __--value__, __-v__: \\[int\\] (default: `255`) The value to set for labeled pixels in the mask.\n", + "- __--footprint__, __-f__: If this flag is set, the mask will include filled-in building footprints as a channel.\n", + "- __--edge__, __-e__: If this flag is set, the mask will include the building edges as a channel.\n", + "- __--edge\\_width__, __-ew__: \\[int\\] (default: `3`) The pixel thickness of the edges in the edge mask. Only has an effect if __--edge__ or __-e__ is used.\n", + "- __--edge\\_type__, __-et__: \\[str\\] (default: `inner`) Type of edge: either `'inner'` or `'outer'`. Only has an effect if __--edge__ or __-e__ is used.\n", + "- __--contact__, __-c__: If this flag is set, the mask will include contact points between buildings as a channel.\n", + "- __--contact\\_spacing__, __-cs__: \\[int\\] (default: `10`) Sets the maximum distance between two buildings, in pixel units unless __--metric_widths__ is provided, that will be identified as a contact. Only has an effect if __--contact__ or __-c__ is used.\n", + "- __--metric\\_widths__, __-m__: Use this flag if any widths should be in metric units instead of pixel units.\n", + "- __--batch__, __-b__: Use this flag if you wish to operate on multiple files in batch. In this case, __--argument\\_csv__ must be provided. See the batch processing section below for more details.\n", + "- __--argument\\_csv__, __-a__: \\[str\\] The reference file for variable values for batch processing. It must contain columns to pass the source_file and reference_image arguments, and can additionally contain columns providing other arguments if you wish to define them differently for items in the batch. Only has an effect if the __--batch__ or __-b__ arguments are used. These columns must have the same names as the corresponding arguments. See the next section for more details on batch processing.\n", + "- __--workers__, __-w__: \\[int\\] (default: `1`) The number of parallel processing workers to use for batch processing. This should not exceed the number of CPU cores available. See the next section for more details on batch processing.\n", + "\n", + "### `make_masks` CLI usage examples\n", + "\n", + "Assume you have fies for a GeoTIFF, `image.tif`, and georegistered building footprint labels, `building_labels.geojson`:\n", + "\n", + "_Creating building footprint labels:_\n", + "\n", + "```console\n", + "$ make_masks --source_file building_labels.geojson --reference_image image.tif --footprint --transform\n", + "```\n", + "\n", + "Let's change the burn value to 1 for the footprints instead of 255:\n", + "\n", + "```console\n", + "$ make_masks --source_file building_labels.geojson --reference_image image.tif --footprint --transform --value 1\n", + "```\n", + "\n", + "\n", + "What if your building labels are already in pixel coordinates in a CSV named `building_labels.csv`, and the geometries are in a column named `WKT_Pix`?\n", + "\n", + "```console\n", + "$ make_masks --source_file building_labels.csv --reference_image image.tif --footprint --geometry_column WKT_Pix\n", + "```\n", + "\n", + "What if you have the same CSV file as above, but instead of making just building footprints, you want outer borders of width 10 and also contact points for anything within 10 meters?\n", + "\n", + "```console\n", + "$ make_masks --source_file building_labels.csv --reference_image image.tif --geometry_column WKT_Pix --footprint --edge --edge-type outer --edge-width 10 --contact --contact_spacing 10 --metric_widths \n", + "```\n", + "\n", + "## Batch mask creation using the `solaris` CLI\n", + "\n", + "There's one additional requirement for batch mask creation: a CSV specifying the location of the label files, the reference images, and optionally any other arguments that you wish to modify on a mask-by-mask basis.\n", + "\n", + "### Creating the argument CSV\n", + "\n", + "The reference CSV has three required columns, which must be named __exactly__ as below:\n", + "\n", + "- __source_file__: the paths to vector-formatted label files that you wish to transform to masks.\n", + "- __reference_image__: The paths to images that correspond to the same geographies as the vector labels that you're using.\n", + "- __output_path__: The paths to save the output masks to.\n", + "The values in these two columns must be matching geographies across the row, or you'll get empty masks! For both cases, we recommend using the absolute path to the files in each column rather than a relative path for consistency and clarity.\n", + "\n", + "If you wish to use different values for the other arguments to `make_masks` (e.g., if you wish to have different burn values for different masks), you can provide those values in the CSV as well. Just create a column with the same name as the argument that you're replacing, and make sure to provide a value for every row.\n", + "\n", + "### `make_masks` CLI batch processing examples\n", + "\n", + "Assume you have a CSV `mask_reference.csv` that specifies the path to your .geojson labels, matching reference images, and where you want those files saved, as described in the last section.\n", + "\n", + "Let's create footprint masks:\n", + "\n", + "```console\n", + "$ make_masks --batch --argument_csv mask_reference.csv --footprint\n", + "```\n", + "\n", + "What if you have a _lot_ of masks to make and you want to parallelize over four CPUs? (Make sure you have access to four CPU cores first!)\n", + "\n", + "```console\n", + "$ make_masks --batch --argument_csv mask_reference.csv --footprint --workers 4\n", + "```" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "solaris", + "language": "python", + "name": "solaris" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/tutorials/notebooks/cli_ml_pipeline.ipynb b/docs/tutorials/notebooks/cli_ml_pipeline.ipynb new file mode 100644 index 00000000..1300d57d --- /dev/null +++ b/docs/tutorials/notebooks/cli_ml_pipeline.ipynb @@ -0,0 +1,46 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Running a deep learning pipeline with the `solaris` CLI\n", + "\n", + "Running a full ML pipeline with `solaris` only takes a single line of code! After [installing solaris](../../installation.rst), all you need to do is [set up your YAML config file](creating_the_yaml_config_file.ipynb). Then, run the following line from a command prompt:\n", + "\n", + "```console\n", + "$ solaris_run_ml [path_to_your_config]\n", + "```\n", + "\n", + "You can try it now! [Download the config file for XD_XD's SpaceNet 4 Model](https://github.com/CosmiQ/solaris/blob/dev/solaris/nets/configs/xdxd_spacenet4.yml), point it to your data, and then run:\n", + "\n", + "```console\n", + "$ solaris_run_ml xdxd_spacenet4.yml\n", + "```\n", + "\n", + "That's all there is to it!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "solaris", + "language": "python", + "name": "solaris" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/tutorials/notebooks/creating_im_reference_csvs.ipynb b/docs/tutorials/notebooks/creating_im_reference_csvs.ipynb new file mode 100644 index 00000000..579fa530 --- /dev/null +++ b/docs/tutorials/notebooks/creating_im_reference_csvs.ipynb @@ -0,0 +1,64 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Creating reference CSVs for model training and inference\n", + "\n", + "When you train models with `solaris`, it uses reference CSV files to find images and matching labels. Let's go through what those are and what they should include. You'll create (up to) three different reference files:\n", + "\n", + "- [Training data](#Training-Data-CSV): Required for Training\n", + "- [Epoch-wise validation data](#Validation-Data-CSV): Optional\n", + "- [Inference data](#Inference-Data-CSV): Required for inference\n", + "- [Using these files](#Using-these-files)\n", + "\n", + "## Training Data CSV\n", + "\n", + "Your training data CSV must have two columns with the __exact__ names below:\n", + "\n", + "- __image__: The `image` column defines the paths to each image file to be used during training, one path per row. You can use either the absolute path to the file or the path relative to the path that you run code in - we recommend using the absolute path for consistency.\n", + "- __label__: The `label` column defines the paths to the label (mask) files. If you need to create masks first, [check out the Python API tutorial](api_masks_tutorial.ipynb) or the [CLI tutorial](../cli_masks.html).\n", + "\n", + "__The image and label in each row must match!__ This is how `solaris` matches your training images to the expected outputs.\n", + "\n", + "If you choose to have `solaris` split validation data out for you, it will randomly select a fraction of the rows for validation. The fraction used for validation is defined in the config YAML file - for more on how to do so, [see the YAML config reference](creating_the_yaml_config_file.ipynb).\n", + "\n", + "For more control over what data is used for training vs. validation, you can create a separate validation CSV.\n", + "\n", + "## Validation Data CSV\n", + "\n", + "This CSV is the same as the Training Data CSV, but specifies images and masks to be used for epoch-wise validation. Make sure there's no overlap between your training and validation sets - you don't want any data leaks! If you want `solaris` to split the validation data out of the training data automatically, you don't need to provide\n", + "\n", + "## Inference Data CSV\n", + "\n", + "This reference file points to the image files that you wish to make predictions on. It therefore only needs to contain one column: __image__.\n", + "\n", + "## Using these files\n", + "\n", + "Once you have made these labels, provide the paths to them [in your configuration file](creating_the_yaml_config_file.ipynb); they'll automatically be loaded into your config when you call [solaris.utils.config.parse()](../../api/utils.rst#solaris.utils.config.parse)." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "solaris", + "language": "python", + "name": "solaris" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/tutorials/notebooks/creating_the_yaml_config_file.ipynb b/docs/tutorials/notebooks/creating_the_yaml_config_file.ipynb new file mode 100644 index 00000000..e1e32f12 --- /dev/null +++ b/docs/tutorials/notebooks/creating_the_yaml_config_file.ipynb @@ -0,0 +1,116 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Creating the YAML Configuration File\n", + "\n", + "`solaris` uses a YAML-formatted config file to specify all of the parameters required for data pre-processing, model training, inference, and more. Here, we'll go through every single element of that file and what it means, as well as how to create your own or modify an existing file for your use.\n", + "\n", + "## Helpful Resources\n", + "\n", + "- [Link to the YAML configuration skeleton](https://github.com/CosmiQ/solaris/blob/dev/solaris/nets/configs/config_skeleton.yml)\n", + "- [Link to a sample YAML config file for XD_XD's SpaceNet 4 solution model](https://github.com/CosmiQ/solaris/blob/dev/solaris/nets/configs/xdxd_spacenet4.yml)\n", + "\n", + "## The elements of the Config file\n", + "\n", + "#### Top-level arguments\n", + "\n", + "- __model\\_name:__ \\[str\\] The name of the model being used. This will be cross-referenced against a list of possible options provided by `solaris`, and if it's not in that list, the user will be expected to provide the model. _Note_: currently, using user-provided models requires use of the Python API.\n", + "- __model\\_src\\_path__: \\[str\\] Leave this blank unless you're using a custom model not native to solaris. solaris will automatically find your model.\n", + "- __train__: \\[bool\\] Should `solaris` to execute model training?\n", + "- __infer__: \\[bool\\] Should `solaris` to execute model inference?\n", + "- __pretrained__: \\[bool\\] Do you wish to use pretrained weights with the model? This must be `true` if `train` is `false`.\n", + "- __nn\\_framework__: \\[str\\] Which neural network framework are you using? This should either be `\"torch\"` or `\"keras\"` (more to be added later!)\n", + "- __batch\\_size__: \\[int\\] What's the batch size for model training/inference?\n", + "\n", + "#### Data specs\n", + "- __width:__ \\[int\\] The pixel width of the model inputs.\n", + "- __height:__ \\[int\\] The pixel height of the model inputs.\n", + "- __image_type:__ \\[str\\] One of `\"normalized\"` (0-1 range), `\"zscore\"`, `\"8bit\"`, `\"16bit\"`. The data type that the model ingests.\n", + "- __rescale:__ \\[bool\\] Should image values be rescaled prior to post-processing?\n", + "- __rescale\\_minima:__ \\[str or list\\] Either `\"auto\"` (in which case Solaris automatically determines this) or a value or list of values to set the minimum to.\n", + "- __rescale\\_maxima:__ \\[str or list\\] Either `\"auto\"` (in which case Solaris automatically determines this) or a value or list of values to set the maximum to.\n", + "- __channels:__ \\[int\\] The number of channels in the input.\n", + "- __label\\_type:__ \\[str\\] currently the only possible value to this argument is `\"mask\"`.\n", + "- __is\\_categorical:__ \\[bool\\] Currently this argument has no effect. When classification/object detection is added, it will be implemented.\n", + "- __mask\\_channels:__ \\[int\\] The number of channels in the training mask to be used as a training target.\n", + "- __val\\_holdout\\_frac:__ \\[float\\] The fraction of the training data to hold out for validation. Note that this argument has no effect if __validation\\_data\\_csv__ (below) is specified. Otherwise, the a random subset of the samples in the training CSV will be held back for end-of-epoch validation.\n", + "- __data\\_workers:__ \\[int\\] This argument is currently unused.\n", + "\n", + "#### Data reference files\n", + "\n", + "See [Creating reference files to help solaris find your imagery](../cli_im_ref) for details on what these files must include.\n", + "\n", + "- __training\\_data\\_csv:__ \\[str\\] The path to the training data CSV. See the link above for more details.\n", + "- __validation\\_data\\_csv:__ \\[str\\] The path to the validation data CSV. See the link above for more details. If you are splitting your training data for validation using __val\\_holdout\\_frac__, ignore this argument.\n", + "- __inference\\_data\\_csv:__ \\[str\\] The path to the inference data CSV. See the link above for more details.\n", + "\n", + "#### Training augmentation\n", + "\n", + "Augmentation is critical in training many models, particularly for geospatial data. If you perform data normalization during your augmentation pipeline, you can also specify that here. See [XD_XD's SpaceNet 4 augmentation pipeline](https://github.com/CosmiQ/solaris/blob/dev/solaris/nets/configs/xdxd_spacenet4.yml) for an example of a pipeline.\n", + "\n", + "- __augmentations:__ \\[dict\\] The augmentations to run. The majority of augmentations implemented in [albumentations](https://albumentations.readthedocs.io/) are available here, either using that implementation or a custom version to enable >3-channel imagery ingestion. Pass the name of the augmentation as keys in this dictionary, and `kwarg: value` pairs as sub-dicts. See the sample linked above if this is unclear.\n", + "- __p:__ \\[float\\] The probability that the augmentation pipeline will be applied to images in a batch.\n", + "- __shuffle:__ \\[bool\\] Should the order of training images be shuffled as their fed into the model? Defaults to `true`.\n", + "\n", + "#### Validation augmentation\n", + "\n", + "The same arguments are valid here as for `training_augmentation`.\n", + "\n", + "#### Inference augmentation\n", + "\n", + "The same arguments are valid here as for `training_augmentation`.\n", + "\n", + "#### Training\n", + "\n", + "This set of parameters define the actual training process.\n", + "\n", + "- __epochs:__ \\[int\\] The number of epochs to train for.\n", + "- __steps\\_per\\_epoch:__ \\[int\\] The number of batches to train for in each epoch. This is determined automatically if not provided.\n", + "- __optimizer:__ \\[str\\] The name of the optimizer to use for training. Options are `\"Adam\"`, `\"SGD\"`, `\"adadelta\"`, `\"RMSProp\"`, `\"Adamax\"`, `\"Nadam\"` (Keras only), `\"Adagrad\"` (Keras only), `\"SparseAdam\"` (Torch only), or `\"ASGD\"` (Torch only). Pass arguments for these optimizers to __opt\\_args__ (see below).\n", + "- __lr:__ \\[float\\] The learning rate to use (at least at the start of the training process).\n", + "- __opt\\_args:__ \\[dict\\] A dictionary of `kwarg: value` pairs to pass to the optimizer.\n", + "- __loss:__ \\[dict\\] A dictionary of loss function name(s). This allows you to create composite loss functions with ease. If there are any arguments that must be passed to the loss function upon initialization (e.g. the gamma parameter for focal loss), pass them as subdicts here.\n", + "- __loss\\_weights:__ \\[dict\\] A dictionary of `loss_name: weight` pairs. If provided, the same names must be passed here as were passed in __loss__. If not provided, the different losses will be weighted equally. Weight values can be ints or floats.\n", + "- __metrics:__ \\[dict\\] A dict of `training: [list of training metrics], validation: [list of validation metrics]`. See the linked example for what this can look like. Note that this only currently has an effect for Keras models.\n", + "- __checkpoint\\_frequency:__ \\[int\\] The frequency at which model checkpoints should be save.\n", + "- __callbacks:__ \\[dict\\] A dict of callback names, whose values are subdicts defining any arguments for the callback. See [callbacks](../api/nets.rst#module-solaris.nets.callbacks) for options.\n", + "- __model\\_dest\\_path:__ \\[str\\] The path to save the final, trained model to.\n", + "- __verbose:__ \\[bool\\] Verbose text output during training.\n", + "\n", + "#### Inference\n", + "\n", + "- __window\\_step\\_size\\_x:__ \\[int\\] If your model takes in an image smaller than your inference chips, this argument will specify how far in the x direction each tile should step. Set to the same value as __width__ if you don't want any overlap between tiles; if you have overlap, `solaris` will average predictions across the overlapping areas to give you more robust estimates.\n", + "- __window\\_step\\_size\\_y:__ \\[int\\] If your model takes in an image smaller than your inference chips, this argument will specify how far in the y direction each tile should step. Set to the same value as __height__ if you don't want any overlap between tiles; if you have overlap, `solaris` will average predictions across the overlapping areas to give you more robust estimates.\n", + "\n", + "\n", + "## How the config file is processed\n", + "\n", + "`solaris` contains a utility function, [solaris.utils.config.parse()](../../api/utils.rst#solaris.utils.config.parse) which takes one argument: the path to a YAML config file to read in. [parse()](../../api/utils.rst#solaris.utils.config.parse) will check to make sure necessary values are present. This is called automatically by CLI functions that take the config file as an argument, but you can also call it with the Python API to use the config as an argument in [solaris.nets](../../api/nets.rst) functions.\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "solaris", + "language": "python", + "name": "solaris" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/environment-gpu.yml b/environment-gpu.yml new file mode 100644 index 00000000..38fae84d --- /dev/null +++ b/environment-gpu.yml @@ -0,0 +1,34 @@ +name: solaris +channels: + - pytorch + - conda-forge + - defaults +dependencies: + - python=3.6 + - pip=19.0 + - shapely=1.6.4 + - fiona=1.8.6 + - pandas=0.24.2 + - geopandas=0.5.0 + - opencv=3.4.4 + - numpy=1.16.3 + - gdal=2.4.1 + - tqdm=4.31.1 + - matplotlib=3.1.0 + - rtree=0.8.3 + - urllib3=1.24.3 + - networkx=2.3 + - rasterio=1.0.22 + - scipy=1.2.1 + - scikit-image=0.15.0 + - tensorflow-gpu=1.13.1 + - cuda92 + - pytorch=1.1.0 + - torchvision=0.3.0 + - pyyaml=5.1 + - pyproj=2.1.3 + - pip: + - affine==2.2.2 + - albumentations==0.2.3 + - rio-tiler==1.2.7 + - rio-cogeo==1.0.0 diff --git a/environment.yml b/environment.yml index 1110cbba..64e5301e 100644 --- a/environment.yml +++ b/environment.yml @@ -14,7 +14,9 @@ dependencies: - numpy=1.16.3 - gdal=2.4.1 - tqdm=4.31.1 + - matplotlib=3.1.0 - rtree=0.8.3 + - urllib3=1.24.3 - networkx=2.3 - rasterio=1.0.22 - scipy=1.2.1 @@ -23,7 +25,9 @@ dependencies: - pytorch=1.1.0 - torchvision=0.3.0 - pyyaml=5.1 + - pyproj=2.1.3 - pip: - affine==2.2.2 - albumentations==0.2.3 - rio-tiler==1.2.7 + - rio-cogeo==1.0.0 diff --git a/old_docs/eval_jupyter_nbs/design_evalfunctions_tests.ipynb b/old_docs/eval_jupyter_nbs/design_evalfunctions_tests.ipynb deleted file mode 100644 index 737a317a..00000000 --- a/old_docs/eval_jupyter_nbs/design_evalfunctions_tests.ipynb +++ /dev/null @@ -1,3266 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "from cw_eval.evalfunctions import calculate_iou" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "import geopandas as gpd" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "import shapely" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "gt_gdf_1 = gpd.read_file('/Users/nweir/code/cosmiq_repos/SpaceNet4_Baseline/val_geojson/spacenet-buildings_736301_3722439.geojson')\n", - "pred_gdf_1 = gpd.read_file('/Users/nweir/code/cosmiq_repos/SpaceNet4_Baseline/Pan-Sharpen_Atlanta_nadir7_catid_1030010003D22F00_736301_3722439.json')" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "pred_poly = pred_gdf_1.iloc[0, 2]" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "image/svg+xml": [ - "" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "pred_poly" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "iou_gdf = evalfunctions.calculate_iou(pred_poly, gt_gdf_1)" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " access addr_house addr_hou_1 addr_inter admin_leve aerialway aeroway \\\n", - "27 \n", - "\n", - " amenity area barrier ... wood z_order tracktype way_area \\\n", - "27 ... -999999 -999999.0 \n", - "\n", - " origarea origlen partialDec truncated \\\n", - "27 152.874256 0 1.0 0 \n", - "\n", - " geometry iou_score \n", - "27 POLYGON ((736351.470539042 3722758.485215372, ... 0.0735 \n", - "\n", - "[1 rows x 75 columns]\n" - ] - } - ], - "source": [ - "print(iou_gdf)" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'type': 'Polygon',\n", - " 'coordinates': (((736348.0, 3722762.5),\n", - " (736353.0, 3722762.0),\n", - " (736354.0, 3722759.0),\n", - " (736352.0, 3722755.5),\n", - " (736348.5, 3722755.5),\n", - " (736346.0, 3722757.5),\n", - " (736348.0, 3722762.5)),)}" - ] - }, - "execution_count": 32, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from shapely.geometry import mapping\n", - "mapping(pred_poly)" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], - "source": [ - "import os" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [], - "source": [ - "apoly = shapely.geometry.Polygon(((736348.0, 3722762.5),\n", - " (736353.0, 3722762.0),\n", - " (736354.0, 3722759.0),\n", - " (736352.0, 3722755.5),\n", - " (736348.5, 3722755.5),\n", - " (736346.0, 3722757.5),\n", - " (736348.0, 3722762.5)))" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "data": { - "image/svg+xml": [ - "" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 34, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "apoly" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [], - "source": [ - "iou_gdf = evalfunctions.calculate_iou(apoly, gt_gdf_1)" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
accessaddr_houseaddr_hou_1addr_interadmin_leveaerialwayaerowayamenityareabarrier...woodz_ordertracktypeway_areaorigareaoriglenpartialDectruncatedgeometryiou_score
27...-999999-999999.0152.87425601.00POLYGON ((736351.470539042 3722758.485215372, ...0.0735
\n", - "

1 rows × 75 columns

\n", - "
" - ], - "text/plain": [ - " access addr_house addr_hou_1 addr_inter admin_leve aerialway aeroway \\\n", - "27 \n", - "\n", - " amenity area barrier ... wood z_order tracktype way_area \\\n", - "27 ... -999999 -999999.0 \n", - "\n", - " origarea origlen partialDec truncated \\\n", - "27 152.874256 0 1.0 0 \n", - "\n", - " geometry iou_score \n", - "27 POLYGON ((736351.470539042 3722758.485215372, ... 0.0735 \n", - "\n", - "[1 rows x 75 columns]" - ] - }, - "execution_count": 36, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "iou_gdf" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [], - "source": [ - "iou_gdf.to_file('/Users/nweir/code/cosmiq_repos/cw-eval/cw_eval/data/overlap_test.geojson', driver='GeoJSON')" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [], - "source": [ - "pred_gdf = gpd.read_file('/Users/nweir/code/cosmiq_repos/cw-eval/cw_eval/data/overlap_test.geojson')" - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [ - { - "ename": "ValueError", - "evalue": "Can only compare identically-labeled DataFrame objects", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0miou_gdf\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mpred_gdf\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m/anaconda3/envs/spacenetutilities/lib/python3.6/site-packages/pandas/core/ops.py\u001b[0m in \u001b[0;36mf\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 1611\u001b[0m \u001b[0;31m# Another DataFrame\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1612\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_indexed_same\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1613\u001b[0;31m raise ValueError('Can only compare identically-labeled '\n\u001b[0m\u001b[1;32m 1614\u001b[0m 'DataFrame objects')\n\u001b[1;32m 1615\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_compare_frame\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstr_rep\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mValueError\u001b[0m: Can only compare identically-labeled DataFrame objects" - ] - } - ], - "source": [ - "iou_gdf == pred_gdf" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
accessaddr_houseaddr_hou_1addr_interadmin_leveaerialwayaerowayamenityareabarrier...woodz_ordertracktypeway_areaorigareaoriglenpartialDectruncatedgeometryiou_score
27...-999999-999999.0152.87425601.00POLYGON ((736351.470539042 3722758.485215372, ...0.0735
\n", - "

1 rows × 75 columns

\n", - "
" - ], - "text/plain": [ - " access addr_house addr_hou_1 addr_inter admin_leve aerialway aeroway \\\n", - "27 \n", - "\n", - " amenity area barrier ... wood z_order tracktype way_area \\\n", - "27 ... -999999 -999999.0 \n", - "\n", - " origarea origlen partialDec truncated \\\n", - "27 152.874256 0 1.0 0 \n", - "\n", - " geometry iou_score \n", - "27 POLYGON ((736351.470539042 3722758.485215372, ... 0.0735 \n", - "\n", - "[1 rows x 75 columns]" - ] - }, - "execution_count": 42, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "iou_gdf" - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
accessaddr_houseaddr_hou_1addr_interadmin_leveaerialwayaerowayamenityareabarrier...woodz_ordertracktypeway_areaorigareaoriglenpartialDectruncatediou_scoregeometry
0...-999999-999999.0152.87425601.000.0735POLYGON ((736351.470539042 3722758.485215372, ...
\n", - "

1 rows × 75 columns

\n", - "
" - ], - "text/plain": [ - " access addr_house addr_hou_1 addr_inter admin_leve aerialway aeroway \\\n", - "0 \n", - "\n", - " amenity area barrier ... \\\n", - "0 ... \n", - "\n", - " wood z_order tracktype way_area origarea origlen partialDec truncated \\\n", - "0 -999999 -999999.0 152.874256 0 1.0 0 \n", - "\n", - " iou_score geometry \n", - "0 0.0735 POLYGON ((736351.470539042 3722758.485215372, ... \n", - "\n", - "[1 rows x 75 columns]" - ] - }, - "execution_count": 43, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "pred_gdf" - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": {}, - "outputs": [], - "source": [ - "assert iou_gdf.index[0] == 27" - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "metadata": {}, - "outputs": [ - { - "ename": "ValueError", - "evalue": "Can only compare identically-labeled DataFrame objects", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0miou_gdf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreset_index\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdrop\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mpred_gdf\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m/anaconda3/envs/spacenetutilities/lib/python3.6/site-packages/pandas/core/ops.py\u001b[0m in \u001b[0;36mf\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 1611\u001b[0m \u001b[0;31m# Another DataFrame\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1612\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_indexed_same\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1613\u001b[0;31m raise ValueError('Can only compare identically-labeled '\n\u001b[0m\u001b[1;32m 1614\u001b[0m 'DataFrame objects')\n\u001b[1;32m 1615\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_compare_frame\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstr_rep\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mValueError\u001b[0m: Can only compare identically-labeled DataFrame objects" - ] - } - ], - "source": [ - "iou_gdf.reset_index(drop=True) == pred_gdf" - ] - }, - { - "cell_type": "code", - "execution_count": 54, - "metadata": {}, - "outputs": [], - "source": [ - "assert iou_gdf.iou_score.iloc[0] == pred_gdf.iou_score.iloc[0]" - ] - }, - { - "cell_type": "code", - "execution_count": 56, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.073499798744833519" - ] - }, - "execution_count": 56, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "iou_gdf.iou_score[27]" - ] - }, - { - "cell_type": "code", - "execution_count": 58, - "metadata": {}, - "outputs": [ - { - "ename": "AssertionError", - "evalue": "", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mAssertionError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32massert\u001b[0m \u001b[0miou_gdf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0miou_score\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0miloc\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0.0735\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mAssertionError\u001b[0m: " - ] - } - ], - "source": [ - "assert iou_gdf.iou_score.iloc[0] == 0.073499798744833519" - ] - }, - { - "cell_type": "code", - "execution_count": 60, - "metadata": {}, - "outputs": [], - "source": [ - "emptypoly = shapely.geometry.Polygon()" - ] - }, - { - "cell_type": "code", - "execution_count": 61, - "metadata": {}, - "outputs": [], - "source": [ - "noiou = evalfunctions.calculate_iou(emptypoly, gt_gdf_1)" - ] - }, - { - "cell_type": "code", - "execution_count": 62, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
\n", - "
" - ], - "text/plain": [ - "Empty GeoDataFrame\n", - "Columns: []\n", - "Index: []" - ] - }, - "execution_count": 62, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "noiou" - ] - }, - { - "cell_type": "code", - "execution_count": 63, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "geopandas.geodataframe.GeoDataFrame" - ] - }, - "execution_count": 63, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(noiou)" - ] - }, - { - "cell_type": "code", - "execution_count": 64, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(0, 0)" - ] - }, - "execution_count": 64, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "noiou.shape" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 65, - "metadata": {}, - "outputs": [], - "source": [ - "pred_poly = pred_gdf_1.iloc[3, 2]" - ] - }, - { - "cell_type": "code", - "execution_count": 66, - "metadata": {}, - "outputs": [], - "source": [ - "iou_gdf = evalfunctions.calculate_iou(pred_poly, gt_gdf_1)" - ] - }, - { - "cell_type": "code", - "execution_count": 77, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
accessaddr_houseaddr_hou_1addr_interadmin_leveaerialwayaerowayamenityareabarrier...woodz_ordertracktypeway_areaorigareaoriglenpartialDectruncatedgeometryiou_score
15...-999999-999999.0494.45819001.00POLYGON ((736372.6805365498 3722519.044206998,...0.277727
21...-999999-999999.0360.54645601.00POLYGON ((736407.6841525573 3722533.446308028,...0.313965
\n", - "

2 rows × 75 columns

\n", - "
" - ], - "text/plain": [ - " access addr_house addr_hou_1 addr_inter admin_leve aerialway aeroway \\\n", - "15 \n", - "21 \n", - "\n", - " amenity area barrier ... wood z_order tracktype way_area \\\n", - "15 ... -999999 -999999.0 \n", - "21 ... -999999 -999999.0 \n", - "\n", - " origarea origlen partialDec truncated \\\n", - "15 494.458190 0 1.0 0 \n", - "21 360.546456 0 1.0 0 \n", - "\n", - " geometry iou_score \n", - "15 POLYGON ((736372.6805365498 3722519.044206998,... 0.277727 \n", - "21 POLYGON ((736407.6841525573 3722533.446308028,... 0.313965 \n", - "\n", - "[2 rows x 75 columns]" - ] - }, - "execution_count": 77, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "pred_poly = pred_gdf_1.iloc[11, 2]\n", - "iou_gdf = evalfunctions.calculate_iou(pred_poly, gt_gdf_1)\n", - "iou_gdf" - ] - }, - { - "cell_type": "code", - "execution_count": 78, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'type': 'Polygon',\n", - " 'coordinates': (((736414.0, 3722573.0),\n", - " (736417.5, 3722572.5),\n", - " (736420.0, 3722568.0),\n", - " (736421.0, 3722556.0),\n", - " (736418.5, 3722538.0),\n", - " (736424.0, 3722532.5),\n", - " (736424.0, 3722527.0),\n", - " (736422.5, 3722525.5),\n", - " (736412.0, 3722524.0),\n", - " (736410.5, 3722521.5),\n", - " (736407.0, 3722520.5),\n", - " (736383.5, 3722521.0),\n", - " (736376.5, 3722528.5),\n", - " (736378.0, 3722532.5),\n", - " (736402.0, 3722532.0),\n", - " (736410.0, 3722539.0),\n", - " (736411.0, 3722544.0),\n", - " (736408.5, 3722553.5),\n", - " (736409.0, 3722569.0),\n", - " (736414.0, 3722573.0)),)}" - ] - }, - "execution_count": 78, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "mapping(pred_poly)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Testing baseeval.py" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "from cw_eval.baseeval import EvalBase\n", - "from cw_eval.data import gt_gdf" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "ename": "AttributeError", - "evalue": "'GeoDataFrame' object has no attribute 'startswith'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mgt\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgt_gdf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mbase_instance\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mEvalBase\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgt\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m/anaconda3/envs/spacenetutilities/lib/python3.6/site-packages/cw_eval/baseeval.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, ground_truth_vector_file)\u001b[0m\n\u001b[1;32m 25\u001b[0m \u001b[0;31m# Load Ground Truth : Ground Truth should be in geojson or shape file\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 26\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 27\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mground_truth_GDF\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgpd\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread_file\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mground_truth_vector_file\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 28\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mCPLE_OpenFailedError\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mDriverError\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# handles empty geojson\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 29\u001b[0m self.ground_truth_GDF = gpd.GeoDataFrame({'sindex': [],\n", - "\u001b[0;32m/anaconda3/envs/spacenetutilities/lib/python3.6/site-packages/geopandas/io/file.py\u001b[0m in \u001b[0;36mread_file\u001b[0;34m(filename, bbox, **kwargs)\u001b[0m\n\u001b[1;32m 63\u001b[0m \u001b[0mreader\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfiona\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mopen\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 64\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 65\u001b[0;31m \u001b[0;32mwith\u001b[0m \u001b[0mreader\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpath_or_bytes\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mfeatures\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 66\u001b[0m \u001b[0mcrs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfeatures\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcrs\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 67\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mbbox\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/anaconda3/envs/spacenetutilities/lib/python3.6/site-packages/fiona/env.py\u001b[0m in \u001b[0;36mwrapper\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 403\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mEnv\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfrom_defaults\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msession\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0msession\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 404\u001b[0m \u001b[0mlog\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdebug\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Credentialized: {!r}\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgetenv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 405\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 406\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mwrapper\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 407\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/anaconda3/envs/spacenetutilities/lib/python3.6/site-packages/fiona/__init__.py\u001b[0m in \u001b[0;36mopen\u001b[0;34m(fp, mode, driver, schema, crs, encoding, layer, vfs, enabled_drivers, crs_wkt, **kwargs)\u001b[0m\n\u001b[1;32m 247\u001b[0m \u001b[0mpath\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mParsedPath\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpath\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0marchive\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mscheme\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 248\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 249\u001b[0;31m \u001b[0mpath\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mparse_path\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfp\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 250\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 251\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mmode\u001b[0m \u001b[0;32min\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;34m'a'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'r'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/anaconda3/envs/spacenetutilities/lib/python3.6/site-packages/fiona/path.py\u001b[0m in \u001b[0;36mparse_path\u001b[0;34m(path)\u001b[0m\n\u001b[1;32m 129\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mUnparsedPath\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpath\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 130\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 131\u001b[0;31m \u001b[0;32melif\u001b[0m \u001b[0mpath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstartswith\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'/vsi'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 132\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mUnparsedPath\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpath\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 133\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/anaconda3/envs/spacenetutilities/lib/python3.6/site-packages/pandas/core/generic.py\u001b[0m in \u001b[0;36m__getattr__\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m 4374\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_info_axis\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_can_hold_identifiers_and_holds_name\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4375\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 4376\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mobject\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__getattribute__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 4377\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4378\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__setattr__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mAttributeError\u001b[0m: 'GeoDataFrame' object has no attribute 'startswith'" - ] - } - ], - "source": [ - "gt = gt_gdf()\n", - "base_instance = EvalBase(gt)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "import cw_eval\n", - "import os" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/anaconda3/envs/spacenetutilities/lib/python3.6/site-packages/cw_eval/data'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "cw_eval.data.data_dir" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "aneval = cw_eval.baseeval.EvalBase(os.path.join(cw_eval.data.data_dir, 'gt.geojson'))\n", - "gt_gdf = cw_eval.data.gt_gdf()" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "aneval.ground_truth_sindex" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "gt_gdf.sindex" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "False" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "aneval.ground_truth_sindex == gt_gdf.sindex" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'properties': {'buffering_capacity': 10, 'custom_storage_callbacks': None, 'custom_storage_callbacks_size': 0, 'dat_extension': 'dat', 'dimension': 2, 'filename': '', 'fill_factor': 0.7, 'idx_extension': 'idx', 'index_capacity': 100, 'index_id': None, 'leaf_capacity': 100, 'near_minimum_overlap_factor': 32, 'overwrite': True, 'pagesize': 4096, 'point_pool_capacity': 500, 'region_pool_capacity': 1000, 'reinsert_factor': 0.3, 'split_distribution_factor': 0.4, 'storage': 0, 'tight_mbr': True, 'tpr_horizon': 20.0, 'type': 0, 'variant': 2, 'writethrough': False},\n", - " 'interleaved': True,\n", - " 'handle': }" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "gt_gdf.sindex.__dict__" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[8,\n", - " 14,\n", - " 23,\n", - " 17,\n", - " 26,\n", - " 27,\n", - " 4,\n", - " 19,\n", - " 11,\n", - " 25,\n", - " 15,\n", - " 2,\n", - " 21,\n", - " 12,\n", - " 5,\n", - " 24,\n", - " 18,\n", - " 6,\n", - " 13,\n", - " 3,\n", - " 20,\n", - " 9,\n", - " 7,\n", - " 22,\n", - " 16,\n", - " 10,\n", - " 0,\n", - " 1]" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "list(gt_gdf.sindex.intersection(aneval.ground_truth_sindex.bounds))" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[736301.0, 3722439.0, 736751.0, 3722758.485215372]" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "aneval.ground_truth_sindex.bounds" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[736301.0, 3722439.0, 736751.0, 3722758.485215372]" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "gt_gdf.sindex.bounds" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
\n", - "
" - ], - "text/plain": [ - "Empty GeoDataFrame\n", - "Columns: []\n", - "Index: []" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import geopandas as gpd\n", - "aneval.proposal_GDF == gpd.GeoDataFrame([])" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "assert aneval.proposal_GDF.equals(gpd.GeoDataFrame([]))" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "assert aneval.ground_truth_GDF.equals(aneval.ground_truth_GDF_Edit)" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "aneval.load_proposal(os.path.join(cw_eval.data.data_dir, 'pred.geojson'))" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [], - "source": [ - "prop_gdf = cw_eval.data.pred_gdf()" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [], - "source": [ - "assert aneval.proposal_GDF.iloc[:, 0:3].sort_index().equals(prop_gdf)" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[0,\n", - " 1,\n", - " 2,\n", - " 3,\n", - " 4,\n", - " 5,\n", - " 6,\n", - " 7,\n", - " 8,\n", - " 9,\n", - " 10,\n", - " 11,\n", - " 12,\n", - " 13,\n", - " 14,\n", - " 15,\n", - " 16,\n", - " 17,\n", - " 18,\n", - " 19,\n", - " 20,\n", - " 21,\n", - " 22,\n", - " 23,\n", - " 24,\n", - " 25,\n", - " 26,\n", - " 27]" - ] - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "list(prop_gdf.index)" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[0,\n", - " 1,\n", - " 26,\n", - " 25,\n", - " 24,\n", - " 23,\n", - " 22,\n", - " 21,\n", - " 20,\n", - " 19,\n", - " 18,\n", - " 17,\n", - " 16,\n", - " 15,\n", - " 14,\n", - " 13,\n", - " 12,\n", - " 11,\n", - " 10,\n", - " 9,\n", - " 8,\n", - " 7,\n", - " 6,\n", - " 5,\n", - " 4,\n", - " 3,\n", - " 2,\n", - " 27]" - ] - }, - "execution_count": 39, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "list(aneval.proposal_GDF.index)" - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "28it [00:00, 98.08it/s]\n" - ] - } - ], - "source": [ - "score = aneval.eval_iou(calculate_class_scores=False)" - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "list" - ] - }, - "execution_count": 44, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(score)" - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "28it [00:00, 96.49it/s]\n" - ] - } - ], - "source": [ - "assert aneval.eval_iou(calculate_class_scores=False)==score" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Create test function for challenge_eval.off_nadir_dataset" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/anaconda3/envs/spacenetutilities/lib/python3.6/site-packages/cw_eval/data'" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import cw_eval\n", - "from cw_eval import data\n", - "data.data_dir" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import os\n", - "proposal_csv = os.path.join(data.data_dir, 'sample_preds_competition.csv')\n", - "truth_csv = os.path.join(data.data_dir, 'sample_truth_competition.csv')\n", - "test_out = 'test'\n", - "import subprocess\n", - "subprocess.call(['spacenet_eval', '--proposal_csv', proposal_csv,\n", - " '--truth_csv', truth_csv, '--output_file', test_out])" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "test_results = pd.read_csv('test.csv')\n", - "full_test_results = pd.read_csv('test_full.csv')" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
F1ScoreFalseNegFalsePosPrecisionRecallTruePosimageIDiou_fieldnadir-category
01.0001.01.059Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
11.0001.01.08Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
21.0001.01.059Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
31.0001.01.018Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
41.0001.01.021Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
51.0001.01.096Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
61.0001.01.043Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
71.0001.01.055Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
81.0001.01.072Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
91.0001.01.0122Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
101.0001.01.078Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
111.0001.01.096Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
121.0001.01.0134Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
131.0001.01.01Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
141.0001.01.0144Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
151.0001.01.060Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
161.0001.01.045Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
171.0001.01.028Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
181.0001.01.067Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
191.0001.01.0154Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
201.0001.01.0117Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
211.0001.01.067Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
221.0001.01.0101Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
231.0001.01.052Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
241.0001.01.0139Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
251.0001.01.0112Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
261.0001.01.071Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
271.0001.01.03Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
281.0001.01.0121Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
291.0001.01.02Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
301.0001.01.091Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
311.0001.01.03Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
321.0001.01.080Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
\n", - "
" - ], - "text/plain": [ - " F1Score FalseNeg FalsePos Precision Recall TruePos \\\n", - "0 1.0 0 0 1.0 1.0 59 \n", - "1 1.0 0 0 1.0 1.0 8 \n", - "2 1.0 0 0 1.0 1.0 59 \n", - "3 1.0 0 0 1.0 1.0 18 \n", - "4 1.0 0 0 1.0 1.0 21 \n", - "5 1.0 0 0 1.0 1.0 96 \n", - "6 1.0 0 0 1.0 1.0 43 \n", - "7 1.0 0 0 1.0 1.0 55 \n", - "8 1.0 0 0 1.0 1.0 72 \n", - "9 1.0 0 0 1.0 1.0 122 \n", - "10 1.0 0 0 1.0 1.0 78 \n", - "11 1.0 0 0 1.0 1.0 96 \n", - "12 1.0 0 0 1.0 1.0 134 \n", - "13 1.0 0 0 1.0 1.0 1 \n", - "14 1.0 0 0 1.0 1.0 144 \n", - "15 1.0 0 0 1.0 1.0 60 \n", - "16 1.0 0 0 1.0 1.0 45 \n", - "17 1.0 0 0 1.0 1.0 28 \n", - "18 1.0 0 0 1.0 1.0 67 \n", - "19 1.0 0 0 1.0 1.0 154 \n", - "20 1.0 0 0 1.0 1.0 117 \n", - "21 1.0 0 0 1.0 1.0 67 \n", - "22 1.0 0 0 1.0 1.0 101 \n", - "23 1.0 0 0 1.0 1.0 52 \n", - "24 1.0 0 0 1.0 1.0 139 \n", - "25 1.0 0 0 1.0 1.0 112 \n", - "26 1.0 0 0 1.0 1.0 71 \n", - "27 1.0 0 0 1.0 1.0 3 \n", - "28 1.0 0 0 1.0 1.0 121 \n", - "29 1.0 0 0 1.0 1.0 2 \n", - "30 1.0 0 0 1.0 1.0 91 \n", - "31 1.0 0 0 1.0 1.0 3 \n", - "32 1.0 0 0 1.0 1.0 80 \n", - "\n", - " imageID iou_field \\\n", - "0 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "1 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "2 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "3 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "4 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "5 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "6 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "7 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "8 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "9 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "10 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "11 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "12 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "13 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "14 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "15 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "16 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "17 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "18 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "19 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "20 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "21 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "22 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "23 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "24 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "25 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "26 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "27 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "28 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "29 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "30 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "31 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "32 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "\n", - " nadir-category \n", - "0 Nadir \n", - "1 Nadir \n", - "2 Nadir \n", - "3 Nadir \n", - "4 Nadir \n", - "5 Nadir \n", - "6 Nadir \n", - "7 Nadir \n", - "8 Nadir \n", - "9 Nadir \n", - "10 Nadir \n", - "11 Nadir \n", - "12 Nadir \n", - "13 Nadir \n", - "14 Nadir \n", - "15 Nadir \n", - "16 Nadir \n", - "17 Nadir \n", - "18 Nadir \n", - "19 Nadir \n", - "20 Nadir \n", - "21 Nadir \n", - "22 Nadir \n", - "23 Nadir \n", - "24 Nadir \n", - "25 Nadir \n", - "26 Nadir \n", - "27 Nadir \n", - "28 Nadir \n", - "29 Nadir \n", - "30 Nadir \n", - "31 Nadir \n", - "32 Nadir " - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "full_test_results" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "pred_results = pd.read_csv(os.path.join(data.data_dir, 'competition_test_results.csv'))\n", - "pred_results_full = pd.read_csv(os.path.join(data.data_dir, 'competition_test_results_full.csv'))" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "assert pred_results.equals(test_results)" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "assert pred_results_full.sort_values(by='imageID').reset_index(drop=True).equals(full_test_results.sort_values(by='imageID').reset_index(drop=True))" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
indexF1ScoreFalseNegFalsePosPrecisionRecallTruePosimageIDiou_fieldnadir-category
0251.0001.01.096Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
1271.0001.01.059Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
261.0001.01.091Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
3221.0001.01.071Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
471.0001.01.0121Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
5261.0001.01.060Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
621.0001.01.072Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
7141.0001.01.055Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
8131.0001.01.028Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
9241.0001.01.03Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
10121.0001.01.02Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
11211.0001.01.03Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
12301.0001.01.067Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
13101.0001.01.021Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
14161.0001.01.018Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
15321.0001.01.08Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
16181.0001.01.059Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
17281.0001.01.043Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
1831.0001.01.01Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
1951.0001.01.067Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
20191.0001.01.045Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
2141.0001.01.052Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
2201.0001.01.080Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
23231.0001.01.0117Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
24151.0001.01.0139Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
2591.0001.01.0154Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
26291.0001.01.0144Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
27171.0001.01.0122Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
2811.0001.01.0112Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
29111.0001.01.0101Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
30311.0001.01.078Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
3181.0001.01.0134Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
32201.0001.01.096Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
\n", - "
" - ], - "text/plain": [ - " index F1Score FalseNeg FalsePos Precision Recall TruePos \\\n", - "0 25 1.0 0 0 1.0 1.0 96 \n", - "1 27 1.0 0 0 1.0 1.0 59 \n", - "2 6 1.0 0 0 1.0 1.0 91 \n", - "3 22 1.0 0 0 1.0 1.0 71 \n", - "4 7 1.0 0 0 1.0 1.0 121 \n", - "5 26 1.0 0 0 1.0 1.0 60 \n", - "6 2 1.0 0 0 1.0 1.0 72 \n", - "7 14 1.0 0 0 1.0 1.0 55 \n", - "8 13 1.0 0 0 1.0 1.0 28 \n", - "9 24 1.0 0 0 1.0 1.0 3 \n", - "10 12 1.0 0 0 1.0 1.0 2 \n", - "11 21 1.0 0 0 1.0 1.0 3 \n", - "12 30 1.0 0 0 1.0 1.0 67 \n", - "13 10 1.0 0 0 1.0 1.0 21 \n", - "14 16 1.0 0 0 1.0 1.0 18 \n", - "15 32 1.0 0 0 1.0 1.0 8 \n", - "16 18 1.0 0 0 1.0 1.0 59 \n", - "17 28 1.0 0 0 1.0 1.0 43 \n", - "18 3 1.0 0 0 1.0 1.0 1 \n", - "19 5 1.0 0 0 1.0 1.0 67 \n", - "20 19 1.0 0 0 1.0 1.0 45 \n", - "21 4 1.0 0 0 1.0 1.0 52 \n", - "22 0 1.0 0 0 1.0 1.0 80 \n", - "23 23 1.0 0 0 1.0 1.0 117 \n", - "24 15 1.0 0 0 1.0 1.0 139 \n", - "25 9 1.0 0 0 1.0 1.0 154 \n", - "26 29 1.0 0 0 1.0 1.0 144 \n", - "27 17 1.0 0 0 1.0 1.0 122 \n", - "28 1 1.0 0 0 1.0 1.0 112 \n", - "29 11 1.0 0 0 1.0 1.0 101 \n", - "30 31 1.0 0 0 1.0 1.0 78 \n", - "31 8 1.0 0 0 1.0 1.0 134 \n", - "32 20 1.0 0 0 1.0 1.0 96 \n", - "\n", - " imageID iou_field \\\n", - "0 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "1 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "2 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "3 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "4 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "5 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "6 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "7 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "8 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "9 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "10 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "11 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "12 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "13 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "14 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "15 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "16 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "17 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "18 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "19 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "20 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "21 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "22 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "23 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "24 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "25 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "26 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "27 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "28 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "29 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "30 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "31 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "32 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "\n", - " nadir-category \n", - "0 Nadir \n", - "1 Nadir \n", - "2 Nadir \n", - "3 Nadir \n", - "4 Nadir \n", - "5 Nadir \n", - "6 Nadir \n", - "7 Nadir \n", - "8 Nadir \n", - "9 Nadir \n", - "10 Nadir \n", - "11 Nadir \n", - "12 Nadir \n", - "13 Nadir \n", - "14 Nadir \n", - "15 Nadir \n", - "16 Nadir \n", - "17 Nadir \n", - "18 Nadir \n", - "19 Nadir \n", - "20 Nadir \n", - "21 Nadir \n", - "22 Nadir \n", - "23 Nadir \n", - "24 Nadir \n", - "25 Nadir \n", - "26 Nadir \n", - "27 Nadir \n", - "28 Nadir \n", - "29 Nadir \n", - "30 Nadir \n", - "31 Nadir \n", - "32 Nadir " - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "pred_results_full.sort_values(by='imageID').reset_index()" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
F1ScoreFalseNegFalsePosPrecisionRecallTruePosimageIDiou_fieldnadir-category
111.0001.01.096Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
01.0001.01.059Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
301.0001.01.091Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
261.0001.01.071Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
281.0001.01.0121Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
151.0001.01.060Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
81.0001.01.072Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
71.0001.01.055Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
171.0001.01.028Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
311.0001.01.03Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
291.0001.01.02Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
271.0001.01.03Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
211.0001.01.067Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
41.0001.01.021Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
31.0001.01.018Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
11.0001.01.08Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
21.0001.01.059Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
61.0001.01.043Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
131.0001.01.01Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
181.0001.01.067Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
161.0001.01.045Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
231.0001.01.052Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
321.0001.01.080Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
201.0001.01.0117Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
241.0001.01.0139Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
191.0001.01.0154Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
141.0001.01.0144Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
91.0001.01.0122Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
251.0001.01.0112Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
221.0001.01.0101Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
101.0001.01.078Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
121.0001.01.0134Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
51.0001.01.096Atlanta_nadir8_catid_10300100023BC100_743501_3...iou_scoreNadir
\n", - "
" - ], - "text/plain": [ - " F1Score FalseNeg FalsePos Precision Recall TruePos \\\n", - "11 1.0 0 0 1.0 1.0 96 \n", - "0 1.0 0 0 1.0 1.0 59 \n", - "30 1.0 0 0 1.0 1.0 91 \n", - "26 1.0 0 0 1.0 1.0 71 \n", - "28 1.0 0 0 1.0 1.0 121 \n", - "15 1.0 0 0 1.0 1.0 60 \n", - "8 1.0 0 0 1.0 1.0 72 \n", - "7 1.0 0 0 1.0 1.0 55 \n", - "17 1.0 0 0 1.0 1.0 28 \n", - "31 1.0 0 0 1.0 1.0 3 \n", - "29 1.0 0 0 1.0 1.0 2 \n", - "27 1.0 0 0 1.0 1.0 3 \n", - "21 1.0 0 0 1.0 1.0 67 \n", - "4 1.0 0 0 1.0 1.0 21 \n", - "3 1.0 0 0 1.0 1.0 18 \n", - "1 1.0 0 0 1.0 1.0 8 \n", - "2 1.0 0 0 1.0 1.0 59 \n", - "6 1.0 0 0 1.0 1.0 43 \n", - "13 1.0 0 0 1.0 1.0 1 \n", - "18 1.0 0 0 1.0 1.0 67 \n", - "16 1.0 0 0 1.0 1.0 45 \n", - "23 1.0 0 0 1.0 1.0 52 \n", - "32 1.0 0 0 1.0 1.0 80 \n", - "20 1.0 0 0 1.0 1.0 117 \n", - "24 1.0 0 0 1.0 1.0 139 \n", - "19 1.0 0 0 1.0 1.0 154 \n", - "14 1.0 0 0 1.0 1.0 144 \n", - "9 1.0 0 0 1.0 1.0 122 \n", - "25 1.0 0 0 1.0 1.0 112 \n", - "22 1.0 0 0 1.0 1.0 101 \n", - "10 1.0 0 0 1.0 1.0 78 \n", - "12 1.0 0 0 1.0 1.0 134 \n", - "5 1.0 0 0 1.0 1.0 96 \n", - "\n", - " imageID iou_field \\\n", - "11 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "0 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "30 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "26 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "28 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "15 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "8 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "7 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "17 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "31 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "29 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "27 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "21 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "4 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "3 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "1 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "2 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "6 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "13 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "18 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "16 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "23 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "32 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "20 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "24 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "19 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "14 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "9 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "25 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "22 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "10 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "12 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "5 Atlanta_nadir8_catid_10300100023BC100_743501_3... iou_score \n", - "\n", - " nadir-category \n", - "11 Nadir \n", - "0 Nadir \n", - "30 Nadir \n", - "26 Nadir \n", - "28 Nadir \n", - "15 Nadir \n", - "8 Nadir \n", - "7 Nadir \n", - "17 Nadir \n", - "31 Nadir \n", - "29 Nadir \n", - "27 Nadir \n", - "21 Nadir \n", - "4 Nadir \n", - "3 Nadir \n", - "1 Nadir \n", - "2 Nadir \n", - "6 Nadir \n", - "13 Nadir \n", - "18 Nadir \n", - "16 Nadir \n", - "23 Nadir \n", - "32 Nadir \n", - "20 Nadir \n", - "24 Nadir \n", - "19 Nadir \n", - "14 Nadir \n", - "9 Nadir \n", - "25 Nadir \n", - "22 Nadir \n", - "10 Nadir \n", - "12 Nadir \n", - "5 Nadir " - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "full_test_results.sort_values(by='imageID')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python [conda env:spacenetutilities]", - "language": "python", - "name": "conda-env-spacenetutilities-py" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.7" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/old_docs/eval_jupyter_nbs/test.csv b/old_docs/eval_jupyter_nbs/test.csv deleted file mode 100644 index 45ea2fe8..00000000 --- a/old_docs/eval_jupyter_nbs/test.csv +++ /dev/null @@ -1,2 +0,0 @@ -F1Score,FalseNeg,FalsePos,Precision,Recall,TruePos -1.0,0,0,1.0,1.0,2319 diff --git a/old_docs/eval_jupyter_nbs/test_full.csv b/old_docs/eval_jupyter_nbs/test_full.csv deleted file mode 100644 index 4ef48fcc..00000000 --- a/old_docs/eval_jupyter_nbs/test_full.csv +++ /dev/null @@ -1,34 +0,0 @@ -F1Score,FalseNeg,FalsePos,Precision,Recall,TruePos,imageID,iou_field,nadir-category -1.0,0,0,1.0,1.0,59,Atlanta_nadir8_catid_10300100023BC100_743501_3721539,iou_score,Nadir -1.0,0,0,1.0,1.0,8,Atlanta_nadir8_catid_10300100023BC100_743501_3730089,iou_score,Nadir -1.0,0,0,1.0,1.0,59,Atlanta_nadir8_catid_10300100023BC100_743501_3730539,iou_score,Nadir -1.0,0,0,1.0,1.0,18,Atlanta_nadir8_catid_10300100023BC100_743501_3729639,iou_score,Nadir -1.0,0,0,1.0,1.0,21,Atlanta_nadir8_catid_10300100023BC100_743501_3729189,iou_score,Nadir -1.0,0,0,1.0,1.0,96,Atlanta_nadir8_catid_10300100023BC100_743501_3743139,iou_score,Nadir -1.0,0,0,1.0,1.0,43,Atlanta_nadir8_catid_10300100023BC100_743501_3731439,iou_score,Nadir -1.0,0,0,1.0,1.0,55,Atlanta_nadir8_catid_10300100023BC100_743501_3725139,iou_score,Nadir -1.0,0,0,1.0,1.0,72,Atlanta_nadir8_catid_10300100023BC100_743501_3724689,iou_score,Nadir -1.0,0,0,1.0,1.0,122,Atlanta_nadir8_catid_10300100023BC100_743501_3739539,iou_score,Nadir -1.0,0,0,1.0,1.0,78,Atlanta_nadir8_catid_10300100023BC100_743501_3741789,iou_score,Nadir -1.0,0,0,1.0,1.0,96,Atlanta_nadir8_catid_10300100023BC100_743501_3721089,iou_score,Nadir -1.0,0,0,1.0,1.0,134,Atlanta_nadir8_catid_10300100023BC100_743501_3742689,iou_score,Nadir -1.0,0,0,1.0,1.0,1,Atlanta_nadir8_catid_10300100023BC100_743501_3732339,iou_score,Nadir -1.0,0,0,1.0,1.0,144,Atlanta_nadir8_catid_10300100023BC100_743501_3739089,iou_score,Nadir -1.0,0,0,1.0,1.0,60,Atlanta_nadir8_catid_10300100023BC100_743501_3724239,iou_score,Nadir -1.0,0,0,1.0,1.0,45,Atlanta_nadir8_catid_10300100023BC100_743501_3734139,iou_score,Nadir -1.0,0,0,1.0,1.0,28,Atlanta_nadir8_catid_10300100023BC100_743501_3725589,iou_score,Nadir -1.0,0,0,1.0,1.0,67,Atlanta_nadir8_catid_10300100023BC100_743501_3733239,iou_score,Nadir -1.0,0,0,1.0,1.0,154,Atlanta_nadir8_catid_10300100023BC100_743501_3738639,iou_score,Nadir -1.0,0,0,1.0,1.0,117,Atlanta_nadir8_catid_10300100023BC100_743501_3736839,iou_score,Nadir -1.0,0,0,1.0,1.0,67,Atlanta_nadir8_catid_10300100023BC100_743501_3728739,iou_score,Nadir -1.0,0,0,1.0,1.0,101,Atlanta_nadir8_catid_10300100023BC100_743501_3740439,iou_score,Nadir -1.0,0,0,1.0,1.0,52,Atlanta_nadir8_catid_10300100023BC100_743501_3734589,iou_score,Nadir -1.0,0,0,1.0,1.0,139,Atlanta_nadir8_catid_10300100023BC100_743501_3737289,iou_score,Nadir -1.0,0,0,1.0,1.0,112,Atlanta_nadir8_catid_10300100023BC100_743501_3739989,iou_score,Nadir -1.0,0,0,1.0,1.0,71,Atlanta_nadir8_catid_10300100023BC100_743501_3722889,iou_score,Nadir -1.0,0,0,1.0,1.0,3,Atlanta_nadir8_catid_10300100023BC100_743501_3727839,iou_score,Nadir -1.0,0,0,1.0,1.0,121,Atlanta_nadir8_catid_10300100023BC100_743501_3723789,iou_score,Nadir -1.0,0,0,1.0,1.0,2,Atlanta_nadir8_catid_10300100023BC100_743501_3726489,iou_score,Nadir -1.0,0,0,1.0,1.0,91,Atlanta_nadir8_catid_10300100023BC100_743501_3722439,iou_score,Nadir -1.0,0,0,1.0,1.0,3,Atlanta_nadir8_catid_10300100023BC100_743501_3726039,iou_score,Nadir -1.0,0,0,1.0,1.0,80,Atlanta_nadir8_catid_10300100023BC100_743501_3735489,iou_score,Nadir diff --git a/old_docs/geodata_docs/Makefile b/old_docs/geodata_docs/Makefile deleted file mode 100644 index 69fe55ec..00000000 --- a/old_docs/geodata_docs/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line. -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) \ No newline at end of file diff --git a/old_docs/geodata_docs/make.bat b/old_docs/geodata_docs/make.bat deleted file mode 100644 index 543c6b13..00000000 --- a/old_docs/geodata_docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@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% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% - -:end -popd diff --git a/old_docs/geodata_docs/source/api.rst b/old_docs/geodata_docs/source/api.rst deleted file mode 100644 index 0f818057..00000000 --- a/old_docs/geodata_docs/source/api.rst +++ /dev/null @@ -1,75 +0,0 @@ -.. title:: API reference - -CosmiQ Works GeoData API reference -===================================== - -.. contents:: - -cw-geodata class and function list ----------------------------------- - -.. autosummary:: - - cw_geodata.raster_image.image.get_geo_transform - cw_geodata.vector_label.polygon.affine_transform_gdf - cw_geodata.vector_label.polygon.convert_poly_coords - cw_geodata.vector_label.polygon.geojson_to_px_gdf - cw_geodata.vector_label.polygon.georegister_px_df - cw_geodata.vector_label.polygon.get_overlapping_subset - cw_geodata.vector_label.graph.geojson_to_graph - cw_geodata.vector_label.graph.get_nodes_paths - cw_geodata.vector_label.graph.process_linestring - cw_geodata.vector_label.mask.boundary_mask - cw_geodata.vector_label.mask.contact_mask - cw_geodata.vector_label.mask.df_to_px_mask - cw_geodata.vector_label.mask.footprint_mask - cw_geodata.utils.geo.geometries_internal_intersection - cw_geodata.utils.geo.list_to_affine - cw_geodata.utils.geo.split_multi_geometries - - - -Raster/Image functionality --------------------------- - -Image submodule -~~~~~~~~~~~~~~~ - -.. automodule:: cw_geodata.raster_image.image - :members: - -Vector/Label functionality --------------------------- - -Polygon submodule -~~~~~~~~~~~~~~~~~ - -.. automodule:: cw_geodata.vector_label.polygon - :members: - -Graph submodule -~~~~~~~~~~~~~~~ - -.. automodule:: cw_geodata.vector_label.graph - :members: - -Mask submodule -~~~~~~~~~~~~~~ - -.. automodule:: cw_geodata.vector_label.mask - :members: - -Utility functions ------------------ - -Core utility submodule -~~~~~~~~~~~~~~~~~~~~~~ - -.. automodule:: cw_geodata.utils.core - :members: - -Geo utility submodule -~~~~~~~~~~~~~~~~~~~~~ - -.. automodule:: cw_geodata.utils.geo - :members: diff --git a/old_docs/geodata_docs/source/conf.py b/old_docs/geodata_docs/source/conf.py deleted file mode 100644 index 44e27012..00000000 --- a/old_docs/geodata_docs/source/conf.py +++ /dev/null @@ -1,197 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Configuration file for the Sphinx documentation builder. -# -# This file does only contain a selection of the most common options. For a -# full list see the documentation: -# http://www.sphinx-doc.org/en/master/config - -# -- 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('.')) - - -# -- Project information ----------------------------------------------------- - -project = 'cw-geodata' -copyright = '2019, CosmiQ Works' -author = 'CosmiQ Works' - -# The short X.Y version -version = '0.1' -# The full version, including alpha/beta/rc tags -release = '0.1.0' - - -# -- 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.napoleon', - 'sphinx.ext.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.intersphinx', - 'sphinx.ext.mathjax', - 'sphinx.ext.viewcode', - 'sphinx.ext.githubpages', - 'sphinx.ext.autosummary' -] - -# 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 master toctree document. -master_doc = 'index' - -# 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 - -# 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 = ['_build', 'Thumbs.db', '.DS_Store', '**.ipynb_checkpoints'] - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = None - - -# -- 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' - -# 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 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'] - -# Custom sidebar templates, must be a dictionary that maps document names -# to template names. -# -# The default sidebars (for documents that don't match any pattern) are -# defined by theme itself. Builtin themes are using these templates by -# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', -# 'searchbox.html']``. -# -# html_sidebars = {} - - -# -- Options for HTMLHelp output --------------------------------------------- - -# Output file base name for HTML help builder. -htmlhelp_basename = 'cw-geodatadoc' - - -# -- 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, 'cw-geodata.tex', 'cw-geodata Documentation', - 'CosmiQ Works', 'manual'), -] - - -# -- 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, 'cw-geodata', 'cw-geodata Documentation', - [author], 1) -] - - -# -- 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, 'cw-geodata', 'cw-geodata Documentation', - author, 'cw-geodata', 'CosmiQ Works Tools for coversion between CV and GeoSpatial formats.', - 'Miscellaneous'), -] - - -# -- Options for Epub output ------------------------------------------------- - -# Bibliographic Dublin Core info. -epub_title = project - -# The unique identifier of the text. This can be a ISBN number -# or the project homepage. -# -# epub_identifier = '' - -# A unique identification for the text. -# -# epub_uid = '' - -# A list of files that should not be packed into the epub file. -epub_exclude_files = ['search.html'] - - -# -- Extension configuration ------------------------------------------------- - -# -- Options for intersphinx extension --------------------------------------- - -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = intersphinx_mapping = { - "python": ('https://docs.python.org/', None), - "rasterio": ('https://rasterio.readthedocs.io/en/latest/', None), - "pandas": ('http://pandas.pydata.org/pandas-docs/stable/', None), - "geopandas": ('http://geopandas.org/', None), - "rtree": ('http://toblerity.org/rtree/', None), - "shapely": ('https://shapely.readthedocs.io/en/stable/', None), - "networkx": ('https://networkx.github.io/documentation/stable/', None) - } diff --git a/old_docs/geodata_docs/source/index.rst b/old_docs/geodata_docs/source/index.rst deleted file mode 100644 index 7f8ef9f5..00000000 --- a/old_docs/geodata_docs/source/index.rst +++ /dev/null @@ -1,22 +0,0 @@ -CosmiQ Works Evaluation (`cw-geodata `__) Documentation -============================================================================================= -:Author: `CosmiQ Works `__ -:Release: |release| -:Copyright: 2018, CosmiQ Works -:License: This work is licensed under an `Apache 2.0 License`__. - -.. __: https://www.apache.org/licenses/LICENSE-2.0 - -.. toctree:: - :name: mastertoc - :maxdepth: 3 - :glob: - - api - -Indices and tables ------------------- - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/old_docs/nets_docs/Makefile b/old_docs/nets_docs/Makefile deleted file mode 100644 index 69fe55ec..00000000 --- a/old_docs/nets_docs/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line. -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) \ No newline at end of file diff --git a/old_docs/nets_docs/make.bat b/old_docs/nets_docs/make.bat deleted file mode 100644 index 543c6b13..00000000 --- a/old_docs/nets_docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@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% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% - -:end -popd diff --git a/old_docs/nets_docs/source/conf.py b/old_docs/nets_docs/source/conf.py deleted file mode 100644 index 1297a35b..00000000 --- a/old_docs/nets_docs/source/conf.py +++ /dev/null @@ -1,185 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Configuration file for the Sphinx documentation builder. -# -# This file does only contain a selection of the most common options. For a -# full list see the documentation: -# http://www.sphinx-doc.org/en/master/config - -# -- 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('.')) - - -# -- Project information ----------------------------------------------------- - -project = 'cw-nets' -copyright = '2019, CosmiQ Works' -author = 'CosmiQ Works' - -# The short X.Y version -version = '' -# The full version, including alpha/beta/rc tags -release = '0.1.0' - - -# -- 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.autodoc', - 'sphinx.ext.intersphinx', - 'sphinx.ext.ifconfig', - 'sphinx.ext.viewcode', -] - -# 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 master toctree document. -master_doc = 'index' - -# 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 - -# 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 = [] - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = None - - -# -- 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 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'] - -# Custom sidebar templates, must be a dictionary that maps document names -# to template names. -# -# The default sidebars (for documents that don't match any pattern) are -# defined by theme itself. Builtin themes are using these templates by -# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', -# 'searchbox.html']``. -# -# html_sidebars = {} - - -# -- Options for HTMLHelp output --------------------------------------------- - -# Output file base name for HTML help builder. -htmlhelp_basename = 'cw-netsdoc' - - -# -- 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, 'cw-nets.tex', 'cw-nets Documentation', - 'CosmiQ Works', 'manual'), -] - - -# -- 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, 'cw-nets', 'cw-nets Documentation', - [author], 1) -] - - -# -- 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, 'cw-nets', 'cw-nets Documentation', - author, 'cw-nets', 'One line description of project.', - 'Miscellaneous'), -] - - -# -- Options for Epub output ------------------------------------------------- - -# Bibliographic Dublin Core info. -epub_title = project - -# The unique identifier of the text. This can be a ISBN number -# or the project homepage. -# -# epub_identifier = '' - -# A unique identification for the text. -# -# epub_uid = '' - -# A list of files that should not be packed into the epub file. -epub_exclude_files = ['search.html'] - - -# -- Extension configuration ------------------------------------------------- - -# -- Options for intersphinx extension --------------------------------------- - -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'https://docs.python.org/': None} \ No newline at end of file diff --git a/old_docs/nets_docs/source/index.rst b/old_docs/nets_docs/source/index.rst deleted file mode 100644 index 0ba790e4..00000000 --- a/old_docs/nets_docs/source/index.rst +++ /dev/null @@ -1,20 +0,0 @@ -.. cw-nets documentation master file, created by - sphinx-quickstart on Wed Jan 23 15:30:41 2019. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to cw-nets's documentation! -=================================== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - - - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/old_docs/tiler_docs/Makefile b/old_docs/tiler_docs/Makefile deleted file mode 100644 index 298ea9e2..00000000 --- a/old_docs/tiler_docs/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -SOURCEDIR = . -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) \ No newline at end of file diff --git a/old_docs/tiler_docs/_build/doctrees/environment.pickle b/old_docs/tiler_docs/_build/doctrees/environment.pickle deleted file mode 100644 index 2a3c8a94..00000000 Binary files a/old_docs/tiler_docs/_build/doctrees/environment.pickle and /dev/null differ diff --git a/old_docs/tiler_docs/_build/doctrees/index.doctree b/old_docs/tiler_docs/_build/doctrees/index.doctree deleted file mode 100644 index 892f9fee..00000000 Binary files a/old_docs/tiler_docs/_build/doctrees/index.doctree and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/.buildinfo b/old_docs/tiler_docs/_build/html/.buildinfo deleted file mode 100644 index edd87f39..00000000 --- a/old_docs/tiler_docs/_build/html/.buildinfo +++ /dev/null @@ -1,4 +0,0 @@ -# Sphinx build info version 1 -# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: e65b7f52c81b3b2685590df30144fec4 -tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/old_docs/tiler_docs/_build/html/.nojekyll b/old_docs/tiler_docs/_build/html/.nojekyll deleted file mode 100644 index e69de29b..00000000 diff --git a/old_docs/tiler_docs/_build/html/_modules/cw_tiler/main.html b/old_docs/tiler_docs/_build/html/_modules/cw_tiler/main.html deleted file mode 100644 index 57a7fb6a..00000000 --- a/old_docs/tiler_docs/_build/html/_modules/cw_tiler/main.html +++ /dev/null @@ -1,592 +0,0 @@ - - - - - - - - - - - cw_tiler.main — cw-tiler 0.2.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -
- - - - - -
- -
- - - - - - - - - - - - - - - - - -
- - - - -
-
-
-
- -

Source code for cw_tiler.main

-import os
-import rasterio
-from rasterio.warp import transform_bounds
-from rasterio.io import DatasetReader
-import math
-from rio_tiler.errors import TileOutsideBounds
-from . import utils
-import numpy as np
-
-
-
[docs]def tile_utm_source(src, ll_x, ll_y, ur_x, ur_y, indexes=None, tilesize=256, - nodata=None, alpha=None, dst_crs='epsg:4326'): - """ - Create a UTM tile from a :py:class:`rasterio.Dataset` in memory. - - Arguments - --------- - src : :py:class:`rasterio.Dataset` - Source imagery dataset to tile. - ll_x : int or float - Lower left x position (i.e. Western bound). - ll_y : int or f - loat - Lower left y position (i.e. Southern bound). - ur_x : int or float - Upper right x position (i.e. Eastern bound). - ur_y : int or float - Upper right y position (i.e. Northern bound). - indexes : tuple of 3 ints, optional - Band indexes for the output. By default, extracts all of the - indexes from `src`. - tilesize : int, optional - Output image X and Y pixel extent. Defaults to ``256``. - nodata : int or float, optional - Value to use for `nodata` pixels during tiling. By default, uses - the existing `nodata` value in `src`. - alpha : int, optional - Alpha band index for tiling. By default, uses the same band as - specified by `src`. - dst_crs : str, optional - Coordinate reference system for output. Defaults to ``"epsg:4326"``. - - Returns - ------- - ``(data, mask, window, window_transform)`` tuple. - data : :py:class:`numpy.ndarray` - int pixel values. Shape is ``(C, Y, X)`` if retrieving multiple - channels, ``(Y, X)`` otherwise. - mask : :py:class:`numpy.ndarray` - int mask indicating which pixels contain information and which are - `nodata`. Pixels containing data have value ``255``, `nodata` - pixels have value ``0``. - window : :py:class:`rasterio.windows.Window` - :py:class:`rasterio.windows.Window` object indicating the raster - location of the dataset subregion being returned in `data`. - window_transform : :py:class:`affine.Affine` - Affine transformation for the window. - - """ - - wgs_bounds = transform_bounds( - *[src.crs, dst_crs] + list(src.bounds), densify_pts=21) - - indexes = indexes if indexes is not None else src.indexes - tile_bounds = (ll_x, ll_y, ur_x, ur_y) - if not utils.tile_exists_utm(wgs_bounds, tile_bounds): - raise TileOutsideBounds( - 'Tile {}/{}/{}/{} is outside image bounds'.format(tile_bounds)) - - return utils.tile_read_utm(src, tile_bounds, tilesize, indexes=indexes, - nodata=nodata, alpha=alpha, dst_crs=dst_crs)
- - -
[docs]def tile_utm(source, ll_x, ll_y, ur_x, ur_y, indexes=None, tilesize=256, - nodata=None, alpha=None, dst_crs='epsg:4326'): - """ - Create a UTM tile from a file or a :py:class:`rasterio.Dataset` in memory. - - This function is a wrapper around :func:`tile_utm_source` to enable passing - of file paths instead of pre-loaded :py:class:`rasterio.Dataset` s. - - Arguments - --------- - source : :py:class:`rasterio.Dataset` - Source imagery dataset to tile. - ll_x : int or float - Lower left x position (i.e. Western bound). - ll_y : int or float - Lower left y position (i.e. Southern bound). - ur_x : int or float - Upper right x position (i.e. Eastern bound). - ur_y : int or float - Upper right y position (i.e. Northern bound). - indexes : tuple of 3 ints, optional - Band indexes for the output. By default, extracts all of the - indexes from `source` . - tilesize : :obj:`int`, optional - Output image X and Y pixel extent. Defaults to ``256``. - nodata : int or float, optional - Value to use for ``nodata`` pixels during tiling. By default, uses - the existing ``nodata`` value in `src`. - alpha : :obj:`int`, optional - Alpha band index for tiling. By default, uses the same band as - specified by `src`. - dst_crs : str, optional - Coordinate reference system for output. Defaults to ``"epsg:4326"``. - - Returns - ------- - ``(data, mask, window, window_transform`` tuple. - data : :py:class:`numpy.ndarray` - int pixel values. Shape is ``(C, Y, X)`` if retrieving multiple - channels, ``(Y, X)`` otherwise. - mask : :class:`numpy.ndarray` - int mask indicating which pixels contain information and which are - `nodata`. Pixels containing data have value ``255``, `nodata` pixels - have value ``0``. - window : :py:class:`rasterio.windows.Window` - :py:class:`rasterio.windows.Window` object indicating the raster - location of the dataset subregion being returned in `data`. - window_transform : :py:class:`affine.Affine` - Affine transformation for the window. - - """ - - if isinstance(source, DatasetReader): - src = source - elif os.path.exists(source): - src = rasterio.open(source) # read in the file - else: - raise ValueError('Source is not a rasterio.Dataset or a valid path.') - - return tile_utm_source(src, ll_x, ll_y, ur_x, ur_y, indexes=indexes, - tilesize=tilesize, nodata=nodata, alpha=alpha, - dst_crs=dst_crs)
- - -
[docs]def get_chip(source, ll_x, ll_y, gsd, - utm_crs='', - indexes=None, - tilesize=256, - nodata=None, - alpha=None): - """Get an image tile of specific pixel size. - - This wrapper function permits passing of `ll_x`, `ll_y`, `gsd`, and - `tile_size_pixels` in place of boundary coordinates to extract an image - region of defined pixel extent. - - Arguments - --------- - source : :py:class:`rasterio.Dataset` - Source imagery dataset to tile. - ll_x : int or float - Lower left x position (i.e. Western bound). - ll_y : int or float - Lower left y position (i.e. Southern bound). - gsd : float - Ground sample distance of the source imagery in meter/pixel units. - utm_crs : :py:class:`rasterio.crs.CRS`, optional - UTM coordinate reference system string for the imagery. If not - provided, this is calculated using - :func:`cw_tiler.utils.get_wgs84_bounds` and - :func:`cw_tiler.utils.calculate_UTM_crs` . - indexes : tuple of 3 ints, optional - Band indexes for the output. By default, extracts all of the - indexes from `source`. - tilesize : int, optional - Output image X and Y pixel extent. Defaults to ``256`` . - nodata : int or float, optional - Value to use for `nodata` pixels during tiling. By default, uses - the existing `nodata` value in `source`. - alpha : int, optional - Alpha band index for tiling. By default, uses the same band as - specified by `source`. - - Returns - ------- - ``(data, mask, window, window_transform`` tuple. - data : :class:`numpy.ndarray` - int pixel values. Shape is ``(C, Y, X)`` if retrieving multiple - channels, ``(Y, X)`` otherwise. - mask : :class:`numpy.ndarray` - int mask indicating which pixels contain information and which are - `nodata`. Pixels containing data have value ``255``, `nodata` pixels - have value ``0``. - window : :py:class:`rasterio.windows.Window` - :py:class:`rasterio.windows.Window` object indicating the raster - location of the dataset subregion being returned in `data`. - window_transform : :py:class:`affine.Affine` - Affine transformation for the window. - """ - - ur_x = ll_x + gsd * tilesize - ur_y = ll_y + gsd * tilesize - - if isinstance(source, DatasetReader): - src = source - else: - src = rasterio.open(source) - - if not utm_crs: - wgs_bounds = utils.get_wgs84_bounds(src) - utm_crs = utils.calculate_UTM_crs(wgs_bounds) - - return tile_utm(source, ll_x, ll_y, ur_x, ur_y, indexes=indexes, - tilesize=tilesize, nodata=nodata, alpha=alpha, - dst_crs=utm_crs)
- - -
[docs]def calculate_anchor_points(utm_bounds, stride_size_meters=400, extend=False, - quad_space=False): - """Get anchor point (lower left corner of bbox) for chips from a tile. - - Arguments - --------- - utm_bounds : tuple of 4 floats - A :obj:`tuple` of shape ``(min_x, min_y, max_x, max_y)`` that defines - the spatial extent of the tile to be split. Coordinates should be in - UTM. - stride_size_meters : int, optional - Stride size in both X and Y directions for generating chips. Defaults - to ``400``. - extend : bool, optional - Defines whether UTM boundaries should be rounded to the nearest integer - outward from `utm_bounds` (`extend` == ``True``) or - inward from `utm_bounds` (`extend` == ``False``). Defaults - to ``False`` (inward). - quad_space : bool, optional - If tiles will overlap by no more than half their X and/or Y extent in - each direction, `quad_space` can be used to split chip - anchors into four non-overlapping subsets. For example, if anchor - points are 400m apart and each chip will be 800m by 800m, `quad_space` - will generate four sets which do not internally overlap; - however, this would fail if tiles are 900m by 900m. Defaults to - ``False``, in which case the returned ``anchor_point_list_dict`` will - comprise a single list of anchor points. - - Returns - ------- - anchor_point_list_dict : dict of list(s) of lists - - If ``quad_space==True`` , `anchor_point_list_dict` is a - :obj:`dict` with four keys ``[0, 1, 2, 3]`` corresponding to the four - subsets of chips generated (see `quad_space` ). If - ``quad_space==False`` , `anchor_point_list_dict` is a - :obj:`dict` with a single key, ``0`` , that corresponds to a list of all - of the generated anchor points. Each anchor point in the list(s) is an - ``[x, y]`` pair of UTM coordinates denoting the SW corner of a chip. - - """ - if extend: - min_x = math.floor(utm_bounds[0]) - min_y = math.floor(utm_bounds[1]) - max_x = math.ceil(utm_bounds[2]) - max_y = math.ceil(utm_bounds[3]) - else: - print("NoExtend") - print('UTM_Bounds: {}'.format(utm_bounds)) - min_x = math.ceil(utm_bounds[0]) - min_y = math.ceil(utm_bounds[1]) - max_x = math.floor(utm_bounds[2]) - max_y = math.floor(utm_bounds[3]) - - if quad_space: - print("quad_space") - row_cell = np.asarray([[0, 1], [2, 3]]) - anchor_point_list_dict = {0: [], 1: [], 2: [], 3: []} - else: - anchor_point_list_dict = {0: []} - - for rowidx, x in enumerate(np.arange(min_x, max_x, stride_size_meters)): - for colidx, y in enumerate(np.arange(min_y, max_y, - stride_size_meters)): - if quad_space: - anchor_point_list_dict[ - row_cell[rowidx % 2, colidx % 2]].append([x, y]) - else: - anchor_point_list_dict[0].append([x, y]) - - return anchor_point_list_dict
- - -
[docs]def calculate_cells(anchor_point_list_dict, cell_size_meters, utm_bounds=[]): - """ Calculate boundaries for image cells (chips) from anchor points. - - This function takes the output from :func:`calculate_anchor_points` as well - as a desired cell size (`cell_size_meters`) and outputs - ``(W, S, E, N)`` tuples for generating cells. - - Arguments - --------- - anchor_point_list_dict : dict - Output of :func:`calculate_anchor_points`. See that function for - details. - cell_size_meters : int or float - Desired width and height of each cell in meters. - utm_bounds : list -like of float s, optional - A :obj:`list`-like of shape ``(W, S, E, N)`` that defines the limits - of an input image tile in UTM coordinates to ensure that no cells - extend beyond those limits. If not provided, all cells will be included - even if they extend beyond the UTM limits of the source imagery. - - Returns - ------- - cells_list_dict : dict of list(s) of lists - A dict whose keys are either ``0`` or ``[0, 1, 2, 3]`` (see - :func:`calculate_anchor_points` . ``quad_space`` ), and whose values are - :obj:`list` s of boundaries in the shape ``[W, S, E, N]`` . Boundaries - are in UTM coordinates. - - """ - cells_list_dict = {} - for anchor_point_list_id, anchor_point_list in anchor_point_list_dict.items(): - cells_list = [] - for anchor_point in anchor_point_list: - if utm_bounds: - if (anchor_point[0] + cell_size_meters < utm_bounds[2]) and ( - anchor_point[1] + cell_size_meters < utm_bounds[3]): - cells_list.append([anchor_point[0], anchor_point[1], - anchor_point[0] + cell_size_meters, - anchor_point[1] + cell_size_meters]) - else: - pass - else: # include it regardless of extending beyond bounds - cells_list.append([anchor_point[0], anchor_point[1], - anchor_point[0] + cell_size_meters, - anchor_point[1] + cell_size_meters]) - - cells_list_dict[anchor_point_list_id] = cells_list - - return cells_list_dict
- - -
[docs]def calculate_analysis_grid(utm_bounds, stride_size_meters=300, - cell_size_meters=400, quad_space=False, - snapToGrid=False): - """Wrapper for :func:`calculate_anchor_points` and :func:`calculate_cells`. - - Based on UTM boundaries of an image tile, stride size, and cell size, - output a dictionary of boundary lists for analysis chips. - - Arguments - --------- - utm_bounds : list-like of shape ``(W, S, E, N)`` - UTM coordinate limits of the input tile. - stride_size_meters : int, optional - Step size in both X and Y directions between cells in units of meters. - Defaults to ``300`` . - cell_size_meters : int, optional - Extent of each cell in both X and Y directions in units of meters. - Defaults to ``400`` . - quad_space : bool, optional - See :func:`calculate_anchor_points` . ``quad_space`` . Defaults to - ``False`` . - snapToGrid : bool, optional - .. :deprecated: 0.2.0 - This argument is deprecated and no longer has any effect. - - Returns - ------- - cells_list_dict : dict of list(s) of lists - A dict whose keys are either ``0`` or ``[0, 1, 2, 3]`` (see - :func:`calculate_anchor_points` . ``quad_space`` ), and whose values are - :obj:`list` s of boundaries in the shape ``[W, S, E, N]`` . Boundaries - are in UTM coordinates. - - """ - anchor_point_list_dict = calculate_anchor_points( - utm_bounds, stride_size_meters=stride_size_meters, - quad_space=quad_space) - cells_list_dict = calculate_cells(anchor_point_list_dict, cell_size_meters, - utm_bounds=utm_bounds) - return cells_list_dict
- - -if __name__ == '__main__': - utmX, utmY = 658029, 4006947 - cll_x = utmX - cur_x = utmX + 500 - cll_y = utmY - cur_y = utmY + 500 - stride_size_meters = 300 - cell_size_meters = 400 - ctile_size_pixels = 1600 - spacenetPath = "s3://spacenet-dataset/AOI_2_Vegas/srcData/rasterData/AOI_2_Vegas_MUL-PanSharpen_Cloud.tif" - address = spacenetPath - - with rasterio.open(address) as src: - cwgs_bounds = utils.get_wgs84_bounds(src) - cutm_crs = utils.calculate_UTM_crs(cwgs_bounds) - cutm_bounds = utils.get_utm_bounds(src, cutm_crs) - - #ccells_list = calculate_analysis_grid(cutm_bounds, stride_size_meters=stride_size_meters, - # cell_size_meters=cell_size_meters) - - #random_cell = random.choice(ccells_list) - #cll_x, cll_y, cur_x, cur_y = random_cell - tile, mask, window, window_transform = tile_utm(src, cll_x, cll_y, cur_x, cur_y, indexes=None, tilesize=ctile_size_pixels, nodata=None, alpha=None, - dst_crs=cutm_crs) -
- -
- -
- - -
-
- -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/old_docs/tiler_docs/_build/html/_modules/cw_tiler/utils.html b/old_docs/tiler_docs/_build/html/_modules/cw_tiler/utils.html deleted file mode 100644 index 6f8c16b1..00000000 --- a/old_docs/tiler_docs/_build/html/_modules/cw_tiler/utils.html +++ /dev/null @@ -1,533 +0,0 @@ - - - - - - - - - - - cw_tiler.utils — cw-tiler 0.2.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -
- - - - - -
- -
- - - - - - - - - - - - - - - - - -
- - - - -
-
-
-
- -

Source code for cw_tiler.utils

-"""cw_tiler.utils: utility functions for raster files."""
-
-
-import numpy as np
-import rasterio
-from rasterio.vrt import WarpedVRT
-from rasterio.enums import Resampling
-from rasterio.io import DatasetReader
-from rasterio.warp import transform_bounds
-from rio_tiler.errors import RioTilerError
-from rasterio import windows
-from rasterio import transform
-from shapely.geometry import box
-import statistics
-
-
-
[docs]def utm_getZone(longitude): - """Calculate UTM Zone from Longitude. - - Arguments - --------- - longitude: float - longitude coordinate (Degrees.decimal degrees) - - Returns - ------- - out: int - UTM Zone number. - - """ - - return (int(1+(longitude+180.0)/6.0))
- - -
[docs]def utm_isNorthern(latitude): - """Determine if a latitude coordinate is in the northern hemisphere. - - Arguments - --------- - latitude: float - latitude coordinate (Deg.decimal degrees) - - Returns - ------- - out: bool - ``True`` if `latitude` is in the northern hemisphere, ``False`` - otherwise. - - """ - - return (latitude > 0.0)
- - -
[docs]def calculate_UTM_crs(coords): - """Calculate UTM Projection String. - - Arguments - --------- - coords: list - ``[longitude, latitude]`` or - ``[min_longitude, min_latitude, max_longitude, max_latitude]`` . - - Returns - ------- - out: str - returns `proj4 projection string <https://proj4.org/usage/quickstart.html>`__ - - """ - - if len(coords) == 2: - longitude, latitude = coords - elif len(coords) == 4: - longitude = statistics.mean([coords[0], coords[2]]) - latitude = statistics.mean([coords[1], coords[3]]) - - utm_zone = utm_getZone(longitude) - - utm_isNorthern(latitude) - - if utm_isNorthern(latitude): - direction_indicator = "+north" - else: - direction_indicator = "+south" - - utm_crs = "+proj=utm +zone={} {} +ellps=WGS84 +datum=WGS84 +units=m +no_defs".format(utm_zone, - direction_indicator) - - return utm_crs
- - -
[docs]def get_utm_vrt(source, crs='EPSG:3857', resampling=Resampling.bilinear, - src_nodata=None, dst_nodata=None): - """Get a :py:class:`rasterio.vrt.WarpedVRT` projection of a dataset. - - Arguments - --------- - source : :py:class:`rasterio.io.DatasetReader` - The dataset to virtually warp using :py:class:`rasterio.vrt.WarpedVRT`. - crs : :py:class:`rasterio.crs.CRS`, optional - Coordinate reference system for the VRT. Defaults to 'EPSG:3857' - (Web Mercator). - resampling : :py:class:`rasterio.enums.Resampling` method, optional - Resampling method to use. Defaults to - :py:func:`rasterio.enums.Resampling.bilinear`. Alternatives include - :py:func:`rasterio.enums.Resampling.average`, - :py:func:`rasterio.enums.Resampling.cubic`, and others. See docs for - :py:class:`rasterio.enums.Resampling` for more information. - src_nodata : int or float, optional - Source nodata value which will be ignored for interpolation. Defaults - to ``None`` (all data used in interpolation). - dst_nodata : int or float, optional - Destination nodata value which will be ignored for interpolation. - Defaults to ``None``, in which case the value of `src_nodata` will be - used if provided, or ``0`` otherwise. - - Returns - ------- - A :py:class:`rasterio.vrt.WarpedVRT` instance with the transformation. - - """ - - vrt_params = dict( - crs=crs, - resampling=Resampling.bilinear, - src_nodata=src_nodata, - dst_nodata=dst_nodata) - - return WarpedVRT(source, **vrt_params)
- - -
[docs]def get_utm_vrt_profile(source, crs='EPSG:3857', - resampling=Resampling.bilinear, - src_nodata=None, dst_nodata=None): - """Get a :py:class:`rasterio.profiles.Profile` for projection of a VRT. - - Arguments - --------- - source : :py:class:`rasterio.io.DatasetReader` - The dataset to virtually warp using :py:class:`rasterio.vrt.WarpedVRT`. - crs : :py:class:`rasterio.crs.CRS`, optional - Coordinate reference system for the VRT. Defaults to ``"EPSG:3857"`` - (Web Mercator). - resampling : :py:class:`rasterio.enums.Resampling` method, optional - Resampling method to use. Defaults to - ``rasterio.enums.Resampling.bilinear``. Alternatives include - ``rasterio.enums.Resampling.average``, - ``rasterio.enums.Resampling.cubic``, and others. See docs for - :py:class:`rasterio.enums.Resampling` for more information. - src_nodata : int or float, optional - Source nodata value which will be ignored for interpolation. Defaults - to ``None`` (all data used in interpolation). - dst_nodata : int or float, optional - Destination nodata value which will be ignored for interpolation. - Defaults to ``None``, in which case the value of `src_nodata` - will be used if provided, or ``0`` otherwise. - - Returns - ------- - A :py:class:`rasterio.profiles.Profile` instance with the transformation - applied. - - """ - - with get_utm_vrt(source, crs=crs, resampling=resampling, - src_nodata=src_nodata, dst_nodata=dst_nodata) as vrt: - vrt_profile = vrt.profile - - return vrt_profile
- - -
[docs]def tile_read_utm(source, bounds, tilesize, indexes=[1], nodata=None, - alpha=None, dst_crs='EPSG:3857', verbose=False, - boundless=False): - """Read data and mask. - - Arguments - --------- - source : str or :py:class:`rasterio.io.DatasetReader` - input file path or :py:class:`rasterio.io.DatasetReader` object. - bounds : ``(W, S, E, N)`` tuple - bounds in `dst_crs` . - tilesize : int - Length of one edge of the output tile in pixels. - indexes : list of ints or int, optional - Channel index(es) to output. Returns a 3D :py:class:`np.ndarray` of - shape (C, Y, X) if `indexes` is a list, or a 2D array if `indexes` is - an int channel index. Defaults to ``1``. - nodata: int or float, optional - nodata value to use in :py:class:`rasterio.vrt.WarpedVRT`. - Defaults to ``None`` (use all data in warping). - alpha: int, optional - Force alphaband if not present in the dataset metadata. Defaults to - ``None`` (don't force). - dst_crs: str, optional - Destination coordinate reference system. Defaults to ``"EPSG:3857"`` - (Web Mercator) - verbose : bool, optional - Verbose text output. Defaults to ``False``. - boundless : bool, optional - This argument is deprecated and should never be used. - - Returns - ------- - data : :py:class:`np.ndarray` - int pixel values. Shape is ``(C, Y, X)`` if retrieving multiple - channels, ``(Y, X)`` otherwise. - mask : :py:class:`np.ndarray` - int mask indicating which pixels contain information and which are - `nodata`. Pixels containing data have value ``255``, `nodata` - pixels have value ``0``. - window : :py:class:`rasterio.windows.Window` - :py:class:`rasterio.windows.Window` object indicating the raster - location of the dataset subregion being returned in `data`. - window_transform : :py:class:`affine.Affine` - Affine transformation for `window` . - - - - """ - w, s, e, n = bounds - if alpha is not None and nodata is not None: - raise RioTilerError('cannot pass alpha and nodata option') - - if isinstance(indexes, int): - indexes = [indexes] - out_shape = (len(indexes), tilesize, tilesize) - if verbose: - print(dst_crs) - vrt_params = dict(crs=dst_crs, resampling=Resampling.bilinear, - src_nodata=nodata, dst_nodata=nodata) - - if not isinstance(source, DatasetReader): - src = rasterio.open(source) - else: - src = source - with WarpedVRT(src, **vrt_params) as vrt: - window = vrt.window(w, s, e, n, precision=21) - if verbose: - print(window) - window_transform = transform.from_bounds(w, s, e, n, - tilesize, tilesize) - - data = vrt.read(window=window, - resampling=Resampling.bilinear, - out_shape=out_shape, - indexes=indexes) - if verbose: - print(bounds) - print(window) - print(out_shape) - print(indexes) - print(boundless) - print(window_transform) - - if nodata is not None: - mask = np.all(data != nodata, axis=0).astype(np.uint8) * 255 - elif alpha is not None: - mask = vrt.read(alpha, window=window, - out_shape=(tilesize, tilesize), - resampling=Resampling.bilinear) - else: - mask = vrt.read_masks(1, window=window, - out_shape=(tilesize, tilesize), - resampling=Resampling.bilinear) - return data, mask, window, window_transform
- - -
[docs]def tile_exists_utm(boundsSrc, boundsTile): - """Check if suggested tile is within bounds. - - Arguments - --------- - boundsSrc : list-like - Bounding box limits for the source data in the shape ``(W, S, E, N)``. - boundsTile : list-like - Bounding box limits for the target tile in the shape ``(W, S, E, N)``. - - Returns - ------- - bool - Do the `boundsSrc` and `boundsTile` bounding boxes overlap? - - """ - - boundsSrcBox = box(*boundsSrc) - boundsTileBox = box(*boundsTile) - - return boundsSrcBox.intersects(boundsTileBox)
- - -
[docs]def get_wgs84_bounds(source): - """Transform dataset bounds from source crs to wgs84. - - Arguments - --------- - source : str or :py:class:`rasterio.io.DatasetReader` - Source dataset to get bounds transformation for. Can either be a string - path to a dataset file or an opened - :py:class:`rasterio.io.DatasetReader`. - - Returns - ------- - wgs_bounds : tuple - Bounds tuple for `source` in wgs84 crs with shape ``(W, S, E, N)``. - - """ - if isinstance(source, DatasetReader): - src = source - else: - src = rasterio.open(source) - wgs_bounds = transform_bounds(*[src.crs, 'epsg:4326'] + - list(src.bounds), densify_pts=21) - return wgs_bounds
- - -
[docs]def get_utm_bounds(source, utm_EPSG): - """Transform bounds from source crs to a UTM crs. - - Arguments - --------- - source : str or :py:class:`rasterio.io.DatasetReader` - Source dataset. Can either be a string path to a dataset GeoTIFF or - a :py:class:`rasterio.io.DatasetReader` object. - utm_EPSG : str - :py:class:`rasterio.crs.CRS` string indicating the UTM crs to transform - into. - - Returns - ------- - utm_bounds : tuple - Bounding box limits in `utm_EPSG` crs coordinates with shape - ``(W, S, E, N)``. - - """ - if isinstance(source, DatasetReader): - src = source - else: - src = rasterio.open(source) - utm_bounds = transform_bounds(*[src.crs, utm_EPSG] + list(src.bounds), - densify_pts=21) - return utm_bounds
-
- -
- -
- - -
-
- -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/old_docs/tiler_docs/_build/html/_modules/cw_tiler/vector_utils.html b/old_docs/tiler_docs/_build/html/_modules/cw_tiler/vector_utils.html deleted file mode 100644 index 9595f069..00000000 --- a/old_docs/tiler_docs/_build/html/_modules/cw_tiler/vector_utils.html +++ /dev/null @@ -1,462 +0,0 @@ - - - - - - - - - - - cw_tiler.vector_utils — cw-tiler 0.2.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -
- - - - - -
- -
- - - - - - - - - - - - - - - - - -
- - - - -
-
-
-
- -

Source code for cw_tiler.vector_utils

-from shapely import geometry
-from shapely.geometry import box
-import geopandas as gpd
-from rasterio import features
-from rasterio import Affine
-import numpy as np
-# Note, for mac osx compatability import something from shapely.geometry before
-# importing fiona or geopandas: https://github.com/Toblerity/Shapely/issues/553
-
-
-
[docs]def read_vector_file(geoFileName): - """Read Fiona-Supported Files into GeoPandas GeoDataFrame. - - Warning - ---- - This will raise an exception for empty GeoJSON files, which GDAL and Fiona - cannot read. ``try/except`` the :py:exc:`Fiona.errors.DriverError` or - :py:exc:`Fiona._err.CPLE_OpenFailedError` if you must use this. - - """ - - return gpd.read_file(geoFileName)
- - -
[docs]def transformToUTM(gdf, utm_crs, estimate=True, calculate_sindex=True): - """Transform GeoDataFrame to UTM coordinate reference system. - - Arguments - --------- - gdf : :py:class:`geopandas.GeoDataFrame` - :py:class:`geopandas.GeoDataFrame` to transform. - utm_crs : str - :py:class:`rasterio.crs.CRS` string for destination UTM CRS. - estimate : bool, optional - .. deprecated:: 0.2.0 - This argument is no longer used. - calculate_sindex : bool, optional - .. deprecated:: 0.2.0 - This argument is no longer used. - - Returns - ------- - gdf : :py:class:`geopandas.GeoDataFrame` - The input :py:class:`geopandas.GeoDataFrame` converted to - `utm_crs` coordinate reference system. - - """ - - gdf = gdf.to_crs(utm_crs) - return gdf
- - -
[docs]def search_gdf_bounds(gdf, tile_bounds): - """Use `tile_bounds` to subset `gdf` and return the intersect. - - Arguments - --------- - gdf : :py:class:`geopandas.GeoDataFrame` - A :py:class:`geopandas.GeoDataFrame` of polygons to subset. - tile_bounds : tuple - A tuple of shape ``(W, S, E, N)`` that denotes the boundaries of a - tile. - - Returns - ------- - smallGdf : :py:class:`geopandas.GeoDataFrame` - The subset of `gdf` that overlaps with `tile_bounds` . - - """ - - tile_polygon = box(tile_bounds) - smallGdf = search_gdf_polygon(gdf, tile_polygon) - - return smallGdf
- - -
[docs]def search_gdf_polygon(gdf, tile_polygon): - """Find polygons in a GeoDataFrame that overlap with `tile_polygon` . - - Arguments - --------- - gdf : :py:class:`geopandas.GeoDataFrame` - A :py:class:`geopandas.GeoDataFrame` of polygons to search. - tile_polygon : :py:class:`shapely.geometry.Polygon` - A :py:class:`shapely.geometry.Polygon` denoting a tile's bounds. - - Returns - ------- - precise_matches : :py:class:`geopandas.GeoDataFrame` - The subset of `gdf` that overlaps with `tile_polygon` . If - there are no overlaps, this will return an empty - :py:class:`geopandas.GeoDataFrame`. - - """ - - sindex = gdf.sindex - possible_matches_index = list(sindex.intersection(tile_polygon.bounds)) - possible_matches = gdf.iloc[possible_matches_index] - precise_matches = possible_matches[ - possible_matches.intersects(tile_polygon) - ] - if precise_matches.empty: - precise_matches = gpd.GeoDataFrame(geometry=[]) - return precise_matches
- - -
[docs]def vector_tile_utm(gdf, tile_bounds, min_partial_perc=0.1, - geom_type="Polygon", use_sindex=True): - """Wrapper for :func:`clip_gdf` that converts `tile_bounds` to a polygon. - - Arguments - --------- - gdf : :class:`geopandas.GeoDataFrame` - A :py:class:`geopandas.GeoDataFrame` of polygons to clip. - tile_bounds : list-like of floats - :obj:`list` of shape ``(W, S, E, N)`` denoting the boundaries of an - imagery tile. Converted to a polygon for :func:`clip_gdf`. - min_partial_perc : float - The minimum fraction of an object in `gdf` that must be - preserved. Defaults to 0.0 (include any object if any part remains - following clipping). - use_sindex : bool, optional - Use the `gdf` sindex be used for searching. Improves efficiency - but requires `libspatialindex <http://libspatialindex.github.io/>`__ . - - Returns - ------- - small_gdf : :py:class:`geopandas.GeoDataFrame` - `gdf` with all contained objects clipped to `tile_bounds`. - See notes above for details on additional clipping columns added. - """ - tile_polygon = box(*tile_bounds) - small_gdf = clip_gdf(gdf, tile_polygon, - min_partial_perc=min_partial_perc, - geom_type=geom_type - ) - - return small_gdf
- - -def getCenterOfGeoFile(gdf, estimate=True): - - #TODO implement calculate UTM from gdf see osmnx implementation - - pass - - -
[docs]def clip_gdf(gdf, poly_to_cut, min_partial_perc=0.0, geom_type="Polygon", - use_sindex=True): - """Clip GDF to a provided polygon. - - Note - ---- - Clips objects within `gdf` to the region defined by - `poly_to_cut`. Also adds several columns to the output: - - `origarea` - The original area of the polygons (only used if `geom_type` == - ``"Polygon"``). - `origlen` - The original length of the objects (only used if `geom_type` == - ``"LineString"``). - `partialDec` - The fraction of the object that remains after clipping - (fraction of area for Polygons, fraction of length for - LineStrings.) Can filter based on this by using `min_partial_perc`. - `truncated` - Boolean indicator of whether or not an object was clipped. - - Arguments - --------- - gdf : :py:class:`geopandas.GeoDataFrame` - A :py:class:`geopandas.GeoDataFrame` of polygons to clip. - poly_to_cut : :py:class:`shapely.geometry.Polygon` - The polygon to clip objects in `gdf` to. - min_partial_perc : float, optional - The minimum fraction of an object in `gdf` that must be - preserved. Defaults to 0.0 (include any object if any part remains - following clipping). - geom_type : str, optional - Type of objects in `gdf`. Can be one of - ``["Polygon", "LineString"]`` . Defaults to ``"Polygon"`` . - use_sindex : bool, optional - Use the `gdf` sindex be used for searching. Improves efficiency - but requires `libspatialindex <http://libspatialindex.github.io/>`__ . - - Returns - ------- - cutGeoDF : :py:class:`geopandas.GeoDataFrame` - `gdf` with all contained objects clipped to `poly_to_cut` . - See notes above for details on additional clipping columns added. - - """ - - # check if geoDF has origAreaField - - if use_sindex: - gdf = search_gdf_polygon(gdf, poly_to_cut) - - # if geom_type == "LineString": - if 'origarea' in gdf.columns: - pass - else: - if "geom_type" == "LineString": - gdf['origarea'] = 0 - else: - gdf['origarea'] = gdf.area - if 'origlen' in gdf.columns: - pass - else: - if "geom_type" == "LineString": - gdf['origlen'] = gdf.length - else: - gdf['origlen'] = 0 - # TODO must implement different case for lines and for spatialIndex - # (Assume RTree is already performed) - - cutGeoDF = gdf.copy() - cutGeoDF.geometry = gdf.intersection(poly_to_cut) - - if geom_type == 'Polygon': - cutGeoDF['partialDec'] = cutGeoDF.area / cutGeoDF['origarea'] - cutGeoDF = cutGeoDF.loc[cutGeoDF['partialDec'] > min_partial_perc, :] - cutGeoDF['truncated'] = (cutGeoDF['partialDec'] != 1.0).astype(int) - else: - cutGeoDF = cutGeoDF[cutGeoDF.geom_type != "GeometryCollection"] - cutGeoDF['partialDec'] = 1 - cutGeoDF['truncated'] = 0 - # TODO: IMPLEMENT TRUNCATION MEASUREMENT FOR LINESTRINGS - - return cutGeoDF
- - -
[docs]def rasterize_gdf(gdf, src_shape, burn_value=1, - src_transform=Affine(1.0, 0.0, 0.0, 0.0, 1.0, 0.0)): - """Convert a GeoDataFrame to a binary image (array) mask. - - Uses :py:func:`rasterio.features.rasterize` to generate a raster mask from - object geometries in `gdf` . - - Arguments - --------- - gdf : :py:class:`geopandas.GeoDataFrame` - A :py:class:`geopandas.GeoDataFrame` of objects to convert into a mask. - src_shape : list-like of 2 ints - Shape of the output array in ``(Y, X)`` pixel units. - burn_value : int in range(0, 255), optional - Integer value for pixels corresponding to objects from `gdf` . - Defaults to 1. - src_transform : :py:class:`affine.Affine`, optional - Affine transformation for the output raster. If not provided, defaults - to arbitrary pixel units. - - Returns - ------- - img : :class:`np.ndarray`, dtype ``uint8`` - A NumPy array of integers with 0s where no pixels from objects in - `gdf` exist, and `burn_value` where they do. Shape is - defined by `src_shape`. - """ - if not gdf.empty: - img = features.rasterize( - ((geom, burn_value) for geom in gdf.geometry), - out_shape=src_shape, - transform=src_transform - ) - else: - img = np.zeros(src_shape).astype(np.uint8) - - return img
-
- -
- -
- - -
-
- -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/old_docs/tiler_docs/_build/html/_modules/index.html b/old_docs/tiler_docs/_build/html/_modules/index.html deleted file mode 100644 index 6c8d627a..00000000 --- a/old_docs/tiler_docs/_build/html/_modules/index.html +++ /dev/null @@ -1,193 +0,0 @@ - - - - - - - - - - - Overview: module code — cw-tiler 0.2.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -
- - - - - -
- -
- - - - - - - - - - - - - - - - - -
- -
    - -
  • Docs »
  • - -
  • Overview: module code
  • - - -
  • - -
  • - -
- - -
-
-
-
- -

All modules for which code is available

- - -
- -
- - -
-
- -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/old_docs/tiler_docs/_build/html/_sources/index.rst.txt b/old_docs/tiler_docs/_build/html/_sources/index.rst.txt deleted file mode 100644 index db64a17d..00000000 --- a/old_docs/tiler_docs/_build/html/_sources/index.rst.txt +++ /dev/null @@ -1,52 +0,0 @@ -CosmiQ Works Tiler (`cw-tiler `__) Documentation -==================================================================================== -:Author: `CosmiQ Works `__ -:Version: |version| -:Copyright: 2018, CosmiQ Works -:License: This work is licensed under the `BSD 3-Clause license`__. - -.. __: https://opensource.org/licenses/BSD-3-Clause - -Sections --------- -* :ref:`tiling-functions` -* :ref:`utility-functions` -* :ref:`raster-utilities` -* :ref:`vector-utilities` - -.. _tiling-functions: - -Tiling functions ----------------- -.. automodule:: cw_tiler.main - :members: - -.. _utility-functions: - -Utility functions ------------------ - -.. _raster-utilities: - -Raster utilities -^^^^^^^^^^^^^^^^ -.. automodule:: cw_tiler.utils - :members: - -.. _vector-utilities: - -Vector utilities -^^^^^^^^^^^^^^^^ -.. automodule:: cw_tiler.vector_utils - :members: - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - -Indices and tables ------------------- - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/old_docs/tiler_docs/_build/html/_static/ajax-loader.gif b/old_docs/tiler_docs/_build/html/_static/ajax-loader.gif deleted file mode 100644 index 61faf8ca..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/ajax-loader.gif and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/basic.css b/old_docs/tiler_docs/_build/html/_static/basic.css deleted file mode 100644 index 104f076a..00000000 --- a/old_docs/tiler_docs/_build/html/_static/basic.css +++ /dev/null @@ -1,676 +0,0 @@ -/* - * basic.css - * ~~~~~~~~~ - * - * Sphinx stylesheet -- basic theme. - * - * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/* -- main layout ----------------------------------------------------------- */ - -div.clearer { - clear: both; -} - -/* -- relbar ---------------------------------------------------------------- */ - -div.related { - width: 100%; - font-size: 90%; -} - -div.related h3 { - display: none; -} - -div.related ul { - margin: 0; - padding: 0 0 0 10px; - list-style: none; -} - -div.related li { - display: inline; -} - -div.related li.right { - float: right; - margin-right: 5px; -} - -/* -- sidebar --------------------------------------------------------------- */ - -div.sphinxsidebarwrapper { - padding: 10px 5px 0 10px; -} - -div.sphinxsidebar { - float: left; - width: 230px; - margin-left: -100%; - font-size: 90%; - word-wrap: break-word; - overflow-wrap : break-word; -} - -div.sphinxsidebar ul { - list-style: none; -} - -div.sphinxsidebar ul ul, -div.sphinxsidebar ul.want-points { - margin-left: 20px; - list-style: square; -} - -div.sphinxsidebar ul ul { - margin-top: 0; - margin-bottom: 0; -} - -div.sphinxsidebar form { - margin-top: 10px; -} - -div.sphinxsidebar input { - border: 1px solid #98dbcc; - font-family: sans-serif; - font-size: 1em; -} - -div.sphinxsidebar #searchbox form.search { - overflow: hidden; -} - -div.sphinxsidebar #searchbox input[type="text"] { - float: left; - width: 80%; - padding: 0.25em; - box-sizing: border-box; -} - -div.sphinxsidebar #searchbox input[type="submit"] { - float: left; - width: 20%; - border-left: none; - padding: 0.25em; - box-sizing: border-box; -} - - -img { - border: 0; - max-width: 100%; -} - -/* -- search page ----------------------------------------------------------- */ - -ul.search { - margin: 10px 0 0 20px; - padding: 0; -} - -ul.search li { - padding: 5px 0 5px 20px; - background-image: url(file.png); - background-repeat: no-repeat; - background-position: 0 7px; -} - -ul.search li a { - font-weight: bold; -} - -ul.search li div.context { - color: #888; - margin: 2px 0 0 30px; - text-align: left; -} - -ul.keywordmatches li.goodmatch a { - font-weight: bold; -} - -/* -- index page ------------------------------------------------------------ */ - -table.contentstable { - width: 90%; - margin-left: auto; - margin-right: auto; -} - -table.contentstable p.biglink { - line-height: 150%; -} - -a.biglink { - font-size: 1.3em; -} - -span.linkdescr { - font-style: italic; - padding-top: 5px; - font-size: 90%; -} - -/* -- general index --------------------------------------------------------- */ - -table.indextable { - width: 100%; -} - -table.indextable td { - text-align: left; - vertical-align: top; -} - -table.indextable ul { - margin-top: 0; - margin-bottom: 0; - list-style-type: none; -} - -table.indextable > tbody > tr > td > ul { - padding-left: 0em; -} - -table.indextable tr.pcap { - height: 10px; -} - -table.indextable tr.cap { - margin-top: 10px; - background-color: #f2f2f2; -} - -img.toggler { - margin-right: 3px; - margin-top: 3px; - cursor: pointer; -} - -div.modindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -div.genindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -/* -- domain module index --------------------------------------------------- */ - -table.modindextable td { - padding: 2px; - border-collapse: collapse; -} - -/* -- general body styles --------------------------------------------------- */ - -div.body { - min-width: 450px; - max-width: 800px; -} - -div.body p, div.body dd, div.body li, div.body blockquote { - -moz-hyphens: auto; - -ms-hyphens: auto; - -webkit-hyphens: auto; - hyphens: auto; -} - -a.headerlink { - visibility: hidden; -} - -h1:hover > a.headerlink, -h2:hover > a.headerlink, -h3:hover > a.headerlink, -h4:hover > a.headerlink, -h5:hover > a.headerlink, -h6:hover > a.headerlink, -dt:hover > a.headerlink, -caption:hover > a.headerlink, -p.caption:hover > a.headerlink, -div.code-block-caption:hover > a.headerlink { - visibility: visible; -} - -div.body p.caption { - text-align: inherit; -} - -div.body td { - text-align: left; -} - -.first { - margin-top: 0 !important; -} - -p.rubric { - margin-top: 30px; - font-weight: bold; -} - -img.align-left, .figure.align-left, object.align-left { - clear: left; - float: left; - margin-right: 1em; -} - -img.align-right, .figure.align-right, object.align-right { - clear: right; - float: right; - margin-left: 1em; -} - -img.align-center, .figure.align-center, object.align-center { - display: block; - margin-left: auto; - margin-right: auto; -} - -.align-left { - text-align: left; -} - -.align-center { - text-align: center; -} - -.align-right { - text-align: right; -} - -/* -- sidebars -------------------------------------------------------------- */ - -div.sidebar { - margin: 0 0 0.5em 1em; - border: 1px solid #ddb; - padding: 7px 7px 0 7px; - background-color: #ffe; - width: 40%; - float: right; -} - -p.sidebar-title { - font-weight: bold; -} - -/* -- topics ---------------------------------------------------------------- */ - -div.topic { - border: 1px solid #ccc; - padding: 7px 7px 0 7px; - margin: 10px 0 10px 0; -} - -p.topic-title { - font-size: 1.1em; - font-weight: bold; - margin-top: 10px; -} - -/* -- admonitions ----------------------------------------------------------- */ - -div.admonition { - margin-top: 10px; - margin-bottom: 10px; - padding: 7px; -} - -div.admonition dt { - font-weight: bold; -} - -div.admonition dl { - margin-bottom: 0; -} - -p.admonition-title { - margin: 0px 10px 5px 0px; - font-weight: bold; -} - -div.body p.centered { - text-align: center; - margin-top: 25px; -} - -/* -- tables ---------------------------------------------------------------- */ - -table.docutils { - border: 0; - border-collapse: collapse; -} - -table.align-center { - margin-left: auto; - margin-right: auto; -} - -table caption span.caption-number { - font-style: italic; -} - -table caption span.caption-text { -} - -table.docutils td, table.docutils th { - padding: 1px 8px 1px 5px; - border-top: 0; - border-left: 0; - border-right: 0; - border-bottom: 1px solid #aaa; -} - -table.footnote td, table.footnote th { - border: 0 !important; -} - -th { - text-align: left; - padding-right: 5px; -} - -table.citation { - border-left: solid 1px gray; - margin-left: 1px; -} - -table.citation td { - border-bottom: none; -} - -/* -- figures --------------------------------------------------------------- */ - -div.figure { - margin: 0.5em; - padding: 0.5em; -} - -div.figure p.caption { - padding: 0.3em; -} - -div.figure p.caption span.caption-number { - font-style: italic; -} - -div.figure p.caption span.caption-text { -} - -/* -- field list styles ----------------------------------------------------- */ - -table.field-list td, table.field-list th { - border: 0 !important; -} - -.field-list ul { - margin: 0; - padding-left: 1em; -} - -.field-list p { - margin: 0; -} - -.field-name { - -moz-hyphens: manual; - -ms-hyphens: manual; - -webkit-hyphens: manual; - hyphens: manual; -} - -/* -- hlist styles ---------------------------------------------------------- */ - -table.hlist td { - vertical-align: top; -} - - -/* -- other body styles ----------------------------------------------------- */ - -ol.arabic { - list-style: decimal; -} - -ol.loweralpha { - list-style: lower-alpha; -} - -ol.upperalpha { - list-style: upper-alpha; -} - -ol.lowerroman { - list-style: lower-roman; -} - -ol.upperroman { - list-style: upper-roman; -} - -dl { - margin-bottom: 15px; -} - -dd p { - margin-top: 0px; -} - -dd ul, dd table { - margin-bottom: 10px; -} - -dd { - margin-top: 3px; - margin-bottom: 10px; - margin-left: 30px; -} - -dt:target, span.highlighted { - background-color: #fbe54e; -} - -rect.highlighted { - fill: #fbe54e; -} - -dl.glossary dt { - font-weight: bold; - font-size: 1.1em; -} - -.optional { - font-size: 1.3em; -} - -.sig-paren { - font-size: larger; -} - -.versionmodified { - font-style: italic; -} - -.system-message { - background-color: #fda; - padding: 5px; - border: 3px solid red; -} - -.footnote:target { - background-color: #ffa; -} - -.line-block { - display: block; - margin-top: 1em; - margin-bottom: 1em; -} - -.line-block .line-block { - margin-top: 0; - margin-bottom: 0; - margin-left: 1.5em; -} - -.guilabel, .menuselection { - font-family: sans-serif; -} - -.accelerator { - text-decoration: underline; -} - -.classifier { - font-style: oblique; -} - -abbr, acronym { - border-bottom: dotted 1px; - cursor: help; -} - -/* -- code displays --------------------------------------------------------- */ - -pre { - overflow: auto; - overflow-y: hidden; /* fixes display issues on Chrome browsers */ -} - -span.pre { - -moz-hyphens: none; - -ms-hyphens: none; - -webkit-hyphens: none; - hyphens: none; -} - -td.linenos pre { - padding: 5px 0px; - border: 0; - background-color: transparent; - color: #aaa; -} - -table.highlighttable { - margin-left: 0.5em; -} - -table.highlighttable td { - padding: 0 0.5em 0 0.5em; -} - -div.code-block-caption { - padding: 2px 5px; - font-size: small; -} - -div.code-block-caption code { - background-color: transparent; -} - -div.code-block-caption + div > div.highlight > pre { - margin-top: 0; -} - -div.code-block-caption span.caption-number { - padding: 0.1em 0.3em; - font-style: italic; -} - -div.code-block-caption span.caption-text { -} - -div.literal-block-wrapper { - padding: 1em 1em 0; -} - -div.literal-block-wrapper div.highlight { - margin: 0; -} - -code.descname { - background-color: transparent; - font-weight: bold; - font-size: 1.2em; -} - -code.descclassname { - background-color: transparent; -} - -code.xref, a code { - background-color: transparent; - font-weight: bold; -} - -h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { - background-color: transparent; -} - -.viewcode-link { - float: right; -} - -.viewcode-back { - float: right; - font-family: sans-serif; -} - -div.viewcode-block:target { - margin: -1px -10px; - padding: 0 10px; -} - -/* -- math display ---------------------------------------------------------- */ - -img.math { - vertical-align: middle; -} - -div.body div.math p { - text-align: center; -} - -span.eqno { - float: right; -} - -span.eqno a.headerlink { - position: relative; - left: 0px; - z-index: 1; -} - -div.math:hover a.headerlink { - visibility: visible; -} - -/* -- printout stylesheet --------------------------------------------------- */ - -@media print { - div.document, - div.documentwrapper, - div.bodywrapper { - margin: 0 !important; - width: 100%; - } - - div.sphinxsidebar, - div.related, - div.footer, - #top-link { - display: none; - } -} \ No newline at end of file diff --git a/old_docs/tiler_docs/_build/html/_static/comment-bright.png b/old_docs/tiler_docs/_build/html/_static/comment-bright.png deleted file mode 100644 index 15e27edb..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/comment-bright.png and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/comment-close.png b/old_docs/tiler_docs/_build/html/_static/comment-close.png deleted file mode 100644 index 4d91bcf5..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/comment-close.png and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/comment.png b/old_docs/tiler_docs/_build/html/_static/comment.png deleted file mode 100644 index dfbc0cbd..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/comment.png and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/css/badge_only.css b/old_docs/tiler_docs/_build/html/_static/css/badge_only.css deleted file mode 100644 index 323730ae..00000000 --- a/old_docs/tiler_docs/_build/html/_static/css/badge_only.css +++ /dev/null @@ -1 +0,0 @@ -.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../fonts/fontawesome-webfont.eot");src:url("../fonts/fontawesome-webfont.eot?#iefix") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff") format("woff"),url("../fonts/fontawesome-webfont.ttf") format("truetype"),url("../fonts/fontawesome-webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:""}.icon-book:before{content:""}.fa-caret-down:before{content:""}.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.icon-caret-up:before{content:""}.fa-caret-left:before{content:""}.icon-caret-left:before{content:""}.fa-caret-right:before{content:""}.icon-caret-right:before{content:""}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up{height:auto;max-height:100%}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} diff --git a/old_docs/tiler_docs/_build/html/_static/css/theme.css b/old_docs/tiler_docs/_build/html/_static/css/theme.css deleted file mode 100644 index b19dbfe5..00000000 --- a/old_docs/tiler_docs/_build/html/_static/css/theme.css +++ /dev/null @@ -1,6 +0,0 @@ -/* sphinx_rtd_theme version 0.4.2 | MIT license */ -/* Built 20181005 13:10 */ -*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}[hidden]{display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:hover,a:active{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;color:#000;text-decoration:none}mark{background:#ff0;color:#000;font-style:italic;font-weight:bold}pre,code,.rst-content tt,.rst-content code,kbd,samp{font-family:monospace,serif;_font-family:"courier new",monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:before,q:after{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}ul,ol,dl{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:0;margin:0;padding:0}label{cursor:pointer}legend{border:0;*margin-left:-7px;padding:0;white-space:normal}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*width:13px;*height:13px}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top;resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none !important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{html,body,section{background:none !important}*{box-shadow:none !important;text-shadow:none !important;filter:none !important;-ms-filter:none !important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:.5cm}p,h2,.rst-content .toctree-wrapper p.caption,h3{orphans:3;widows:3}h2,.rst-content .toctree-wrapper p.caption,h3{page-break-after:avoid}}.fa:before,.wy-menu-vertical li span.toctree-expand:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li.current>a span.toctree-expand:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo,.rst-content .admonition,.btn,input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"],select,textarea,.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a,.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a,.wy-nav-top a{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}/*! - * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:'FontAwesome';src:url("../fonts/fontawesome-webfont.eot?v=4.7.0");src:url("../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff2?v=4.7.0") format("woff2"),url("../fonts/fontawesome-webfont.woff?v=4.7.0") format("woff"),url("../fonts/fontawesome-webfont.ttf?v=4.7.0") format("truetype"),url("../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa,.wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content p.caption .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.rst-content code.download span:first-child,.icon{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.3333333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.2857142857em;text-align:center}.fa-ul{padding-left:0;margin-left:2.1428571429em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.1428571429em;width:2.1428571429em;top:.1428571429em;text-align:center}.fa-li.fa-lg{left:-1.8571428571em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.wy-menu-vertical li span.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a span.fa-pull-left.toctree-expand,.wy-menu-vertical li.current>a span.fa-pull-left.toctree-expand,.rst-content .fa-pull-left.admonition-title,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content dl dt .fa-pull-left.headerlink,.rst-content p.caption .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.rst-content code.download span.fa-pull-left:first-child,.fa-pull-left.icon{margin-right:.3em}.fa.fa-pull-right,.wy-menu-vertical li span.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a span.fa-pull-right.toctree-expand,.wy-menu-vertical li.current>a span.fa-pull-right.toctree-expand,.rst-content .fa-pull-right.admonition-title,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content dl dt .fa-pull-right.headerlink,.rst-content p.caption .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.rst-content code.download span.fa-pull-right:first-child,.fa-pull-right.icon{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.wy-menu-vertical li span.pull-left.toctree-expand,.wy-menu-vertical li.on a span.pull-left.toctree-expand,.wy-menu-vertical li.current>a span.pull-left.toctree-expand,.rst-content .pull-left.admonition-title,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content dl dt .pull-left.headerlink,.rst-content p.caption .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.rst-content code.download span.pull-left:first-child,.pull-left.icon{margin-right:.3em}.fa.pull-right,.wy-menu-vertical li span.pull-right.toctree-expand,.wy-menu-vertical li.on a span.pull-right.toctree-expand,.wy-menu-vertical li.current>a span.pull-right.toctree-expand,.rst-content .pull-right.admonition-title,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content dl dt .pull-right.headerlink,.rst-content p.caption .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.rst-content code.download span.pull-right:first-child,.pull-right.icon{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-remove:before,.fa-close:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-gear:before,.fa-cog:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-rotate-right:before,.fa-repeat:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.rst-content .admonition-title:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-warning:before,.fa-exclamation-triangle:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-gears:before,.fa-cogs:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-save:before,.fa-floppy-o:before{content:""}.fa-square:before{content:""}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.wy-dropdown .caret:before,.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-unsorted:before,.fa-sort:before{content:""}.fa-sort-down:before,.fa-sort-desc:before{content:""}.fa-sort-up:before,.fa-sort-asc:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-legal:before,.fa-gavel:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-flash:before,.fa-bolt:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-paste:before,.fa-clipboard:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-unlink:before,.fa-chain-broken:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li.current>a span.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:""}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:""}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:""}.fa-euro:before,.fa-eur:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-rupee:before,.fa-inr:before{content:""}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:""}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:""}.fa-won:before,.fa-krw:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-turkish-lira:before,.fa-try:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li span.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-institution:before,.fa-bank:before,.fa-university:before{content:""}.fa-mortar-board:before,.fa-graduation-cap:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:""}.fa-file-zip-o:before,.fa-file-archive-o:before{content:""}.fa-file-sound-o:before,.fa-file-audio-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:""}.fa-ge:before,.fa-empire:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-send:before,.fa-paper-plane:before{content:""}.fa-send-o:before,.fa-paper-plane-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-hotel:before,.fa-bed:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-yc:before,.fa-y-combinator:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-tv:before,.fa-television:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:""}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-signing:before,.fa-sign-language:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-vcard:before,.fa-address-card:before{content:""}.fa-vcard-o:before,.fa-address-card-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content p.caption .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.rst-content code.download span:first-child,.icon,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context{font-family:inherit}.fa:before,.wy-menu-vertical li span.toctree-expand:before,.wy-menu-vertical li.on a span.toctree-expand:before,.wy-menu-vertical li.current>a span.toctree-expand:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before{font-family:"FontAwesome";display:inline-block;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa,a .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li a span.toctree-expand,.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand,a .rst-content .admonition-title,.rst-content a .admonition-title,a .rst-content h1 .headerlink,.rst-content h1 a .headerlink,a .rst-content h2 .headerlink,.rst-content h2 a .headerlink,a .rst-content h3 .headerlink,.rst-content h3 a .headerlink,a .rst-content h4 .headerlink,.rst-content h4 a .headerlink,a .rst-content h5 .headerlink,.rst-content h5 a .headerlink,a .rst-content h6 .headerlink,.rst-content h6 a .headerlink,a .rst-content dl dt .headerlink,.rst-content dl dt a .headerlink,a .rst-content p.caption .headerlink,.rst-content p.caption a .headerlink,a .rst-content table>caption .headerlink,.rst-content table>caption a .headerlink,a .rst-content tt.download span:first-child,.rst-content tt.download a span:first-child,a .rst-content code.download span:first-child,.rst-content code.download a span:first-child,a .icon{display:inline-block;text-decoration:inherit}.btn .fa,.btn .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .btn span.toctree-expand,.btn .wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.on a .btn span.toctree-expand,.btn .wy-menu-vertical li.current>a span.toctree-expand,.wy-menu-vertical li.current>a .btn span.toctree-expand,.btn .rst-content .admonition-title,.rst-content .btn .admonition-title,.btn .rst-content h1 .headerlink,.rst-content h1 .btn .headerlink,.btn .rst-content h2 .headerlink,.rst-content h2 .btn .headerlink,.btn .rst-content h3 .headerlink,.rst-content h3 .btn .headerlink,.btn .rst-content h4 .headerlink,.rst-content h4 .btn .headerlink,.btn .rst-content h5 .headerlink,.rst-content h5 .btn .headerlink,.btn .rst-content h6 .headerlink,.rst-content h6 .btn .headerlink,.btn .rst-content dl dt .headerlink,.rst-content dl dt .btn .headerlink,.btn .rst-content p.caption .headerlink,.rst-content p.caption .btn .headerlink,.btn .rst-content table>caption .headerlink,.rst-content table>caption .btn .headerlink,.btn .rst-content tt.download span:first-child,.rst-content tt.download .btn span:first-child,.btn .rst-content code.download span:first-child,.rst-content code.download .btn span:first-child,.btn .icon,.nav .fa,.nav .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .nav span.toctree-expand,.nav .wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.on a .nav span.toctree-expand,.nav .wy-menu-vertical li.current>a span.toctree-expand,.wy-menu-vertical li.current>a .nav span.toctree-expand,.nav .rst-content .admonition-title,.rst-content .nav .admonition-title,.nav .rst-content h1 .headerlink,.rst-content h1 .nav .headerlink,.nav .rst-content h2 .headerlink,.rst-content h2 .nav .headerlink,.nav .rst-content h3 .headerlink,.rst-content h3 .nav .headerlink,.nav .rst-content h4 .headerlink,.rst-content h4 .nav .headerlink,.nav .rst-content h5 .headerlink,.rst-content h5 .nav .headerlink,.nav .rst-content h6 .headerlink,.rst-content h6 .nav .headerlink,.nav .rst-content dl dt .headerlink,.rst-content dl dt .nav .headerlink,.nav .rst-content p.caption .headerlink,.rst-content p.caption .nav .headerlink,.nav .rst-content table>caption .headerlink,.rst-content table>caption .nav .headerlink,.nav .rst-content tt.download span:first-child,.rst-content tt.download .nav span:first-child,.nav .rst-content code.download span:first-child,.rst-content code.download .nav span:first-child,.nav .icon{display:inline}.btn .fa.fa-large,.btn .wy-menu-vertical li span.fa-large.toctree-expand,.wy-menu-vertical li .btn span.fa-large.toctree-expand,.btn .rst-content .fa-large.admonition-title,.rst-content .btn .fa-large.admonition-title,.btn .rst-content h1 .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.btn .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .btn .fa-large.headerlink,.btn .rst-content p.caption .fa-large.headerlink,.rst-content p.caption .btn .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.rst-content tt.download .btn span.fa-large:first-child,.btn .rst-content code.download span.fa-large:first-child,.rst-content code.download .btn span.fa-large:first-child,.btn .fa-large.icon,.nav .fa.fa-large,.nav .wy-menu-vertical li span.fa-large.toctree-expand,.wy-menu-vertical li .nav span.fa-large.toctree-expand,.nav .rst-content .fa-large.admonition-title,.rst-content .nav .fa-large.admonition-title,.nav .rst-content h1 .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.nav .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.nav .rst-content p.caption .fa-large.headerlink,.rst-content p.caption .nav .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.nav .rst-content code.download span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.nav .fa-large.icon{line-height:.9em}.btn .fa.fa-spin,.btn .wy-menu-vertical li span.fa-spin.toctree-expand,.wy-menu-vertical li .btn span.fa-spin.toctree-expand,.btn .rst-content .fa-spin.admonition-title,.rst-content .btn .fa-spin.admonition-title,.btn .rst-content h1 .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.btn .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .btn .fa-spin.headerlink,.btn .rst-content p.caption .fa-spin.headerlink,.rst-content p.caption .btn .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.rst-content tt.download .btn span.fa-spin:first-child,.btn .rst-content code.download span.fa-spin:first-child,.rst-content code.download .btn span.fa-spin:first-child,.btn .fa-spin.icon,.nav .fa.fa-spin,.nav .wy-menu-vertical li span.fa-spin.toctree-expand,.wy-menu-vertical li .nav span.fa-spin.toctree-expand,.nav .rst-content .fa-spin.admonition-title,.rst-content .nav .fa-spin.admonition-title,.nav .rst-content h1 .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.nav .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.nav .rst-content p.caption .fa-spin.headerlink,.rst-content p.caption .nav .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.nav .rst-content code.download span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.nav .fa-spin.icon{display:inline-block}.btn.fa:before,.wy-menu-vertical li span.btn.toctree-expand:before,.rst-content .btn.admonition-title:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content dl dt .btn.headerlink:before,.rst-content p.caption .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.rst-content code.download span.btn:first-child:before,.btn.icon:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.wy-menu-vertical li span.btn.toctree-expand:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content p.caption .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.rst-content code.download span.btn:first-child:hover:before,.btn.icon:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .wy-menu-vertical li span.toctree-expand:before,.wy-menu-vertical li .btn-mini span.toctree-expand:before,.btn-mini .rst-content .admonition-title:before,.rst-content .btn-mini .admonition-title:before,.btn-mini .rst-content h1 .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.btn-mini .rst-content dl dt .headerlink:before,.rst-content dl dt .btn-mini .headerlink:before,.btn-mini .rst-content p.caption .headerlink:before,.rst-content p.caption .btn-mini .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.rst-content tt.download .btn-mini span:first-child:before,.btn-mini .rst-content code.download span:first-child:before,.rst-content code.download .btn-mini span:first-child:before,.btn-mini .icon:before{font-size:14px;vertical-align:-15%}.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo,.rst-content .admonition{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.wy-alert-title,.rst-content .admonition-title{color:#fff;font-weight:bold;display:block;color:#fff;background:#6ab0de;margin:-12px;padding:6px 12px;margin-bottom:12px}.wy-alert.wy-alert-danger,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.admonition-todo,.rst-content .wy-alert-danger.admonition{background:#fdf3f2}.wy-alert.wy-alert-danger .wy-alert-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .danger .wy-alert-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.rst-content .wy-alert-danger.admonition .wy-alert-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .danger .admonition-title,.rst-content .error .admonition-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title,.rst-content .wy-alert-danger.admonition .admonition-title{background:#f29f97}.wy-alert.wy-alert-warning,.rst-content .wy-alert-warning.note,.rst-content .attention,.rst-content .caution,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.tip,.rst-content .warning,.rst-content .wy-alert-warning.seealso,.rst-content .admonition-todo,.rst-content .wy-alert-warning.admonition{background:#ffedcc}.wy-alert.wy-alert-warning .wy-alert-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .attention .wy-alert-title,.rst-content .caution .wy-alert-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .admonition-todo .wy-alert-title,.rst-content .wy-alert-warning.admonition .wy-alert-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .attention .admonition-title,.rst-content .caution .admonition-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .warning .admonition-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .admonition-todo .admonition-title,.rst-content .wy-alert-warning.admonition .admonition-title{background:#f0b37e}.wy-alert.wy-alert-info,.rst-content .note,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.rst-content .seealso,.rst-content .wy-alert-info.admonition-todo,.rst-content .wy-alert-info.admonition{background:#e7f2fa}.wy-alert.wy-alert-info .wy-alert-title,.rst-content .note .wy-alert-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.rst-content .wy-alert-info.admonition .wy-alert-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.rst-content .note .admonition-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .seealso .admonition-title,.rst-content .wy-alert-info.admonition-todo .admonition-title,.rst-content .wy-alert-info.admonition .admonition-title{background:#6ab0de}.wy-alert.wy-alert-success,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.warning,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.admonition-todo,.rst-content .wy-alert-success.admonition{background:#dbfaf4}.wy-alert.wy-alert-success .wy-alert-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .hint .wy-alert-title,.rst-content .important .wy-alert-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.rst-content .wy-alert-success.admonition .wy-alert-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .hint .admonition-title,.rst-content .important .admonition-title,.rst-content .tip .admonition-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.admonition-todo .admonition-title,.rst-content .wy-alert-success.admonition .admonition-title{background:#1abc9c}.wy-alert.wy-alert-neutral,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.admonition-todo,.rst-content .wy-alert-neutral.admonition{background:#f3f6f6}.wy-alert.wy-alert-neutral .wy-alert-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.rst-content .wy-alert-neutral.admonition .wy-alert-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .admonition-title,.rst-content .wy-alert-neutral.admonition .admonition-title{color:#404040;background:#e1e4e5}.wy-alert.wy-alert-neutral a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.admonition-todo a,.rst-content .wy-alert-neutral.admonition a{color:#2980B9}.wy-alert p:last-child,.rst-content .note p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.rst-content .seealso p:last-child,.rst-content .admonition-todo p:last-child,.rst-content .admonition p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0px;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,0.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27AE60}.wy-tray-container li.wy-tray-item-info{background:#2980B9}.wy-tray-container li.wy-tray-item-warning{background:#E67E22}.wy-tray-container li.wy-tray-item-danger{background:#E74C3C}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width: 768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px 12px;color:#fff;border:1px solid rgba(0,0,0,0.1);background-color:#27AE60;text-decoration:none;font-weight:normal;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:0px 1px 2px -1px rgba(255,255,255,0.5) inset,0px -2px 0px 0px rgba(0,0,0,0.1) inset;outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:0px -1px 0px 0px rgba(0,0,0,0.05) inset,0px 2px 0px 0px rgba(0,0,0,0.1) inset;padding:8px 12px 6px 12px}.btn:visited{color:#fff}.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn-disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn-disabled:hover,.btn-disabled:focus,.btn-disabled:active{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980B9 !important}.btn-info:hover{background-color:#2e8ece !important}.btn-neutral{background-color:#f3f6f6 !important;color:#404040 !important}.btn-neutral:hover{background-color:#e5ebeb !important;color:#404040}.btn-neutral:visited{color:#404040 !important}.btn-success{background-color:#27AE60 !important}.btn-success:hover{background-color:#295 !important}.btn-danger{background-color:#E74C3C !important}.btn-danger:hover{background-color:#ea6153 !important}.btn-warning{background-color:#E67E22 !important}.btn-warning:hover{background-color:#e98b39 !important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f !important}.btn-link{background-color:transparent !important;color:#2980B9;box-shadow:none;border-color:transparent !important}.btn-link:hover{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:active{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:visited{color:#9B59B6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:before,.wy-btn-group:after{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:solid 1px #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,0.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980B9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:solid 1px #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type="search"]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980B9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned input,.wy-form-aligned textarea,.wy-form-aligned select,.wy-form-aligned .wy-help-inline,.wy-form-aligned label{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{border:0;margin:0;padding:0}legend{display:block;width:100%;border:0;padding:0;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label{display:block;margin:0 0 .3125em 0;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;*zoom:1;max-width:68em;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#E74C3C}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full input[type="text"],.wy-control-group .wy-form-full input[type="password"],.wy-control-group .wy-form-full input[type="email"],.wy-control-group .wy-form-full input[type="url"],.wy-control-group .wy-form-full input[type="date"],.wy-control-group .wy-form-full input[type="month"],.wy-control-group .wy-form-full input[type="time"],.wy-control-group .wy-form-full input[type="datetime"],.wy-control-group .wy-form-full input[type="datetime-local"],.wy-control-group .wy-form-full input[type="week"],.wy-control-group .wy-form-full input[type="number"],.wy-control-group .wy-form-full input[type="search"],.wy-control-group .wy-form-full input[type="tel"],.wy-control-group .wy-form-full input[type="color"],.wy-control-group .wy-form-halves input[type="text"],.wy-control-group .wy-form-halves input[type="password"],.wy-control-group .wy-form-halves input[type="email"],.wy-control-group .wy-form-halves input[type="url"],.wy-control-group .wy-form-halves input[type="date"],.wy-control-group .wy-form-halves input[type="month"],.wy-control-group .wy-form-halves input[type="time"],.wy-control-group .wy-form-halves input[type="datetime"],.wy-control-group .wy-form-halves input[type="datetime-local"],.wy-control-group .wy-form-halves input[type="week"],.wy-control-group .wy-form-halves input[type="number"],.wy-control-group .wy-form-halves input[type="search"],.wy-control-group .wy-form-halves input[type="tel"],.wy-control-group .wy-form-halves input[type="color"],.wy-control-group .wy-form-thirds input[type="text"],.wy-control-group .wy-form-thirds input[type="password"],.wy-control-group .wy-form-thirds input[type="email"],.wy-control-group .wy-form-thirds input[type="url"],.wy-control-group .wy-form-thirds input[type="date"],.wy-control-group .wy-form-thirds input[type="month"],.wy-control-group .wy-form-thirds input[type="time"],.wy-control-group .wy-form-thirds input[type="datetime"],.wy-control-group .wy-form-thirds input[type="datetime-local"],.wy-control-group .wy-form-thirds input[type="week"],.wy-control-group .wy-form-thirds input[type="number"],.wy-control-group .wy-form-thirds input[type="search"],.wy-control-group .wy-form-thirds input[type="tel"],.wy-control-group .wy-form-thirds input[type="color"]{width:100%}.wy-control-group .wy-form-full{float:left;display:block;margin-right:2.3576515979%;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.3576515979%;width:48.821174201%}.wy-control-group .wy-form-halves:last-child{margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n+1){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.3576515979%;width:31.7615656014%}.wy-control-group .wy-form-thirds:last-child{margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control{margin:6px 0 0 0;font-size:90%}.wy-control-no-input{display:inline-block;margin:6px 0 0 0;font-size:90%}.wy-control-group.fluid-input input[type="text"],.wy-control-group.fluid-input input[type="password"],.wy-control-group.fluid-input input[type="email"],.wy-control-group.fluid-input input[type="url"],.wy-control-group.fluid-input input[type="date"],.wy-control-group.fluid-input input[type="month"],.wy-control-group.fluid-input input[type="time"],.wy-control-group.fluid-input input[type="datetime"],.wy-control-group.fluid-input input[type="datetime-local"],.wy-control-group.fluid-input input[type="week"],.wy-control-group.fluid-input input[type="number"],.wy-control-group.fluid-input input[type="search"],.wy-control-group.fluid-input input[type="tel"],.wy-control-group.fluid-input input[type="color"]{width:100%}.wy-form-message-inline{display:inline-block;padding-left:.3em;color:#666;vertical-align:middle;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;*overflow:visible}input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type="datetime-local"]{padding:.34375em .625em}input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}input[type="text"]:focus,input[type="password"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus{outline:0;outline:thin dotted \9;border-color:#333}input.no-focus:focus{border-color:#ccc !important}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:1px auto #129FEA}input[type="text"][disabled],input[type="password"][disabled],input[type="email"][disabled],input[type="url"][disabled],input[type="date"][disabled],input[type="month"][disabled],input[type="time"][disabled],input[type="datetime"][disabled],input[type="datetime-local"][disabled],input[type="week"][disabled],input[type="number"][disabled],input[type="search"][disabled],input[type="tel"][disabled],input[type="color"][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#E74C3C;border:1px solid #E74C3C}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#E74C3C}input[type="file"]:focus:invalid:focus,input[type="radio"]:focus:invalid:focus,input[type="checkbox"]:focus:invalid:focus{outline-color:#E74C3C}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type="radio"][disabled],input[type="checkbox"][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:solid 1px #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:24px;margin-top:12px;cursor:pointer}.wy-switch:before{position:absolute;content:"";display:block;left:0;top:0;width:36px;height:12px;border-radius:4px;background:#ccc;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{position:absolute;content:"";display:block;width:18px;height:18px;border-radius:4px;background:#999;left:-3px;top:-3px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch span{position:absolute;left:48px;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:24px;background:#27AE60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#E74C3C}.wy-control-group.wy-control-group-error input[type="text"],.wy-control-group.wy-control-group-error input[type="password"],.wy-control-group.wy-control-group-error input[type="email"],.wy-control-group.wy-control-group-error input[type="url"],.wy-control-group.wy-control-group-error input[type="date"],.wy-control-group.wy-control-group-error input[type="month"],.wy-control-group.wy-control-group-error input[type="time"],.wy-control-group.wy-control-group-error input[type="datetime"],.wy-control-group.wy-control-group-error input[type="datetime-local"],.wy-control-group.wy-control-group-error input[type="week"],.wy-control-group.wy-control-group-error input[type="number"],.wy-control-group.wy-control-group-error input[type="search"],.wy-control-group.wy-control-group-error input[type="tel"],.wy-control-group.wy-control-group-error input[type="color"]{border:solid 1px #E74C3C}.wy-control-group.wy-control-group-error textarea{border:solid 1px #E74C3C}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27AE60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#E74C3C}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#E67E22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980B9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width: 480px){.wy-form button[type="submit"]{margin:.7em 0 0}.wy-form input[type="text"],.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:.3em;display:block}.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0 0}.wy-form .wy-help-inline,.wy-form-message-inline,.wy-form-message{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width: 768px){.tablet-hide{display:none}}@media screen and (max-width: 480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.wy-table,.rst-content table.docutils,.rst-content table.field-list{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.wy-table caption,.rst-content table.docutils caption,.rst-content table.field-list caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td,.wy-table th,.rst-content table.docutils th,.rst-content table.field-list th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.wy-table td:first-child,.rst-content table.docutils td:first-child,.rst-content table.field-list td:first-child,.wy-table th:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list th:first-child{border-left-width:0}.wy-table thead,.rst-content table.docutils thead,.rst-content table.field-list thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.wy-table thead th,.rst-content table.docutils thead th,.rst-content table.field-list thead th{font-weight:bold;border-bottom:solid 2px #e1e4e5}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td{background-color:transparent;vertical-align:middle}.wy-table td p,.rst-content table.docutils td p,.rst-content table.field-list td p{line-height:18px}.wy-table td p:last-child,.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child{margin-bottom:0}.wy-table .wy-table-cell-min,.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min{width:1%;padding-right:0}.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:gray;font-size:90%}.wy-table-tertiary{color:gray;font-size:80%}.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td,.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td{background-color:#f3f6f6}.wy-table-backed{background-color:#f3f6f6}.wy-table-bordered-all,.rst-content table.docutils{border:1px solid #e1e4e5}.wy-table-bordered-all td,.rst-content table.docutils td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.wy-table-bordered-all tbody>tr:last-child td,.rst-content table.docutils tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0 !important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980B9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9B59B6}html{height:100%;overflow-x:hidden}body{font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;font-weight:normal;color:#404040;min-height:100%;overflow-x:hidden;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#E67E22 !important}a.wy-text-warning:hover{color:#eb9950 !important}.wy-text-info{color:#2980B9 !important}a.wy-text-info:hover{color:#409ad5 !important}.wy-text-success{color:#27AE60 !important}a.wy-text-success:hover{color:#36d278 !important}.wy-text-danger{color:#E74C3C !important}a.wy-text-danger:hover{color:#ed7669 !important}.wy-text-neutral{color:#404040 !important}a.wy-text-neutral:hover{color:#595959 !important}h1,h2,.rst-content .toctree-wrapper p.caption,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif}p{line-height:24px;margin:0;font-size:16px;margin-bottom:24px}h1{font-size:175%}h2,.rst-content .toctree-wrapper p.caption{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}code,.rst-content tt,.rst-content code{white-space:nowrap;max-width:100%;background:#fff;border:solid 1px #e1e4e5;font-size:75%;padding:0 5px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace;color:#E74C3C;overflow-x:auto}code.code-large,.rst-content tt.code-large{font-size:90%}.wy-plain-list-disc,.rst-content .section ul,.rst-content .toctree-wrapper ul,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.wy-plain-list-disc li,.rst-content .section ul li,.rst-content .toctree-wrapper ul li,article ul li{list-style:disc;margin-left:24px}.wy-plain-list-disc li p:last-child,.rst-content .section ul li p:last-child,.rst-content .toctree-wrapper ul li p:last-child,article ul li p:last-child{margin-bottom:0}.wy-plain-list-disc li ul,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li ul,article ul li ul{margin-bottom:0}.wy-plain-list-disc li li,.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,article ul li li{list-style:circle}.wy-plain-list-disc li li li,.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,article ul li li li{list-style:square}.wy-plain-list-disc li ol li,.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,article ul li ol li{list-style:decimal}.wy-plain-list-decimal,.rst-content .section ol,.rst-content ol.arabic,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.wy-plain-list-decimal li,.rst-content .section ol li,.rst-content ol.arabic li,article ol li{list-style:decimal;margin-left:24px}.wy-plain-list-decimal li p:last-child,.rst-content .section ol li p:last-child,.rst-content ol.arabic li p:last-child,article ol li p:last-child{margin-bottom:0}.wy-plain-list-decimal li ul,.rst-content .section ol li ul,.rst-content ol.arabic li ul,article ol li ul{margin-bottom:0}.wy-plain-list-decimal li ul li,.rst-content .section ol li ul li,.rst-content ol.arabic li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1}.wy-breadcrumbs:before,.wy-breadcrumbs:after{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs li{display:inline-block}.wy-breadcrumbs li.wy-breadcrumbs-aside{float:right}.wy-breadcrumbs li a{display:inline-block;padding:5px}.wy-breadcrumbs li a:first-child{padding-left:0}.wy-breadcrumbs li code,.wy-breadcrumbs li .rst-content tt,.rst-content .wy-breadcrumbs li tt{padding:5px;border:none;background:none}.wy-breadcrumbs li code.literal,.wy-breadcrumbs li .rst-content tt.literal,.rst-content .wy-breadcrumbs li tt.literal{color:#404040}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width: 480px){.wy-breadcrumbs-extra{display:none}.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:16px}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:before,.wy-menu-horiz:after{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz ul,.wy-menu-horiz li{display:inline-block}.wy-menu-horiz li:hover{background:rgba(255,255,255,0.1)}.wy-menu-horiz li.divide-left{border-left:solid 1px #404040}.wy-menu-horiz li.divide-right{border-right:solid 1px #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{height:32px;display:inline-block;line-height:32px;padding:0 1.618em;margin-bottom:0;display:block;font-weight:bold;text-transform:uppercase;font-size:80%;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:solid 1px #404040}.wy-menu-vertical li.divide-bottom{border-bottom:solid 1px #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:gray;border-right:solid 1px #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.wy-menu-vertical li code,.wy-menu-vertical li .rst-content tt,.rst-content .wy-menu-vertical li tt{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li span.toctree-expand{display:block;float:left;margin-left:-1.2em;font-size:.8em;line-height:1.6em;color:#4d4d4d}.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a{color:#404040;padding:.4045em 1.618em;font-weight:bold;position:relative;background:#fcfcfc;border:none;padding-left:1.618em -4px}.wy-menu-vertical li.on a:hover,.wy-menu-vertical li.current>a:hover{background:#fcfcfc}.wy-menu-vertical li.on a:hover span.toctree-expand,.wy-menu-vertical li.current>a:hover span.toctree-expand{color:gray}.wy-menu-vertical li.on a span.toctree-expand,.wy-menu-vertical li.current>a span.toctree-expand{display:block;font-size:.8em;line-height:1.6em;color:#333}.wy-menu-vertical li.toctree-l1.current>a{border-bottom:solid 1px #c9c9c9;border-top:solid 1px #c9c9c9}.wy-menu-vertical li.toctree-l2 a,.wy-menu-vertical li.toctree-l3 a,.wy-menu-vertical li.toctree-l4 a{color:#404040}.wy-menu-vertical li.toctree-l1.current li.toctree-l2>ul,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>ul{display:none}.wy-menu-vertical li.toctree-l1.current li.toctree-l2.current>ul,.wy-menu-vertical li.toctree-l2.current li.toctree-l3.current>ul{display:block}.wy-menu-vertical li.toctree-l2.current>a{background:#c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{display:block;background:#c9c9c9;padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l2 a:hover span.toctree-expand{color:gray}.wy-menu-vertical li.toctree-l2 span.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3{font-size:.9em}.wy-menu-vertical li.toctree-l3.current>a{background:#bdbdbd;padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{display:block;background:#bdbdbd;padding:.4045em 5.663em}.wy-menu-vertical li.toctree-l3 a:hover span.toctree-expand{color:gray}.wy-menu-vertical li.toctree-l3 span.toctree-expand{color:#969696}.wy-menu-vertical li.toctree-l4{font-size:.9em}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#d9d9d9;font-weight:normal}.wy-menu-vertical a{display:inline-block;line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#d9d9d9}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover span.toctree-expand{color:#d9d9d9}.wy-menu-vertical a:active{background-color:#2980B9;cursor:pointer;color:#fff}.wy-menu-vertical a:active span.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980B9;text-align:center;padding:.809em;display:block;color:#fcfcfc;margin-bottom:.809em}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em auto;height:45px;width:45px;background-color:#2980B9;padding:5px;border-radius:100%}.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a{color:#fcfcfc;font-size:100%;font-weight:bold;display:inline-block;padding:4px 6px;margin-bottom:.809em}.wy-side-nav-search>a:hover,.wy-side-nav-search .wy-dropdown>a:hover{background:rgba(255,255,255,0.1)}.wy-side-nav-search>a img.logo,.wy-side-nav-search .wy-dropdown>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search>a.icon img.logo,.wy-side-nav-search .wy-dropdown>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.version{margin-top:-.4045em;margin-bottom:.809em;font-weight:normal;color:rgba(255,255,255,0.3)}.wy-nav .wy-menu-vertical header{color:#2980B9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980B9;color:#fff}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:#fcfcfc}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:#9b9b9b;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980B9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:before,.wy-nav-top:after{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:bold}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980B9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,0.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:gray}footer p{margin-bottom:12px}footer span.commit code,footer span.commit .rst-content tt,.rst-content footer span.commit tt{padding:0px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace;font-size:1em;background:none;border:none;color:gray}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:before,.rst-footer-buttons:after{width:100%}.rst-footer-buttons:before,.rst-footer-buttons:after{display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:before,.rst-breadcrumbs-buttons:after{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:solid 1px #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:solid 1px #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:gray;font-size:90%}@media screen and (max-width: 768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-side-scroll{width:auto}.wy-side-nav-search{width:auto}.wy-menu.wy-menu-vertical{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width: 1100px){.wy-nav-content-wrap{background:rgba(0,0,0,0.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,footer,.wy-nav-side{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .wy-menu-vertical li span.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version span.toctree-expand,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content p.caption .headerlink,.rst-content p.caption .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .icon{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up{height:auto;max-height:100%}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}.rst-content img{max-width:100%;height:auto}.rst-content div.figure{margin-bottom:24px}.rst-content div.figure p.caption{font-style:italic}.rst-content div.figure p:last-child.caption{margin-bottom:0px}.rst-content div.figure.align-center{text-align:center}.rst-content .section>img,.rst-content .section>a>img{margin-bottom:24px}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"";color:#b3b3b3;vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content pre.literal-block{white-space:pre;margin:0;padding:12px 12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace;display:block;overflow:auto}.rst-content pre.literal-block,.rst-content div[class^='highlight']{border:1px solid #e1e4e5;overflow-x:auto;margin:1px 0 24px 0}.rst-content pre.literal-block div[class^='highlight'],.rst-content div[class^='highlight'] div[class^='highlight']{padding:0px;border:none;margin:0}.rst-content div[class^='highlight'] td.code{width:100%}.rst-content .linenodiv pre{border-right:solid 1px #e6e9ea;margin:0;padding:12px 12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace;user-select:none;pointer-events:none}.rst-content div[class^='highlight'] pre{white-space:pre;margin:0;padding:12px 12px;display:block;overflow:auto}.rst-content div[class^='highlight'] pre .hll{display:block;margin:0 -12px;padding:0 12px}.rst-content pre.literal-block,.rst-content div[class^='highlight'] pre,.rst-content .linenodiv pre{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace;font-size:12px;line-height:1.4}@media print{.rst-content .codeblock,.rst-content div[class^='highlight'],.rst-content div[class^='highlight'] pre{white-space:pre-wrap}}.rst-content .note .last,.rst-content .attention .last,.rst-content .caution .last,.rst-content .danger .last,.rst-content .error .last,.rst-content .hint .last,.rst-content .important .last,.rst-content .tip .last,.rst-content .warning .last,.rst-content .seealso .last,.rst-content .admonition-todo .last,.rst-content .admonition .last{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,0.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent !important;border-color:rgba(0,0,0,0.1) !important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha li{list-style:upper-alpha}.rst-content .section ol p,.rst-content .section ul p{margin-bottom:12px}.rst-content .section ol p:last-child,.rst-content .section ul p:last-child{margin-bottom:24px}.rst-content .line-block{margin-left:0px;margin-bottom:24px;line-height:24px}.rst-content .line-block .line-block{margin-left:24px;margin-bottom:0px}.rst-content .topic-title{font-weight:bold;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0px 0px 24px 24px}.rst-content .align-left{float:left;margin:0px 24px 24px 0px}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content .toctree-wrapper p.caption .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.rst-content p.caption .headerlink,.rst-content table>caption .headerlink{visibility:hidden;font-size:14px}.rst-content h1 .headerlink:after,.rst-content h2 .headerlink:after,.rst-content .toctree-wrapper p.caption .headerlink:after,.rst-content h3 .headerlink:after,.rst-content h4 .headerlink:after,.rst-content h5 .headerlink:after,.rst-content h6 .headerlink:after,.rst-content dl dt .headerlink:after,.rst-content p.caption .headerlink:after,.rst-content table>caption .headerlink:after{content:"";font-family:FontAwesome}.rst-content h1:hover .headerlink:after,.rst-content h2:hover .headerlink:after,.rst-content .toctree-wrapper p.caption:hover .headerlink:after,.rst-content h3:hover .headerlink:after,.rst-content h4:hover .headerlink:after,.rst-content h5:hover .headerlink:after,.rst-content h6:hover .headerlink:after,.rst-content dl dt:hover .headerlink:after,.rst-content p.caption:hover .headerlink:after,.rst-content table>caption:hover .headerlink:after{visibility:visible}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:solid 1px #e1e4e5}.rst-content .sidebar p,.rst-content .sidebar ul,.rst-content .sidebar dl{font-size:90%}.rst-content .sidebar .last{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif;font-weight:bold;background:#e1e4e5;padding:6px 12px;margin:-24px;margin-bottom:24px;font-size:100%}.rst-content .highlighted{background:#F1C40F;display:inline-block;font-weight:bold;padding:0 6px}.rst-content .footnote-reference,.rst-content .citation-reference{vertical-align:baseline;position:relative;top:-0.4em;line-height:0;font-size:90%}.rst-content table.docutils.citation,.rst-content table.docutils.footnote{background:none;border:none;color:gray}.rst-content table.docutils.citation td,.rst-content table.docutils.citation tr,.rst-content table.docutils.footnote td,.rst-content table.docutils.footnote tr{border:none;background-color:transparent !important;white-space:normal}.rst-content table.docutils.citation td.label,.rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}.rst-content table.docutils.citation tt,.rst-content table.docutils.citation code,.rst-content table.docutils.footnote tt,.rst-content table.docutils.footnote code{color:#555}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:24px}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:24px}.rst-content table.docutils th{border-color:#e1e4e5}.rst-content table.docutils td .last,.rst-content table.docutils td .last :last-child{margin-bottom:0}.rst-content table.field-list{border:none}.rst-content table.field-list td{border:none}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content tt,.rst-content tt,.rst-content code{color:#000;font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace;padding:2px 5px}.rst-content tt big,.rst-content tt em,.rst-content tt big,.rst-content code big,.rst-content tt em,.rst-content code em{font-size:100% !important;line-height:normal}.rst-content tt.literal,.rst-content tt.literal,.rst-content code.literal{color:#E74C3C}.rst-content tt.xref,a .rst-content tt,.rst-content tt.xref,.rst-content code.xref,a .rst-content tt,a .rst-content code{font-weight:bold;color:#404040}.rst-content pre,.rst-content kbd,.rst-content samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace}.rst-content a tt,.rst-content a tt,.rst-content a code{color:#2980B9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:bold;margin-bottom:12px}.rst-content dl p,.rst-content dl table,.rst-content dl ul,.rst-content dl ol{margin-bottom:12px !important}.rst-content dl dd{margin:0 0 12px 24px;line-height:24px}.rst-content dl:not(.docutils){margin-bottom:24px}.rst-content dl:not(.docutils) dt{display:table;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980B9;border-top:solid 3px #6ab0de;padding:6px;position:relative}.rst-content dl:not(.docutils) dt:before{color:#6ab0de}.rst-content dl:not(.docutils) dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dl dt{margin-bottom:6px;border:none;border-left:solid 3px #ccc;background:#f0f0f0;color:#555}.rst-content dl:not(.docutils) dl dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dt:first-child{margin-top:0}.rst-content dl:not(.docutils) tt,.rst-content dl:not(.docutils) tt,.rst-content dl:not(.docutils) code{font-weight:bold}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descclassname,.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) code.descname,.rst-content dl:not(.docutils) tt.descclassname,.rst-content dl:not(.docutils) code.descclassname{background-color:transparent;border:none;padding:0;font-size:100% !important}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) code.descname{font-weight:bold}.rst-content dl:not(.docutils) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:bold}.rst-content dl:not(.docutils) .property{display:inline-block;padding-right:8px}.rst-content .viewcode-link,.rst-content .viewcode-back{display:inline-block;color:#27AE60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:bold}.rst-content tt.download,.rst-content code.download{background:inherit;padding:inherit;font-weight:normal;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content tt.download span:first-child,.rst-content code.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content tt.download span:first-child:before,.rst-content code.download span:first-child:before{margin-right:4px}.rst-content .guilabel{border:1px solid #7fbbe3;background:#e7f2fa;font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}.rst-content .versionmodified{font-style:italic}@media screen and (max-width: 480px){.rst-content .sidebar{width:100%}}span[id*='MathJax-Span']{color:#404040}.math{text-align:center}@font-face{font-family:"Lato";src:url("../fonts/Lato/lato-regular.eot");src:url("../fonts/Lato/lato-regular.eot?#iefix") format("embedded-opentype"),url("../fonts/Lato/lato-regular.woff2") format("woff2"),url("../fonts/Lato/lato-regular.woff") format("woff"),url("../fonts/Lato/lato-regular.ttf") format("truetype");font-weight:400;font-style:normal}@font-face{font-family:"Lato";src:url("../fonts/Lato/lato-bold.eot");src:url("../fonts/Lato/lato-bold.eot?#iefix") format("embedded-opentype"),url("../fonts/Lato/lato-bold.woff2") format("woff2"),url("../fonts/Lato/lato-bold.woff") format("woff"),url("../fonts/Lato/lato-bold.ttf") format("truetype");font-weight:700;font-style:normal}@font-face{font-family:"Lato";src:url("../fonts/Lato/lato-bolditalic.eot");src:url("../fonts/Lato/lato-bolditalic.eot?#iefix") format("embedded-opentype"),url("../fonts/Lato/lato-bolditalic.woff2") format("woff2"),url("../fonts/Lato/lato-bolditalic.woff") format("woff"),url("../fonts/Lato/lato-bolditalic.ttf") format("truetype");font-weight:700;font-style:italic}@font-face{font-family:"Lato";src:url("../fonts/Lato/lato-italic.eot");src:url("../fonts/Lato/lato-italic.eot?#iefix") format("embedded-opentype"),url("../fonts/Lato/lato-italic.woff2") format("woff2"),url("../fonts/Lato/lato-italic.woff") format("woff"),url("../fonts/Lato/lato-italic.ttf") format("truetype");font-weight:400;font-style:italic}@font-face{font-family:"Roboto Slab";font-style:normal;font-weight:400;src:url("../fonts/RobotoSlab/roboto-slab.eot");src:url("../fonts/RobotoSlab/roboto-slab-v7-regular.eot?#iefix") format("embedded-opentype"),url("../fonts/RobotoSlab/roboto-slab-v7-regular.woff2") format("woff2"),url("../fonts/RobotoSlab/roboto-slab-v7-regular.woff") format("woff"),url("../fonts/RobotoSlab/roboto-slab-v7-regular.ttf") format("truetype")}@font-face{font-family:"Roboto Slab";font-style:normal;font-weight:700;src:url("../fonts/RobotoSlab/roboto-slab-v7-bold.eot");src:url("../fonts/RobotoSlab/roboto-slab-v7-bold.eot?#iefix") format("embedded-opentype"),url("../fonts/RobotoSlab/roboto-slab-v7-bold.woff2") format("woff2"),url("../fonts/RobotoSlab/roboto-slab-v7-bold.woff") format("woff"),url("../fonts/RobotoSlab/roboto-slab-v7-bold.ttf") format("truetype")} diff --git a/old_docs/tiler_docs/_build/html/_static/doctools.js b/old_docs/tiler_docs/_build/html/_static/doctools.js deleted file mode 100644 index ffadbec1..00000000 --- a/old_docs/tiler_docs/_build/html/_static/doctools.js +++ /dev/null @@ -1,315 +0,0 @@ -/* - * doctools.js - * ~~~~~~~~~~~ - * - * Sphinx JavaScript utilities for all documentation. - * - * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/** - * select a different prefix for underscore - */ -$u = _.noConflict(); - -/** - * make the code below compatible with browsers without - * an installed firebug like debugger -if (!window.console || !console.firebug) { - var names = ["log", "debug", "info", "warn", "error", "assert", "dir", - "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", - "profile", "profileEnd"]; - window.console = {}; - for (var i = 0; i < names.length; ++i) - window.console[names[i]] = function() {}; -} - */ - -/** - * small helper function to urldecode strings - */ -jQuery.urldecode = function(x) { - return decodeURIComponent(x).replace(/\+/g, ' '); -}; - -/** - * small helper function to urlencode strings - */ -jQuery.urlencode = encodeURIComponent; - -/** - * This function returns the parsed url parameters of the - * current request. Multiple values per key are supported, - * it will always return arrays of strings for the value parts. - */ -jQuery.getQueryParameters = function(s) { - if (typeof s === 'undefined') - s = document.location.search; - var parts = s.substr(s.indexOf('?') + 1).split('&'); - var result = {}; - for (var i = 0; i < parts.length; i++) { - var tmp = parts[i].split('=', 2); - var key = jQuery.urldecode(tmp[0]); - var value = jQuery.urldecode(tmp[1]); - if (key in result) - result[key].push(value); - else - result[key] = [value]; - } - return result; -}; - -/** - * highlight a given string on a jquery object by wrapping it in - * span elements with the given class name. - */ -jQuery.fn.highlightText = function(text, className) { - function highlight(node, addItems) { - if (node.nodeType === 3) { - var val = node.nodeValue; - var pos = val.toLowerCase().indexOf(text); - if (pos >= 0 && - !jQuery(node.parentNode).hasClass(className) && - !jQuery(node.parentNode).hasClass("nohighlight")) { - var span; - var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); - if (isInSVG) { - span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); - } else { - span = document.createElement("span"); - span.className = className; - } - span.appendChild(document.createTextNode(val.substr(pos, text.length))); - node.parentNode.insertBefore(span, node.parentNode.insertBefore( - document.createTextNode(val.substr(pos + text.length)), - node.nextSibling)); - node.nodeValue = val.substr(0, pos); - if (isInSVG) { - var bbox = span.getBBox(); - var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); - rect.x.baseVal.value = bbox.x; - rect.y.baseVal.value = bbox.y; - rect.width.baseVal.value = bbox.width; - rect.height.baseVal.value = bbox.height; - rect.setAttribute('class', className); - var parentOfText = node.parentNode.parentNode; - addItems.push({ - "parent": node.parentNode, - "target": rect}); - } - } - } - else if (!jQuery(node).is("button, select, textarea")) { - jQuery.each(node.childNodes, function() { - highlight(this, addItems); - }); - } - } - var addItems = []; - var result = this.each(function() { - highlight(this, addItems); - }); - for (var i = 0; i < addItems.length; ++i) { - jQuery(addItems[i].parent).before(addItems[i].target); - } - return result; -}; - -/* - * backward compatibility for jQuery.browser - * This will be supported until firefox bug is fixed. - */ -if (!jQuery.browser) { - jQuery.uaMatch = function(ua) { - ua = ua.toLowerCase(); - - var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || - /(webkit)[ \/]([\w.]+)/.exec(ua) || - /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || - /(msie) ([\w.]+)/.exec(ua) || - ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || - []; - - return { - browser: match[ 1 ] || "", - version: match[ 2 ] || "0" - }; - }; - jQuery.browser = {}; - jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; -} - -/** - * Small JavaScript module for the documentation. - */ -var Documentation = { - - init : function() { - this.fixFirefoxAnchorBug(); - this.highlightSearchWords(); - this.initIndexTable(); - if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) { - this.initOnKeyListeners(); - } - }, - - /** - * i18n support - */ - TRANSLATIONS : {}, - PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; }, - LOCALE : 'unknown', - - // gettext and ngettext don't access this so that the functions - // can safely bound to a different name (_ = Documentation.gettext) - gettext : function(string) { - var translated = Documentation.TRANSLATIONS[string]; - if (typeof translated === 'undefined') - return string; - return (typeof translated === 'string') ? translated : translated[0]; - }, - - ngettext : function(singular, plural, n) { - var translated = Documentation.TRANSLATIONS[singular]; - if (typeof translated === 'undefined') - return (n == 1) ? singular : plural; - return translated[Documentation.PLURALEXPR(n)]; - }, - - addTranslations : function(catalog) { - for (var key in catalog.messages) - this.TRANSLATIONS[key] = catalog.messages[key]; - this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); - this.LOCALE = catalog.locale; - }, - - /** - * add context elements like header anchor links - */ - addContextElements : function() { - $('div[id] > :header:first').each(function() { - $('\u00B6'). - attr('href', '#' + this.id). - attr('title', _('Permalink to this headline')). - appendTo(this); - }); - $('dt[id]').each(function() { - $('\u00B6'). - attr('href', '#' + this.id). - attr('title', _('Permalink to this definition')). - appendTo(this); - }); - }, - - /** - * workaround a firefox stupidity - * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 - */ - fixFirefoxAnchorBug : function() { - if (document.location.hash && $.browser.mozilla) - window.setTimeout(function() { - document.location.href += ''; - }, 10); - }, - - /** - * highlight the search words provided in the url in the text - */ - highlightSearchWords : function() { - var params = $.getQueryParameters(); - var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; - if (terms.length) { - var body = $('div.body'); - if (!body.length) { - body = $('body'); - } - window.setTimeout(function() { - $.each(terms, function() { - body.highlightText(this.toLowerCase(), 'highlighted'); - }); - }, 10); - $('') - .appendTo($('#searchbox')); - } - }, - - /** - * init the domain index toggle buttons - */ - initIndexTable : function() { - var togglers = $('img.toggler').click(function() { - var src = $(this).attr('src'); - var idnum = $(this).attr('id').substr(7); - $('tr.cg-' + idnum).toggle(); - if (src.substr(-9) === 'minus.png') - $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); - else - $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); - }).css('display', ''); - if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { - togglers.click(); - } - }, - - /** - * helper function to hide the search marks again - */ - hideSearchWords : function() { - $('#searchbox .highlight-link').fadeOut(300); - $('span.highlighted').removeClass('highlighted'); - }, - - /** - * make the url absolute - */ - makeURL : function(relativeURL) { - return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; - }, - - /** - * get the current relative url - */ - getCurrentURL : function() { - var path = document.location.pathname; - var parts = path.split(/\//); - $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { - if (this === '..') - parts.pop(); - }); - var url = parts.join('/'); - return path.substring(url.lastIndexOf('/') + 1, path.length - 1); - }, - - initOnKeyListeners: function() { - $(document).keyup(function(event) { - var activeElementType = document.activeElement.tagName; - // don't navigate when in search box or textarea - if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT') { - switch (event.keyCode) { - case 37: // left - var prevHref = $('link[rel="prev"]').prop('href'); - if (prevHref) { - window.location.href = prevHref; - return false; - } - case 39: // right - var nextHref = $('link[rel="next"]').prop('href'); - if (nextHref) { - window.location.href = nextHref; - return false; - } - } - } - }); - } -}; - -// quick alias for translations -_ = Documentation.gettext; - -$(document).ready(function() { - Documentation.init(); -}); diff --git a/old_docs/tiler_docs/_build/html/_static/documentation_options.js b/old_docs/tiler_docs/_build/html/_static/documentation_options.js deleted file mode 100644 index 0d129846..00000000 --- a/old_docs/tiler_docs/_build/html/_static/documentation_options.js +++ /dev/null @@ -1,296 +0,0 @@ -var DOCUMENTATION_OPTIONS = { - URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), - VERSION: '0.2.0', - LANGUAGE: 'None', - COLLAPSE_INDEX: false, - FILE_SUFFIX: '.html', - HAS_SOURCE: true, - SOURCELINK_SUFFIX: '.txt', - NAVIGATION_WITH_KEYS: false, - SEARCH_LANGUAGE_STOP_WORDS: ["a","and","are","as","at","be","but","by","for","if","in","into","is","it","near","no","not","of","on","or","such","that","the","their","then","there","these","they","this","to","was","will","with"] -}; - - - -/* Non-minified version JS is _stemmer.js if file is provided */ -/** - * Porter Stemmer - */ -var Stemmer = function() { - - var step2list = { - ational: 'ate', - tional: 'tion', - enci: 'ence', - anci: 'ance', - izer: 'ize', - bli: 'ble', - alli: 'al', - entli: 'ent', - eli: 'e', - ousli: 'ous', - ization: 'ize', - ation: 'ate', - ator: 'ate', - alism: 'al', - iveness: 'ive', - fulness: 'ful', - ousness: 'ous', - aliti: 'al', - iviti: 'ive', - biliti: 'ble', - logi: 'log' - }; - - var step3list = { - icate: 'ic', - ative: '', - alize: 'al', - iciti: 'ic', - ical: 'ic', - ful: '', - ness: '' - }; - - var c = "[^aeiou]"; // consonant - var v = "[aeiouy]"; // vowel - var C = c + "[^aeiouy]*"; // consonant sequence - var V = v + "[aeiou]*"; // vowel sequence - - var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 - var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 - var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 - var s_v = "^(" + C + ")?" + v; // vowel in stem - - this.stemWord = function (w) { - var stem; - var suffix; - var firstch; - var origword = w; - - if (w.length < 3) - return w; - - var re; - var re2; - var re3; - var re4; - - firstch = w.substr(0,1); - if (firstch == "y") - w = firstch.toUpperCase() + w.substr(1); - - // Step 1a - re = /^(.+?)(ss|i)es$/; - re2 = /^(.+?)([^s])s$/; - - if (re.test(w)) - w = w.replace(re,"$1$2"); - else if (re2.test(w)) - w = w.replace(re2,"$1$2"); - - // Step 1b - re = /^(.+?)eed$/; - re2 = /^(.+?)(ed|ing)$/; - if (re.test(w)) { - var fp = re.exec(w); - re = new RegExp(mgr0); - if (re.test(fp[1])) { - re = /.$/; - w = w.replace(re,""); - } - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1]; - re2 = new RegExp(s_v); - if (re2.test(stem)) { - w = stem; - re2 = /(at|bl|iz)$/; - re3 = new RegExp("([^aeiouylsz])\\1$"); - re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re2.test(w)) - w = w + "e"; - else if (re3.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - else if (re4.test(w)) - w = w + "e"; - } - } - - // Step 1c - re = /^(.+?)y$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(s_v); - if (re.test(stem)) - w = stem + "i"; - } - - // Step 2 - re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step2list[suffix]; - } - - // Step 3 - re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step3list[suffix]; - } - - // Step 4 - re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; - re2 = /^(.+?)(s|t)(ion)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - if (re.test(stem)) - w = stem; - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1] + fp[2]; - re2 = new RegExp(mgr1); - if (re2.test(stem)) - w = stem; - } - - // Step 5 - re = /^(.+?)e$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - re2 = new RegExp(meq1); - re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) - w = stem; - } - re = /ll$/; - re2 = new RegExp(mgr1); - if (re.test(w) && re2.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - - // and turn initial Y back to y - if (firstch == "y") - w = firstch.toLowerCase() + w.substr(1); - return w; - } -} - - - - - -var splitChars = (function() { - var result = {}; - var singles = [96, 180, 187, 191, 215, 247, 749, 885, 903, 907, 909, 930, 1014, 1648, - 1748, 1809, 2416, 2473, 2481, 2526, 2601, 2609, 2612, 2615, 2653, 2702, - 2706, 2729, 2737, 2740, 2857, 2865, 2868, 2910, 2928, 2948, 2961, 2971, - 2973, 3085, 3089, 3113, 3124, 3213, 3217, 3241, 3252, 3295, 3341, 3345, - 3369, 3506, 3516, 3633, 3715, 3721, 3736, 3744, 3748, 3750, 3756, 3761, - 3781, 3912, 4239, 4347, 4681, 4695, 4697, 4745, 4785, 4799, 4801, 4823, - 4881, 5760, 5901, 5997, 6313, 7405, 8024, 8026, 8028, 8030, 8117, 8125, - 8133, 8181, 8468, 8485, 8487, 8489, 8494, 8527, 11311, 11359, 11687, 11695, - 11703, 11711, 11719, 11727, 11735, 12448, 12539, 43010, 43014, 43019, 43587, - 43696, 43713, 64286, 64297, 64311, 64317, 64319, 64322, 64325, 65141]; - var i, j, start, end; - for (i = 0; i < singles.length; i++) { - result[singles[i]] = true; - } - var ranges = [[0, 47], [58, 64], [91, 94], [123, 169], [171, 177], [182, 184], [706, 709], - [722, 735], [741, 747], [751, 879], [888, 889], [894, 901], [1154, 1161], - [1318, 1328], [1367, 1368], [1370, 1376], [1416, 1487], [1515, 1519], [1523, 1568], - [1611, 1631], [1642, 1645], [1750, 1764], [1767, 1773], [1789, 1790], [1792, 1807], - [1840, 1868], [1958, 1968], [1970, 1983], [2027, 2035], [2038, 2041], [2043, 2047], - [2070, 2073], [2075, 2083], [2085, 2087], [2089, 2307], [2362, 2364], [2366, 2383], - [2385, 2391], [2402, 2405], [2419, 2424], [2432, 2436], [2445, 2446], [2449, 2450], - [2483, 2485], [2490, 2492], [2494, 2509], [2511, 2523], [2530, 2533], [2546, 2547], - [2554, 2564], [2571, 2574], [2577, 2578], [2618, 2648], [2655, 2661], [2672, 2673], - [2677, 2692], [2746, 2748], [2750, 2767], [2769, 2783], [2786, 2789], [2800, 2820], - [2829, 2830], [2833, 2834], [2874, 2876], [2878, 2907], [2914, 2917], [2930, 2946], - [2955, 2957], [2966, 2968], [2976, 2978], [2981, 2983], [2987, 2989], [3002, 3023], - [3025, 3045], [3059, 3076], [3130, 3132], [3134, 3159], [3162, 3167], [3170, 3173], - [3184, 3191], [3199, 3204], [3258, 3260], [3262, 3293], [3298, 3301], [3312, 3332], - [3386, 3388], [3390, 3423], [3426, 3429], [3446, 3449], [3456, 3460], [3479, 3481], - [3518, 3519], [3527, 3584], [3636, 3647], [3655, 3663], [3674, 3712], [3717, 3718], - [3723, 3724], [3726, 3731], [3752, 3753], [3764, 3772], [3774, 3775], [3783, 3791], - [3802, 3803], [3806, 3839], [3841, 3871], [3892, 3903], [3949, 3975], [3980, 4095], - [4139, 4158], [4170, 4175], [4182, 4185], [4190, 4192], [4194, 4196], [4199, 4205], - [4209, 4212], [4226, 4237], [4250, 4255], [4294, 4303], [4349, 4351], [4686, 4687], - [4702, 4703], [4750, 4751], [4790, 4791], [4806, 4807], [4886, 4887], [4955, 4968], - [4989, 4991], [5008, 5023], [5109, 5120], [5741, 5742], [5787, 5791], [5867, 5869], - [5873, 5887], [5906, 5919], [5938, 5951], [5970, 5983], [6001, 6015], [6068, 6102], - [6104, 6107], [6109, 6111], [6122, 6127], [6138, 6159], [6170, 6175], [6264, 6271], - [6315, 6319], [6390, 6399], [6429, 6469], [6510, 6511], [6517, 6527], [6572, 6592], - [6600, 6607], [6619, 6655], [6679, 6687], [6741, 6783], [6794, 6799], [6810, 6822], - [6824, 6916], [6964, 6980], [6988, 6991], [7002, 7042], [7073, 7085], [7098, 7167], - [7204, 7231], [7242, 7244], [7294, 7400], [7410, 7423], [7616, 7679], [7958, 7959], - [7966, 7967], [8006, 8007], [8014, 8015], [8062, 8063], [8127, 8129], [8141, 8143], - [8148, 8149], [8156, 8159], [8173, 8177], [8189, 8303], [8306, 8307], [8314, 8318], - [8330, 8335], [8341, 8449], [8451, 8454], [8456, 8457], [8470, 8472], [8478, 8483], - [8506, 8507], [8512, 8516], [8522, 8525], [8586, 9311], [9372, 9449], [9472, 10101], - [10132, 11263], [11493, 11498], [11503, 11516], [11518, 11519], [11558, 11567], - [11622, 11630], [11632, 11647], [11671, 11679], [11743, 11822], [11824, 12292], - [12296, 12320], [12330, 12336], [12342, 12343], [12349, 12352], [12439, 12444], - [12544, 12548], [12590, 12592], [12687, 12689], [12694, 12703], [12728, 12783], - [12800, 12831], [12842, 12880], [12896, 12927], [12938, 12976], [12992, 13311], - [19894, 19967], [40908, 40959], [42125, 42191], [42238, 42239], [42509, 42511], - [42540, 42559], [42592, 42593], [42607, 42622], [42648, 42655], [42736, 42774], - [42784, 42785], [42889, 42890], [42893, 43002], [43043, 43055], [43062, 43071], - [43124, 43137], [43188, 43215], [43226, 43249], [43256, 43258], [43260, 43263], - [43302, 43311], [43335, 43359], [43389, 43395], [43443, 43470], [43482, 43519], - [43561, 43583], [43596, 43599], [43610, 43615], [43639, 43641], [43643, 43647], - [43698, 43700], [43703, 43704], [43710, 43711], [43715, 43738], [43742, 43967], - [44003, 44015], [44026, 44031], [55204, 55215], [55239, 55242], [55292, 55295], - [57344, 63743], [64046, 64047], [64110, 64111], [64218, 64255], [64263, 64274], - [64280, 64284], [64434, 64466], [64830, 64847], [64912, 64913], [64968, 65007], - [65020, 65135], [65277, 65295], [65306, 65312], [65339, 65344], [65371, 65381], - [65471, 65473], [65480, 65481], [65488, 65489], [65496, 65497]]; - for (i = 0; i < ranges.length; i++) { - start = ranges[i][0]; - end = ranges[i][1]; - for (j = start; j <= end; j++) { - result[j] = true; - } - } - return result; -})(); - -function splitQuery(query) { - var result = []; - var start = -1; - for (var i = 0; i < query.length; i++) { - if (splitChars[query.charCodeAt(i)]) { - if (start !== -1) { - result.push(query.slice(start, i)); - start = -1; - } - } else if (start === -1) { - start = i; - } - } - if (start !== -1) { - result.push(query.slice(start)); - } - return result; -} - - diff --git a/old_docs/tiler_docs/_build/html/_static/down-pressed.png b/old_docs/tiler_docs/_build/html/_static/down-pressed.png deleted file mode 100644 index 5756c8ca..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/down-pressed.png and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/down.png b/old_docs/tiler_docs/_build/html/_static/down.png deleted file mode 100644 index 1b3bdad2..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/down.png and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/file.png b/old_docs/tiler_docs/_build/html/_static/file.png deleted file mode 100644 index a858a410..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/file.png and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/Inconsolata-Bold.ttf b/old_docs/tiler_docs/_build/html/_static/fonts/Inconsolata-Bold.ttf deleted file mode 100644 index 809c1f58..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/Inconsolata-Bold.ttf and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/Inconsolata-Regular.ttf b/old_docs/tiler_docs/_build/html/_static/fonts/Inconsolata-Regular.ttf deleted file mode 100644 index fc981ce7..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/Inconsolata-Regular.ttf and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/Inconsolata.ttf b/old_docs/tiler_docs/_build/html/_static/fonts/Inconsolata.ttf deleted file mode 100644 index 4b8a36d2..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/Inconsolata.ttf and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/Lato-Bold.ttf b/old_docs/tiler_docs/_build/html/_static/fonts/Lato-Bold.ttf deleted file mode 100644 index 1d23c706..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/Lato-Bold.ttf and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/Lato-Regular.ttf b/old_docs/tiler_docs/_build/html/_static/fonts/Lato-Regular.ttf deleted file mode 100644 index 0f3d0f83..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/Lato-Regular.ttf and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-bold.eot b/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-bold.eot deleted file mode 100644 index 3361183a..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-bold.eot and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-bold.ttf b/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-bold.ttf deleted file mode 100644 index 29f691d5..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-bold.ttf and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-bold.woff b/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-bold.woff deleted file mode 100644 index c6dff51f..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-bold.woff and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-bold.woff2 b/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-bold.woff2 deleted file mode 100644 index bb195043..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-bold.woff2 and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-bolditalic.eot b/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-bolditalic.eot deleted file mode 100644 index 3d415493..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-bolditalic.eot and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-bolditalic.ttf b/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-bolditalic.ttf deleted file mode 100644 index f402040b..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-bolditalic.ttf and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-bolditalic.woff b/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-bolditalic.woff deleted file mode 100644 index 88ad05b9..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-bolditalic.woff and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-bolditalic.woff2 b/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-bolditalic.woff2 deleted file mode 100644 index c4e3d804..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-bolditalic.woff2 and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-italic.eot b/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-italic.eot deleted file mode 100644 index 3f826421..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-italic.eot and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-italic.ttf b/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-italic.ttf deleted file mode 100644 index b4bfc9b2..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-italic.ttf and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-italic.woff b/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-italic.woff deleted file mode 100644 index 76114bc0..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-italic.woff and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-italic.woff2 b/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-italic.woff2 deleted file mode 100644 index 3404f37e..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-italic.woff2 and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-regular.eot b/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-regular.eot deleted file mode 100644 index 11e3f2a5..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-regular.eot and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-regular.ttf b/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-regular.ttf deleted file mode 100644 index 74decd9e..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-regular.ttf and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-regular.woff b/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-regular.woff deleted file mode 100644 index ae1307ff..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-regular.woff and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-regular.woff2 b/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-regular.woff2 deleted file mode 100644 index 3bf98433..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/Lato/lato-regular.woff2 and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab-Bold.ttf b/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab-Bold.ttf deleted file mode 100644 index df5d1df2..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab-Bold.ttf and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab-Regular.ttf b/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab-Regular.ttf deleted file mode 100644 index eb52a790..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab-Regular.ttf and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot b/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot deleted file mode 100644 index 79dc8efe..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf b/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf deleted file mode 100644 index df5d1df2..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff b/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff deleted file mode 100644 index 6cb60000..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 b/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 deleted file mode 100644 index 7059e231..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot b/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot deleted file mode 100644 index 2f7ca78a..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf b/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf deleted file mode 100644 index eb52a790..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff b/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff deleted file mode 100644 index f815f63f..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 b/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 deleted file mode 100644 index f2c76e5b..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/fontawesome-webfont.eot b/old_docs/tiler_docs/_build/html/_static/fonts/fontawesome-webfont.eot deleted file mode 100644 index e9f60ca9..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/fontawesome-webfont.eot and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/fontawesome-webfont.svg b/old_docs/tiler_docs/_build/html/_static/fonts/fontawesome-webfont.svg deleted file mode 100644 index 855c845e..00000000 --- a/old_docs/tiler_docs/_build/html/_static/fonts/fontawesome-webfont.svg +++ /dev/null @@ -1,2671 +0,0 @@ - - - - -Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 - By ,,, -Copyright Dave Gandy 2016. All rights reserved. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/fontawesome-webfont.ttf b/old_docs/tiler_docs/_build/html/_static/fonts/fontawesome-webfont.ttf deleted file mode 100644 index 35acda2f..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/fontawesome-webfont.ttf and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/fontawesome-webfont.woff b/old_docs/tiler_docs/_build/html/_static/fonts/fontawesome-webfont.woff deleted file mode 100644 index 400014a4..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/fontawesome-webfont.woff and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/fonts/fontawesome-webfont.woff2 b/old_docs/tiler_docs/_build/html/_static/fonts/fontawesome-webfont.woff2 deleted file mode 100644 index 4d13fc60..00000000 Binary files a/old_docs/tiler_docs/_build/html/_static/fonts/fontawesome-webfont.woff2 and /dev/null differ diff --git a/old_docs/tiler_docs/_build/html/_static/jquery-3.2.1.js b/old_docs/tiler_docs/_build/html/_static/jquery-3.2.1.js deleted file mode 100644 index d2d8ca47..00000000 --- a/old_docs/tiler_docs/_build/html/_static/jquery-3.2.1.js +++ /dev/null @@ -1,10253 +0,0 @@ -/*! - * jQuery JavaScript Library v3.2.1 - * https://jquery.com/ - * - * Includes Sizzle.js - * https://sizzlejs.com/ - * - * Copyright JS Foundation and other contributors - * Released under the MIT license - * https://jquery.org/license - * - * Date: 2017-03-20T18:59Z - */ -( function( global, factory ) { - - "use strict"; - - if ( typeof module === "object" && typeof module.exports === "object" ) { - - // For CommonJS and CommonJS-like environments where a proper `window` - // is present, execute the factory and get jQuery. - // For environments that do not have a `window` with a `document` - // (such as Node.js), expose a factory as module.exports. - // This accentuates the need for the creation of a real `window`. - // e.g. var jQuery = require("jquery")(window); - // See ticket #14549 for more info. - module.exports = global.document ? - factory( global, true ) : - function( w ) { - if ( !w.document ) { - throw new Error( "jQuery requires a window with a document" ); - } - return factory( w ); - }; - } else { - factory( global ); - } - -// Pass this if window is not defined yet -} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { - -// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 -// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode -// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common -// enough that all such attempts are guarded in a try block. -"use strict"; - -var arr = []; - -var document = window.document; - -var getProto = Object.getPrototypeOf; - -var slice = arr.slice; - -var concat = arr.concat; - -var push = arr.push; - -var indexOf = arr.indexOf; - -var class2type = {}; - -var toString = class2type.toString; - -var hasOwn = class2type.hasOwnProperty; - -var fnToString = hasOwn.toString; - -var ObjectFunctionString = fnToString.call( Object ); - -var support = {}; - - - - function DOMEval( code, doc ) { - doc = doc || document; - - var script = doc.createElement( "script" ); - - script.text = code; - doc.head.appendChild( script ).parentNode.removeChild( script ); - } -/* global Symbol */ -// Defining this global in .eslintrc.json would create a danger of using the global -// unguarded in another place, it seems safer to define global only for this module - - - -var - version = "3.2.1", - - // Define a local copy of jQuery - jQuery = function( selector, context ) { - - // The jQuery object is actually just the init constructor 'enhanced' - // Need init if jQuery is called (just allow error to be thrown if not included) - return new jQuery.fn.init( selector, context ); - }, - - // Support: Android <=4.0 only - // Make sure we trim BOM and NBSP - rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, - - // Matches dashed string for camelizing - rmsPrefix = /^-ms-/, - rdashAlpha = /-([a-z])/g, - - // Used by jQuery.camelCase as callback to replace() - fcamelCase = function( all, letter ) { - return letter.toUpperCase(); - }; - -jQuery.fn = jQuery.prototype = { - - // The current version of jQuery being used - jquery: version, - - constructor: jQuery, - - // The default length of a jQuery object is 0 - length: 0, - - toArray: function() { - return slice.call( this ); - }, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - - // Return all the elements in a clean array - if ( num == null ) { - return slice.call( this ); - } - - // Return just the one element from the set - return num < 0 ? this[ num + this.length ] : this[ num ]; - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems ) { - - // Build a new jQuery matched element set - var ret = jQuery.merge( this.constructor(), elems ); - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - - // Return the newly-formed element set - return ret; - }, - - // Execute a callback for every element in the matched set. - each: function( callback ) { - return jQuery.each( this, callback ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map( this, function( elem, i ) { - return callback.call( elem, i, elem ); - } ) ); - }, - - slice: function() { - return this.pushStack( slice.apply( this, arguments ) ); - }, - - first: function() { - return this.eq( 0 ); - }, - - last: function() { - return this.eq( -1 ); - }, - - eq: function( i ) { - var len = this.length, - j = +i + ( i < 0 ? len : 0 ); - return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); - }, - - end: function() { - return this.prevObject || this.constructor(); - }, - - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: push, - sort: arr.sort, - splice: arr.splice -}; - -jQuery.extend = jQuery.fn.extend = function() { - var options, name, src, copy, copyIsArray, clone, - target = arguments[ 0 ] || {}, - i = 1, - length = arguments.length, - deep = false; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - - // Skip the boolean and the target - target = arguments[ i ] || {}; - i++; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction( target ) ) { - target = {}; - } - - // Extend jQuery itself if only one argument is passed - if ( i === length ) { - target = this; - i--; - } - - for ( ; i < length; i++ ) { - - // Only deal with non-null/undefined values - if ( ( options = arguments[ i ] ) != null ) { - - // Extend the base object - for ( name in options ) { - src = target[ name ]; - copy = options[ name ]; - - // Prevent never-ending loop - if ( target === copy ) { - continue; - } - - // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( jQuery.isPlainObject( copy ) || - ( copyIsArray = Array.isArray( copy ) ) ) ) { - - if ( copyIsArray ) { - copyIsArray = false; - clone = src && Array.isArray( src ) ? src : []; - - } else { - clone = src && jQuery.isPlainObject( src ) ? src : {}; - } - - // Never move original objects, clone them - target[ name ] = jQuery.extend( deep, clone, copy ); - - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } - - // Return the modified object - return target; -}; - -jQuery.extend( { - - // Unique for each copy of jQuery on the page - expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), - - // Assume jQuery is ready without the ready module - isReady: true, - - error: function( msg ) { - throw new Error( msg ); - }, - - noop: function() {}, - - isFunction: function( obj ) { - return jQuery.type( obj ) === "function"; - }, - - isWindow: function( obj ) { - return obj != null && obj === obj.window; - }, - - isNumeric: function( obj ) { - - // As of jQuery 3.0, isNumeric is limited to - // strings and numbers (primitives or objects) - // that can be coerced to finite numbers (gh-2662) - var type = jQuery.type( obj ); - return ( type === "number" || type === "string" ) && - - // parseFloat NaNs numeric-cast false positives ("") - // ...but misinterprets leading-number strings, particularly hex literals ("0x...") - // subtraction forces infinities to NaN - !isNaN( obj - parseFloat( obj ) ); - }, - - isPlainObject: function( obj ) { - var proto, Ctor; - - // Detect obvious negatives - // Use toString instead of jQuery.type to catch host objects - if ( !obj || toString.call( obj ) !== "[object Object]" ) { - return false; - } - - proto = getProto( obj ); - - // Objects with no prototype (e.g., `Object.create( null )`) are plain - if ( !proto ) { - return true; - } - - // Objects with prototype are plain iff they were constructed by a global Object function - Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; - return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; - }, - - isEmptyObject: function( obj ) { - - /* eslint-disable no-unused-vars */ - // See https://github.com/eslint/eslint/issues/6125 - var name; - - for ( name in obj ) { - return false; - } - return true; - }, - - type: function( obj ) { - if ( obj == null ) { - return obj + ""; - } - - // Support: Android <=2.3 only (functionish RegExp) - return typeof obj === "object" || typeof obj === "function" ? - class2type[ toString.call( obj ) ] || "object" : - typeof obj; - }, - - // Evaluates a script in a global context - globalEval: function( code ) { - DOMEval( code ); - }, - - // Convert dashed to camelCase; used by the css and data modules - // Support: IE <=9 - 11, Edge 12 - 13 - // Microsoft forgot to hump their vendor prefix (#9572) - camelCase: function( string ) { - return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); - }, - - each: function( obj, callback ) { - var length, i = 0; - - if ( isArrayLike( obj ) ) { - length = obj.length; - for ( ; i < length; i++ ) { - if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { - break; - } - } - } else { - for ( i in obj ) { - if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { - break; - } - } - } - - return obj; - }, - - // Support: Android <=4.0 only - trim: function( text ) { - return text == null ? - "" : - ( text + "" ).replace( rtrim, "" ); - }, - - // results is for internal usage only - makeArray: function( arr, results ) { - var ret = results || []; - - if ( arr != null ) { - if ( isArrayLike( Object( arr ) ) ) { - jQuery.merge( ret, - typeof arr === "string" ? - [ arr ] : arr - ); - } else { - push.call( ret, arr ); - } - } - - return ret; - }, - - inArray: function( elem, arr, i ) { - return arr == null ? -1 : indexOf.call( arr, elem, i ); - }, - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - merge: function( first, second ) { - var len = +second.length, - j = 0, - i = first.length; - - for ( ; j < len; j++ ) { - first[ i++ ] = second[ j ]; - } - - first.length = i; - - return first; - }, - - grep: function( elems, callback, invert ) { - var callbackInverse, - matches = [], - i = 0, - length = elems.length, - callbackExpect = !invert; - - // Go through the array, only saving the items - // that pass the validator function - for ( ; i < length; i++ ) { - callbackInverse = !callback( elems[ i ], i ); - if ( callbackInverse !== callbackExpect ) { - matches.push( elems[ i ] ); - } - } - - return matches; - }, - - // arg is for internal usage only - map: function( elems, callback, arg ) { - var length, value, - i = 0, - ret = []; - - // Go through the array, translating each of the items to their new values - if ( isArrayLike( elems ) ) { - length = elems.length; - for ( ; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - - // Go through every key on the object, - } else { - for ( i in elems ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - } - - // Flatten any nested arrays - return concat.apply( [], ret ); - }, - - // A global GUID counter for objects - guid: 1, - - // Bind a function to a context, optionally partially applying any - // arguments. - proxy: function( fn, context ) { - var tmp, args, proxy; - - if ( typeof context === "string" ) { - tmp = fn[ context ]; - context = fn; - fn = tmp; - } - - // Quick check to determine if target is callable, in the spec - // this throws a TypeError, but we will just return undefined. - if ( !jQuery.isFunction( fn ) ) { - return undefined; - } - - // Simulated bind - args = slice.call( arguments, 2 ); - proxy = function() { - return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); - }; - - // Set the guid of unique handler to the same of original handler, so it can be removed - proxy.guid = fn.guid = fn.guid || jQuery.guid++; - - return proxy; - }, - - now: Date.now, - - // jQuery.support is not used in Core but other projects attach their - // properties to it so it needs to exist. - support: support -} ); - -if ( typeof Symbol === "function" ) { - jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; -} - -// Populate the class2type map -jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), -function( i, name ) { - class2type[ "[object " + name + "]" ] = name.toLowerCase(); -} ); - -function isArrayLike( obj ) { - - // Support: real iOS 8.2 only (not reproducible in simulator) - // `in` check used to prevent JIT error (gh-2145) - // hasOwn isn't used here due to false negatives - // regarding Nodelist length in IE - var length = !!obj && "length" in obj && obj.length, - type = jQuery.type( obj ); - - if ( type === "function" || jQuery.isWindow( obj ) ) { - return false; - } - - return type === "array" || length === 0 || - typeof length === "number" && length > 0 && ( length - 1 ) in obj; -} -var Sizzle = -/*! - * Sizzle CSS Selector Engine v2.3.3 - * https://sizzlejs.com/ - * - * Copyright jQuery Foundation and other contributors - * Released under the MIT license - * http://jquery.org/license - * - * Date: 2016-08-08 - */ -(function( window ) { - -var i, - support, - Expr, - getText, - isXML, - tokenize, - compile, - select, - outermostContext, - sortInput, - hasDuplicate, - - // Local document vars - setDocument, - document, - docElem, - documentIsHTML, - rbuggyQSA, - rbuggyMatches, - matches, - contains, - - // Instance-specific data - expando = "sizzle" + 1 * new Date(), - preferredDoc = window.document, - dirruns = 0, - done = 0, - classCache = createCache(), - tokenCache = createCache(), - compilerCache = createCache(), - sortOrder = function( a, b ) { - if ( a === b ) { - hasDuplicate = true; - } - return 0; - }, - - // Instance methods - hasOwn = ({}).hasOwnProperty, - arr = [], - pop = arr.pop, - push_native = arr.push, - push = arr.push, - slice = arr.slice, - // Use a stripped-down indexOf as it's faster than native - // https://jsperf.com/thor-indexof-vs-for/5 - indexOf = function( list, elem ) { - var i = 0, - len = list.length; - for ( ; i < len; i++ ) { - if ( list[i] === elem ) { - return i; - } - } - return -1; - }, - - booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", - - // Regular expressions - - // http://www.w3.org/TR/css3-selectors/#whitespace - whitespace = "[\\x20\\t\\r\\n\\f]", - - // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier - identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+", - - // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors - attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + - // Operator (capture 2) - "*([*^$|!~]?=)" + whitespace + - // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" - "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + - "*\\]", - - pseudos = ":(" + identifier + ")(?:\\((" + - // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: - // 1. quoted (capture 3; capture 4 or capture 5) - "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + - // 2. simple (capture 6) - "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + - // 3. anything else (capture 2) - ".*" + - ")\\)|)", - - // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter - rwhitespace = new RegExp( whitespace + "+", "g" ), - rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), - - rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), - rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), - - rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), - - rpseudo = new RegExp( pseudos ), - ridentifier = new RegExp( "^" + identifier + "$" ), - - matchExpr = { - "ID": new RegExp( "^#(" + identifier + ")" ), - "CLASS": new RegExp( "^\\.(" + identifier + ")" ), - "TAG": new RegExp( "^(" + identifier + "|[*])" ), - "ATTR": new RegExp( "^" + attributes ), - "PSEUDO": new RegExp( "^" + pseudos ), - "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + - "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + - "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), - "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), - // For use in libraries implementing .is() - // We use this for POS matching in `select` - "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + - whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) - }, - - rinputs = /^(?:input|select|textarea|button)$/i, - rheader = /^h\d$/i, - - rnative = /^[^{]+\{\s*\[native \w/, - - // Easily-parseable/retrievable ID or TAG or CLASS selectors - rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, - - rsibling = /[+~]/, - - // CSS escapes - // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters - runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), - funescape = function( _, escaped, escapedWhitespace ) { - var high = "0x" + escaped - 0x10000; - // NaN means non-codepoint - // Support: Firefox<24 - // Workaround erroneous numeric interpretation of +"0x" - return high !== high || escapedWhitespace ? - escaped : - high < 0 ? - // BMP codepoint - String.fromCharCode( high + 0x10000 ) : - // Supplemental Plane codepoint (surrogate pair) - String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); - }, - - // CSS string/identifier serialization - // https://drafts.csswg.org/cssom/#common-serializing-idioms - rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, - fcssescape = function( ch, asCodePoint ) { - if ( asCodePoint ) { - - // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER - if ( ch === "\0" ) { - return "\uFFFD"; - } - - // Control characters and (dependent upon position) numbers get escaped as code points - return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; - } - - // Other potentially-special ASCII characters get backslash-escaped - return "\\" + ch; - }, - - // Used for iframes - // See setDocument() - // Removing the function wrapper causes a "Permission Denied" - // error in IE - unloadHandler = function() { - setDocument(); - }, - - disabledAncestor = addCombinator( - function( elem ) { - return elem.disabled === true && ("form" in elem || "label" in elem); - }, - { dir: "parentNode", next: "legend" } - ); - -// Optimize for push.apply( _, NodeList ) -try { - push.apply( - (arr = slice.call( preferredDoc.childNodes )), - preferredDoc.childNodes - ); - // Support: Android<4.0 - // Detect silently failing push.apply - arr[ preferredDoc.childNodes.length ].nodeType; -} catch ( e ) { - push = { apply: arr.length ? - - // Leverage slice if possible - function( target, els ) { - push_native.apply( target, slice.call(els) ); - } : - - // Support: IE<9 - // Otherwise append directly - function( target, els ) { - var j = target.length, - i = 0; - // Can't trust NodeList.length - while ( (target[j++] = els[i++]) ) {} - target.length = j - 1; - } - }; -} - -function Sizzle( selector, context, results, seed ) { - var m, i, elem, nid, match, groups, newSelector, - newContext = context && context.ownerDocument, - - // nodeType defaults to 9, since context defaults to document - nodeType = context ? context.nodeType : 9; - - results = results || []; - - // Return early from calls with invalid selector or context - if ( typeof selector !== "string" || !selector || - nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { - - return results; - } - - // Try to shortcut find operations (as opposed to filters) in HTML documents - if ( !seed ) { - - if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { - setDocument( context ); - } - context = context || document; - - if ( documentIsHTML ) { - - // If the selector is sufficiently simple, try using a "get*By*" DOM method - // (excepting DocumentFragment context, where the methods don't exist) - if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { - - // ID selector - if ( (m = match[1]) ) { - - // Document context - if ( nodeType === 9 ) { - if ( (elem = context.getElementById( m )) ) { - - // Support: IE, Opera, Webkit - // TODO: identify versions - // getElementById can match elements by name instead of ID - if ( elem.id === m ) { - results.push( elem ); - return results; - } - } else { - return results; - } - - // Element context - } else { - - // Support: IE, Opera, Webkit - // TODO: identify versions - // getElementById can match elements by name instead of ID - if ( newContext && (elem = newContext.getElementById( m )) && - contains( context, elem ) && - elem.id === m ) { - - results.push( elem ); - return results; - } - } - - // Type selector - } else if ( match[2] ) { - push.apply( results, context.getElementsByTagName( selector ) ); - return results; - - // Class selector - } else if ( (m = match[3]) && support.getElementsByClassName && - context.getElementsByClassName ) { - - push.apply( results, context.getElementsByClassName( m ) ); - return results; - } - } - - // Take advantage of querySelectorAll - if ( support.qsa && - !compilerCache[ selector + " " ] && - (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { - - if ( nodeType !== 1 ) { - newContext = context; - newSelector = selector; - - // qSA looks outside Element context, which is not what we want - // Thanks to Andrew Dupont for this workaround technique - // Support: IE <=8 - // Exclude object elements - } else if ( context.nodeName.toLowerCase() !== "object" ) { - - // Capture the context ID, setting it first if necessary - if ( (nid = context.getAttribute( "id" )) ) { - nid = nid.replace( rcssescape, fcssescape ); - } else { - context.setAttribute( "id", (nid = expando) ); - } - - // Prefix every selector in the list - groups = tokenize( selector ); - i = groups.length; - while ( i-- ) { - groups[i] = "#" + nid + " " + toSelector( groups[i] ); - } - newSelector = groups.join( "," ); - - // Expand context for sibling selectors - newContext = rsibling.test( selector ) && testContext( context.parentNode ) || - context; - } - - if ( newSelector ) { - try { - push.apply( results, - newContext.querySelectorAll( newSelector ) - ); - return results; - } catch ( qsaError ) { - } finally { - if ( nid === expando ) { - context.removeAttribute( "id" ); - } - } - } - } - } - } - - // All others - return select( selector.replace( rtrim, "$1" ), context, results, seed ); -} - -/** - * Create key-value caches of limited size - * @returns {function(string, object)} Returns the Object data after storing it on itself with - * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) - * deleting the oldest entry - */ -function createCache() { - var keys = []; - - function cache( key, value ) { - // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) - if ( keys.push( key + " " ) > Expr.cacheLength ) { - // Only keep the most recent entries - delete cache[ keys.shift() ]; - } - return (cache[ key + " " ] = value); - } - return cache; -} - -/** - * Mark a function for special use by Sizzle - * @param {Function} fn The function to mark - */ -function markFunction( fn ) { - fn[ expando ] = true; - return fn; -} - -/** - * Support testing using an element - * @param {Function} fn Passed the created element and returns a boolean result - */ -function assert( fn ) { - var el = document.createElement("fieldset"); - - try { - return !!fn( el ); - } catch (e) { - return false; - } finally { - // Remove from its parent by default - if ( el.parentNode ) { - el.parentNode.removeChild( el ); - } - // release memory in IE - el = null; - } -} - -/** - * Adds the same handler for all of the specified attrs - * @param {String} attrs Pipe-separated list of attributes - * @param {Function} handler The method that will be applied - */ -function addHandle( attrs, handler ) { - var arr = attrs.split("|"), - i = arr.length; - - while ( i-- ) { - Expr.attrHandle[ arr[i] ] = handler; - } -} - -/** - * Checks document order of two siblings - * @param {Element} a - * @param {Element} b - * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b - */ -function siblingCheck( a, b ) { - var cur = b && a, - diff = cur && a.nodeType === 1 && b.nodeType === 1 && - a.sourceIndex - b.sourceIndex; - - // Use IE sourceIndex if available on both nodes - if ( diff ) { - return diff; - } - - // Check if b follows a - if ( cur ) { - while ( (cur = cur.nextSibling) ) { - if ( cur === b ) { - return -1; - } - } - } - - return a ? 1 : -1; -} - -/** - * Returns a function to use in pseudos for input types - * @param {String} type - */ -function createInputPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for buttons - * @param {String} type - */ -function createButtonPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for :enabled/:disabled - * @param {Boolean} disabled true for :disabled; false for :enabled - */ -function createDisabledPseudo( disabled ) { - - // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable - return function( elem ) { - - // Only certain elements can match :enabled or :disabled - // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled - // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled - if ( "form" in elem ) { - - // Check for inherited disabledness on relevant non-disabled elements: - // * listed form-associated elements in a disabled fieldset - // https://html.spec.whatwg.org/multipage/forms.html#category-listed - // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled - // * option elements in a disabled optgroup - // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled - // All such elements have a "form" property. - if ( elem.parentNode && elem.disabled === false ) { - - // Option elements defer to a parent optgroup if present - if ( "label" in elem ) { - if ( "label" in elem.parentNode ) { - return elem.parentNode.disabled === disabled; - } else { - return elem.disabled === disabled; - } - } - - // Support: IE 6 - 11 - // Use the isDisabled shortcut property to check for disabled fieldset ancestors - return elem.isDisabled === disabled || - - // Where there is no isDisabled, check manually - /* jshint -W018 */ - elem.isDisabled !== !disabled && - disabledAncestor( elem ) === disabled; - } - - return elem.disabled === disabled; - - // Try to winnow out elements that can't be disabled before trusting the disabled property. - // Some victims get caught in our net (label, legend, menu, track), but it shouldn't - // even exist on them, let alone have a boolean value. - } else if ( "label" in elem ) { - return elem.disabled === disabled; - } - - // Remaining elements are neither :enabled nor :disabled - return false; - }; -} - -/** - * Returns a function to use in pseudos for positionals - * @param {Function} fn - */ -function createPositionalPseudo( fn ) { - return markFunction(function( argument ) { - argument = +argument; - return markFunction(function( seed, matches ) { - var j, - matchIndexes = fn( [], seed.length, argument ), - i = matchIndexes.length; - - // Match elements found at the specified indexes - while ( i-- ) { - if ( seed[ (j = matchIndexes[i]) ] ) { - seed[j] = !(matches[j] = seed[j]); - } - } - }); - }); -} - -/** - * Checks a node for validity as a Sizzle context - * @param {Element|Object=} context - * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value - */ -function testContext( context ) { - return context && typeof context.getElementsByTagName !== "undefined" && context; -} - -// Expose support vars for convenience -support = Sizzle.support = {}; - -/** - * Detects XML nodes - * @param {Element|Object} elem An element or a document - * @returns {Boolean} True iff elem is a non-HTML XML node - */ -isXML = Sizzle.isXML = function( elem ) { - // documentElement is verified for cases where it doesn't yet exist - // (such as loading iframes in IE - #4833) - var documentElement = elem && (elem.ownerDocument || elem).documentElement; - return documentElement ? documentElement.nodeName !== "HTML" : false; -}; - -/** - * Sets document-related variables once based on the current document - * @param {Element|Object} [doc] An element or document object to use to set the document - * @returns {Object} Returns the current document - */ -setDocument = Sizzle.setDocument = function( node ) { - var hasCompare, subWindow, - doc = node ? node.ownerDocument || node : preferredDoc; - - // Return early if doc is invalid or already selected - if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { - return document; - } - - // Update global variables - document = doc; - docElem = document.documentElement; - documentIsHTML = !isXML( document ); - - // Support: IE 9-11, Edge - // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) - if ( preferredDoc !== document && - (subWindow = document.defaultView) && subWindow.top !== subWindow ) { - - // Support: IE 11, Edge - if ( subWindow.addEventListener ) { - subWindow.addEventListener( "unload", unloadHandler, false ); - - // Support: IE 9 - 10 only - } else if ( subWindow.attachEvent ) { - subWindow.attachEvent( "onunload", unloadHandler ); - } - } - - /* Attributes - ---------------------------------------------------------------------- */ - - // Support: IE<8 - // Verify that getAttribute really returns attributes and not properties - // (excepting IE8 booleans) - support.attributes = assert(function( el ) { - el.className = "i"; - return !el.getAttribute("className"); - }); - - /* getElement(s)By* - ---------------------------------------------------------------------- */ - - // Check if getElementsByTagName("*") returns only elements - support.getElementsByTagName = assert(function( el ) { - el.appendChild( document.createComment("") ); - return !el.getElementsByTagName("*").length; - }); - - // Support: IE<9 - support.getElementsByClassName = rnative.test( document.getElementsByClassName ); - - // Support: IE<10 - // Check if getElementById returns elements by name - // The broken getElementById methods don't pick up programmatically-set names, - // so use a roundabout getElementsByName test - support.getById = assert(function( el ) { - docElem.appendChild( el ).id = expando; - return !document.getElementsByName || !document.getElementsByName( expando ).length; - }); - - // ID filter and find - if ( support.getById ) { - Expr.filter["ID"] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - return elem.getAttribute("id") === attrId; - }; - }; - Expr.find["ID"] = function( id, context ) { - if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { - var elem = context.getElementById( id ); - return elem ? [ elem ] : []; - } - }; - } else { - Expr.filter["ID"] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - var node = typeof elem.getAttributeNode !== "undefined" && - elem.getAttributeNode("id"); - return node && node.value === attrId; - }; - }; - - // Support: IE 6 - 7 only - // getElementById is not reliable as a find shortcut - Expr.find["ID"] = function( id, context ) { - if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { - var node, i, elems, - elem = context.getElementById( id ); - - if ( elem ) { - - // Verify the id attribute - node = elem.getAttributeNode("id"); - if ( node && node.value === id ) { - return [ elem ]; - } - - // Fall back on getElementsByName - elems = context.getElementsByName( id ); - i = 0; - while ( (elem = elems[i++]) ) { - node = elem.getAttributeNode("id"); - if ( node && node.value === id ) { - return [ elem ]; - } - } - } - - return []; - } - }; - } - - // Tag - Expr.find["TAG"] = support.getElementsByTagName ? - function( tag, context ) { - if ( typeof context.getElementsByTagName !== "undefined" ) { - return context.getElementsByTagName( tag ); - - // DocumentFragment nodes don't have gEBTN - } else if ( support.qsa ) { - return context.querySelectorAll( tag ); - } - } : - - function( tag, context ) { - var elem, - tmp = [], - i = 0, - // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too - results = context.getElementsByTagName( tag ); - - // Filter out possible comments - if ( tag === "*" ) { - while ( (elem = results[i++]) ) { - if ( elem.nodeType === 1 ) { - tmp.push( elem ); - } - } - - return tmp; - } - return results; - }; - - // Class - Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { - if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { - return context.getElementsByClassName( className ); - } - }; - - /* QSA/matchesSelector - ---------------------------------------------------------------------- */ - - // QSA and matchesSelector support - - // matchesSelector(:active) reports false when true (IE9/Opera 11.5) - rbuggyMatches = []; - - // qSa(:focus) reports false when true (Chrome 21) - // We allow this because of a bug in IE8/9 that throws an error - // whenever `document.activeElement` is accessed on an iframe - // So, we allow :focus to pass through QSA all the time to avoid the IE error - // See https://bugs.jquery.com/ticket/13378 - rbuggyQSA = []; - - if ( (support.qsa = rnative.test( document.querySelectorAll )) ) { - // Build QSA regex - // Regex strategy adopted from Diego Perini - assert(function( el ) { - // Select is set to empty string on purpose - // This is to test IE's treatment of not explicitly - // setting a boolean content attribute, - // since its presence should be enough - // https://bugs.jquery.com/ticket/12359 - docElem.appendChild( el ).innerHTML = "" + - ""; - - // Support: IE8, Opera 11-12.16 - // Nothing should be selected when empty strings follow ^= or $= or *= - // The test attribute must be unknown in Opera but "safe" for WinRT - // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section - if ( el.querySelectorAll("[msallowcapture^='']").length ) { - rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); - } - - // Support: IE8 - // Boolean attributes and "value" are not treated correctly - if ( !el.querySelectorAll("[selected]").length ) { - rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); - } - - // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ - if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { - rbuggyQSA.push("~="); - } - - // Webkit/Opera - :checked should return selected option elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - // IE8 throws error here and will not see later tests - if ( !el.querySelectorAll(":checked").length ) { - rbuggyQSA.push(":checked"); - } - - // Support: Safari 8+, iOS 8+ - // https://bugs.webkit.org/show_bug.cgi?id=136851 - // In-page `selector#id sibling-combinator selector` fails - if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { - rbuggyQSA.push(".#.+[+~]"); - } - }); - - assert(function( el ) { - el.innerHTML = "" + - ""; - - // Support: Windows 8 Native Apps - // The type and name attributes are restricted during .innerHTML assignment - var input = document.createElement("input"); - input.setAttribute( "type", "hidden" ); - el.appendChild( input ).setAttribute( "name", "D" ); - - // Support: IE8 - // Enforce case-sensitivity of name attribute - if ( el.querySelectorAll("[name=d]").length ) { - rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); - } - - // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) - // IE8 throws error here and will not see later tests - if ( el.querySelectorAll(":enabled").length !== 2 ) { - rbuggyQSA.push( ":enabled", ":disabled" ); - } - - // Support: IE9-11+ - // IE's :disabled selector does not pick up the children of disabled fieldsets - docElem.appendChild( el ).disabled = true; - if ( el.querySelectorAll(":disabled").length !== 2 ) { - rbuggyQSA.push( ":enabled", ":disabled" ); - } - - // Opera 10-11 does not throw on post-comma invalid pseudos - el.querySelectorAll("*,:x"); - rbuggyQSA.push(",.*:"); - }); - } - - if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || - docElem.webkitMatchesSelector || - docElem.mozMatchesSelector || - docElem.oMatchesSelector || - docElem.msMatchesSelector) )) ) { - - assert(function( el ) { - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9) - support.disconnectedMatch = matches.call( el, "*" ); - - // This should fail with an exception - // Gecko does not error, returns false instead - matches.call( el, "[s!='']:x" ); - rbuggyMatches.push( "!=", pseudos ); - }); - } - - rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); - rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); - - /* Contains - ---------------------------------------------------------------------- */ - hasCompare = rnative.test( docElem.compareDocumentPosition ); - - // Element contains another - // Purposefully self-exclusive - // As in, an element does not contain itself - contains = hasCompare || rnative.test( docElem.contains ) ? - function( a, b ) { - var adown = a.nodeType === 9 ? a.documentElement : a, - bup = b && b.parentNode; - return a === bup || !!( bup && bup.nodeType === 1 && ( - adown.contains ? - adown.contains( bup ) : - a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 - )); - } : - function( a, b ) { - if ( b ) { - while ( (b = b.parentNode) ) { - if ( b === a ) { - return true; - } - } - } - return false; - }; - - /* Sorting - ---------------------------------------------------------------------- */ - - // Document order sorting - sortOrder = hasCompare ? - function( a, b ) { - - // Flag for duplicate removal - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - // Sort on method existence if only one input has compareDocumentPosition - var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; - if ( compare ) { - return compare; - } - - // Calculate position if both inputs belong to the same document - compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? - a.compareDocumentPosition( b ) : - - // Otherwise we know they are disconnected - 1; - - // Disconnected nodes - if ( compare & 1 || - (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { - - // Choose the first element that is related to our preferred document - if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { - return -1; - } - if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { - return 1; - } - - // Maintain original order - return sortInput ? - ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : - 0; - } - - return compare & 4 ? -1 : 1; - } : - function( a, b ) { - // Exit early if the nodes are identical - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - var cur, - i = 0, - aup = a.parentNode, - bup = b.parentNode, - ap = [ a ], - bp = [ b ]; - - // Parentless nodes are either documents or disconnected - if ( !aup || !bup ) { - return a === document ? -1 : - b === document ? 1 : - aup ? -1 : - bup ? 1 : - sortInput ? - ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : - 0; - - // If the nodes are siblings, we can do a quick check - } else if ( aup === bup ) { - return siblingCheck( a, b ); - } - - // Otherwise we need full lists of their ancestors for comparison - cur = a; - while ( (cur = cur.parentNode) ) { - ap.unshift( cur ); - } - cur = b; - while ( (cur = cur.parentNode) ) { - bp.unshift( cur ); - } - - // Walk down the tree looking for a discrepancy - while ( ap[i] === bp[i] ) { - i++; - } - - return i ? - // Do a sibling check if the nodes have a common ancestor - siblingCheck( ap[i], bp[i] ) : - - // Otherwise nodes in our document sort first - ap[i] === preferredDoc ? -1 : - bp[i] === preferredDoc ? 1 : - 0; - }; - - return document; -}; - -Sizzle.matches = function( expr, elements ) { - return Sizzle( expr, null, null, elements ); -}; - -Sizzle.matchesSelector = function( elem, expr ) { - // Set document vars if needed - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } - - // Make sure that attribute selectors are quoted - expr = expr.replace( rattributeQuotes, "='$1']" ); - - if ( support.matchesSelector && documentIsHTML && - !compilerCache[ expr + " " ] && - ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && - ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { - - try { - var ret = matches.call( elem, expr ); - - // IE 9's matchesSelector returns false on disconnected nodes - if ( ret || support.disconnectedMatch || - // As well, disconnected nodes are said to be in a document - // fragment in IE 9 - elem.document && elem.document.nodeType !== 11 ) { - return ret; - } - } catch (e) {} - } - - return Sizzle( expr, document, null, [ elem ] ).length > 0; -}; - -Sizzle.contains = function( context, elem ) { - // Set document vars if needed - if ( ( context.ownerDocument || context ) !== document ) { - setDocument( context ); - } - return contains( context, elem ); -}; - -Sizzle.attr = function( elem, name ) { - // Set document vars if needed - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } - - var fn = Expr.attrHandle[ name.toLowerCase() ], - // Don't get fooled by Object.prototype properties (jQuery #13807) - val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? - fn( elem, name, !documentIsHTML ) : - undefined; - - return val !== undefined ? - val : - support.attributes || !documentIsHTML ? - elem.getAttribute( name ) : - (val = elem.getAttributeNode(name)) && val.specified ? - val.value : - null; -}; - -Sizzle.escape = function( sel ) { - return (sel + "").replace( rcssescape, fcssescape ); -}; - -Sizzle.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); -}; - -/** - * Document sorting and removing duplicates - * @param {ArrayLike} results - */ -Sizzle.uniqueSort = function( results ) { - var elem, - duplicates = [], - j = 0, - i = 0; - - // Unless we *know* we can detect duplicates, assume their presence - hasDuplicate = !support.detectDuplicates; - sortInput = !support.sortStable && results.slice( 0 ); - results.sort( sortOrder ); - - if ( hasDuplicate ) { - while ( (elem = results[i++]) ) { - if ( elem === results[ i ] ) { - j = duplicates.push( i ); - } - } - while ( j-- ) { - results.splice( duplicates[ j ], 1 ); - } - } - - // Clear input after sorting to release objects - // See https://github.com/jquery/sizzle/pull/225 - sortInput = null; - - return results; -}; - -/** - * Utility function for retrieving the text value of an array of DOM nodes - * @param {Array|Element} elem - */ -getText = Sizzle.getText = function( elem ) { - var node, - ret = "", - i = 0, - nodeType = elem.nodeType; - - if ( !nodeType ) { - // If no nodeType, this is expected to be an array - while ( (node = elem[i++]) ) { - // Do not traverse comment nodes - ret += getText( node ); - } - } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { - // Use textContent for elements - // innerText usage removed for consistency of new lines (jQuery #11153) - if ( typeof elem.textContent === "string" ) { - return elem.textContent; - } else { - // Traverse its children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - ret += getText( elem ); - } - } - } else if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - // Do not include comment or processing instruction nodes - - return ret; -}; - -Expr = Sizzle.selectors = { - - // Can be adjusted by the user - cacheLength: 50, - - createPseudo: markFunction, - - match: matchExpr, - - attrHandle: {}, - - find: {}, - - relative: { - ">": { dir: "parentNode", first: true }, - " ": { dir: "parentNode" }, - "+": { dir: "previousSibling", first: true }, - "~": { dir: "previousSibling" } - }, - - preFilter: { - "ATTR": function( match ) { - match[1] = match[1].replace( runescape, funescape ); - - // Move the given value to match[3] whether quoted or unquoted - match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); - - if ( match[2] === "~=" ) { - match[3] = " " + match[3] + " "; - } - - return match.slice( 0, 4 ); - }, - - "CHILD": function( match ) { - /* matches from matchExpr["CHILD"] - 1 type (only|nth|...) - 2 what (child|of-type) - 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) - 4 xn-component of xn+y argument ([+-]?\d*n|) - 5 sign of xn-component - 6 x of xn-component - 7 sign of y-component - 8 y of y-component - */ - match[1] = match[1].toLowerCase(); - - if ( match[1].slice( 0, 3 ) === "nth" ) { - // nth-* requires argument - if ( !match[3] ) { - Sizzle.error( match[0] ); - } - - // numeric x and y parameters for Expr.filter.CHILD - // remember that false/true cast respectively to 0/1 - match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); - match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); - - // other types prohibit arguments - } else if ( match[3] ) { - Sizzle.error( match[0] ); - } - - return match; - }, - - "PSEUDO": function( match ) { - var excess, - unquoted = !match[6] && match[2]; - - if ( matchExpr["CHILD"].test( match[0] ) ) { - return null; - } - - // Accept quoted arguments as-is - if ( match[3] ) { - match[2] = match[4] || match[5] || ""; - - // Strip excess characters from unquoted arguments - } else if ( unquoted && rpseudo.test( unquoted ) && - // Get excess from tokenize (recursively) - (excess = tokenize( unquoted, true )) && - // advance to the next closing parenthesis - (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { - - // excess is a negative index - match[0] = match[0].slice( 0, excess ); - match[2] = unquoted.slice( 0, excess ); - } - - // Return only captures needed by the pseudo filter method (type and argument) - return match.slice( 0, 3 ); - } - }, - - filter: { - - "TAG": function( nodeNameSelector ) { - var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); - return nodeNameSelector === "*" ? - function() { return true; } : - function( elem ) { - return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; - }; - }, - - "CLASS": function( className ) { - var pattern = classCache[ className + " " ]; - - return pattern || - (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && - classCache( className, function( elem ) { - return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); - }); - }, - - "ATTR": function( name, operator, check ) { - return function( elem ) { - var result = Sizzle.attr( elem, name ); - - if ( result == null ) { - return operator === "!="; - } - if ( !operator ) { - return true; - } - - result += ""; - - return operator === "=" ? result === check : - operator === "!=" ? result !== check : - operator === "^=" ? check && result.indexOf( check ) === 0 : - operator === "*=" ? check && result.indexOf( check ) > -1 : - operator === "$=" ? check && result.slice( -check.length ) === check : - operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : - operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : - false; - }; - }, - - "CHILD": function( type, what, argument, first, last ) { - var simple = type.slice( 0, 3 ) !== "nth", - forward = type.slice( -4 ) !== "last", - ofType = what === "of-type"; - - return first === 1 && last === 0 ? - - // Shortcut for :nth-*(n) - function( elem ) { - return !!elem.parentNode; - } : - - function( elem, context, xml ) { - var cache, uniqueCache, outerCache, node, nodeIndex, start, - dir = simple !== forward ? "nextSibling" : "previousSibling", - parent = elem.parentNode, - name = ofType && elem.nodeName.toLowerCase(), - useCache = !xml && !ofType, - diff = false; - - if ( parent ) { - - // :(first|last|only)-(child|of-type) - if ( simple ) { - while ( dir ) { - node = elem; - while ( (node = node[ dir ]) ) { - if ( ofType ? - node.nodeName.toLowerCase() === name : - node.nodeType === 1 ) { - - return false; - } - } - // Reverse direction for :only-* (if we haven't yet done so) - start = dir = type === "only" && !start && "nextSibling"; - } - return true; - } - - start = [ forward ? parent.firstChild : parent.lastChild ]; - - // non-xml :nth-child(...) stores cache data on `parent` - if ( forward && useCache ) { - - // Seek `elem` from a previously-cached index - - // ...in a gzip-friendly way - node = parent; - outerCache = node[ expando ] || (node[ expando ] = {}); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ node.uniqueID ] || - (outerCache[ node.uniqueID ] = {}); - - cache = uniqueCache[ type ] || []; - nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; - diff = nodeIndex && cache[ 2 ]; - node = nodeIndex && parent.childNodes[ nodeIndex ]; - - while ( (node = ++nodeIndex && node && node[ dir ] || - - // Fallback to seeking `elem` from the start - (diff = nodeIndex = 0) || start.pop()) ) { - - // When found, cache indexes on `parent` and break - if ( node.nodeType === 1 && ++diff && node === elem ) { - uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; - break; - } - } - - } else { - // Use previously-cached element index if available - if ( useCache ) { - // ...in a gzip-friendly way - node = elem; - outerCache = node[ expando ] || (node[ expando ] = {}); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ node.uniqueID ] || - (outerCache[ node.uniqueID ] = {}); - - cache = uniqueCache[ type ] || []; - nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; - diff = nodeIndex; - } - - // xml :nth-child(...) - // or :nth-last-child(...) or :nth(-last)?-of-type(...) - if ( diff === false ) { - // Use the same loop as above to seek `elem` from the start - while ( (node = ++nodeIndex && node && node[ dir ] || - (diff = nodeIndex = 0) || start.pop()) ) { - - if ( ( ofType ? - node.nodeName.toLowerCase() === name : - node.nodeType === 1 ) && - ++diff ) { - - // Cache the index of each encountered element - if ( useCache ) { - outerCache = node[ expando ] || (node[ expando ] = {}); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ node.uniqueID ] || - (outerCache[ node.uniqueID ] = {}); - - uniqueCache[ type ] = [ dirruns, diff ]; - } - - if ( node === elem ) { - break; - } - } - } - } - } - - // Incorporate the offset, then check against cycle size - diff -= last; - return diff === first || ( diff % first === 0 && diff / first >= 0 ); - } - }; - }, - - "PSEUDO": function( pseudo, argument ) { - // pseudo-class names are case-insensitive - // http://www.w3.org/TR/selectors/#pseudo-classes - // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters - // Remember that setFilters inherits from pseudos - var args, - fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || - Sizzle.error( "unsupported pseudo: " + pseudo ); - - // The user may use createPseudo to indicate that - // arguments are needed to create the filter function - // just as Sizzle does - if ( fn[ expando ] ) { - return fn( argument ); - } - - // But maintain support for old signatures - if ( fn.length > 1 ) { - args = [ pseudo, pseudo, "", argument ]; - return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? - markFunction(function( seed, matches ) { - var idx, - matched = fn( seed, argument ), - i = matched.length; - while ( i-- ) { - idx = indexOf( seed, matched[i] ); - seed[ idx ] = !( matches[ idx ] = matched[i] ); - } - }) : - function( elem ) { - return fn( elem, 0, args ); - }; - } - - return fn; - } - }, - - pseudos: { - // Potentially complex pseudos - "not": markFunction(function( selector ) { - // Trim the selector passed to compile - // to avoid treating leading and trailing - // spaces as combinators - var input = [], - results = [], - matcher = compile( selector.replace( rtrim, "$1" ) ); - - return matcher[ expando ] ? - markFunction(function( seed, matches, context, xml ) { - var elem, - unmatched = matcher( seed, null, xml, [] ), - i = seed.length; - - // Match elements unmatched by `matcher` - while ( i-- ) { - if ( (elem = unmatched[i]) ) { - seed[i] = !(matches[i] = elem); - } - } - }) : - function( elem, context, xml ) { - input[0] = elem; - matcher( input, null, xml, results ); - // Don't keep the element (issue #299) - input[0] = null; - return !results.pop(); - }; - }), - - "has": markFunction(function( selector ) { - return function( elem ) { - return Sizzle( selector, elem ).length > 0; - }; - }), - - "contains": markFunction(function( text ) { - text = text.replace( runescape, funescape ); - return function( elem ) { - return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; - }; - }), - - // "Whether an element is represented by a :lang() selector - // is based solely on the element's language value - // being equal to the identifier C, - // or beginning with the identifier C immediately followed by "-". - // The matching of C against the element's language value is performed case-insensitively. - // The identifier C does not have to be a valid language name." - // http://www.w3.org/TR/selectors/#lang-pseudo - "lang": markFunction( function( lang ) { - // lang value must be a valid identifier - if ( !ridentifier.test(lang || "") ) { - Sizzle.error( "unsupported lang: " + lang ); - } - lang = lang.replace( runescape, funescape ).toLowerCase(); - return function( elem ) { - var elemLang; - do { - if ( (elemLang = documentIsHTML ? - elem.lang : - elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { - - elemLang = elemLang.toLowerCase(); - return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; - } - } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); - return false; - }; - }), - - // Miscellaneous - "target": function( elem ) { - var hash = window.location && window.location.hash; - return hash && hash.slice( 1 ) === elem.id; - }, - - "root": function( elem ) { - return elem === docElem; - }, - - "focus": function( elem ) { - return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); - }, - - // Boolean properties - "enabled": createDisabledPseudo( false ), - "disabled": createDisabledPseudo( true ), - - "checked": function( elem ) { - // In CSS3, :checked should return both checked and selected elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - var nodeName = elem.nodeName.toLowerCase(); - return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); - }, - - "selected": function( elem ) { - // Accessing this property makes selected-by-default - // options in Safari work properly - if ( elem.parentNode ) { - elem.parentNode.selectedIndex; - } - - return elem.selected === true; - }, - - // Contents - "empty": function( elem ) { - // http://www.w3.org/TR/selectors/#empty-pseudo - // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), - // but not by others (comment: 8; processing instruction: 7; etc.) - // nodeType < 6 works because attributes (2) do not appear as children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - if ( elem.nodeType < 6 ) { - return false; - } - } - return true; - }, - - "parent": function( elem ) { - return !Expr.pseudos["empty"]( elem ); - }, - - // Element/input types - "header": function( elem ) { - return rheader.test( elem.nodeName ); - }, - - "input": function( elem ) { - return rinputs.test( elem.nodeName ); - }, - - "button": function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === "button" || name === "button"; - }, - - "text": function( elem ) { - var attr; - return elem.nodeName.toLowerCase() === "input" && - elem.type === "text" && - - // Support: IE<8 - // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" - ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); - }, - - // Position-in-collection - "first": createPositionalPseudo(function() { - return [ 0 ]; - }), - - "last": createPositionalPseudo(function( matchIndexes, length ) { - return [ length - 1 ]; - }), - - "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { - return [ argument < 0 ? argument + length : argument ]; - }), - - "even": createPositionalPseudo(function( matchIndexes, length ) { - var i = 0; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "odd": createPositionalPseudo(function( matchIndexes, length ) { - var i = 1; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; --i >= 0; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; ++i < length; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }) - } -}; - -Expr.pseudos["nth"] = Expr.pseudos["eq"]; - -// Add button/input type pseudos -for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { - Expr.pseudos[ i ] = createInputPseudo( i ); -} -for ( i in { submit: true, reset: true } ) { - Expr.pseudos[ i ] = createButtonPseudo( i ); -} - -// Easy API for creating new setFilters -function setFilters() {} -setFilters.prototype = Expr.filters = Expr.pseudos; -Expr.setFilters = new setFilters(); - -tokenize = Sizzle.tokenize = function( selector, parseOnly ) { - var matched, match, tokens, type, - soFar, groups, preFilters, - cached = tokenCache[ selector + " " ]; - - if ( cached ) { - return parseOnly ? 0 : cached.slice( 0 ); - } - - soFar = selector; - groups = []; - preFilters = Expr.preFilter; - - while ( soFar ) { - - // Comma and first run - if ( !matched || (match = rcomma.exec( soFar )) ) { - if ( match ) { - // Don't consume trailing commas as valid - soFar = soFar.slice( match[0].length ) || soFar; - } - groups.push( (tokens = []) ); - } - - matched = false; - - // Combinators - if ( (match = rcombinators.exec( soFar )) ) { - matched = match.shift(); - tokens.push({ - value: matched, - // Cast descendant combinators to space - type: match[0].replace( rtrim, " " ) - }); - soFar = soFar.slice( matched.length ); - } - - // Filters - for ( type in Expr.filter ) { - if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || - (match = preFilters[ type ]( match ))) ) { - matched = match.shift(); - tokens.push({ - value: matched, - type: type, - matches: match - }); - soFar = soFar.slice( matched.length ); - } - } - - if ( !matched ) { - break; - } - } - - // Return the length of the invalid excess - // if we're just parsing - // Otherwise, throw an error or return tokens - return parseOnly ? - soFar.length : - soFar ? - Sizzle.error( selector ) : - // Cache the tokens - tokenCache( selector, groups ).slice( 0 ); -}; - -function toSelector( tokens ) { - var i = 0, - len = tokens.length, - selector = ""; - for ( ; i < len; i++ ) { - selector += tokens[i].value; - } - return selector; -} - -function addCombinator( matcher, combinator, base ) { - var dir = combinator.dir, - skip = combinator.next, - key = skip || dir, - checkNonElements = base && key === "parentNode", - doneName = done++; - - return combinator.first ? - // Check against closest ancestor/preceding element - function( elem, context, xml ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - return matcher( elem, context, xml ); - } - } - return false; - } : - - // Check against all ancestor/preceding elements - function( elem, context, xml ) { - var oldCache, uniqueCache, outerCache, - newCache = [ dirruns, doneName ]; - - // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching - if ( xml ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - if ( matcher( elem, context, xml ) ) { - return true; - } - } - } - } else { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - outerCache = elem[ expando ] || (elem[ expando ] = {}); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {}); - - if ( skip && skip === elem.nodeName.toLowerCase() ) { - elem = elem[ dir ] || elem; - } else if ( (oldCache = uniqueCache[ key ]) && - oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { - - // Assign to newCache so results back-propagate to previous elements - return (newCache[ 2 ] = oldCache[ 2 ]); - } else { - // Reuse newcache so results back-propagate to previous elements - uniqueCache[ key ] = newCache; - - // A match means we're done; a fail means we have to keep checking - if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { - return true; - } - } - } - } - } - return false; - }; -} - -function elementMatcher( matchers ) { - return matchers.length > 1 ? - function( elem, context, xml ) { - var i = matchers.length; - while ( i-- ) { - if ( !matchers[i]( elem, context, xml ) ) { - return false; - } - } - return true; - } : - matchers[0]; -} - -function multipleContexts( selector, contexts, results ) { - var i = 0, - len = contexts.length; - for ( ; i < len; i++ ) { - Sizzle( selector, contexts[i], results ); - } - return results; -} - -function condense( unmatched, map, filter, context, xml ) { - var elem, - newUnmatched = [], - i = 0, - len = unmatched.length, - mapped = map != null; - - for ( ; i < len; i++ ) { - if ( (elem = unmatched[i]) ) { - if ( !filter || filter( elem, context, xml ) ) { - newUnmatched.push( elem ); - if ( mapped ) { - map.push( i ); - } - } - } - } - - return newUnmatched; -} - -function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { - if ( postFilter && !postFilter[ expando ] ) { - postFilter = setMatcher( postFilter ); - } - if ( postFinder && !postFinder[ expando ] ) { - postFinder = setMatcher( postFinder, postSelector ); - } - return markFunction(function( seed, results, context, xml ) { - var temp, i, elem, - preMap = [], - postMap = [], - preexisting = results.length, - - // Get initial elements from seed or context - elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), - - // Prefilter to get matcher input, preserving a map for seed-results synchronization - matcherIn = preFilter && ( seed || !selector ) ? - condense( elems, preMap, preFilter, context, xml ) : - elems, - - matcherOut = matcher ? - // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, - postFinder || ( seed ? preFilter : preexisting || postFilter ) ? - - // ...intermediate processing is necessary - [] : - - // ...otherwise use results directly - results : - matcherIn; - - // Find primary matches - if ( matcher ) { - matcher( matcherIn, matcherOut, context, xml ); - } - - // Apply postFilter - if ( postFilter ) { - temp = condense( matcherOut, postMap ); - postFilter( temp, [], context, xml ); - - // Un-match failing elements by moving them back to matcherIn - i = temp.length; - while ( i-- ) { - if ( (elem = temp[i]) ) { - matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); - } - } - } - - if ( seed ) { - if ( postFinder || preFilter ) { - if ( postFinder ) { - // Get the final matcherOut by condensing this intermediate into postFinder contexts - temp = []; - i = matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) ) { - // Restore matcherIn since elem is not yet a final match - temp.push( (matcherIn[i] = elem) ); - } - } - postFinder( null, (matcherOut = []), temp, xml ); - } - - // Move matched elements from seed to results to keep them synchronized - i = matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) && - (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { - - seed[temp] = !(results[temp] = elem); - } - } - } - - // Add elements to results, through postFinder if defined - } else { - matcherOut = condense( - matcherOut === results ? - matcherOut.splice( preexisting, matcherOut.length ) : - matcherOut - ); - if ( postFinder ) { - postFinder( null, results, matcherOut, xml ); - } else { - push.apply( results, matcherOut ); - } - } - }); -} - -function matcherFromTokens( tokens ) { - var checkContext, matcher, j, - len = tokens.length, - leadingRelative = Expr.relative[ tokens[0].type ], - implicitRelative = leadingRelative || Expr.relative[" "], - i = leadingRelative ? 1 : 0, - - // The foundational matcher ensures that elements are reachable from top-level context(s) - matchContext = addCombinator( function( elem ) { - return elem === checkContext; - }, implicitRelative, true ), - matchAnyContext = addCombinator( function( elem ) { - return indexOf( checkContext, elem ) > -1; - }, implicitRelative, true ), - matchers = [ function( elem, context, xml ) { - var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( - (checkContext = context).nodeType ? - matchContext( elem, context, xml ) : - matchAnyContext( elem, context, xml ) ); - // Avoid hanging onto element (issue #299) - checkContext = null; - return ret; - } ]; - - for ( ; i < len; i++ ) { - if ( (matcher = Expr.relative[ tokens[i].type ]) ) { - matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; - } else { - matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); - - // Return special upon seeing a positional matcher - if ( matcher[ expando ] ) { - // Find the next relative operator (if any) for proper handling - j = ++i; - for ( ; j < len; j++ ) { - if ( Expr.relative[ tokens[j].type ] ) { - break; - } - } - return setMatcher( - i > 1 && elementMatcher( matchers ), - i > 1 && toSelector( - // If the preceding token was a descendant combinator, insert an implicit any-element `*` - tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) - ).replace( rtrim, "$1" ), - matcher, - i < j && matcherFromTokens( tokens.slice( i, j ) ), - j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), - j < len && toSelector( tokens ) - ); - } - matchers.push( matcher ); - } - } - - return elementMatcher( matchers ); -} - -function matcherFromGroupMatchers( elementMatchers, setMatchers ) { - var bySet = setMatchers.length > 0, - byElement = elementMatchers.length > 0, - superMatcher = function( seed, context, xml, results, outermost ) { - var elem, j, matcher, - matchedCount = 0, - i = "0", - unmatched = seed && [], - setMatched = [], - contextBackup = outermostContext, - // We must always have either seed elements or outermost context - elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), - // Use integer dirruns iff this is the outermost matcher - dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), - len = elems.length; - - if ( outermost ) { - outermostContext = context === document || context || outermost; - } - - // Add elements passing elementMatchers directly to results - // Support: IE<9, Safari - // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id - for ( ; i !== len && (elem = elems[i]) != null; i++ ) { - if ( byElement && elem ) { - j = 0; - if ( !context && elem.ownerDocument !== document ) { - setDocument( elem ); - xml = !documentIsHTML; - } - while ( (matcher = elementMatchers[j++]) ) { - if ( matcher( elem, context || document, xml) ) { - results.push( elem ); - break; - } - } - if ( outermost ) { - dirruns = dirrunsUnique; - } - } - - // Track unmatched elements for set filters - if ( bySet ) { - // They will have gone through all possible matchers - if ( (elem = !matcher && elem) ) { - matchedCount--; - } - - // Lengthen the array for every element, matched or not - if ( seed ) { - unmatched.push( elem ); - } - } - } - - // `i` is now the count of elements visited above, and adding it to `matchedCount` - // makes the latter nonnegative. - matchedCount += i; - - // Apply set filters to unmatched elements - // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` - // equals `i`), unless we didn't visit _any_ elements in the above loop because we have - // no element matchers and no seed. - // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that - // case, which will result in a "00" `matchedCount` that differs from `i` but is also - // numerically zero. - if ( bySet && i !== matchedCount ) { - j = 0; - while ( (matcher = setMatchers[j++]) ) { - matcher( unmatched, setMatched, context, xml ); - } - - if ( seed ) { - // Reintegrate element matches to eliminate the need for sorting - if ( matchedCount > 0 ) { - while ( i-- ) { - if ( !(unmatched[i] || setMatched[i]) ) { - setMatched[i] = pop.call( results ); - } - } - } - - // Discard index placeholder values to get only actual matches - setMatched = condense( setMatched ); - } - - // Add matches to results - push.apply( results, setMatched ); - - // Seedless set matches succeeding multiple successful matchers stipulate sorting - if ( outermost && !seed && setMatched.length > 0 && - ( matchedCount + setMatchers.length ) > 1 ) { - - Sizzle.uniqueSort( results ); - } - } - - // Override manipulation of globals by nested matchers - if ( outermost ) { - dirruns = dirrunsUnique; - outermostContext = contextBackup; - } - - return unmatched; - }; - - return bySet ? - markFunction( superMatcher ) : - superMatcher; -} - -compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { - var i, - setMatchers = [], - elementMatchers = [], - cached = compilerCache[ selector + " " ]; - - if ( !cached ) { - // Generate a function of recursive functions that can be used to check each element - if ( !match ) { - match = tokenize( selector ); - } - i = match.length; - while ( i-- ) { - cached = matcherFromTokens( match[i] ); - if ( cached[ expando ] ) { - setMatchers.push( cached ); - } else { - elementMatchers.push( cached ); - } - } - - // Cache the compiled function - cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); - - // Save selector and tokenization - cached.selector = selector; - } - return cached; -}; - -/** - * A low-level selection function that works with Sizzle's compiled - * selector functions - * @param {String|Function} selector A selector or a pre-compiled - * selector function built with Sizzle.compile - * @param {Element} context - * @param {Array} [results] - * @param {Array} [seed] A set of elements to match against - */ -select = Sizzle.select = function( selector, context, results, seed ) { - var i, tokens, token, type, find, - compiled = typeof selector === "function" && selector, - match = !seed && tokenize( (selector = compiled.selector || selector) ); - - results = results || []; - - // Try to minimize operations if there is only one selector in the list and no seed - // (the latter of which guarantees us context) - if ( match.length === 1 ) { - - // Reduce context if the leading compound selector is an ID - tokens = match[0] = match[0].slice( 0 ); - if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && - context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[1].type ] ) { - - context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; - if ( !context ) { - return results; - - // Precompiled matchers will still verify ancestry, so step up a level - } else if ( compiled ) { - context = context.parentNode; - } - - selector = selector.slice( tokens.shift().value.length ); - } - - // Fetch a seed set for right-to-left matching - i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; - while ( i-- ) { - token = tokens[i]; - - // Abort if we hit a combinator - if ( Expr.relative[ (type = token.type) ] ) { - break; - } - if ( (find = Expr.find[ type ]) ) { - // Search, expanding context for leading sibling combinators - if ( (seed = find( - token.matches[0].replace( runescape, funescape ), - rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context - )) ) { - - // If seed is empty or no tokens remain, we can return early - tokens.splice( i, 1 ); - selector = seed.length && toSelector( tokens ); - if ( !selector ) { - push.apply( results, seed ); - return results; - } - - break; - } - } - } - } - - // Compile and execute a filtering function if one is not provided - // Provide `match` to avoid retokenization if we modified the selector above - ( compiled || compile( selector, match ) )( - seed, - context, - !documentIsHTML, - results, - !context || rsibling.test( selector ) && testContext( context.parentNode ) || context - ); - return results; -}; - -// One-time assignments - -// Sort stability -support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; - -// Support: Chrome 14-35+ -// Always assume duplicates if they aren't passed to the comparison function -support.detectDuplicates = !!hasDuplicate; - -// Initialize against the default document -setDocument(); - -// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) -// Detached nodes confoundingly follow *each other* -support.sortDetached = assert(function( el ) { - // Should return 1, but returns 4 (following) - return el.compareDocumentPosition( document.createElement("fieldset") ) & 1; -}); - -// Support: IE<8 -// Prevent attribute/property "interpolation" -// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx -if ( !assert(function( el ) { - el.innerHTML = ""; - return el.firstChild.getAttribute("href") === "#" ; -}) ) { - addHandle( "type|href|height|width", function( elem, name, isXML ) { - if ( !isXML ) { - return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); - } - }); -} - -// Support: IE<9 -// Use defaultValue in place of getAttribute("value") -if ( !support.attributes || !assert(function( el ) { - el.innerHTML = ""; - el.firstChild.setAttribute( "value", "" ); - return el.firstChild.getAttribute( "value" ) === ""; -}) ) { - addHandle( "value", function( elem, name, isXML ) { - if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { - return elem.defaultValue; - } - }); -} - -// Support: IE<9 -// Use getAttributeNode to fetch booleans when getAttribute lies -if ( !assert(function( el ) { - return el.getAttribute("disabled") == null; -}) ) { - addHandle( booleans, function( elem, name, isXML ) { - var val; - if ( !isXML ) { - return elem[ name ] === true ? name.toLowerCase() : - (val = elem.getAttributeNode( name )) && val.specified ? - val.value : - null; - } - }); -} - -return Sizzle; - -})( window ); - - - -jQuery.find = Sizzle; -jQuery.expr = Sizzle.selectors; - -// Deprecated -jQuery.expr[ ":" ] = jQuery.expr.pseudos; -jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; -jQuery.text = Sizzle.getText; -jQuery.isXMLDoc = Sizzle.isXML; -jQuery.contains = Sizzle.contains; -jQuery.escapeSelector = Sizzle.escape; - - - - -var dir = function( elem, dir, until ) { - var matched = [], - truncate = until !== undefined; - - while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { - if ( elem.nodeType === 1 ) { - if ( truncate && jQuery( elem ).is( until ) ) { - break; - } - matched.push( elem ); - } - } - return matched; -}; - - -var siblings = function( n, elem ) { - var matched = []; - - for ( ; n; n = n.nextSibling ) { - if ( n.nodeType === 1 && n !== elem ) { - matched.push( n ); - } - } - - return matched; -}; - - -var rneedsContext = jQuery.expr.match.needsContext; - - - -function nodeName( elem, name ) { - - return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); - -}; -var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); - - - -var risSimple = /^.[^:#\[\.,]*$/; - -// Implement the identical functionality for filter and not -function winnow( elements, qualifier, not ) { - if ( jQuery.isFunction( qualifier ) ) { - return jQuery.grep( elements, function( elem, i ) { - return !!qualifier.call( elem, i, elem ) !== not; - } ); - } - - // Single element - if ( qualifier.nodeType ) { - return jQuery.grep( elements, function( elem ) { - return ( elem === qualifier ) !== not; - } ); - } - - // Arraylike of elements (jQuery, arguments, Array) - if ( typeof qualifier !== "string" ) { - return jQuery.grep( elements, function( elem ) { - return ( indexOf.call( qualifier, elem ) > -1 ) !== not; - } ); - } - - // Simple selector that can be filtered directly, removing non-Elements - if ( risSimple.test( qualifier ) ) { - return jQuery.filter( qualifier, elements, not ); - } - - // Complex selector, compare the two sets, removing non-Elements - qualifier = jQuery.filter( qualifier, elements ); - return jQuery.grep( elements, function( elem ) { - return ( indexOf.call( qualifier, elem ) > -1 ) !== not && elem.nodeType === 1; - } ); -} - -jQuery.filter = function( expr, elems, not ) { - var elem = elems[ 0 ]; - - if ( not ) { - expr = ":not(" + expr + ")"; - } - - if ( elems.length === 1 && elem.nodeType === 1 ) { - return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; - } - - return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { - return elem.nodeType === 1; - } ) ); -}; - -jQuery.fn.extend( { - find: function( selector ) { - var i, ret, - len = this.length, - self = this; - - if ( typeof selector !== "string" ) { - return this.pushStack( jQuery( selector ).filter( function() { - for ( i = 0; i < len; i++ ) { - if ( jQuery.contains( self[ i ], this ) ) { - return true; - } - } - } ) ); - } - - ret = this.pushStack( [] ); - - for ( i = 0; i < len; i++ ) { - jQuery.find( selector, self[ i ], ret ); - } - - return len > 1 ? jQuery.uniqueSort( ret ) : ret; - }, - filter: function( selector ) { - return this.pushStack( winnow( this, selector || [], false ) ); - }, - not: function( selector ) { - return this.pushStack( winnow( this, selector || [], true ) ); - }, - is: function( selector ) { - return !!winnow( - this, - - // If this is a positional/relative selector, check membership in the returned set - // so $("p:first").is("p:last") won't return true for a doc with two "p". - typeof selector === "string" && rneedsContext.test( selector ) ? - jQuery( selector ) : - selector || [], - false - ).length; - } -} ); - - -// Initialize a jQuery object - - -// A central reference to the root jQuery(document) -var rootjQuery, - - // A simple way to check for HTML strings - // Prioritize #id over to avoid XSS via location.hash (#9521) - // Strict HTML recognition (#11290: must start with <) - // Shortcut simple #id case for speed - rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, - - init = jQuery.fn.init = function( selector, context, root ) { - var match, elem; - - // HANDLE: $(""), $(null), $(undefined), $(false) - if ( !selector ) { - return this; - } - - // Method init() accepts an alternate rootjQuery - // so migrate can support jQuery.sub (gh-2101) - root = root || rootjQuery; - - // Handle HTML strings - if ( typeof selector === "string" ) { - if ( selector[ 0 ] === "<" && - selector[ selector.length - 1 ] === ">" && - selector.length >= 3 ) { - - // Assume that strings that start and end with <> are HTML and skip the regex check - match = [ null, selector, null ]; - - } else { - match = rquickExpr.exec( selector ); - } - - // Match html or make sure no context is specified for #id - if ( match && ( match[ 1 ] || !context ) ) { - - // HANDLE: $(html) -> $(array) - if ( match[ 1 ] ) { - context = context instanceof jQuery ? context[ 0 ] : context; - - // Option to run scripts is true for back-compat - // Intentionally let the error be thrown if parseHTML is not present - jQuery.merge( this, jQuery.parseHTML( - match[ 1 ], - context && context.nodeType ? context.ownerDocument || context : document, - true - ) ); - - // HANDLE: $(html, props) - if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { - for ( match in context ) { - - // Properties of context are called as methods if possible - if ( jQuery.isFunction( this[ match ] ) ) { - this[ match ]( context[ match ] ); - - // ...and otherwise set as attributes - } else { - this.attr( match, context[ match ] ); - } - } - } - - return this; - - // HANDLE: $(#id) - } else { - elem = document.getElementById( match[ 2 ] ); - - if ( elem ) { - - // Inject the element directly into the jQuery object - this[ 0 ] = elem; - this.length = 1; - } - return this; - } - - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return ( context || root ).find( selector ); - - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return this.constructor( context ).find( selector ); - } - - // HANDLE: $(DOMElement) - } else if ( selector.nodeType ) { - this[ 0 ] = selector; - this.length = 1; - return this; - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) { - return root.ready !== undefined ? - root.ready( selector ) : - - // Execute immediately if ready is not present - selector( jQuery ); - } - - return jQuery.makeArray( selector, this ); - }; - -// Give the init function the jQuery prototype for later instantiation -init.prototype = jQuery.fn; - -// Initialize central reference -rootjQuery = jQuery( document ); - - -var rparentsprev = /^(?:parents|prev(?:Until|All))/, - - // Methods guaranteed to produce a unique set when starting from a unique set - guaranteedUnique = { - children: true, - contents: true, - next: true, - prev: true - }; - -jQuery.fn.extend( { - has: function( target ) { - var targets = jQuery( target, this ), - l = targets.length; - - return this.filter( function() { - var i = 0; - for ( ; i < l; i++ ) { - if ( jQuery.contains( this, targets[ i ] ) ) { - return true; - } - } - } ); - }, - - closest: function( selectors, context ) { - var cur, - i = 0, - l = this.length, - matched = [], - targets = typeof selectors !== "string" && jQuery( selectors ); - - // Positional selectors never match, since there's no _selection_ context - if ( !rneedsContext.test( selectors ) ) { - for ( ; i < l; i++ ) { - for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { - - // Always skip document fragments - if ( cur.nodeType < 11 && ( targets ? - targets.index( cur ) > -1 : - - // Don't pass non-elements to Sizzle - cur.nodeType === 1 && - jQuery.find.matchesSelector( cur, selectors ) ) ) { - - matched.push( cur ); - break; - } - } - } - } - - return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); - }, - - // Determine the position of an element within the set - index: function( elem ) { - - // No argument, return index in parent - if ( !elem ) { - return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; - } - - // Index in selector - if ( typeof elem === "string" ) { - return indexOf.call( jQuery( elem ), this[ 0 ] ); - } - - // Locate the position of the desired element - return indexOf.call( this, - - // If it receives a jQuery object, the first element is used - elem.jquery ? elem[ 0 ] : elem - ); - }, - - add: function( selector, context ) { - return this.pushStack( - jQuery.uniqueSort( - jQuery.merge( this.get(), jQuery( selector, context ) ) - ) - ); - }, - - addBack: function( selector ) { - return this.add( selector == null ? - this.prevObject : this.prevObject.filter( selector ) - ); - } -} ); - -function sibling( cur, dir ) { - while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} - return cur; -} - -jQuery.each( { - parent: function( elem ) { - var parent = elem.parentNode; - return parent && parent.nodeType !== 11 ? parent : null; - }, - parents: function( elem ) { - return dir( elem, "parentNode" ); - }, - parentsUntil: function( elem, i, until ) { - return dir( elem, "parentNode", until ); - }, - next: function( elem ) { - return sibling( elem, "nextSibling" ); - }, - prev: function( elem ) { - return sibling( elem, "previousSibling" ); - }, - nextAll: function( elem ) { - return dir( elem, "nextSibling" ); - }, - prevAll: function( elem ) { - return dir( elem, "previousSibling" ); - }, - nextUntil: function( elem, i, until ) { - return dir( elem, "nextSibling", until ); - }, - prevUntil: function( elem, i, until ) { - return dir( elem, "previousSibling", until ); - }, - siblings: function( elem ) { - return siblings( ( elem.parentNode || {} ).firstChild, elem ); - }, - children: function( elem ) { - return siblings( elem.firstChild ); - }, - contents: function( elem ) { - if ( nodeName( elem, "iframe" ) ) { - return elem.contentDocument; - } - - // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only - // Treat the template element as a regular one in browsers that - // don't support it. - if ( nodeName( elem, "template" ) ) { - elem = elem.content || elem; - } - - return jQuery.merge( [], elem.childNodes ); - } -}, function( name, fn ) { - jQuery.fn[ name ] = function( until, selector ) { - var matched = jQuery.map( this, fn, until ); - - if ( name.slice( -5 ) !== "Until" ) { - selector = until; - } - - if ( selector && typeof selector === "string" ) { - matched = jQuery.filter( selector, matched ); - } - - if ( this.length > 1 ) { - - // Remove duplicates - if ( !guaranteedUnique[ name ] ) { - jQuery.uniqueSort( matched ); - } - - // Reverse order for parents* and prev-derivatives - if ( rparentsprev.test( name ) ) { - matched.reverse(); - } - } - - return this.pushStack( matched ); - }; -} ); -var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); - - - -// Convert String-formatted options into Object-formatted ones -function createOptions( options ) { - var object = {}; - jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { - object[ flag ] = true; - } ); - return object; -} - -/* - * Create a callback list using the following parameters: - * - * options: an optional list of space-separated options that will change how - * the callback list behaves or a more traditional option object - * - * By default a callback list will act like an event callback list and can be - * "fired" multiple times. - * - * Possible options: - * - * once: will ensure the callback list can only be fired once (like a Deferred) - * - * memory: will keep track of previous values and will call any callback added - * after the list has been fired right away with the latest "memorized" - * values (like a Deferred) - * - * unique: will ensure a callback can only be added once (no duplicate in the list) - * - * stopOnFalse: interrupt callings when a callback returns false - * - */ -jQuery.Callbacks = function( options ) { - - // Convert options from String-formatted to Object-formatted if needed - // (we check in cache first) - options = typeof options === "string" ? - createOptions( options ) : - jQuery.extend( {}, options ); - - var // Flag to know if list is currently firing - firing, - - // Last fire value for non-forgettable lists - memory, - - // Flag to know if list was already fired - fired, - - // Flag to prevent firing - locked, - - // Actual callback list - list = [], - - // Queue of execution data for repeatable lists - queue = [], - - // Index of currently firing callback (modified by add/remove as needed) - firingIndex = -1, - - // Fire callbacks - fire = function() { - - // Enforce single-firing - locked = locked || options.once; - - // Execute callbacks for all pending executions, - // respecting firingIndex overrides and runtime changes - fired = firing = true; - for ( ; queue.length; firingIndex = -1 ) { - memory = queue.shift(); - while ( ++firingIndex < list.length ) { - - // Run callback and check for early termination - if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && - options.stopOnFalse ) { - - // Jump to end and forget the data so .add doesn't re-fire - firingIndex = list.length; - memory = false; - } - } - } - - // Forget the data if we're done with it - if ( !options.memory ) { - memory = false; - } - - firing = false; - - // Clean up if we're done firing for good - if ( locked ) { - - // Keep an empty list if we have data for future add calls - if ( memory ) { - list = []; - - // Otherwise, this object is spent - } else { - list = ""; - } - } - }, - - // Actual Callbacks object - self = { - - // Add a callback or a collection of callbacks to the list - add: function() { - if ( list ) { - - // If we have memory from a past run, we should fire after adding - if ( memory && !firing ) { - firingIndex = list.length - 1; - queue.push( memory ); - } - - ( function add( args ) { - jQuery.each( args, function( _, arg ) { - if ( jQuery.isFunction( arg ) ) { - if ( !options.unique || !self.has( arg ) ) { - list.push( arg ); - } - } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) { - - // Inspect recursively - add( arg ); - } - } ); - } )( arguments ); - - if ( memory && !firing ) { - fire(); - } - } - return this; - }, - - // Remove a callback from the list - remove: function() { - jQuery.each( arguments, function( _, arg ) { - var index; - while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { - list.splice( index, 1 ); - - // Handle firing indexes - if ( index <= firingIndex ) { - firingIndex--; - } - } - } ); - return this; - }, - - // Check if a given callback is in the list. - // If no argument is given, return whether or not list has callbacks attached. - has: function( fn ) { - return fn ? - jQuery.inArray( fn, list ) > -1 : - list.length > 0; - }, - - // Remove all callbacks from the list - empty: function() { - if ( list ) { - list = []; - } - return this; - }, - - // Disable .fire and .add - // Abort any current/pending executions - // Clear all callbacks and values - disable: function() { - locked = queue = []; - list = memory = ""; - return this; - }, - disabled: function() { - return !list; - }, - - // Disable .fire - // Also disable .add unless we have memory (since it would have no effect) - // Abort any pending executions - lock: function() { - locked = queue = []; - if ( !memory && !firing ) { - list = memory = ""; - } - return this; - }, - locked: function() { - return !!locked; - }, - - // Call all callbacks with the given context and arguments - fireWith: function( context, args ) { - if ( !locked ) { - args = args || []; - args = [ context, args.slice ? args.slice() : args ]; - queue.push( args ); - if ( !firing ) { - fire(); - } - } - return this; - }, - - // Call all the callbacks with the given arguments - fire: function() { - self.fireWith( this, arguments ); - return this; - }, - - // To know if the callbacks have already been called at least once - fired: function() { - return !!fired; - } - }; - - return self; -}; - - -function Identity( v ) { - return v; -} -function Thrower( ex ) { - throw ex; -} - -function adoptValue( value, resolve, reject, noValue ) { - var method; - - try { - - // Check for promise aspect first to privilege synchronous behavior - if ( value && jQuery.isFunction( ( method = value.promise ) ) ) { - method.call( value ).done( resolve ).fail( reject ); - - // Other thenables - } else if ( value && jQuery.isFunction( ( method = value.then ) ) ) { - method.call( value, resolve, reject ); - - // Other non-thenables - } else { - - // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: - // * false: [ value ].slice( 0 ) => resolve( value ) - // * true: [ value ].slice( 1 ) => resolve() - resolve.apply( undefined, [ value ].slice( noValue ) ); - } - - // For Promises/A+, convert exceptions into rejections - // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in - // Deferred#then to conditionally suppress rejection. - } catch ( value ) { - - // Support: Android 4.0 only - // Strict mode functions invoked without .call/.apply get global-object context - reject.apply( undefined, [ value ] ); - } -} - -jQuery.extend( { - - Deferred: function( func ) { - var tuples = [ - - // action, add listener, callbacks, - // ... .then handlers, argument index, [final state] - [ "notify", "progress", jQuery.Callbacks( "memory" ), - jQuery.Callbacks( "memory" ), 2 ], - [ "resolve", "done", jQuery.Callbacks( "once memory" ), - jQuery.Callbacks( "once memory" ), 0, "resolved" ], - [ "reject", "fail", jQuery.Callbacks( "once memory" ), - jQuery.Callbacks( "once memory" ), 1, "rejected" ] - ], - state = "pending", - promise = { - state: function() { - return state; - }, - always: function() { - deferred.done( arguments ).fail( arguments ); - return this; - }, - "catch": function( fn ) { - return promise.then( null, fn ); - }, - - // Keep pipe for back-compat - pipe: function( /* fnDone, fnFail, fnProgress */ ) { - var fns = arguments; - - return jQuery.Deferred( function( newDefer ) { - jQuery.each( tuples, function( i, tuple ) { - - // Map tuples (progress, done, fail) to arguments (done, fail, progress) - var fn = jQuery.isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; - - // deferred.progress(function() { bind to newDefer or newDefer.notify }) - // deferred.done(function() { bind to newDefer or newDefer.resolve }) - // deferred.fail(function() { bind to newDefer or newDefer.reject }) - deferred[ tuple[ 1 ] ]( function() { - var returned = fn && fn.apply( this, arguments ); - if ( returned && jQuery.isFunction( returned.promise ) ) { - returned.promise() - .progress( newDefer.notify ) - .done( newDefer.resolve ) - .fail( newDefer.reject ); - } else { - newDefer[ tuple[ 0 ] + "With" ]( - this, - fn ? [ returned ] : arguments - ); - } - } ); - } ); - fns = null; - } ).promise(); - }, - then: function( onFulfilled, onRejected, onProgress ) { - var maxDepth = 0; - function resolve( depth, deferred, handler, special ) { - return function() { - var that = this, - args = arguments, - mightThrow = function() { - var returned, then; - - // Support: Promises/A+ section 2.3.3.3.3 - // https://promisesaplus.com/#point-59 - // Ignore double-resolution attempts - if ( depth < maxDepth ) { - return; - } - - returned = handler.apply( that, args ); - - // Support: Promises/A+ section 2.3.1 - // https://promisesaplus.com/#point-48 - if ( returned === deferred.promise() ) { - throw new TypeError( "Thenable self-resolution" ); - } - - // Support: Promises/A+ sections 2.3.3.1, 3.5 - // https://promisesaplus.com/#point-54 - // https://promisesaplus.com/#point-75 - // Retrieve `then` only once - then = returned && - - // Support: Promises/A+ section 2.3.4 - // https://promisesaplus.com/#point-64 - // Only check objects and functions for thenability - ( typeof returned === "object" || - typeof returned === "function" ) && - returned.then; - - // Handle a returned thenable - if ( jQuery.isFunction( then ) ) { - - // Special processors (notify) just wait for resolution - if ( special ) { - then.call( - returned, - resolve( maxDepth, deferred, Identity, special ), - resolve( maxDepth, deferred, Thrower, special ) - ); - - // Normal processors (resolve) also hook into progress - } else { - - // ...and disregard older resolution values - maxDepth++; - - then.call( - returned, - resolve( maxDepth, deferred, Identity, special ), - resolve( maxDepth, deferred, Thrower, special ), - resolve( maxDepth, deferred, Identity, - deferred.notifyWith ) - ); - } - - // Handle all other returned values - } else { - - // Only substitute handlers pass on context - // and multiple values (non-spec behavior) - if ( handler !== Identity ) { - that = undefined; - args = [ returned ]; - } - - // Process the value(s) - // Default process is resolve - ( special || deferred.resolveWith )( that, args ); - } - }, - - // Only normal processors (resolve) catch and reject exceptions - process = special ? - mightThrow : - function() { - try { - mightThrow(); - } catch ( e ) { - - if ( jQuery.Deferred.exceptionHook ) { - jQuery.Deferred.exceptionHook( e, - process.stackTrace ); - } - - // Support: Promises/A+ section 2.3.3.3.4.1 - // https://promisesaplus.com/#point-61 - // Ignore post-resolution exceptions - if ( depth + 1 >= maxDepth ) { - - // Only substitute handlers pass on context - // and multiple values (non-spec behavior) - if ( handler !== Thrower ) { - that = undefined; - args = [ e ]; - } - - deferred.rejectWith( that, args ); - } - } - }; - - // Support: Promises/A+ section 2.3.3.3.1 - // https://promisesaplus.com/#point-57 - // Re-resolve promises immediately to dodge false rejection from - // subsequent errors - if ( depth ) { - process(); - } else { - - // Call an optional hook to record the stack, in case of exception - // since it's otherwise lost when execution goes async - if ( jQuery.Deferred.getStackHook ) { - process.stackTrace = jQuery.Deferred.getStackHook(); - } - window.setTimeout( process ); - } - }; - } - - return jQuery.Deferred( function( newDefer ) { - - // progress_handlers.add( ... ) - tuples[ 0 ][ 3 ].add( - resolve( - 0, - newDefer, - jQuery.isFunction( onProgress ) ? - onProgress : - Identity, - newDefer.notifyWith - ) - ); - - // fulfilled_handlers.add( ... ) - tuples[ 1 ][ 3 ].add( - resolve( - 0, - newDefer, - jQuery.isFunction( onFulfilled ) ? - onFulfilled : - Identity - ) - ); - - // rejected_handlers.add( ... ) - tuples[ 2 ][ 3 ].add( - resolve( - 0, - newDefer, - jQuery.isFunction( onRejected ) ? - onRejected : - Thrower - ) - ); - } ).promise(); - }, - - // Get a promise for this deferred - // If obj is provided, the promise aspect is added to the object - promise: function( obj ) { - return obj != null ? jQuery.extend( obj, promise ) : promise; - } - }, - deferred = {}; - - // Add list-specific methods - jQuery.each( tuples, function( i, tuple ) { - var list = tuple[ 2 ], - stateString = tuple[ 5 ]; - - // promise.progress = list.add - // promise.done = list.add - // promise.fail = list.add - promise[ tuple[ 1 ] ] = list.add; - - // Handle state - if ( stateString ) { - list.add( - function() { - - // state = "resolved" (i.e., fulfilled) - // state = "rejected" - state = stateString; - }, - - // rejected_callbacks.disable - // fulfilled_callbacks.disable - tuples[ 3 - i ][ 2 ].disable, - - // progress_callbacks.lock - tuples[ 0 ][ 2 ].lock - ); - } - - // progress_handlers.fire - // fulfilled_handlers.fire - // rejected_handlers.fire - list.add( tuple[ 3 ].fire ); - - // deferred.notify = function() { deferred.notifyWith(...) } - // deferred.resolve = function() { deferred.resolveWith(...) } - // deferred.reject = function() { deferred.rejectWith(...) } - deferred[ tuple[ 0 ] ] = function() { - deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); - return this; - }; - - // deferred.notifyWith = list.fireWith - // deferred.resolveWith = list.fireWith - // deferred.rejectWith = list.fireWith - deferred[ tuple[ 0 ] + "With" ] = list.fireWith; - } ); - - // Make the deferred a promise - promise.promise( deferred ); - - // Call given func if any - if ( func ) { - func.call( deferred, deferred ); - } - - // All done! - return deferred; - }, - - // Deferred helper - when: function( singleValue ) { - var - - // count of uncompleted subordinates - remaining = arguments.length, - - // count of unprocessed arguments - i = remaining, - - // subordinate fulfillment data - resolveContexts = Array( i ), - resolveValues = slice.call( arguments ), - - // the master Deferred - master = jQuery.Deferred(), - - // subordinate callback factory - updateFunc = function( i ) { - return function( value ) { - resolveContexts[ i ] = this; - resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; - if ( !( --remaining ) ) { - master.resolveWith( resolveContexts, resolveValues ); - } - }; - }; - - // Single- and empty arguments are adopted like Promise.resolve - if ( remaining <= 1 ) { - adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, - !remaining ); - - // Use .then() to unwrap secondary thenables (cf. gh-3000) - if ( master.state() === "pending" || - jQuery.isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { - - return master.then(); - } - } - - // Multiple arguments are aggregated like Promise.all array elements - while ( i-- ) { - adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); - } - - return master.promise(); - } -} ); - - -// These usually indicate a programmer mistake during development, -// warn about them ASAP rather than swallowing them by default. -var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; - -jQuery.Deferred.exceptionHook = function( error, stack ) { - - // Support: IE 8 - 9 only - // Console exists when dev tools are open, which can happen at any time - if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { - window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); - } -}; - - - - -jQuery.readyException = function( error ) { - window.setTimeout( function() { - throw error; - } ); -}; - - - - -// The deferred used on DOM ready -var readyList = jQuery.Deferred(); - -jQuery.fn.ready = function( fn ) { - - readyList - .then( fn ) - - // Wrap jQuery.readyException in a function so that the lookup - // happens at the time of error handling instead of callback - // registration. - .catch( function( error ) { - jQuery.readyException( error ); - } ); - - return this; -}; - -jQuery.extend( { - - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, - - // A counter to track how many items to wait for before - // the ready event fires. See #6781 - readyWait: 1, - - // Handle when the DOM is ready - ready: function( wait ) { - - // Abort if there are pending holds or we're already ready - if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { - return; - } - - // Remember that the DOM is ready - jQuery.isReady = true; - - // If a normal DOM Ready event fired, decrement, and wait if need be - if ( wait !== true && --jQuery.readyWait > 0 ) { - return; - } - - // If there are functions bound, to execute - readyList.resolveWith( document, [ jQuery ] ); - } -} ); - -jQuery.ready.then = readyList.then; - -// The ready event handler and self cleanup method -function completed() { - document.removeEventListener( "DOMContentLoaded", completed ); - window.removeEventListener( "load", completed ); - jQuery.ready(); -} - -// Catch cases where $(document).ready() is called -// after the browser event has already occurred. -// Support: IE <=9 - 10 only -// Older IE sometimes signals "interactive" too soon -if ( document.readyState === "complete" || - ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { - - // Handle it asynchronously to allow scripts the opportunity to delay ready - window.setTimeout( jQuery.ready ); - -} else { - - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", completed ); - - // A fallback to window.onload, that will always work - window.addEventListener( "load", completed ); -} - - - - -// Multifunctional method to get and set values of a collection -// The value/s can optionally be executed if it's a function -var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { - var i = 0, - len = elems.length, - bulk = key == null; - - // Sets many values - if ( jQuery.type( key ) === "object" ) { - chainable = true; - for ( i in key ) { - access( elems, fn, i, key[ i ], true, emptyGet, raw ); - } - - // Sets one value - } else if ( value !== undefined ) { - chainable = true; - - if ( !jQuery.isFunction( value ) ) { - raw = true; - } - - if ( bulk ) { - - // Bulk operations run against the entire set - if ( raw ) { - fn.call( elems, value ); - fn = null; - - // ...except when executing function values - } else { - bulk = fn; - fn = function( elem, key, value ) { - return bulk.call( jQuery( elem ), value ); - }; - } - } - - if ( fn ) { - for ( ; i < len; i++ ) { - fn( - elems[ i ], key, raw ? - value : - value.call( elems[ i ], i, fn( elems[ i ], key ) ) - ); - } - } - } - - if ( chainable ) { - return elems; - } - - // Gets - if ( bulk ) { - return fn.call( elems ); - } - - return len ? fn( elems[ 0 ], key ) : emptyGet; -}; -var acceptData = function( owner ) { - - // Accepts only: - // - Node - // - Node.ELEMENT_NODE - // - Node.DOCUMENT_NODE - // - Object - // - Any - return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); -}; - - - - -function Data() { - this.expando = jQuery.expando + Data.uid++; -} - -Data.uid = 1; - -Data.prototype = { - - cache: function( owner ) { - - // Check if the owner object already has a cache - var value = owner[ this.expando ]; - - // If not, create one - if ( !value ) { - value = {}; - - // We can accept data for non-element nodes in modern browsers, - // but we should not, see #8335. - // Always return an empty object. - if ( acceptData( owner ) ) { - - // If it is a node unlikely to be stringify-ed or looped over - // use plain assignment - if ( owner.nodeType ) { - owner[ this.expando ] = value; - - // Otherwise secure it in a non-enumerable property - // configurable must be true to allow the property to be - // deleted when data is removed - } else { - Object.defineProperty( owner, this.expando, { - value: value, - configurable: true - } ); - } - } - } - - return value; - }, - set: function( owner, data, value ) { - var prop, - cache = this.cache( owner ); - - // Handle: [ owner, key, value ] args - // Always use camelCase key (gh-2257) - if ( typeof data === "string" ) { - cache[ jQuery.camelCase( data ) ] = value; - - // Handle: [ owner, { properties } ] args - } else { - - // Copy the properties one-by-one to the cache object - for ( prop in data ) { - cache[ jQuery.camelCase( prop ) ] = data[ prop ]; - } - } - return cache; - }, - get: function( owner, key ) { - return key === undefined ? - this.cache( owner ) : - - // Always use camelCase key (gh-2257) - owner[ this.expando ] && owner[ this.expando ][ jQuery.camelCase( key ) ]; - }, - access: function( owner, key, value ) { - - // In cases where either: - // - // 1. No key was specified - // 2. A string key was specified, but no value provided - // - // Take the "read" path and allow the get method to determine - // which value to return, respectively either: - // - // 1. The entire cache object - // 2. The data stored at the key - // - if ( key === undefined || - ( ( key && typeof key === "string" ) && value === undefined ) ) { - - return this.get( owner, key ); - } - - // When the key is not a string, or both a key and value - // are specified, set or extend (existing objects) with either: - // - // 1. An object of properties - // 2. A key and value - // - this.set( owner, key, value ); - - // Since the "set" path can have two possible entry points - // return the expected data based on which path was taken[*] - return value !== undefined ? value : key; - }, - remove: function( owner, key ) { - var i, - cache = owner[ this.expando ]; - - if ( cache === undefined ) { - return; - } - - if ( key !== undefined ) { - - // Support array or space separated string of keys - if ( Array.isArray( key ) ) { - - // If key is an array of keys... - // We always set camelCase keys, so remove that. - key = key.map( jQuery.camelCase ); - } else { - key = jQuery.camelCase( key ); - - // If a key with the spaces exists, use it. - // Otherwise, create an array by matching non-whitespace - key = key in cache ? - [ key ] : - ( key.match( rnothtmlwhite ) || [] ); - } - - i = key.length; - - while ( i-- ) { - delete cache[ key[ i ] ]; - } - } - - // Remove the expando if there's no more data - if ( key === undefined || jQuery.isEmptyObject( cache ) ) { - - // Support: Chrome <=35 - 45 - // Webkit & Blink performance suffers when deleting properties - // from DOM nodes, so set to undefined instead - // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) - if ( owner.nodeType ) { - owner[ this.expando ] = undefined; - } else { - delete owner[ this.expando ]; - } - } - }, - hasData: function( owner ) { - var cache = owner[ this.expando ]; - return cache !== undefined && !jQuery.isEmptyObject( cache ); - } -}; -var dataPriv = new Data(); - -var dataUser = new Data(); - - - -// Implementation Summary -// -// 1. Enforce API surface and semantic compatibility with 1.9.x branch -// 2. Improve the module's maintainability by reducing the storage -// paths to a single mechanism. -// 3. Use the same single mechanism to support "private" and "user" data. -// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) -// 5. Avoid exposing implementation details on user objects (eg. expando properties) -// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 - -var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, - rmultiDash = /[A-Z]/g; - -function getData( data ) { - if ( data === "true" ) { - return true; - } - - if ( data === "false" ) { - return false; - } - - if ( data === "null" ) { - return null; - } - - // Only convert to a number if it doesn't change the string - if ( data === +data + "" ) { - return +data; - } - - if ( rbrace.test( data ) ) { - return JSON.parse( data ); - } - - return data; -} - -function dataAttr( elem, key, data ) { - var name; - - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if ( data === undefined && elem.nodeType === 1 ) { - name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); - data = elem.getAttribute( name ); - - if ( typeof data === "string" ) { - try { - data = getData( data ); - } catch ( e ) {} - - // Make sure we set the data so it isn't changed later - dataUser.set( elem, key, data ); - } else { - data = undefined; - } - } - return data; -} - -jQuery.extend( { - hasData: function( elem ) { - return dataUser.hasData( elem ) || dataPriv.hasData( elem ); - }, - - data: function( elem, name, data ) { - return dataUser.access( elem, name, data ); - }, - - removeData: function( elem, name ) { - dataUser.remove( elem, name ); - }, - - // TODO: Now that all calls to _data and _removeData have been replaced - // with direct calls to dataPriv methods, these can be deprecated. - _data: function( elem, name, data ) { - return dataPriv.access( elem, name, data ); - }, - - _removeData: function( elem, name ) { - dataPriv.remove( elem, name ); - } -} ); - -jQuery.fn.extend( { - data: function( key, value ) { - var i, name, data, - elem = this[ 0 ], - attrs = elem && elem.attributes; - - // Gets all values - if ( key === undefined ) { - if ( this.length ) { - data = dataUser.get( elem ); - - if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { - i = attrs.length; - while ( i-- ) { - - // Support: IE 11 only - // The attrs elements can be null (#14894) - if ( attrs[ i ] ) { - name = attrs[ i ].name; - if ( name.indexOf( "data-" ) === 0 ) { - name = jQuery.camelCase( name.slice( 5 ) ); - dataAttr( elem, name, data[ name ] ); - } - } - } - dataPriv.set( elem, "hasDataAttrs", true ); - } - } - - return data; - } - - // Sets multiple values - if ( typeof key === "object" ) { - return this.each( function() { - dataUser.set( this, key ); - } ); - } - - return access( this, function( value ) { - var data; - - // The calling jQuery object (element matches) is not empty - // (and therefore has an element appears at this[ 0 ]) and the - // `value` parameter was not undefined. An empty jQuery object - // will result in `undefined` for elem = this[ 0 ] which will - // throw an exception if an attempt to read a data cache is made. - if ( elem && value === undefined ) { - - // Attempt to get data from the cache - // The key will always be camelCased in Data - data = dataUser.get( elem, key ); - if ( data !== undefined ) { - return data; - } - - // Attempt to "discover" the data in - // HTML5 custom data-* attrs - data = dataAttr( elem, key ); - if ( data !== undefined ) { - return data; - } - - // We tried really hard, but the data doesn't exist. - return; - } - - // Set the data... - this.each( function() { - - // We always store the camelCased key - dataUser.set( this, key, value ); - } ); - }, null, value, arguments.length > 1, null, true ); - }, - - removeData: function( key ) { - return this.each( function() { - dataUser.remove( this, key ); - } ); - } -} ); - - -jQuery.extend( { - queue: function( elem, type, data ) { - var queue; - - if ( elem ) { - type = ( type || "fx" ) + "queue"; - queue = dataPriv.get( elem, type ); - - // Speed up dequeue by getting out quickly if this is just a lookup - if ( data ) { - if ( !queue || Array.isArray( data ) ) { - queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); - } else { - queue.push( data ); - } - } - return queue || []; - } - }, - - dequeue: function( elem, type ) { - type = type || "fx"; - - var queue = jQuery.queue( elem, type ), - startLength = queue.length, - fn = queue.shift(), - hooks = jQuery._queueHooks( elem, type ), - next = function() { - jQuery.dequeue( elem, type ); - }; - - // If the fx queue is dequeued, always remove the progress sentinel - if ( fn === "inprogress" ) { - fn = queue.shift(); - startLength--; - } - - if ( fn ) { - - // Add a progress sentinel to prevent the fx queue from being - // automatically dequeued - if ( type === "fx" ) { - queue.unshift( "inprogress" ); - } - - // Clear up the last queue stop function - delete hooks.stop; - fn.call( elem, next, hooks ); - } - - if ( !startLength && hooks ) { - hooks.empty.fire(); - } - }, - - // Not public - generate a queueHooks object, or return the current one - _queueHooks: function( elem, type ) { - var key = type + "queueHooks"; - return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { - empty: jQuery.Callbacks( "once memory" ).add( function() { - dataPriv.remove( elem, [ type + "queue", key ] ); - } ) - } ); - } -} ); - -jQuery.fn.extend( { - queue: function( type, data ) { - var setter = 2; - - if ( typeof type !== "string" ) { - data = type; - type = "fx"; - setter--; - } - - if ( arguments.length < setter ) { - return jQuery.queue( this[ 0 ], type ); - } - - return data === undefined ? - this : - this.each( function() { - var queue = jQuery.queue( this, type, data ); - - // Ensure a hooks for this queue - jQuery._queueHooks( this, type ); - - if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { - jQuery.dequeue( this, type ); - } - } ); - }, - dequeue: function( type ) { - return this.each( function() { - jQuery.dequeue( this, type ); - } ); - }, - clearQueue: function( type ) { - return this.queue( type || "fx", [] ); - }, - - // Get a promise resolved when queues of a certain type - // are emptied (fx is the type by default) - promise: function( type, obj ) { - var tmp, - count = 1, - defer = jQuery.Deferred(), - elements = this, - i = this.length, - resolve = function() { - if ( !( --count ) ) { - defer.resolveWith( elements, [ elements ] ); - } - }; - - if ( typeof type !== "string" ) { - obj = type; - type = undefined; - } - type = type || "fx"; - - while ( i-- ) { - tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); - if ( tmp && tmp.empty ) { - count++; - tmp.empty.add( resolve ); - } - } - resolve(); - return defer.promise( obj ); - } -} ); -var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; - -var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); - - -var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; - -var isHiddenWithinTree = function( elem, el ) { - - // isHiddenWithinTree might be called from jQuery#filter function; - // in that case, element will be second argument - elem = el || elem; - - // Inline style trumps all - return elem.style.display === "none" || - elem.style.display === "" && - - // Otherwise, check computed style - // Support: Firefox <=43 - 45 - // Disconnected elements can have computed display: none, so first confirm that elem is - // in the document. - jQuery.contains( elem.ownerDocument, elem ) && - - jQuery.css( elem, "display" ) === "none"; - }; - -var swap = function( elem, options, callback, args ) { - var ret, name, - old = {}; - - // Remember the old values, and insert the new ones - for ( name in options ) { - old[ name ] = elem.style[ name ]; - elem.style[ name ] = options[ name ]; - } - - ret = callback.apply( elem, args || [] ); - - // Revert the old values - for ( name in options ) { - elem.style[ name ] = old[ name ]; - } - - return ret; -}; - - - - -function adjustCSS( elem, prop, valueParts, tween ) { - var adjusted, - scale = 1, - maxIterations = 20, - currentValue = tween ? - function() { - return tween.cur(); - } : - function() { - return jQuery.css( elem, prop, "" ); - }, - initial = currentValue(), - unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), - - // Starting value computation is required for potential unit mismatches - initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && - rcssNum.exec( jQuery.css( elem, prop ) ); - - if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { - - // Trust units reported by jQuery.css - unit = unit || initialInUnit[ 3 ]; - - // Make sure we update the tween properties later on - valueParts = valueParts || []; - - // Iteratively approximate from a nonzero starting point - initialInUnit = +initial || 1; - - do { - - // If previous iteration zeroed out, double until we get *something*. - // Use string for doubling so we don't accidentally see scale as unchanged below - scale = scale || ".5"; - - // Adjust and apply - initialInUnit = initialInUnit / scale; - jQuery.style( elem, prop, initialInUnit + unit ); - - // Update scale, tolerating zero or NaN from tween.cur() - // Break the loop if scale is unchanged or perfect, or if we've just had enough. - } while ( - scale !== ( scale = currentValue() / initial ) && scale !== 1 && --maxIterations - ); - } - - if ( valueParts ) { - initialInUnit = +initialInUnit || +initial || 0; - - // Apply relative offset (+=/-=) if specified - adjusted = valueParts[ 1 ] ? - initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : - +valueParts[ 2 ]; - if ( tween ) { - tween.unit = unit; - tween.start = initialInUnit; - tween.end = adjusted; - } - } - return adjusted; -} - - -var defaultDisplayMap = {}; - -function getDefaultDisplay( elem ) { - var temp, - doc = elem.ownerDocument, - nodeName = elem.nodeName, - display = defaultDisplayMap[ nodeName ]; - - if ( display ) { - return display; - } - - temp = doc.body.appendChild( doc.createElement( nodeName ) ); - display = jQuery.css( temp, "display" ); - - temp.parentNode.removeChild( temp ); - - if ( display === "none" ) { - display = "block"; - } - defaultDisplayMap[ nodeName ] = display; - - return display; -} - -function showHide( elements, show ) { - var display, elem, - values = [], - index = 0, - length = elements.length; - - // Determine new display value for elements that need to change - for ( ; index < length; index++ ) { - elem = elements[ index ]; - if ( !elem.style ) { - continue; - } - - display = elem.style.display; - if ( show ) { - - // Since we force visibility upon cascade-hidden elements, an immediate (and slow) - // check is required in this first loop unless we have a nonempty display value (either - // inline or about-to-be-restored) - if ( display === "none" ) { - values[ index ] = dataPriv.get( elem, "display" ) || null; - if ( !values[ index ] ) { - elem.style.display = ""; - } - } - if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { - values[ index ] = getDefaultDisplay( elem ); - } - } else { - if ( display !== "none" ) { - values[ index ] = "none"; - - // Remember what we're overwriting - dataPriv.set( elem, "display", display ); - } - } - } - - // Set the display of the elements in a second loop to avoid constant reflow - for ( index = 0; index < length; index++ ) { - if ( values[ index ] != null ) { - elements[ index ].style.display = values[ index ]; - } - } - - return elements; -} - -jQuery.fn.extend( { - show: function() { - return showHide( this, true ); - }, - hide: function() { - return showHide( this ); - }, - toggle: function( state ) { - if ( typeof state === "boolean" ) { - return state ? this.show() : this.hide(); - } - - return this.each( function() { - if ( isHiddenWithinTree( this ) ) { - jQuery( this ).show(); - } else { - jQuery( this ).hide(); - } - } ); - } -} ); -var rcheckableType = ( /^(?:checkbox|radio)$/i ); - -var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]+)/i ); - -var rscriptType = ( /^$|\/(?:java|ecma)script/i ); - - - -// We have to close these tags to support XHTML (#13200) -var wrapMap = { - - // Support: IE <=9 only - option: [ 1, "" ], - - // XHTML parsers do not magically insert elements in the - // same way that tag soup parsers do. So we cannot shorten - // this by omitting or other required elements. - thead: [ 1, "", "
" ], - col: [ 2, "", "
" ], - tr: [ 2, "", "
" ], - td: [ 3, "", "
" ], - - _default: [ 0, "", "" ] -}; - -// Support: IE <=9 only -wrapMap.optgroup = wrapMap.option; - -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - - -function getAll( context, tag ) { - - // Support: IE <=9 - 11 only - // Use typeof to avoid zero-argument method invocation on host objects (#15151) - var ret; - - if ( typeof context.getElementsByTagName !== "undefined" ) { - ret = context.getElementsByTagName( tag || "*" ); - - } else if ( typeof context.querySelectorAll !== "undefined" ) { - ret = context.querySelectorAll( tag || "*" ); - - } else { - ret = []; - } - - if ( tag === undefined || tag && nodeName( context, tag ) ) { - return jQuery.merge( [ context ], ret ); - } - - return ret; -} - - -// Mark scripts as having already been evaluated -function setGlobalEval( elems, refElements ) { - var i = 0, - l = elems.length; - - for ( ; i < l; i++ ) { - dataPriv.set( - elems[ i ], - "globalEval", - !refElements || dataPriv.get( refElements[ i ], "globalEval" ) - ); - } -} - - -var rhtml = /<|&#?\w+;/; - -function buildFragment( elems, context, scripts, selection, ignored ) { - var elem, tmp, tag, wrap, contains, j, - fragment = context.createDocumentFragment(), - nodes = [], - i = 0, - l = elems.length; - - for ( ; i < l; i++ ) { - elem = elems[ i ]; - - if ( elem || elem === 0 ) { - - // Add nodes directly - if ( jQuery.type( elem ) === "object" ) { - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); - - // Convert non-html into a text node - } else if ( !rhtml.test( elem ) ) { - nodes.push( context.createTextNode( elem ) ); - - // Convert html into DOM nodes - } else { - tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); - - // Deserialize a standard representation - tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); - wrap = wrapMap[ tag ] || wrapMap._default; - tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; - - // Descend through wrappers to the right content - j = wrap[ 0 ]; - while ( j-- ) { - tmp = tmp.lastChild; - } - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( nodes, tmp.childNodes ); - - // Remember the top-level container - tmp = fragment.firstChild; - - // Ensure the created nodes are orphaned (#12392) - tmp.textContent = ""; - } - } - } - - // Remove wrapper from fragment - fragment.textContent = ""; - - i = 0; - while ( ( elem = nodes[ i++ ] ) ) { - - // Skip elements already in the context collection (trac-4087) - if ( selection && jQuery.inArray( elem, selection ) > -1 ) { - if ( ignored ) { - ignored.push( elem ); - } - continue; - } - - contains = jQuery.contains( elem.ownerDocument, elem ); - - // Append to fragment - tmp = getAll( fragment.appendChild( elem ), "script" ); - - // Preserve script evaluation history - if ( contains ) { - setGlobalEval( tmp ); - } - - // Capture executables - if ( scripts ) { - j = 0; - while ( ( elem = tmp[ j++ ] ) ) { - if ( rscriptType.test( elem.type || "" ) ) { - scripts.push( elem ); - } - } - } - } - - return fragment; -} - - -( function() { - var fragment = document.createDocumentFragment(), - div = fragment.appendChild( document.createElement( "div" ) ), - input = document.createElement( "input" ); - - // Support: Android 4.0 - 4.3 only - // Check state lost if the name is set (#11217) - // Support: Windows Web Apps (WWA) - // `name` and `type` must use .setAttribute for WWA (#14901) - input.setAttribute( "type", "radio" ); - input.setAttribute( "checked", "checked" ); - input.setAttribute( "name", "t" ); - - div.appendChild( input ); - - // Support: Android <=4.1 only - // Older WebKit doesn't clone checked state correctly in fragments - support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; - - // Support: IE <=11 only - // Make sure textarea (and checkbox) defaultValue is properly cloned - div.innerHTML = ""; - support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; -} )(); -var documentElement = document.documentElement; - - - -var - rkeyEvent = /^key/, - rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, - rtypenamespace = /^([^.]*)(?:\.(.+)|)/; - -function returnTrue() { - return true; -} - -function returnFalse() { - return false; -} - -// Support: IE <=9 only -// See #13393 for more info -function safeActiveElement() { - try { - return document.activeElement; - } catch ( err ) { } -} - -function on( elem, types, selector, data, fn, one ) { - var origFn, type; - - // Types can be a map of types/handlers - if ( typeof types === "object" ) { - - // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { - - // ( types-Object, data ) - data = data || selector; - selector = undefined; - } - for ( type in types ) { - on( elem, type, selector, data, types[ type ], one ); - } - return elem; - } - - if ( data == null && fn == null ) { - - // ( types, fn ) - fn = selector; - data = selector = undefined; - } else if ( fn == null ) { - if ( typeof selector === "string" ) { - - // ( types, selector, fn ) - fn = data; - data = undefined; - } else { - - // ( types, data, fn ) - fn = data; - data = selector; - selector = undefined; - } - } - if ( fn === false ) { - fn = returnFalse; - } else if ( !fn ) { - return elem; - } - - if ( one === 1 ) { - origFn = fn; - fn = function( event ) { - - // Can use an empty set, since event contains the info - jQuery().off( event ); - return origFn.apply( this, arguments ); - }; - - // Use same guid so caller can remove using origFn - fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); - } - return elem.each( function() { - jQuery.event.add( this, types, fn, data, selector ); - } ); -} - -/* - * Helper functions for managing events -- not part of the public interface. - * Props to Dean Edwards' addEvent library for many of the ideas. - */ -jQuery.event = { - - global: {}, - - add: function( elem, types, handler, data, selector ) { - - var handleObjIn, eventHandle, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = dataPriv.get( elem ); - - // Don't attach events to noData or text/comment nodes (but allow plain objects) - if ( !elemData ) { - return; - } - - // Caller can pass in an object of custom data in lieu of the handler - if ( handler.handler ) { - handleObjIn = handler; - handler = handleObjIn.handler; - selector = handleObjIn.selector; - } - - // Ensure that invalid selectors throw exceptions at attach time - // Evaluate against documentElement in case elem is a non-element node (e.g., document) - if ( selector ) { - jQuery.find.matchesSelector( documentElement, selector ); - } - - // Make sure that the handler has a unique ID, used to find/remove it later - if ( !handler.guid ) { - handler.guid = jQuery.guid++; - } - - // Init the element's event structure and main handler, if this is the first - if ( !( events = elemData.events ) ) { - events = elemData.events = {}; - } - if ( !( eventHandle = elemData.handle ) ) { - eventHandle = elemData.handle = function( e ) { - - // Discard the second event of a jQuery.event.trigger() and - // when an event is called after a page has unloaded - return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? - jQuery.event.dispatch.apply( elem, arguments ) : undefined; - }; - } - - // Handle multiple events separated by a space - types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[ t ] ) || []; - type = origType = tmp[ 1 ]; - namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); - - // There *must* be a type, no attaching namespace-only handlers - if ( !type ) { - continue; - } - - // If event changes its type, use the special event handlers for the changed type - special = jQuery.event.special[ type ] || {}; - - // If selector defined, determine special event api type, otherwise given type - type = ( selector ? special.delegateType : special.bindType ) || type; - - // Update special based on newly reset type - special = jQuery.event.special[ type ] || {}; - - // handleObj is passed to all event handlers - handleObj = jQuery.extend( { - type: type, - origType: origType, - data: data, - handler: handler, - guid: handler.guid, - selector: selector, - needsContext: selector && jQuery.expr.match.needsContext.test( selector ), - namespace: namespaces.join( "." ) - }, handleObjIn ); - - // Init the event handler queue if we're the first - if ( !( handlers = events[ type ] ) ) { - handlers = events[ type ] = []; - handlers.delegateCount = 0; - - // Only use addEventListener if the special events handler returns false - if ( !special.setup || - special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle ); - } - } - } - - if ( special.add ) { - special.add.call( elem, handleObj ); - - if ( !handleObj.handler.guid ) { - handleObj.handler.guid = handler.guid; - } - } - - // Add to the element's handler list, delegates in front - if ( selector ) { - handlers.splice( handlers.delegateCount++, 0, handleObj ); - } else { - handlers.push( handleObj ); - } - - // Keep track of which events have ever been used, for event optimization - jQuery.event.global[ type ] = true; - } - - }, - - // Detach an event or set of events from an element - remove: function( elem, types, handler, selector, mappedTypes ) { - - var j, origCount, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); - - if ( !elemData || !( events = elemData.events ) ) { - return; - } - - // Once for each type.namespace in types; type may be omitted - types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[ t ] ) || []; - type = origType = tmp[ 1 ]; - namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); - - // Unbind all events (on this namespace, if provided) for the element - if ( !type ) { - for ( type in events ) { - jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); - } - continue; - } - - special = jQuery.event.special[ type ] || {}; - type = ( selector ? special.delegateType : special.bindType ) || type; - handlers = events[ type ] || []; - tmp = tmp[ 2 ] && - new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); - - // Remove matching events - origCount = j = handlers.length; - while ( j-- ) { - handleObj = handlers[ j ]; - - if ( ( mappedTypes || origType === handleObj.origType ) && - ( !handler || handler.guid === handleObj.guid ) && - ( !tmp || tmp.test( handleObj.namespace ) ) && - ( !selector || selector === handleObj.selector || - selector === "**" && handleObj.selector ) ) { - handlers.splice( j, 1 ); - - if ( handleObj.selector ) { - handlers.delegateCount--; - } - if ( special.remove ) { - special.remove.call( elem, handleObj ); - } - } - } - - // Remove generic event handler if we removed something and no more handlers exist - // (avoids potential for endless recursion during removal of special event handlers) - if ( origCount && !handlers.length ) { - if ( !special.teardown || - special.teardown.call( elem, namespaces, elemData.handle ) === false ) { - - jQuery.removeEvent( elem, type, elemData.handle ); - } - - delete events[ type ]; - } - } - - // Remove data and the expando if it's no longer used - if ( jQuery.isEmptyObject( events ) ) { - dataPriv.remove( elem, "handle events" ); - } - }, - - dispatch: function( nativeEvent ) { - - // Make a writable jQuery.Event from the native event object - var event = jQuery.event.fix( nativeEvent ); - - var i, j, ret, matched, handleObj, handlerQueue, - args = new Array( arguments.length ), - handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [], - special = jQuery.event.special[ event.type ] || {}; - - // Use the fix-ed jQuery.Event rather than the (read-only) native event - args[ 0 ] = event; - - for ( i = 1; i < arguments.length; i++ ) { - args[ i ] = arguments[ i ]; - } - - event.delegateTarget = this; - - // Call the preDispatch hook for the mapped type, and let it bail if desired - if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { - return; - } - - // Determine handlers - handlerQueue = jQuery.event.handlers.call( this, event, handlers ); - - // Run delegates first; they may want to stop propagation beneath us - i = 0; - while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { - event.currentTarget = matched.elem; - - j = 0; - while ( ( handleObj = matched.handlers[ j++ ] ) && - !event.isImmediatePropagationStopped() ) { - - // Triggered event must either 1) have no namespace, or 2) have namespace(s) - // a subset or equal to those in the bound event (both can have no namespace). - if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) { - - event.handleObj = handleObj; - event.data = handleObj.data; - - ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || - handleObj.handler ).apply( matched.elem, args ); - - if ( ret !== undefined ) { - if ( ( event.result = ret ) === false ) { - event.preventDefault(); - event.stopPropagation(); - } - } - } - } - } - - // Call the postDispatch hook for the mapped type - if ( special.postDispatch ) { - special.postDispatch.call( this, event ); - } - - return event.result; - }, - - handlers: function( event, handlers ) { - var i, handleObj, sel, matchedHandlers, matchedSelectors, - handlerQueue = [], - delegateCount = handlers.delegateCount, - cur = event.target; - - // Find delegate handlers - if ( delegateCount && - - // Support: IE <=9 - // Black-hole SVG instance trees (trac-13180) - cur.nodeType && - - // Support: Firefox <=42 - // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) - // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click - // Support: IE 11 only - // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) - !( event.type === "click" && event.button >= 1 ) ) { - - for ( ; cur !== this; cur = cur.parentNode || this ) { - - // Don't check non-elements (#13208) - // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) - if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { - matchedHandlers = []; - matchedSelectors = {}; - for ( i = 0; i < delegateCount; i++ ) { - handleObj = handlers[ i ]; - - // Don't conflict with Object.prototype properties (#13203) - sel = handleObj.selector + " "; - - if ( matchedSelectors[ sel ] === undefined ) { - matchedSelectors[ sel ] = handleObj.needsContext ? - jQuery( sel, this ).index( cur ) > -1 : - jQuery.find( sel, this, null, [ cur ] ).length; - } - if ( matchedSelectors[ sel ] ) { - matchedHandlers.push( handleObj ); - } - } - if ( matchedHandlers.length ) { - handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); - } - } - } - } - - // Add the remaining (directly-bound) handlers - cur = this; - if ( delegateCount < handlers.length ) { - handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); - } - - return handlerQueue; - }, - - addProp: function( name, hook ) { - Object.defineProperty( jQuery.Event.prototype, name, { - enumerable: true, - configurable: true, - - get: jQuery.isFunction( hook ) ? - function() { - if ( this.originalEvent ) { - return hook( this.originalEvent ); - } - } : - function() { - if ( this.originalEvent ) { - return this.originalEvent[ name ]; - } - }, - - set: function( value ) { - Object.defineProperty( this, name, { - enumerable: true, - configurable: true, - writable: true, - value: value - } ); - } - } ); - }, - - fix: function( originalEvent ) { - return originalEvent[ jQuery.expando ] ? - originalEvent : - new jQuery.Event( originalEvent ); - }, - - special: { - load: { - - // Prevent triggered image.load events from bubbling to window.load - noBubble: true - }, - focus: { - - // Fire native event if possible so blur/focus sequence is correct - trigger: function() { - if ( this !== safeActiveElement() && this.focus ) { - this.focus(); - return false; - } - }, - delegateType: "focusin" - }, - blur: { - trigger: function() { - if ( this === safeActiveElement() && this.blur ) { - this.blur(); - return false; - } - }, - delegateType: "focusout" - }, - click: { - - // For checkbox, fire native event so checked state will be right - trigger: function() { - if ( this.type === "checkbox" && this.click && nodeName( this, "input" ) ) { - this.click(); - return false; - } - }, - - // For cross-browser consistency, don't fire native .click() on links - _default: function( event ) { - return nodeName( event.target, "a" ); - } - }, - - beforeunload: { - postDispatch: function( event ) { - - // Support: Firefox 20+ - // Firefox doesn't alert if the returnValue field is not set. - if ( event.result !== undefined && event.originalEvent ) { - event.originalEvent.returnValue = event.result; - } - } - } - } -}; - -jQuery.removeEvent = function( elem, type, handle ) { - - // This "if" is needed for plain objects - if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle ); - } -}; - -jQuery.Event = function( src, props ) { - - // Allow instantiation without the 'new' keyword - if ( !( this instanceof jQuery.Event ) ) { - return new jQuery.Event( src, props ); - } - - // Event object - if ( src && src.type ) { - this.originalEvent = src; - this.type = src.type; - - // Events bubbling up the document may have been marked as prevented - // by a handler lower down the tree; reflect the correct value. - this.isDefaultPrevented = src.defaultPrevented || - src.defaultPrevented === undefined && - - // Support: Android <=2.3 only - src.returnValue === false ? - returnTrue : - returnFalse; - - // Create target properties - // Support: Safari <=6 - 7 only - // Target should not be a text node (#504, #13143) - this.target = ( src.target && src.target.nodeType === 3 ) ? - src.target.parentNode : - src.target; - - this.currentTarget = src.currentTarget; - this.relatedTarget = src.relatedTarget; - - // Event type - } else { - this.type = src; - } - - // Put explicitly provided properties onto the event object - if ( props ) { - jQuery.extend( this, props ); - } - - // Create a timestamp if incoming event doesn't have one - this.timeStamp = src && src.timeStamp || jQuery.now(); - - // Mark it as fixed - this[ jQuery.expando ] = true; -}; - -// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding -// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html -jQuery.Event.prototype = { - constructor: jQuery.Event, - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse, - isSimulated: false, - - preventDefault: function() { - var e = this.originalEvent; - - this.isDefaultPrevented = returnTrue; - - if ( e && !this.isSimulated ) { - e.preventDefault(); - } - }, - stopPropagation: function() { - var e = this.originalEvent; - - this.isPropagationStopped = returnTrue; - - if ( e && !this.isSimulated ) { - e.stopPropagation(); - } - }, - stopImmediatePropagation: function() { - var e = this.originalEvent; - - this.isImmediatePropagationStopped = returnTrue; - - if ( e && !this.isSimulated ) { - e.stopImmediatePropagation(); - } - - this.stopPropagation(); - } -}; - -// Includes all common event props including KeyEvent and MouseEvent specific props -jQuery.each( { - altKey: true, - bubbles: true, - cancelable: true, - changedTouches: true, - ctrlKey: true, - detail: true, - eventPhase: true, - metaKey: true, - pageX: true, - pageY: true, - shiftKey: true, - view: true, - "char": true, - charCode: true, - key: true, - keyCode: true, - button: true, - buttons: true, - clientX: true, - clientY: true, - offsetX: true, - offsetY: true, - pointerId: true, - pointerType: true, - screenX: true, - screenY: true, - targetTouches: true, - toElement: true, - touches: true, - - which: function( event ) { - var button = event.button; - - // Add which for key events - if ( event.which == null && rkeyEvent.test( event.type ) ) { - return event.charCode != null ? event.charCode : event.keyCode; - } - - // Add which for click: 1 === left; 2 === middle; 3 === right - if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { - if ( button & 1 ) { - return 1; - } - - if ( button & 2 ) { - return 3; - } - - if ( button & 4 ) { - return 2; - } - - return 0; - } - - return event.which; - } -}, jQuery.event.addProp ); - -// Create mouseenter/leave events using mouseover/out and event-time checks -// so that event delegation works in jQuery. -// Do the same for pointerenter/pointerleave and pointerover/pointerout -// -// Support: Safari 7 only -// Safari sends mouseenter too often; see: -// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 -// for the description of the bug (it existed in older Chrome versions as well). -jQuery.each( { - mouseenter: "mouseover", - mouseleave: "mouseout", - pointerenter: "pointerover", - pointerleave: "pointerout" -}, function( orig, fix ) { - jQuery.event.special[ orig ] = { - delegateType: fix, - bindType: fix, - - handle: function( event ) { - var ret, - target = this, - related = event.relatedTarget, - handleObj = event.handleObj; - - // For mouseenter/leave call the handler if related is outside the target. - // NB: No relatedTarget if the mouse left/entered the browser window - if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { - event.type = handleObj.origType; - ret = handleObj.handler.apply( this, arguments ); - event.type = fix; - } - return ret; - } - }; -} ); - -jQuery.fn.extend( { - - on: function( types, selector, data, fn ) { - return on( this, types, selector, data, fn ); - }, - one: function( types, selector, data, fn ) { - return on( this, types, selector, data, fn, 1 ); - }, - off: function( types, selector, fn ) { - var handleObj, type; - if ( types && types.preventDefault && types.handleObj ) { - - // ( event ) dispatched jQuery.Event - handleObj = types.handleObj; - jQuery( types.delegateTarget ).off( - handleObj.namespace ? - handleObj.origType + "." + handleObj.namespace : - handleObj.origType, - handleObj.selector, - handleObj.handler - ); - return this; - } - if ( typeof types === "object" ) { - - // ( types-object [, selector] ) - for ( type in types ) { - this.off( type, selector, types[ type ] ); - } - return this; - } - if ( selector === false || typeof selector === "function" ) { - - // ( types [, fn] ) - fn = selector; - selector = undefined; - } - if ( fn === false ) { - fn = returnFalse; - } - return this.each( function() { - jQuery.event.remove( this, types, fn, selector ); - } ); - } -} ); - - -var - - /* eslint-disable max-len */ - - // See https://github.com/eslint/eslint/issues/3229 - rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi, - - /* eslint-enable */ - - // Support: IE <=10 - 11, Edge 12 - 13 - // In IE/Edge using regex groups here causes severe slowdowns. - // See https://connect.microsoft.com/IE/feedback/details/1736512/ - rnoInnerhtml = /\s*$/g; - -// Prefer a tbody over its parent table for containing new rows -function manipulationTarget( elem, content ) { - if ( nodeName( elem, "table" ) && - nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { - - return jQuery( ">tbody", elem )[ 0 ] || elem; - } - - return elem; -} - -// Replace/restore the type attribute of script elements for safe DOM manipulation -function disableScript( elem ) { - elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; - return elem; -} -function restoreScript( elem ) { - var match = rscriptTypeMasked.exec( elem.type ); - - if ( match ) { - elem.type = match[ 1 ]; - } else { - elem.removeAttribute( "type" ); - } - - return elem; -} - -function cloneCopyEvent( src, dest ) { - var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; - - if ( dest.nodeType !== 1 ) { - return; - } - - // 1. Copy private data: events, handlers, etc. - if ( dataPriv.hasData( src ) ) { - pdataOld = dataPriv.access( src ); - pdataCur = dataPriv.set( dest, pdataOld ); - events = pdataOld.events; - - if ( events ) { - delete pdataCur.handle; - pdataCur.events = {}; - - for ( type in events ) { - for ( i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( dest, type, events[ type ][ i ] ); - } - } - } - } - - // 2. Copy user data - if ( dataUser.hasData( src ) ) { - udataOld = dataUser.access( src ); - udataCur = jQuery.extend( {}, udataOld ); - - dataUser.set( dest, udataCur ); - } -} - -// Fix IE bugs, see support tests -function fixInput( src, dest ) { - var nodeName = dest.nodeName.toLowerCase(); - - // Fails to persist the checked state of a cloned checkbox or radio button. - if ( nodeName === "input" && rcheckableType.test( src.type ) ) { - dest.checked = src.checked; - - // Fails to return the selected option to the default selected state when cloning options - } else if ( nodeName === "input" || nodeName === "textarea" ) { - dest.defaultValue = src.defaultValue; - } -} - -function domManip( collection, args, callback, ignored ) { - - // Flatten any nested arrays - args = concat.apply( [], args ); - - var fragment, first, scripts, hasScripts, node, doc, - i = 0, - l = collection.length, - iNoClone = l - 1, - value = args[ 0 ], - isFunction = jQuery.isFunction( value ); - - // We can't cloneNode fragments that contain checked, in WebKit - if ( isFunction || - ( l > 1 && typeof value === "string" && - !support.checkClone && rchecked.test( value ) ) ) { - return collection.each( function( index ) { - var self = collection.eq( index ); - if ( isFunction ) { - args[ 0 ] = value.call( this, index, self.html() ); - } - domManip( self, args, callback, ignored ); - } ); - } - - if ( l ) { - fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); - first = fragment.firstChild; - - if ( fragment.childNodes.length === 1 ) { - fragment = first; - } - - // Require either new content or an interest in ignored elements to invoke the callback - if ( first || ignored ) { - scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); - hasScripts = scripts.length; - - // Use the original fragment for the last item - // instead of the first because it can end up - // being emptied incorrectly in certain situations (#8070). - for ( ; i < l; i++ ) { - node = fragment; - - if ( i !== iNoClone ) { - node = jQuery.clone( node, true, true ); - - // Keep references to cloned scripts for later restoration - if ( hasScripts ) { - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( scripts, getAll( node, "script" ) ); - } - } - - callback.call( collection[ i ], node, i ); - } - - if ( hasScripts ) { - doc = scripts[ scripts.length - 1 ].ownerDocument; - - // Reenable scripts - jQuery.map( scripts, restoreScript ); - - // Evaluate executable scripts on first document insertion - for ( i = 0; i < hasScripts; i++ ) { - node = scripts[ i ]; - if ( rscriptType.test( node.type || "" ) && - !dataPriv.access( node, "globalEval" ) && - jQuery.contains( doc, node ) ) { - - if ( node.src ) { - - // Optional AJAX dependency, but won't run scripts if not present - if ( jQuery._evalUrl ) { - jQuery._evalUrl( node.src ); - } - } else { - DOMEval( node.textContent.replace( rcleanScript, "" ), doc ); - } - } - } - } - } - } - - return collection; -} - -function remove( elem, selector, keepData ) { - var node, - nodes = selector ? jQuery.filter( selector, elem ) : elem, - i = 0; - - for ( ; ( node = nodes[ i ] ) != null; i++ ) { - if ( !keepData && node.nodeType === 1 ) { - jQuery.cleanData( getAll( node ) ); - } - - if ( node.parentNode ) { - if ( keepData && jQuery.contains( node.ownerDocument, node ) ) { - setGlobalEval( getAll( node, "script" ) ); - } - node.parentNode.removeChild( node ); - } - } - - return elem; -} - -jQuery.extend( { - htmlPrefilter: function( html ) { - return html.replace( rxhtmlTag, "<$1>" ); - }, - - clone: function( elem, dataAndEvents, deepDataAndEvents ) { - var i, l, srcElements, destElements, - clone = elem.cloneNode( true ), - inPage = jQuery.contains( elem.ownerDocument, elem ); - - // Fix IE cloning issues - if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && - !jQuery.isXMLDoc( elem ) ) { - - // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 - destElements = getAll( clone ); - srcElements = getAll( elem ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - fixInput( srcElements[ i ], destElements[ i ] ); - } - } - - // Copy the events from the original to the clone - if ( dataAndEvents ) { - if ( deepDataAndEvents ) { - srcElements = srcElements || getAll( elem ); - destElements = destElements || getAll( clone ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - cloneCopyEvent( srcElements[ i ], destElements[ i ] ); - } - } else { - cloneCopyEvent( elem, clone ); - } - } - - // Preserve script evaluation history - destElements = getAll( clone, "script" ); - if ( destElements.length > 0 ) { - setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); - } - - // Return the cloned set - return clone; - }, - - cleanData: function( elems ) { - var data, elem, type, - special = jQuery.event.special, - i = 0; - - for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { - if ( acceptData( elem ) ) { - if ( ( data = elem[ dataPriv.expando ] ) ) { - if ( data.events ) { - for ( type in data.events ) { - if ( special[ type ] ) { - jQuery.event.remove( elem, type ); - - // This is a shortcut to avoid jQuery.event.remove's overhead - } else { - jQuery.removeEvent( elem, type, data.handle ); - } - } - } - - // Support: Chrome <=35 - 45+ - // Assign undefined instead of using delete, see Data#remove - elem[ dataPriv.expando ] = undefined; - } - if ( elem[ dataUser.expando ] ) { - - // Support: Chrome <=35 - 45+ - // Assign undefined instead of using delete, see Data#remove - elem[ dataUser.expando ] = undefined; - } - } - } - } -} ); - -jQuery.fn.extend( { - detach: function( selector ) { - return remove( this, selector, true ); - }, - - remove: function( selector ) { - return remove( this, selector ); - }, - - text: function( value ) { - return access( this, function( value ) { - return value === undefined ? - jQuery.text( this ) : - this.empty().each( function() { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - this.textContent = value; - } - } ); - }, null, value, arguments.length ); - }, - - append: function() { - return domManip( this, arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.appendChild( elem ); - } - } ); - }, - - prepend: function() { - return domManip( this, arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.insertBefore( elem, target.firstChild ); - } - } ); - }, - - before: function() { - return domManip( this, arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this ); - } - } ); - }, - - after: function() { - return domManip( this, arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this.nextSibling ); - } - } ); - }, - - empty: function() { - var elem, - i = 0; - - for ( ; ( elem = this[ i ] ) != null; i++ ) { - if ( elem.nodeType === 1 ) { - - // Prevent memory leaks - jQuery.cleanData( getAll( elem, false ) ); - - // Remove any remaining nodes - elem.textContent = ""; - } - } - - return this; - }, - - clone: function( dataAndEvents, deepDataAndEvents ) { - dataAndEvents = dataAndEvents == null ? false : dataAndEvents; - deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; - - return this.map( function() { - return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); - } ); - }, - - html: function( value ) { - return access( this, function( value ) { - var elem = this[ 0 ] || {}, - i = 0, - l = this.length; - - if ( value === undefined && elem.nodeType === 1 ) { - return elem.innerHTML; - } - - // See if we can take a shortcut and just use innerHTML - if ( typeof value === "string" && !rnoInnerhtml.test( value ) && - !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { - - value = jQuery.htmlPrefilter( value ); - - try { - for ( ; i < l; i++ ) { - elem = this[ i ] || {}; - - // Remove element nodes and prevent memory leaks - if ( elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem, false ) ); - elem.innerHTML = value; - } - } - - elem = 0; - - // If using innerHTML throws an exception, use the fallback method - } catch ( e ) {} - } - - if ( elem ) { - this.empty().append( value ); - } - }, null, value, arguments.length ); - }, - - replaceWith: function() { - var ignored = []; - - // Make the changes, replacing each non-ignored context element with the new content - return domManip( this, arguments, function( elem ) { - var parent = this.parentNode; - - if ( jQuery.inArray( this, ignored ) < 0 ) { - jQuery.cleanData( getAll( this ) ); - if ( parent ) { - parent.replaceChild( elem, this ); - } - } - - // Force callback invocation - }, ignored ); - } -} ); - -jQuery.each( { - appendTo: "append", - prependTo: "prepend", - insertBefore: "before", - insertAfter: "after", - replaceAll: "replaceWith" -}, function( name, original ) { - jQuery.fn[ name ] = function( selector ) { - var elems, - ret = [], - insert = jQuery( selector ), - last = insert.length - 1, - i = 0; - - for ( ; i <= last; i++ ) { - elems = i === last ? this : this.clone( true ); - jQuery( insert[ i ] )[ original ]( elems ); - - // Support: Android <=4.0 only, PhantomJS 1 only - // .get() because push.apply(_, arraylike) throws on ancient WebKit - push.apply( ret, elems.get() ); - } - - return this.pushStack( ret ); - }; -} ); -var rmargin = ( /^margin/ ); - -var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); - -var getStyles = function( elem ) { - - // Support: IE <=11 only, Firefox <=30 (#15098, #14150) - // IE throws on elements created in popups - // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" - var view = elem.ownerDocument.defaultView; - - if ( !view || !view.opener ) { - view = window; - } - - return view.getComputedStyle( elem ); - }; - - - -( function() { - - // Executing both pixelPosition & boxSizingReliable tests require only one layout - // so they're executed at the same time to save the second computation. - function computeStyleTests() { - - // This is a singleton, we need to execute it only once - if ( !div ) { - return; - } - - div.style.cssText = - "box-sizing:border-box;" + - "position:relative;display:block;" + - "margin:auto;border:1px;padding:1px;" + - "top:1%;width:50%"; - div.innerHTML = ""; - documentElement.appendChild( container ); - - var divStyle = window.getComputedStyle( div ); - pixelPositionVal = divStyle.top !== "1%"; - - // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 - reliableMarginLeftVal = divStyle.marginLeft === "2px"; - boxSizingReliableVal = divStyle.width === "4px"; - - // Support: Android 4.0 - 4.3 only - // Some styles come back with percentage values, even though they shouldn't - div.style.marginRight = "50%"; - pixelMarginRightVal = divStyle.marginRight === "4px"; - - documentElement.removeChild( container ); - - // Nullify the div so it wouldn't be stored in the memory and - // it will also be a sign that checks already performed - div = null; - } - - var pixelPositionVal, boxSizingReliableVal, pixelMarginRightVal, reliableMarginLeftVal, - container = document.createElement( "div" ), - div = document.createElement( "div" ); - - // Finish early in limited (non-browser) environments - if ( !div.style ) { - return; - } - - // Support: IE <=9 - 11 only - // Style of cloned element affects source element cloned (#8908) - div.style.backgroundClip = "content-box"; - div.cloneNode( true ).style.backgroundClip = ""; - support.clearCloneStyle = div.style.backgroundClip === "content-box"; - - container.style.cssText = "border:0;width:8px;height:0;top:0;left:-9999px;" + - "padding:0;margin-top:1px;position:absolute"; - container.appendChild( div ); - - jQuery.extend( support, { - pixelPosition: function() { - computeStyleTests(); - return pixelPositionVal; - }, - boxSizingReliable: function() { - computeStyleTests(); - return boxSizingReliableVal; - }, - pixelMarginRight: function() { - computeStyleTests(); - return pixelMarginRightVal; - }, - reliableMarginLeft: function() { - computeStyleTests(); - return reliableMarginLeftVal; - } - } ); -} )(); - - -function curCSS( elem, name, computed ) { - var width, minWidth, maxWidth, ret, - - // Support: Firefox 51+ - // Retrieving style before computed somehow - // fixes an issue with getting wrong values - // on detached elements - style = elem.style; - - computed = computed || getStyles( elem ); - - // getPropertyValue is needed for: - // .css('filter') (IE 9 only, #12537) - // .css('--customProperty) (#3144) - if ( computed ) { - ret = computed.getPropertyValue( name ) || computed[ name ]; - - if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { - ret = jQuery.style( elem, name ); - } - - // A tribute to the "awesome hack by Dean Edwards" - // Android Browser returns percentage for some values, - // but width seems to be reliably pixels. - // This is against the CSSOM draft spec: - // https://drafts.csswg.org/cssom/#resolved-values - if ( !support.pixelMarginRight() && rnumnonpx.test( ret ) && rmargin.test( name ) ) { - - // Remember the original values - width = style.width; - minWidth = style.minWidth; - maxWidth = style.maxWidth; - - // Put in the new values to get a computed value out - style.minWidth = style.maxWidth = style.width = ret; - ret = computed.width; - - // Revert the changed values - style.width = width; - style.minWidth = minWidth; - style.maxWidth = maxWidth; - } - } - - return ret !== undefined ? - - // Support: IE <=9 - 11 only - // IE returns zIndex value as an integer. - ret + "" : - ret; -} - - -function addGetHookIf( conditionFn, hookFn ) { - - // Define the hook, we'll check on the first run if it's really needed. - return { - get: function() { - if ( conditionFn() ) { - - // Hook not needed (or it's not possible to use it due - // to missing dependency), remove it. - delete this.get; - return; - } - - // Hook needed; redefine it so that the support test is not executed again. - return ( this.get = hookFn ).apply( this, arguments ); - } - }; -} - - -var - - // Swappable if display is none or starts with table - // except "table", "table-cell", or "table-caption" - // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display - rdisplayswap = /^(none|table(?!-c[ea]).+)/, - rcustomProp = /^--/, - cssShow = { position: "absolute", visibility: "hidden", display: "block" }, - cssNormalTransform = { - letterSpacing: "0", - fontWeight: "400" - }, - - cssPrefixes = [ "Webkit", "Moz", "ms" ], - emptyStyle = document.createElement( "div" ).style; - -// Return a css property mapped to a potentially vendor prefixed property -function vendorPropName( name ) { - - // Shortcut for names that are not vendor prefixed - if ( name in emptyStyle ) { - return name; - } - - // Check for vendor prefixed names - var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), - i = cssPrefixes.length; - - while ( i-- ) { - name = cssPrefixes[ i ] + capName; - if ( name in emptyStyle ) { - return name; - } - } -} - -// Return a property mapped along what jQuery.cssProps suggests or to -// a vendor prefixed property. -function finalPropName( name ) { - var ret = jQuery.cssProps[ name ]; - if ( !ret ) { - ret = jQuery.cssProps[ name ] = vendorPropName( name ) || name; - } - return ret; -} - -function setPositiveNumber( elem, value, subtract ) { - - // Any relative (+/-) values have already been - // normalized at this point - var matches = rcssNum.exec( value ); - return matches ? - - // Guard against undefined "subtract", e.g., when used as in cssHooks - Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : - value; -} - -function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { - var i, - val = 0; - - // If we already have the right measurement, avoid augmentation - if ( extra === ( isBorderBox ? "border" : "content" ) ) { - i = 4; - - // Otherwise initialize for horizontal or vertical properties - } else { - i = name === "width" ? 1 : 0; - } - - for ( ; i < 4; i += 2 ) { - - // Both box models exclude margin, so add it if we want it - if ( extra === "margin" ) { - val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); - } - - if ( isBorderBox ) { - - // border-box includes padding, so remove it if we want content - if ( extra === "content" ) { - val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); - } - - // At this point, extra isn't border nor margin, so remove border - if ( extra !== "margin" ) { - val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - } - } else { - - // At this point, extra isn't content, so add padding - val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); - - // At this point, extra isn't content nor padding, so add border - if ( extra !== "padding" ) { - val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - } - } - } - - return val; -} - -function getWidthOrHeight( elem, name, extra ) { - - // Start with computed style - var valueIsBorderBox, - styles = getStyles( elem ), - val = curCSS( elem, name, styles ), - isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; - - // Computed unit is not pixels. Stop here and return. - if ( rnumnonpx.test( val ) ) { - return val; - } - - // Check for style in case a browser which returns unreliable values - // for getComputedStyle silently falls back to the reliable elem.style - valueIsBorderBox = isBorderBox && - ( support.boxSizingReliable() || val === elem.style[ name ] ); - - // Fall back to offsetWidth/Height when value is "auto" - // This happens for inline elements with no explicit setting (gh-3571) - if ( val === "auto" ) { - val = elem[ "offset" + name[ 0 ].toUpperCase() + name.slice( 1 ) ]; - } - - // Normalize "", auto, and prepare for extra - val = parseFloat( val ) || 0; - - // Use the active box-sizing model to add/subtract irrelevant styles - return ( val + - augmentWidthOrHeight( - elem, - name, - extra || ( isBorderBox ? "border" : "content" ), - valueIsBorderBox, - styles - ) - ) + "px"; -} - -jQuery.extend( { - - // Add in style property hooks for overriding the default - // behavior of getting and setting a style property - cssHooks: { - opacity: { - get: function( elem, computed ) { - if ( computed ) { - - // We should always get a number back from opacity - var ret = curCSS( elem, "opacity" ); - return ret === "" ? "1" : ret; - } - } - } - }, - - // Don't automatically add "px" to these possibly-unitless properties - cssNumber: { - "animationIterationCount": true, - "columnCount": true, - "fillOpacity": true, - "flexGrow": true, - "flexShrink": true, - "fontWeight": true, - "lineHeight": true, - "opacity": true, - "order": true, - "orphans": true, - "widows": true, - "zIndex": true, - "zoom": true - }, - - // Add in properties whose names you wish to fix before - // setting or getting the value - cssProps: { - "float": "cssFloat" - }, - - // Get and set the style property on a DOM Node - style: function( elem, name, value, extra ) { - - // Don't set styles on text and comment nodes - if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { - return; - } - - // Make sure that we're working with the right name - var ret, type, hooks, - origName = jQuery.camelCase( name ), - isCustomProp = rcustomProp.test( name ), - style = elem.style; - - // Make sure that we're working with the right name. We don't - // want to query the value if it is a CSS custom property - // since they are user-defined. - if ( !isCustomProp ) { - name = finalPropName( origName ); - } - - // Gets hook for the prefixed version, then unprefixed version - hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; - - // Check if we're setting a value - if ( value !== undefined ) { - type = typeof value; - - // Convert "+=" or "-=" to relative numbers (#7345) - if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { - value = adjustCSS( elem, name, ret ); - - // Fixes bug #9237 - type = "number"; - } - - // Make sure that null and NaN values aren't set (#7116) - if ( value == null || value !== value ) { - return; - } - - // If a number was passed in, add the unit (except for certain CSS properties) - if ( type === "number" ) { - value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); - } - - // background-* props affect original clone's values - if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { - style[ name ] = "inherit"; - } - - // If a hook was provided, use that value, otherwise just set the specified value - if ( !hooks || !( "set" in hooks ) || - ( value = hooks.set( elem, value, extra ) ) !== undefined ) { - - if ( isCustomProp ) { - style.setProperty( name, value ); - } else { - style[ name ] = value; - } - } - - } else { - - // If a hook was provided get the non-computed value from there - if ( hooks && "get" in hooks && - ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { - - return ret; - } - - // Otherwise just get the value from the style object - return style[ name ]; - } - }, - - css: function( elem, name, extra, styles ) { - var val, num, hooks, - origName = jQuery.camelCase( name ), - isCustomProp = rcustomProp.test( name ); - - // Make sure that we're working with the right name. We don't - // want to modify the value if it is a CSS custom property - // since they are user-defined. - if ( !isCustomProp ) { - name = finalPropName( origName ); - } - - // Try prefixed name followed by the unprefixed name - hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; - - // If a hook was provided get the computed value from there - if ( hooks && "get" in hooks ) { - val = hooks.get( elem, true, extra ); - } - - // Otherwise, if a way to get the computed value exists, use that - if ( val === undefined ) { - val = curCSS( elem, name, styles ); - } - - // Convert "normal" to computed value - if ( val === "normal" && name in cssNormalTransform ) { - val = cssNormalTransform[ name ]; - } - - // Make numeric if forced or a qualifier was provided and val looks numeric - if ( extra === "" || extra ) { - num = parseFloat( val ); - return extra === true || isFinite( num ) ? num || 0 : val; - } - - return val; - } -} ); - -jQuery.each( [ "height", "width" ], function( i, name ) { - jQuery.cssHooks[ name ] = { - get: function( elem, computed, extra ) { - if ( computed ) { - - // Certain elements can have dimension info if we invisibly show them - // but it must have a current display style that would benefit - return rdisplayswap.test( jQuery.css( elem, "display" ) ) && - - // Support: Safari 8+ - // Table columns in Safari have non-zero offsetWidth & zero - // getBoundingClientRect().width unless display is changed. - // Support: IE <=11 only - // Running getBoundingClientRect on a disconnected node - // in IE throws an error. - ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? - swap( elem, cssShow, function() { - return getWidthOrHeight( elem, name, extra ); - } ) : - getWidthOrHeight( elem, name, extra ); - } - }, - - set: function( elem, value, extra ) { - var matches, - styles = extra && getStyles( elem ), - subtract = extra && augmentWidthOrHeight( - elem, - name, - extra, - jQuery.css( elem, "boxSizing", false, styles ) === "border-box", - styles - ); - - // Convert to pixels if value adjustment is needed - if ( subtract && ( matches = rcssNum.exec( value ) ) && - ( matches[ 3 ] || "px" ) !== "px" ) { - - elem.style[ name ] = value; - value = jQuery.css( elem, name ); - } - - return setPositiveNumber( elem, value, subtract ); - } - }; -} ); - -jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, - function( elem, computed ) { - if ( computed ) { - return ( parseFloat( curCSS( elem, "marginLeft" ) ) || - elem.getBoundingClientRect().left - - swap( elem, { marginLeft: 0 }, function() { - return elem.getBoundingClientRect().left; - } ) - ) + "px"; - } - } -); - -// These hooks are used by animate to expand properties -jQuery.each( { - margin: "", - padding: "", - border: "Width" -}, function( prefix, suffix ) { - jQuery.cssHooks[ prefix + suffix ] = { - expand: function( value ) { - var i = 0, - expanded = {}, - - // Assumes a single number if not a string - parts = typeof value === "string" ? value.split( " " ) : [ value ]; - - for ( ; i < 4; i++ ) { - expanded[ prefix + cssExpand[ i ] + suffix ] = - parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; - } - - return expanded; - } - }; - - if ( !rmargin.test( prefix ) ) { - jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; - } -} ); - -jQuery.fn.extend( { - css: function( name, value ) { - return access( this, function( elem, name, value ) { - var styles, len, - map = {}, - i = 0; - - if ( Array.isArray( name ) ) { - styles = getStyles( elem ); - len = name.length; - - for ( ; i < len; i++ ) { - map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); - } - - return map; - } - - return value !== undefined ? - jQuery.style( elem, name, value ) : - jQuery.css( elem, name ); - }, name, value, arguments.length > 1 ); - } -} ); - - -function Tween( elem, options, prop, end, easing ) { - return new Tween.prototype.init( elem, options, prop, end, easing ); -} -jQuery.Tween = Tween; - -Tween.prototype = { - constructor: Tween, - init: function( elem, options, prop, end, easing, unit ) { - this.elem = elem; - this.prop = prop; - this.easing = easing || jQuery.easing._default; - this.options = options; - this.start = this.now = this.cur(); - this.end = end; - this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); - }, - cur: function() { - var hooks = Tween.propHooks[ this.prop ]; - - return hooks && hooks.get ? - hooks.get( this ) : - Tween.propHooks._default.get( this ); - }, - run: function( percent ) { - var eased, - hooks = Tween.propHooks[ this.prop ]; - - if ( this.options.duration ) { - this.pos = eased = jQuery.easing[ this.easing ]( - percent, this.options.duration * percent, 0, 1, this.options.duration - ); - } else { - this.pos = eased = percent; - } - this.now = ( this.end - this.start ) * eased + this.start; - - if ( this.options.step ) { - this.options.step.call( this.elem, this.now, this ); - } - - if ( hooks && hooks.set ) { - hooks.set( this ); - } else { - Tween.propHooks._default.set( this ); - } - return this; - } -}; - -Tween.prototype.init.prototype = Tween.prototype; - -Tween.propHooks = { - _default: { - get: function( tween ) { - var result; - - // Use a property on the element directly when it is not a DOM element, - // or when there is no matching style property that exists. - if ( tween.elem.nodeType !== 1 || - tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { - return tween.elem[ tween.prop ]; - } - - // Passing an empty string as a 3rd parameter to .css will automatically - // attempt a parseFloat and fallback to a string if the parse fails. - // Simple values such as "10px" are parsed to Float; - // complex values such as "rotate(1rad)" are returned as-is. - result = jQuery.css( tween.elem, tween.prop, "" ); - - // Empty strings, null, undefined and "auto" are converted to 0. - return !result || result === "auto" ? 0 : result; - }, - set: function( tween ) { - - // Use step hook for back compat. - // Use cssHook if its there. - // Use .style if available and use plain properties where available. - if ( jQuery.fx.step[ tween.prop ] ) { - jQuery.fx.step[ tween.prop ]( tween ); - } else if ( tween.elem.nodeType === 1 && - ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || - jQuery.cssHooks[ tween.prop ] ) ) { - jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); - } else { - tween.elem[ tween.prop ] = tween.now; - } - } - } -}; - -// Support: IE <=9 only -// Panic based approach to setting things on disconnected nodes -Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { - set: function( tween ) { - if ( tween.elem.nodeType && tween.elem.parentNode ) { - tween.elem[ tween.prop ] = tween.now; - } - } -}; - -jQuery.easing = { - linear: function( p ) { - return p; - }, - swing: function( p ) { - return 0.5 - Math.cos( p * Math.PI ) / 2; - }, - _default: "swing" -}; - -jQuery.fx = Tween.prototype.init; - -// Back compat <1.8 extension point -jQuery.fx.step = {}; - - - - -var - fxNow, inProgress, - rfxtypes = /^(?:toggle|show|hide)$/, - rrun = /queueHooks$/; - -function schedule() { - if ( inProgress ) { - if ( document.hidden === false && window.requestAnimationFrame ) { - window.requestAnimationFrame( schedule ); - } else { - window.setTimeout( schedule, jQuery.fx.interval ); - } - - jQuery.fx.tick(); - } -} - -// Animations created synchronously will run synchronously -function createFxNow() { - window.setTimeout( function() { - fxNow = undefined; - } ); - return ( fxNow = jQuery.now() ); -} - -// Generate parameters to create a standard animation -function genFx( type, includeWidth ) { - var which, - i = 0, - attrs = { height: type }; - - // If we include width, step value is 1 to do all cssExpand values, - // otherwise step value is 2 to skip over Left and Right - includeWidth = includeWidth ? 1 : 0; - for ( ; i < 4; i += 2 - includeWidth ) { - which = cssExpand[ i ]; - attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; - } - - if ( includeWidth ) { - attrs.opacity = attrs.width = type; - } - - return attrs; -} - -function createTween( value, prop, animation ) { - var tween, - collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), - index = 0, - length = collection.length; - for ( ; index < length; index++ ) { - if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { - - // We're done with this property - return tween; - } - } -} - -function defaultPrefilter( elem, props, opts ) { - var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, - isBox = "width" in props || "height" in props, - anim = this, - orig = {}, - style = elem.style, - hidden = elem.nodeType && isHiddenWithinTree( elem ), - dataShow = dataPriv.get( elem, "fxshow" ); - - // Queue-skipping animations hijack the fx hooks - if ( !opts.queue ) { - hooks = jQuery._queueHooks( elem, "fx" ); - if ( hooks.unqueued == null ) { - hooks.unqueued = 0; - oldfire = hooks.empty.fire; - hooks.empty.fire = function() { - if ( !hooks.unqueued ) { - oldfire(); - } - }; - } - hooks.unqueued++; - - anim.always( function() { - - // Ensure the complete handler is called before this completes - anim.always( function() { - hooks.unqueued--; - if ( !jQuery.queue( elem, "fx" ).length ) { - hooks.empty.fire(); - } - } ); - } ); - } - - // Detect show/hide animations - for ( prop in props ) { - value = props[ prop ]; - if ( rfxtypes.test( value ) ) { - delete props[ prop ]; - toggle = toggle || value === "toggle"; - if ( value === ( hidden ? "hide" : "show" ) ) { - - // Pretend to be hidden if this is a "show" and - // there is still data from a stopped show/hide - if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { - hidden = true; - - // Ignore all other no-op show/hide data - } else { - continue; - } - } - orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); - } - } - - // Bail out if this is a no-op like .hide().hide() - propTween = !jQuery.isEmptyObject( props ); - if ( !propTween && jQuery.isEmptyObject( orig ) ) { - return; - } - - // Restrict "overflow" and "display" styles during box animations - if ( isBox && elem.nodeType === 1 ) { - - // Support: IE <=9 - 11, Edge 12 - 13 - // Record all 3 overflow attributes because IE does not infer the shorthand - // from identically-valued overflowX and overflowY - opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; - - // Identify a display type, preferring old show/hide data over the CSS cascade - restoreDisplay = dataShow && dataShow.display; - if ( restoreDisplay == null ) { - restoreDisplay = dataPriv.get( elem, "display" ); - } - display = jQuery.css( elem, "display" ); - if ( display === "none" ) { - if ( restoreDisplay ) { - display = restoreDisplay; - } else { - - // Get nonempty value(s) by temporarily forcing visibility - showHide( [ elem ], true ); - restoreDisplay = elem.style.display || restoreDisplay; - display = jQuery.css( elem, "display" ); - showHide( [ elem ] ); - } - } - - // Animate inline elements as inline-block - if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { - if ( jQuery.css( elem, "float" ) === "none" ) { - - // Restore the original display value at the end of pure show/hide animations - if ( !propTween ) { - anim.done( function() { - style.display = restoreDisplay; - } ); - if ( restoreDisplay == null ) { - display = style.display; - restoreDisplay = display === "none" ? "" : display; - } - } - style.display = "inline-block"; - } - } - } - - if ( opts.overflow ) { - style.overflow = "hidden"; - anim.always( function() { - style.overflow = opts.overflow[ 0 ]; - style.overflowX = opts.overflow[ 1 ]; - style.overflowY = opts.overflow[ 2 ]; - } ); - } - - // Implement show/hide animations - propTween = false; - for ( prop in orig ) { - - // General show/hide setup for this element animation - if ( !propTween ) { - if ( dataShow ) { - if ( "hidden" in dataShow ) { - hidden = dataShow.hidden; - } - } else { - dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); - } - - // Store hidden/visible for toggle so `.stop().toggle()` "reverses" - if ( toggle ) { - dataShow.hidden = !hidden; - } - - // Show elements before animating them - if ( hidden ) { - showHide( [ elem ], true ); - } - - /* eslint-disable no-loop-func */ - - anim.done( function() { - - /* eslint-enable no-loop-func */ - - // The final step of a "hide" animation is actually hiding the element - if ( !hidden ) { - showHide( [ elem ] ); - } - dataPriv.remove( elem, "fxshow" ); - for ( prop in orig ) { - jQuery.style( elem, prop, orig[ prop ] ); - } - } ); - } - - // Per-property setup - propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); - if ( !( prop in dataShow ) ) { - dataShow[ prop ] = propTween.start; - if ( hidden ) { - propTween.end = propTween.start; - propTween.start = 0; - } - } - } -} - -function propFilter( props, specialEasing ) { - var index, name, easing, value, hooks; - - // camelCase, specialEasing and expand cssHook pass - for ( index in props ) { - name = jQuery.camelCase( index ); - easing = specialEasing[ name ]; - value = props[ index ]; - if ( Array.isArray( value ) ) { - easing = value[ 1 ]; - value = props[ index ] = value[ 0 ]; - } - - if ( index !== name ) { - props[ name ] = value; - delete props[ index ]; - } - - hooks = jQuery.cssHooks[ name ]; - if ( hooks && "expand" in hooks ) { - value = hooks.expand( value ); - delete props[ name ]; - - // Not quite $.extend, this won't overwrite existing keys. - // Reusing 'index' because we have the correct "name" - for ( index in value ) { - if ( !( index in props ) ) { - props[ index ] = value[ index ]; - specialEasing[ index ] = easing; - } - } - } else { - specialEasing[ name ] = easing; - } - } -} - -function Animation( elem, properties, options ) { - var result, - stopped, - index = 0, - length = Animation.prefilters.length, - deferred = jQuery.Deferred().always( function() { - - // Don't match elem in the :animated selector - delete tick.elem; - } ), - tick = function() { - if ( stopped ) { - return false; - } - var currentTime = fxNow || createFxNow(), - remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), - - // Support: Android 2.3 only - // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) - temp = remaining / animation.duration || 0, - percent = 1 - temp, - index = 0, - length = animation.tweens.length; - - for ( ; index < length; index++ ) { - animation.tweens[ index ].run( percent ); - } - - deferred.notifyWith( elem, [ animation, percent, remaining ] ); - - // If there's more to do, yield - if ( percent < 1 && length ) { - return remaining; - } - - // If this was an empty animation, synthesize a final progress notification - if ( !length ) { - deferred.notifyWith( elem, [ animation, 1, 0 ] ); - } - - // Resolve the animation and report its conclusion - deferred.resolveWith( elem, [ animation ] ); - return false; - }, - animation = deferred.promise( { - elem: elem, - props: jQuery.extend( {}, properties ), - opts: jQuery.extend( true, { - specialEasing: {}, - easing: jQuery.easing._default - }, options ), - originalProperties: properties, - originalOptions: options, - startTime: fxNow || createFxNow(), - duration: options.duration, - tweens: [], - createTween: function( prop, end ) { - var tween = jQuery.Tween( elem, animation.opts, prop, end, - animation.opts.specialEasing[ prop ] || animation.opts.easing ); - animation.tweens.push( tween ); - return tween; - }, - stop: function( gotoEnd ) { - var index = 0, - - // If we are going to the end, we want to run all the tweens - // otherwise we skip this part - length = gotoEnd ? animation.tweens.length : 0; - if ( stopped ) { - return this; - } - stopped = true; - for ( ; index < length; index++ ) { - animation.tweens[ index ].run( 1 ); - } - - // Resolve when we played the last frame; otherwise, reject - if ( gotoEnd ) { - deferred.notifyWith( elem, [ animation, 1, 0 ] ); - deferred.resolveWith( elem, [ animation, gotoEnd ] ); - } else { - deferred.rejectWith( elem, [ animation, gotoEnd ] ); - } - return this; - } - } ), - props = animation.props; - - propFilter( props, animation.opts.specialEasing ); - - for ( ; index < length; index++ ) { - result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); - if ( result ) { - if ( jQuery.isFunction( result.stop ) ) { - jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = - jQuery.proxy( result.stop, result ); - } - return result; - } - } - - jQuery.map( props, createTween, animation ); - - if ( jQuery.isFunction( animation.opts.start ) ) { - animation.opts.start.call( elem, animation ); - } - - // Attach callbacks from options - animation - .progress( animation.opts.progress ) - .done( animation.opts.done, animation.opts.complete ) - .fail( animation.opts.fail ) - .always( animation.opts.always ); - - jQuery.fx.timer( - jQuery.extend( tick, { - elem: elem, - anim: animation, - queue: animation.opts.queue - } ) - ); - - return animation; -} - -jQuery.Animation = jQuery.extend( Animation, { - - tweeners: { - "*": [ function( prop, value ) { - var tween = this.createTween( prop, value ); - adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); - return tween; - } ] - }, - - tweener: function( props, callback ) { - if ( jQuery.isFunction( props ) ) { - callback = props; - props = [ "*" ]; - } else { - props = props.match( rnothtmlwhite ); - } - - var prop, - index = 0, - length = props.length; - - for ( ; index < length; index++ ) { - prop = props[ index ]; - Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; - Animation.tweeners[ prop ].unshift( callback ); - } - }, - - prefilters: [ defaultPrefilter ], - - prefilter: function( callback, prepend ) { - if ( prepend ) { - Animation.prefilters.unshift( callback ); - } else { - Animation.prefilters.push( callback ); - } - } -} ); - -jQuery.speed = function( speed, easing, fn ) { - var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { - complete: fn || !fn && easing || - jQuery.isFunction( speed ) && speed, - duration: speed, - easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing - }; - - // Go to the end state if fx are off - if ( jQuery.fx.off ) { - opt.duration = 0; - - } else { - if ( typeof opt.duration !== "number" ) { - if ( opt.duration in jQuery.fx.speeds ) { - opt.duration = jQuery.fx.speeds[ opt.duration ]; - - } else { - opt.duration = jQuery.fx.speeds._default; - } - } - } - - // Normalize opt.queue - true/undefined/null -> "fx" - if ( opt.queue == null || opt.queue === true ) { - opt.queue = "fx"; - } - - // Queueing - opt.old = opt.complete; - - opt.complete = function() { - if ( jQuery.isFunction( opt.old ) ) { - opt.old.call( this ); - } - - if ( opt.queue ) { - jQuery.dequeue( this, opt.queue ); - } - }; - - return opt; -}; - -jQuery.fn.extend( { - fadeTo: function( speed, to, easing, callback ) { - - // Show any hidden elements after setting opacity to 0 - return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() - - // Animate to the value specified - .end().animate( { opacity: to }, speed, easing, callback ); - }, - animate: function( prop, speed, easing, callback ) { - var empty = jQuery.isEmptyObject( prop ), - optall = jQuery.speed( speed, easing, callback ), - doAnimation = function() { - - // Operate on a copy of prop so per-property easing won't be lost - var anim = Animation( this, jQuery.extend( {}, prop ), optall ); - - // Empty animations, or finishing resolves immediately - if ( empty || dataPriv.get( this, "finish" ) ) { - anim.stop( true ); - } - }; - doAnimation.finish = doAnimation; - - return empty || optall.queue === false ? - this.each( doAnimation ) : - this.queue( optall.queue, doAnimation ); - }, - stop: function( type, clearQueue, gotoEnd ) { - var stopQueue = function( hooks ) { - var stop = hooks.stop; - delete hooks.stop; - stop( gotoEnd ); - }; - - if ( typeof type !== "string" ) { - gotoEnd = clearQueue; - clearQueue = type; - type = undefined; - } - if ( clearQueue && type !== false ) { - this.queue( type || "fx", [] ); - } - - return this.each( function() { - var dequeue = true, - index = type != null && type + "queueHooks", - timers = jQuery.timers, - data = dataPriv.get( this ); - - if ( index ) { - if ( data[ index ] && data[ index ].stop ) { - stopQueue( data[ index ] ); - } - } else { - for ( index in data ) { - if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { - stopQueue( data[ index ] ); - } - } - } - - for ( index = timers.length; index--; ) { - if ( timers[ index ].elem === this && - ( type == null || timers[ index ].queue === type ) ) { - - timers[ index ].anim.stop( gotoEnd ); - dequeue = false; - timers.splice( index, 1 ); - } - } - - // Start the next in the queue if the last step wasn't forced. - // Timers currently will call their complete callbacks, which - // will dequeue but only if they were gotoEnd. - if ( dequeue || !gotoEnd ) { - jQuery.dequeue( this, type ); - } - } ); - }, - finish: function( type ) { - if ( type !== false ) { - type = type || "fx"; - } - return this.each( function() { - var index, - data = dataPriv.get( this ), - queue = data[ type + "queue" ], - hooks = data[ type + "queueHooks" ], - timers = jQuery.timers, - length = queue ? queue.length : 0; - - // Enable finishing flag on private data - data.finish = true; - - // Empty the queue first - jQuery.queue( this, type, [] ); - - if ( hooks && hooks.stop ) { - hooks.stop.call( this, true ); - } - - // Look for any active animations, and finish them - for ( index = timers.length; index--; ) { - if ( timers[ index ].elem === this && timers[ index ].queue === type ) { - timers[ index ].anim.stop( true ); - timers.splice( index, 1 ); - } - } - - // Look for any animations in the old queue and finish them - for ( index = 0; index < length; index++ ) { - if ( queue[ index ] && queue[ index ].finish ) { - queue[ index ].finish.call( this ); - } - } - - // Turn off finishing flag - delete data.finish; - } ); - } -} ); - -jQuery.each( [ "toggle", "show", "hide" ], function( i, name ) { - var cssFn = jQuery.fn[ name ]; - jQuery.fn[ name ] = function( speed, easing, callback ) { - return speed == null || typeof speed === "boolean" ? - cssFn.apply( this, arguments ) : - this.animate( genFx( name, true ), speed, easing, callback ); - }; -} ); - -// Generate shortcuts for custom animations -jQuery.each( { - slideDown: genFx( "show" ), - slideUp: genFx( "hide" ), - slideToggle: genFx( "toggle" ), - fadeIn: { opacity: "show" }, - fadeOut: { opacity: "hide" }, - fadeToggle: { opacity: "toggle" } -}, function( name, props ) { - jQuery.fn[ name ] = function( speed, easing, callback ) { - return this.animate( props, speed, easing, callback ); - }; -} ); - -jQuery.timers = []; -jQuery.fx.tick = function() { - var timer, - i = 0, - timers = jQuery.timers; - - fxNow = jQuery.now(); - - for ( ; i < timers.length; i++ ) { - timer = timers[ i ]; - - // Run the timer and safely remove it when done (allowing for external removal) - if ( !timer() && timers[ i ] === timer ) { - timers.splice( i--, 1 ); - } - } - - if ( !timers.length ) { - jQuery.fx.stop(); - } - fxNow = undefined; -}; - -jQuery.fx.timer = function( timer ) { - jQuery.timers.push( timer ); - jQuery.fx.start(); -}; - -jQuery.fx.interval = 13; -jQuery.fx.start = function() { - if ( inProgress ) { - return; - } - - inProgress = true; - schedule(); -}; - -jQuery.fx.stop = function() { - inProgress = null; -}; - -jQuery.fx.speeds = { - slow: 600, - fast: 200, - - // Default speed - _default: 400 -}; - - -// Based off of the plugin by Clint Helfers, with permission. -// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ -jQuery.fn.delay = function( time, type ) { - time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; - type = type || "fx"; - - return this.queue( type, function( next, hooks ) { - var timeout = window.setTimeout( next, time ); - hooks.stop = function() { - window.clearTimeout( timeout ); - }; - } ); -}; - - -( function() { - var input = document.createElement( "input" ), - select = document.createElement( "select" ), - opt = select.appendChild( document.createElement( "option" ) ); - - input.type = "checkbox"; - - // Support: Android <=4.3 only - // Default value for a checkbox should be "on" - support.checkOn = input.value !== ""; - - // Support: IE <=11 only - // Must access selectedIndex to make default options select - support.optSelected = opt.selected; - - // Support: IE <=11 only - // An input loses its value after becoming a radio - input = document.createElement( "input" ); - input.value = "t"; - input.type = "radio"; - support.radioValue = input.value === "t"; -} )(); - - -var boolHook, - attrHandle = jQuery.expr.attrHandle; - -jQuery.fn.extend( { - attr: function( name, value ) { - return access( this, jQuery.attr, name, value, arguments.length > 1 ); - }, - - removeAttr: function( name ) { - return this.each( function() { - jQuery.removeAttr( this, name ); - } ); - } -} ); - -jQuery.extend( { - attr: function( elem, name, value ) { - var ret, hooks, - nType = elem.nodeType; - - // Don't get/set attributes on text, comment and attribute nodes - if ( nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - // Fallback to prop when attributes are not supported - if ( typeof elem.getAttribute === "undefined" ) { - return jQuery.prop( elem, name, value ); - } - - // Attribute hooks are determined by the lowercase version - // Grab necessary hook if one is defined - if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { - hooks = jQuery.attrHooks[ name.toLowerCase() ] || - ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); - } - - if ( value !== undefined ) { - if ( value === null ) { - jQuery.removeAttr( elem, name ); - return; - } - - if ( hooks && "set" in hooks && - ( ret = hooks.set( elem, value, name ) ) !== undefined ) { - return ret; - } - - elem.setAttribute( name, value + "" ); - return value; - } - - if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { - return ret; - } - - ret = jQuery.find.attr( elem, name ); - - // Non-existent attributes return null, we normalize to undefined - return ret == null ? undefined : ret; - }, - - attrHooks: { - type: { - set: function( elem, value ) { - if ( !support.radioValue && value === "radio" && - nodeName( elem, "input" ) ) { - var val = elem.value; - elem.setAttribute( "type", value ); - if ( val ) { - elem.value = val; - } - return value; - } - } - } - }, - - removeAttr: function( elem, value ) { - var name, - i = 0, - - // Attribute names can contain non-HTML whitespace characters - // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 - attrNames = value && value.match( rnothtmlwhite ); - - if ( attrNames && elem.nodeType === 1 ) { - while ( ( name = attrNames[ i++ ] ) ) { - elem.removeAttribute( name ); - } - } - } -} ); - -// Hooks for boolean attributes -boolHook = { - set: function( elem, value, name ) { - if ( value === false ) { - - // Remove boolean attributes when set to false - jQuery.removeAttr( elem, name ); - } else { - elem.setAttribute( name, name ); - } - return name; - } -}; - -jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { - var getter = attrHandle[ name ] || jQuery.find.attr; - - attrHandle[ name ] = function( elem, name, isXML ) { - var ret, handle, - lowercaseName = name.toLowerCase(); - - if ( !isXML ) { - - // Avoid an infinite loop by temporarily removing this function from the getter - handle = attrHandle[ lowercaseName ]; - attrHandle[ lowercaseName ] = ret; - ret = getter( elem, name, isXML ) != null ? - lowercaseName : - null; - attrHandle[ lowercaseName ] = handle; - } - return ret; - }; -} ); - - - - -var rfocusable = /^(?:input|select|textarea|button)$/i, - rclickable = /^(?:a|area)$/i; - -jQuery.fn.extend( { - prop: function( name, value ) { - return access( this, jQuery.prop, name, value, arguments.length > 1 ); - }, - - removeProp: function( name ) { - return this.each( function() { - delete this[ jQuery.propFix[ name ] || name ]; - } ); - } -} ); - -jQuery.extend( { - prop: function( elem, name, value ) { - var ret, hooks, - nType = elem.nodeType; - - // Don't get/set properties on text, comment and attribute nodes - if ( nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { - - // Fix name and attach hooks - name = jQuery.propFix[ name ] || name; - hooks = jQuery.propHooks[ name ]; - } - - if ( value !== undefined ) { - if ( hooks && "set" in hooks && - ( ret = hooks.set( elem, value, name ) ) !== undefined ) { - return ret; - } - - return ( elem[ name ] = value ); - } - - if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { - return ret; - } - - return elem[ name ]; - }, - - propHooks: { - tabIndex: { - get: function( elem ) { - - // Support: IE <=9 - 11 only - // elem.tabIndex doesn't always return the - // correct value when it hasn't been explicitly set - // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ - // Use proper attribute retrieval(#12072) - var tabindex = jQuery.find.attr( elem, "tabindex" ); - - if ( tabindex ) { - return parseInt( tabindex, 10 ); - } - - if ( - rfocusable.test( elem.nodeName ) || - rclickable.test( elem.nodeName ) && - elem.href - ) { - return 0; - } - - return -1; - } - } - }, - - propFix: { - "for": "htmlFor", - "class": "className" - } -} ); - -// Support: IE <=11 only -// Accessing the selectedIndex property -// forces the browser to respect setting selected -// on the option -// The getter ensures a default option is selected -// when in an optgroup -// eslint rule "no-unused-expressions" is disabled for this code -// since it considers such accessions noop -if ( !support.optSelected ) { - jQuery.propHooks.selected = { - get: function( elem ) { - - /* eslint no-unused-expressions: "off" */ - - var parent = elem.parentNode; - if ( parent && parent.parentNode ) { - parent.parentNode.selectedIndex; - } - return null; - }, - set: function( elem ) { - - /* eslint no-unused-expressions: "off" */ - - var parent = elem.parentNode; - if ( parent ) { - parent.selectedIndex; - - if ( parent.parentNode ) { - parent.parentNode.selectedIndex; - } - } - } - }; -} - -jQuery.each( [ - "tabIndex", - "readOnly", - "maxLength", - "cellSpacing", - "cellPadding", - "rowSpan", - "colSpan", - "useMap", - "frameBorder", - "contentEditable" -], function() { - jQuery.propFix[ this.toLowerCase() ] = this; -} ); - - - - - // Strip and collapse whitespace according to HTML spec - // https://html.spec.whatwg.org/multipage/infrastructure.html#strip-and-collapse-whitespace - function stripAndCollapse( value ) { - var tokens = value.match( rnothtmlwhite ) || []; - return tokens.join( " " ); - } - - -function getClass( elem ) { - return elem.getAttribute && elem.getAttribute( "class" ) || ""; -} - -jQuery.fn.extend( { - addClass: function( value ) { - var classes, elem, cur, curValue, clazz, j, finalValue, - i = 0; - - if ( jQuery.isFunction( value ) ) { - return this.each( function( j ) { - jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); - } ); - } - - if ( typeof value === "string" && value ) { - classes = value.match( rnothtmlwhite ) || []; - - while ( ( elem = this[ i++ ] ) ) { - curValue = getClass( elem ); - cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); - - if ( cur ) { - j = 0; - while ( ( clazz = classes[ j++ ] ) ) { - if ( cur.indexOf( " " + clazz + " " ) < 0 ) { - cur += clazz + " "; - } - } - - // Only assign if different to avoid unneeded rendering. - finalValue = stripAndCollapse( cur ); - if ( curValue !== finalValue ) { - elem.setAttribute( "class", finalValue ); - } - } - } - } - - return this; - }, - - removeClass: function( value ) { - var classes, elem, cur, curValue, clazz, j, finalValue, - i = 0; - - if ( jQuery.isFunction( value ) ) { - return this.each( function( j ) { - jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); - } ); - } - - if ( !arguments.length ) { - return this.attr( "class", "" ); - } - - if ( typeof value === "string" && value ) { - classes = value.match( rnothtmlwhite ) || []; - - while ( ( elem = this[ i++ ] ) ) { - curValue = getClass( elem ); - - // This expression is here for better compressibility (see addClass) - cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); - - if ( cur ) { - j = 0; - while ( ( clazz = classes[ j++ ] ) ) { - - // Remove *all* instances - while ( cur.indexOf( " " + clazz + " " ) > -1 ) { - cur = cur.replace( " " + clazz + " ", " " ); - } - } - - // Only assign if different to avoid unneeded rendering. - finalValue = stripAndCollapse( cur ); - if ( curValue !== finalValue ) { - elem.setAttribute( "class", finalValue ); - } - } - } - } - - return this; - }, - - toggleClass: function( value, stateVal ) { - var type = typeof value; - - if ( typeof stateVal === "boolean" && type === "string" ) { - return stateVal ? this.addClass( value ) : this.removeClass( value ); - } - - if ( jQuery.isFunction( value ) ) { - return this.each( function( i ) { - jQuery( this ).toggleClass( - value.call( this, i, getClass( this ), stateVal ), - stateVal - ); - } ); - } - - return this.each( function() { - var className, i, self, classNames; - - if ( type === "string" ) { - - // Toggle individual class names - i = 0; - self = jQuery( this ); - classNames = value.match( rnothtmlwhite ) || []; - - while ( ( className = classNames[ i++ ] ) ) { - - // Check each className given, space separated list - if ( self.hasClass( className ) ) { - self.removeClass( className ); - } else { - self.addClass( className ); - } - } - - // Toggle whole class name - } else if ( value === undefined || type === "boolean" ) { - className = getClass( this ); - if ( className ) { - - // Store className if set - dataPriv.set( this, "__className__", className ); - } - - // If the element has a class name or if we're passed `false`, - // then remove the whole classname (if there was one, the above saved it). - // Otherwise bring back whatever was previously saved (if anything), - // falling back to the empty string if nothing was stored. - if ( this.setAttribute ) { - this.setAttribute( "class", - className || value === false ? - "" : - dataPriv.get( this, "__className__" ) || "" - ); - } - } - } ); - }, - - hasClass: function( selector ) { - var className, elem, - i = 0; - - className = " " + selector + " "; - while ( ( elem = this[ i++ ] ) ) { - if ( elem.nodeType === 1 && - ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { - return true; - } - } - - return false; - } -} ); - - - - -var rreturn = /\r/g; - -jQuery.fn.extend( { - val: function( value ) { - var hooks, ret, isFunction, - elem = this[ 0 ]; - - if ( !arguments.length ) { - if ( elem ) { - hooks = jQuery.valHooks[ elem.type ] || - jQuery.valHooks[ elem.nodeName.toLowerCase() ]; - - if ( hooks && - "get" in hooks && - ( ret = hooks.get( elem, "value" ) ) !== undefined - ) { - return ret; - } - - ret = elem.value; - - // Handle most common string cases - if ( typeof ret === "string" ) { - return ret.replace( rreturn, "" ); - } - - // Handle cases where value is null/undef or number - return ret == null ? "" : ret; - } - - return; - } - - isFunction = jQuery.isFunction( value ); - - return this.each( function( i ) { - var val; - - if ( this.nodeType !== 1 ) { - return; - } - - if ( isFunction ) { - val = value.call( this, i, jQuery( this ).val() ); - } else { - val = value; - } - - // Treat null/undefined as ""; convert numbers to string - if ( val == null ) { - val = ""; - - } else if ( typeof val === "number" ) { - val += ""; - - } else if ( Array.isArray( val ) ) { - val = jQuery.map( val, function( value ) { - return value == null ? "" : value + ""; - } ); - } - - hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; - - // If set returns undefined, fall back to normal setting - if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { - this.value = val; - } - } ); - } -} ); - -jQuery.extend( { - valHooks: { - option: { - get: function( elem ) { - - var val = jQuery.find.attr( elem, "value" ); - return val != null ? - val : - - // Support: IE <=10 - 11 only - // option.text throws exceptions (#14686, #14858) - // Strip and collapse whitespace - // https://html.spec.whatwg.org/#strip-and-collapse-whitespace - stripAndCollapse( jQuery.text( elem ) ); - } - }, - select: { - get: function( elem ) { - var value, option, i, - options = elem.options, - index = elem.selectedIndex, - one = elem.type === "select-one", - values = one ? null : [], - max = one ? index + 1 : options.length; - - if ( index < 0 ) { - i = max; - - } else { - i = one ? index : 0; - } - - // Loop through all the selected options - for ( ; i < max; i++ ) { - option = options[ i ]; - - // Support: IE <=9 only - // IE8-9 doesn't update selected after form reset (#2551) - if ( ( option.selected || i === index ) && - - // Don't return options that are disabled or in a disabled optgroup - !option.disabled && - ( !option.parentNode.disabled || - !nodeName( option.parentNode, "optgroup" ) ) ) { - - // Get the specific value for the option - value = jQuery( option ).val(); - - // We don't need an array for one selects - if ( one ) { - return value; - } - - // Multi-Selects return an array - values.push( value ); - } - } - - return values; - }, - - set: function( elem, value ) { - var optionSet, option, - options = elem.options, - values = jQuery.makeArray( value ), - i = options.length; - - while ( i-- ) { - option = options[ i ]; - - /* eslint-disable no-cond-assign */ - - if ( option.selected = - jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 - ) { - optionSet = true; - } - - /* eslint-enable no-cond-assign */ - } - - // Force browsers to behave consistently when non-matching value is set - if ( !optionSet ) { - elem.selectedIndex = -1; - } - return values; - } - } - } -} ); - -// Radios and checkboxes getter/setter -jQuery.each( [ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = { - set: function( elem, value ) { - if ( Array.isArray( value ) ) { - return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); - } - } - }; - if ( !support.checkOn ) { - jQuery.valHooks[ this ].get = function( elem ) { - return elem.getAttribute( "value" ) === null ? "on" : elem.value; - }; - } -} ); - - - - -// Return jQuery for attributes-only inclusion - - -var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/; - -jQuery.extend( jQuery.event, { - - trigger: function( event, data, elem, onlyHandlers ) { - - var i, cur, tmp, bubbleType, ontype, handle, special, - eventPath = [ elem || document ], - type = hasOwn.call( event, "type" ) ? event.type : event, - namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; - - cur = tmp = elem = elem || document; - - // Don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - // focus/blur morphs to focusin/out; ensure we're not firing them right now - if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { - return; - } - - if ( type.indexOf( "." ) > -1 ) { - - // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split( "." ); - type = namespaces.shift(); - namespaces.sort(); - } - ontype = type.indexOf( ":" ) < 0 && "on" + type; - - // Caller can pass in a jQuery.Event object, Object, or just an event type string - event = event[ jQuery.expando ] ? - event : - new jQuery.Event( type, typeof event === "object" && event ); - - // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) - event.isTrigger = onlyHandlers ? 2 : 3; - event.namespace = namespaces.join( "." ); - event.rnamespace = event.namespace ? - new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : - null; - - // Clean up the event in case it is being reused - event.result = undefined; - if ( !event.target ) { - event.target = elem; - } - - // Clone any incoming data and prepend the event, creating the handler arg list - data = data == null ? - [ event ] : - jQuery.makeArray( data, [ event ] ); - - // Allow special events to draw outside the lines - special = jQuery.event.special[ type ] || {}; - if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { - return; - } - - // Determine event propagation path in advance, per W3C events spec (#9951) - // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) - if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { - - bubbleType = special.delegateType || type; - if ( !rfocusMorph.test( bubbleType + type ) ) { - cur = cur.parentNode; - } - for ( ; cur; cur = cur.parentNode ) { - eventPath.push( cur ); - tmp = cur; - } - - // Only add window if we got to document (e.g., not plain obj or detached DOM) - if ( tmp === ( elem.ownerDocument || document ) ) { - eventPath.push( tmp.defaultView || tmp.parentWindow || window ); - } - } - - // Fire handlers on the event path - i = 0; - while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { - - event.type = i > 1 ? - bubbleType : - special.bindType || type; - - // jQuery handler - handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] && - dataPriv.get( cur, "handle" ); - if ( handle ) { - handle.apply( cur, data ); - } - - // Native handler - handle = ontype && cur[ ontype ]; - if ( handle && handle.apply && acceptData( cur ) ) { - event.result = handle.apply( cur, data ); - if ( event.result === false ) { - event.preventDefault(); - } - } - } - event.type = type; - - // If nobody prevented the default action, do it now - if ( !onlyHandlers && !event.isDefaultPrevented() ) { - - if ( ( !special._default || - special._default.apply( eventPath.pop(), data ) === false ) && - acceptData( elem ) ) { - - // Call a native DOM method on the target with the same name as the event. - // Don't do default actions on window, that's where global variables be (#6170) - if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) { - - // Don't re-trigger an onFOO event when we call its FOO() method - tmp = elem[ ontype ]; - - if ( tmp ) { - elem[ ontype ] = null; - } - - // Prevent re-triggering of the same event, since we already bubbled it above - jQuery.event.triggered = type; - elem[ type ](); - jQuery.event.triggered = undefined; - - if ( tmp ) { - elem[ ontype ] = tmp; - } - } - } - } - - return event.result; - }, - - // Piggyback on a donor event to simulate a different one - // Used only for `focus(in | out)` events - simulate: function( type, elem, event ) { - var e = jQuery.extend( - new jQuery.Event(), - event, - { - type: type, - isSimulated: true - } - ); - - jQuery.event.trigger( e, null, elem ); - } - -} ); - -jQuery.fn.extend( { - - trigger: function( type, data ) { - return this.each( function() { - jQuery.event.trigger( type, data, this ); - } ); - }, - triggerHandler: function( type, data ) { - var elem = this[ 0 ]; - if ( elem ) { - return jQuery.event.trigger( type, data, elem, true ); - } - } -} ); - - -jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " + - "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + - "change select submit keydown keypress keyup contextmenu" ).split( " " ), - function( i, name ) { - - // Handle event binding - jQuery.fn[ name ] = function( data, fn ) { - return arguments.length > 0 ? - this.on( name, null, data, fn ) : - this.trigger( name ); - }; -} ); - -jQuery.fn.extend( { - hover: function( fnOver, fnOut ) { - return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); - } -} ); - - - - -support.focusin = "onfocusin" in window; - - -// Support: Firefox <=44 -// Firefox doesn't have focus(in | out) events -// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 -// -// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 -// focus(in | out) events fire after focus & blur events, -// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order -// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 -if ( !support.focusin ) { - jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { - - // Attach a single capturing handler on the document while someone wants focusin/focusout - var handler = function( event ) { - jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); - }; - - jQuery.event.special[ fix ] = { - setup: function() { - var doc = this.ownerDocument || this, - attaches = dataPriv.access( doc, fix ); - - if ( !attaches ) { - doc.addEventListener( orig, handler, true ); - } - dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); - }, - teardown: function() { - var doc = this.ownerDocument || this, - attaches = dataPriv.access( doc, fix ) - 1; - - if ( !attaches ) { - doc.removeEventListener( orig, handler, true ); - dataPriv.remove( doc, fix ); - - } else { - dataPriv.access( doc, fix, attaches ); - } - } - }; - } ); -} -var location = window.location; - -var nonce = jQuery.now(); - -var rquery = ( /\?/ ); - - - -// Cross-browser xml parsing -jQuery.parseXML = function( data ) { - var xml; - if ( !data || typeof data !== "string" ) { - return null; - } - - // Support: IE 9 - 11 only - // IE throws on parseFromString with invalid input. - try { - xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); - } catch ( e ) { - xml = undefined; - } - - if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { - jQuery.error( "Invalid XML: " + data ); - } - return xml; -}; - - -var - rbracket = /\[\]$/, - rCRLF = /\r?\n/g, - rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, - rsubmittable = /^(?:input|select|textarea|keygen)/i; - -function buildParams( prefix, obj, traditional, add ) { - var name; - - if ( Array.isArray( obj ) ) { - - // Serialize array item. - jQuery.each( obj, function( i, v ) { - if ( traditional || rbracket.test( prefix ) ) { - - // Treat each array item as a scalar. - add( prefix, v ); - - } else { - - // Item is non-scalar (array or object), encode its numeric index. - buildParams( - prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", - v, - traditional, - add - ); - } - } ); - - } else if ( !traditional && jQuery.type( obj ) === "object" ) { - - // Serialize object item. - for ( name in obj ) { - buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); - } - - } else { - - // Serialize scalar item. - add( prefix, obj ); - } -} - -// Serialize an array of form elements or a set of -// key/values into a query string -jQuery.param = function( a, traditional ) { - var prefix, - s = [], - add = function( key, valueOrFunction ) { - - // If value is a function, invoke it and use its return value - var value = jQuery.isFunction( valueOrFunction ) ? - valueOrFunction() : - valueOrFunction; - - s[ s.length ] = encodeURIComponent( key ) + "=" + - encodeURIComponent( value == null ? "" : value ); - }; - - // If an array was passed in, assume that it is an array of form elements. - if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { - - // Serialize the form elements - jQuery.each( a, function() { - add( this.name, this.value ); - } ); - - } else { - - // If traditional, encode the "old" way (the way 1.3.2 or older - // did it), otherwise encode params recursively. - for ( prefix in a ) { - buildParams( prefix, a[ prefix ], traditional, add ); - } - } - - // Return the resulting serialization - return s.join( "&" ); -}; - -jQuery.fn.extend( { - serialize: function() { - return jQuery.param( this.serializeArray() ); - }, - serializeArray: function() { - return this.map( function() { - - // Can add propHook for "elements" to filter or add form elements - var elements = jQuery.prop( this, "elements" ); - return elements ? jQuery.makeArray( elements ) : this; - } ) - .filter( function() { - var type = this.type; - - // Use .is( ":disabled" ) so that fieldset[disabled] works - return this.name && !jQuery( this ).is( ":disabled" ) && - rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && - ( this.checked || !rcheckableType.test( type ) ); - } ) - .map( function( i, elem ) { - var val = jQuery( this ).val(); - - if ( val == null ) { - return null; - } - - if ( Array.isArray( val ) ) { - return jQuery.map( val, function( val ) { - return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - } ); - } - - return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - } ).get(); - } -} ); - - -var - r20 = /%20/g, - rhash = /#.*$/, - rantiCache = /([?&])_=[^&]*/, - rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, - - // #7653, #8125, #8152: local protocol detection - rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, - rnoContent = /^(?:GET|HEAD)$/, - rprotocol = /^\/\//, - - /* Prefilters - * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) - * 2) These are called: - * - BEFORE asking for a transport - * - AFTER param serialization (s.data is a string if s.processData is true) - * 3) key is the dataType - * 4) the catchall symbol "*" can be used - * 5) execution will start with transport dataType and THEN continue down to "*" if needed - */ - prefilters = {}, - - /* Transports bindings - * 1) key is the dataType - * 2) the catchall symbol "*" can be used - * 3) selection will start with transport dataType and THEN go to "*" if needed - */ - transports = {}, - - // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression - allTypes = "*/".concat( "*" ), - - // Anchor tag for parsing the document origin - originAnchor = document.createElement( "a" ); - originAnchor.href = location.href; - -// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport -function addToPrefiltersOrTransports( structure ) { - - // dataTypeExpression is optional and defaults to "*" - return function( dataTypeExpression, func ) { - - if ( typeof dataTypeExpression !== "string" ) { - func = dataTypeExpression; - dataTypeExpression = "*"; - } - - var dataType, - i = 0, - dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; - - if ( jQuery.isFunction( func ) ) { - - // For each dataType in the dataTypeExpression - while ( ( dataType = dataTypes[ i++ ] ) ) { - - // Prepend if requested - if ( dataType[ 0 ] === "+" ) { - dataType = dataType.slice( 1 ) || "*"; - ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); - - // Otherwise append - } else { - ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); - } - } - } - }; -} - -// Base inspection function for prefilters and transports -function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { - - var inspected = {}, - seekingTransport = ( structure === transports ); - - function inspect( dataType ) { - var selected; - inspected[ dataType ] = true; - jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { - var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); - if ( typeof dataTypeOrTransport === "string" && - !seekingTransport && !inspected[ dataTypeOrTransport ] ) { - - options.dataTypes.unshift( dataTypeOrTransport ); - inspect( dataTypeOrTransport ); - return false; - } else if ( seekingTransport ) { - return !( selected = dataTypeOrTransport ); - } - } ); - return selected; - } - - return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); -} - -// A special extend for ajax options -// that takes "flat" options (not to be deep extended) -// Fixes #9887 -function ajaxExtend( target, src ) { - var key, deep, - flatOptions = jQuery.ajaxSettings.flatOptions || {}; - - for ( key in src ) { - if ( src[ key ] !== undefined ) { - ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; - } - } - if ( deep ) { - jQuery.extend( true, target, deep ); - } - - return target; -} - -/* Handles responses to an ajax request: - * - finds the right dataType (mediates between content-type and expected dataType) - * - returns the corresponding response - */ -function ajaxHandleResponses( s, jqXHR, responses ) { - - var ct, type, finalDataType, firstDataType, - contents = s.contents, - dataTypes = s.dataTypes; - - // Remove auto dataType and get content-type in the process - while ( dataTypes[ 0 ] === "*" ) { - dataTypes.shift(); - if ( ct === undefined ) { - ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); - } - } - - // Check if we're dealing with a known content-type - if ( ct ) { - for ( type in contents ) { - if ( contents[ type ] && contents[ type ].test( ct ) ) { - dataTypes.unshift( type ); - break; - } - } - } - - // Check to see if we have a response for the expected dataType - if ( dataTypes[ 0 ] in responses ) { - finalDataType = dataTypes[ 0 ]; - } else { - - // Try convertible dataTypes - for ( type in responses ) { - if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { - finalDataType = type; - break; - } - if ( !firstDataType ) { - firstDataType = type; - } - } - - // Or just use first one - finalDataType = finalDataType || firstDataType; - } - - // If we found a dataType - // We add the dataType to the list if needed - // and return the corresponding response - if ( finalDataType ) { - if ( finalDataType !== dataTypes[ 0 ] ) { - dataTypes.unshift( finalDataType ); - } - return responses[ finalDataType ]; - } -} - -/* Chain conversions given the request and the original response - * Also sets the responseXXX fields on the jqXHR instance - */ -function ajaxConvert( s, response, jqXHR, isSuccess ) { - var conv2, current, conv, tmp, prev, - converters = {}, - - // Work with a copy of dataTypes in case we need to modify it for conversion - dataTypes = s.dataTypes.slice(); - - // Create converters map with lowercased keys - if ( dataTypes[ 1 ] ) { - for ( conv in s.converters ) { - converters[ conv.toLowerCase() ] = s.converters[ conv ]; - } - } - - current = dataTypes.shift(); - - // Convert to each sequential dataType - while ( current ) { - - if ( s.responseFields[ current ] ) { - jqXHR[ s.responseFields[ current ] ] = response; - } - - // Apply the dataFilter if provided - if ( !prev && isSuccess && s.dataFilter ) { - response = s.dataFilter( response, s.dataType ); - } - - prev = current; - current = dataTypes.shift(); - - if ( current ) { - - // There's only work to do if current dataType is non-auto - if ( current === "*" ) { - - current = prev; - - // Convert response if prev dataType is non-auto and differs from current - } else if ( prev !== "*" && prev !== current ) { - - // Seek a direct converter - conv = converters[ prev + " " + current ] || converters[ "* " + current ]; - - // If none found, seek a pair - if ( !conv ) { - for ( conv2 in converters ) { - - // If conv2 outputs current - tmp = conv2.split( " " ); - if ( tmp[ 1 ] === current ) { - - // If prev can be converted to accepted input - conv = converters[ prev + " " + tmp[ 0 ] ] || - converters[ "* " + tmp[ 0 ] ]; - if ( conv ) { - - // Condense equivalence converters - if ( conv === true ) { - conv = converters[ conv2 ]; - - // Otherwise, insert the intermediate dataType - } else if ( converters[ conv2 ] !== true ) { - current = tmp[ 0 ]; - dataTypes.unshift( tmp[ 1 ] ); - } - break; - } - } - } - } - - // Apply converter (if not an equivalence) - if ( conv !== true ) { - - // Unless errors are allowed to bubble, catch and return them - if ( conv && s.throws ) { - response = conv( response ); - } else { - try { - response = conv( response ); - } catch ( e ) { - return { - state: "parsererror", - error: conv ? e : "No conversion from " + prev + " to " + current - }; - } - } - } - } - } - } - - return { state: "success", data: response }; -} - -jQuery.extend( { - - // Counter for holding the number of active queries - active: 0, - - // Last-Modified header cache for next request - lastModified: {}, - etag: {}, - - ajaxSettings: { - url: location.href, - type: "GET", - isLocal: rlocalProtocol.test( location.protocol ), - global: true, - processData: true, - async: true, - contentType: "application/x-www-form-urlencoded; charset=UTF-8", - - /* - timeout: 0, - data: null, - dataType: null, - username: null, - password: null, - cache: null, - throws: false, - traditional: false, - headers: {}, - */ - - accepts: { - "*": allTypes, - text: "text/plain", - html: "text/html", - xml: "application/xml, text/xml", - json: "application/json, text/javascript" - }, - - contents: { - xml: /\bxml\b/, - html: /\bhtml/, - json: /\bjson\b/ - }, - - responseFields: { - xml: "responseXML", - text: "responseText", - json: "responseJSON" - }, - - // Data converters - // Keys separate source (or catchall "*") and destination types with a single space - converters: { - - // Convert anything to text - "* text": String, - - // Text to html (true = no transformation) - "text html": true, - - // Evaluate text as a json expression - "text json": JSON.parse, - - // Parse text as xml - "text xml": jQuery.parseXML - }, - - // For options that shouldn't be deep extended: - // you can add your own custom options here if - // and when you create one that shouldn't be - // deep extended (see ajaxExtend) - flatOptions: { - url: true, - context: true - } - }, - - // Creates a full fledged settings object into target - // with both ajaxSettings and settings fields. - // If target is omitted, writes into ajaxSettings. - ajaxSetup: function( target, settings ) { - return settings ? - - // Building a settings object - ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : - - // Extending ajaxSettings - ajaxExtend( jQuery.ajaxSettings, target ); - }, - - ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), - ajaxTransport: addToPrefiltersOrTransports( transports ), - - // Main method - ajax: function( url, options ) { - - // If url is an object, simulate pre-1.5 signature - if ( typeof url === "object" ) { - options = url; - url = undefined; - } - - // Force options to be an object - options = options || {}; - - var transport, - - // URL without anti-cache param - cacheURL, - - // Response headers - responseHeadersString, - responseHeaders, - - // timeout handle - timeoutTimer, - - // Url cleanup var - urlAnchor, - - // Request state (becomes false upon send and true upon completion) - completed, - - // To know if global events are to be dispatched - fireGlobals, - - // Loop variable - i, - - // uncached part of the url - uncached, - - // Create the final options object - s = jQuery.ajaxSetup( {}, options ), - - // Callbacks context - callbackContext = s.context || s, - - // Context for global events is callbackContext if it is a DOM node or jQuery collection - globalEventContext = s.context && - ( callbackContext.nodeType || callbackContext.jquery ) ? - jQuery( callbackContext ) : - jQuery.event, - - // Deferreds - deferred = jQuery.Deferred(), - completeDeferred = jQuery.Callbacks( "once memory" ), - - // Status-dependent callbacks - statusCode = s.statusCode || {}, - - // Headers (they are sent all at once) - requestHeaders = {}, - requestHeadersNames = {}, - - // Default abort message - strAbort = "canceled", - - // Fake xhr - jqXHR = { - readyState: 0, - - // Builds headers hashtable if needed - getResponseHeader: function( key ) { - var match; - if ( completed ) { - if ( !responseHeaders ) { - responseHeaders = {}; - while ( ( match = rheaders.exec( responseHeadersString ) ) ) { - responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ]; - } - } - match = responseHeaders[ key.toLowerCase() ]; - } - return match == null ? null : match; - }, - - // Raw string - getAllResponseHeaders: function() { - return completed ? responseHeadersString : null; - }, - - // Caches the header - setRequestHeader: function( name, value ) { - if ( completed == null ) { - name = requestHeadersNames[ name.toLowerCase() ] = - requestHeadersNames[ name.toLowerCase() ] || name; - requestHeaders[ name ] = value; - } - return this; - }, - - // Overrides response content-type header - overrideMimeType: function( type ) { - if ( completed == null ) { - s.mimeType = type; - } - return this; - }, - - // Status-dependent callbacks - statusCode: function( map ) { - var code; - if ( map ) { - if ( completed ) { - - // Execute the appropriate callbacks - jqXHR.always( map[ jqXHR.status ] ); - } else { - - // Lazy-add the new callbacks in a way that preserves old ones - for ( code in map ) { - statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; - } - } - } - return this; - }, - - // Cancel the request - abort: function( statusText ) { - var finalText = statusText || strAbort; - if ( transport ) { - transport.abort( finalText ); - } - done( 0, finalText ); - return this; - } - }; - - // Attach deferreds - deferred.promise( jqXHR ); - - // Add protocol if not provided (prefilters might expect it) - // Handle falsy url in the settings object (#10093: consistency with old signature) - // We also use the url parameter if available - s.url = ( ( url || s.url || location.href ) + "" ) - .replace( rprotocol, location.protocol + "//" ); - - // Alias method option to type as per ticket #12004 - s.type = options.method || options.type || s.method || s.type; - - // Extract dataTypes list - s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; - - // A cross-domain request is in order when the origin doesn't match the current origin. - if ( s.crossDomain == null ) { - urlAnchor = document.createElement( "a" ); - - // Support: IE <=8 - 11, Edge 12 - 13 - // IE throws exception on accessing the href property if url is malformed, - // e.g. http://example.com:80x/ - try { - urlAnchor.href = s.url; - - // Support: IE <=8 - 11 only - // Anchor's host property isn't correctly set when s.url is relative - urlAnchor.href = urlAnchor.href; - s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== - urlAnchor.protocol + "//" + urlAnchor.host; - } catch ( e ) { - - // If there is an error parsing the URL, assume it is crossDomain, - // it can be rejected by the transport if it is invalid - s.crossDomain = true; - } - } - - // Convert data if not already a string - if ( s.data && s.processData && typeof s.data !== "string" ) { - s.data = jQuery.param( s.data, s.traditional ); - } - - // Apply prefilters - inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); - - // If request was aborted inside a prefilter, stop there - if ( completed ) { - return jqXHR; - } - - // We can fire global events as of now if asked to - // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) - fireGlobals = jQuery.event && s.global; - - // Watch for a new set of requests - if ( fireGlobals && jQuery.active++ === 0 ) { - jQuery.event.trigger( "ajaxStart" ); - } - - // Uppercase the type - s.type = s.type.toUpperCase(); - - // Determine if request has content - s.hasContent = !rnoContent.test( s.type ); - - // Save the URL in case we're toying with the If-Modified-Since - // and/or If-None-Match header later on - // Remove hash to simplify url manipulation - cacheURL = s.url.replace( rhash, "" ); - - // More options handling for requests with no content - if ( !s.hasContent ) { - - // Remember the hash so we can put it back - uncached = s.url.slice( cacheURL.length ); - - // If data is available, append data to url - if ( s.data ) { - cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; - - // #9682: remove data so that it's not used in an eventual retry - delete s.data; - } - - // Add or update anti-cache param if needed - if ( s.cache === false ) { - cacheURL = cacheURL.replace( rantiCache, "$1" ); - uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce++ ) + uncached; - } - - // Put hash and anti-cache on the URL that will be requested (gh-1732) - s.url = cacheURL + uncached; - - // Change '%20' to '+' if this is encoded form body content (gh-2658) - } else if ( s.data && s.processData && - ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { - s.data = s.data.replace( r20, "+" ); - } - - // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - if ( jQuery.lastModified[ cacheURL ] ) { - jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); - } - if ( jQuery.etag[ cacheURL ] ) { - jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); - } - } - - // Set the correct header, if data is being sent - if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { - jqXHR.setRequestHeader( "Content-Type", s.contentType ); - } - - // Set the Accepts header for the server, depending on the dataType - jqXHR.setRequestHeader( - "Accept", - s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? - s.accepts[ s.dataTypes[ 0 ] ] + - ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : - s.accepts[ "*" ] - ); - - // Check for headers option - for ( i in s.headers ) { - jqXHR.setRequestHeader( i, s.headers[ i ] ); - } - - // Allow custom headers/mimetypes and early abort - if ( s.beforeSend && - ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { - - // Abort if not done already and return - return jqXHR.abort(); - } - - // Aborting is no longer a cancellation - strAbort = "abort"; - - // Install callbacks on deferreds - completeDeferred.add( s.complete ); - jqXHR.done( s.success ); - jqXHR.fail( s.error ); - - // Get transport - transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); - - // If no transport, we auto-abort - if ( !transport ) { - done( -1, "No Transport" ); - } else { - jqXHR.readyState = 1; - - // Send global event - if ( fireGlobals ) { - globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); - } - - // If request was aborted inside ajaxSend, stop there - if ( completed ) { - return jqXHR; - } - - // Timeout - if ( s.async && s.timeout > 0 ) { - timeoutTimer = window.setTimeout( function() { - jqXHR.abort( "timeout" ); - }, s.timeout ); - } - - try { - completed = false; - transport.send( requestHeaders, done ); - } catch ( e ) { - - // Rethrow post-completion exceptions - if ( completed ) { - throw e; - } - - // Propagate others as results - done( -1, e ); - } - } - - // Callback for when everything is done - function done( status, nativeStatusText, responses, headers ) { - var isSuccess, success, error, response, modified, - statusText = nativeStatusText; - - // Ignore repeat invocations - if ( completed ) { - return; - } - - completed = true; - - // Clear timeout if it exists - if ( timeoutTimer ) { - window.clearTimeout( timeoutTimer ); - } - - // Dereference transport for early garbage collection - // (no matter how long the jqXHR object will be used) - transport = undefined; - - // Cache response headers - responseHeadersString = headers || ""; - - // Set readyState - jqXHR.readyState = status > 0 ? 4 : 0; - - // Determine if successful - isSuccess = status >= 200 && status < 300 || status === 304; - - // Get response data - if ( responses ) { - response = ajaxHandleResponses( s, jqXHR, responses ); - } - - // Convert no matter what (that way responseXXX fields are always set) - response = ajaxConvert( s, response, jqXHR, isSuccess ); - - // If successful, handle type chaining - if ( isSuccess ) { - - // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - modified = jqXHR.getResponseHeader( "Last-Modified" ); - if ( modified ) { - jQuery.lastModified[ cacheURL ] = modified; - } - modified = jqXHR.getResponseHeader( "etag" ); - if ( modified ) { - jQuery.etag[ cacheURL ] = modified; - } - } - - // if no content - if ( status === 204 || s.type === "HEAD" ) { - statusText = "nocontent"; - - // if not modified - } else if ( status === 304 ) { - statusText = "notmodified"; - - // If we have data, let's convert it - } else { - statusText = response.state; - success = response.data; - error = response.error; - isSuccess = !error; - } - } else { - - // Extract error from statusText and normalize for non-aborts - error = statusText; - if ( status || !statusText ) { - statusText = "error"; - if ( status < 0 ) { - status = 0; - } - } - } - - // Set data for the fake xhr object - jqXHR.status = status; - jqXHR.statusText = ( nativeStatusText || statusText ) + ""; - - // Success/Error - if ( isSuccess ) { - deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); - } else { - deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); - } - - // Status-dependent callbacks - jqXHR.statusCode( statusCode ); - statusCode = undefined; - - if ( fireGlobals ) { - globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", - [ jqXHR, s, isSuccess ? success : error ] ); - } - - // Complete - completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); - - if ( fireGlobals ) { - globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); - - // Handle the global AJAX counter - if ( !( --jQuery.active ) ) { - jQuery.event.trigger( "ajaxStop" ); - } - } - } - - return jqXHR; - }, - - getJSON: function( url, data, callback ) { - return jQuery.get( url, data, callback, "json" ); - }, - - getScript: function( url, callback ) { - return jQuery.get( url, undefined, callback, "script" ); - } -} ); - -jQuery.each( [ "get", "post" ], function( i, method ) { - jQuery[ method ] = function( url, data, callback, type ) { - - // Shift arguments if data argument was omitted - if ( jQuery.isFunction( data ) ) { - type = type || callback; - callback = data; - data = undefined; - } - - // The url can be an options object (which then must have .url) - return jQuery.ajax( jQuery.extend( { - url: url, - type: method, - dataType: type, - data: data, - success: callback - }, jQuery.isPlainObject( url ) && url ) ); - }; -} ); - - -jQuery._evalUrl = function( url ) { - return jQuery.ajax( { - url: url, - - // Make this explicit, since user can override this through ajaxSetup (#11264) - type: "GET", - dataType: "script", - cache: true, - async: false, - global: false, - "throws": true - } ); -}; - - -jQuery.fn.extend( { - wrapAll: function( html ) { - var wrap; - - if ( this[ 0 ] ) { - if ( jQuery.isFunction( html ) ) { - html = html.call( this[ 0 ] ); - } - - // The elements to wrap the target around - wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); - - if ( this[ 0 ].parentNode ) { - wrap.insertBefore( this[ 0 ] ); - } - - wrap.map( function() { - var elem = this; - - while ( elem.firstElementChild ) { - elem = elem.firstElementChild; - } - - return elem; - } ).append( this ); - } - - return this; - }, - - wrapInner: function( html ) { - if ( jQuery.isFunction( html ) ) { - return this.each( function( i ) { - jQuery( this ).wrapInner( html.call( this, i ) ); - } ); - } - - return this.each( function() { - var self = jQuery( this ), - contents = self.contents(); - - if ( contents.length ) { - contents.wrapAll( html ); - - } else { - self.append( html ); - } - } ); - }, - - wrap: function( html ) { - var isFunction = jQuery.isFunction( html ); - - return this.each( function( i ) { - jQuery( this ).wrapAll( isFunction ? html.call( this, i ) : html ); - } ); - }, - - unwrap: function( selector ) { - this.parent( selector ).not( "body" ).each( function() { - jQuery( this ).replaceWith( this.childNodes ); - } ); - return this; - } -} ); - - -jQuery.expr.pseudos.hidden = function( elem ) { - return !jQuery.expr.pseudos.visible( elem ); -}; -jQuery.expr.pseudos.visible = function( elem ) { - return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); -}; - - - - -jQuery.ajaxSettings.xhr = function() { - try { - return new window.XMLHttpRequest(); - } catch ( e ) {} -}; - -var xhrSuccessStatus = { - - // File protocol always yields status code 0, assume 200 - 0: 200, - - // Support: IE <=9 only - // #1450: sometimes IE returns 1223 when it should be 204 - 1223: 204 - }, - xhrSupported = jQuery.ajaxSettings.xhr(); - -support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); -support.ajax = xhrSupported = !!xhrSupported; - -jQuery.ajaxTransport( function( options ) { - var callback, errorCallback; - - // Cross domain only allowed if supported through XMLHttpRequest - if ( support.cors || xhrSupported && !options.crossDomain ) { - return { - send: function( headers, complete ) { - var i, - xhr = options.xhr(); - - xhr.open( - options.type, - options.url, - options.async, - options.username, - options.password - ); - - // Apply custom fields if provided - if ( options.xhrFields ) { - for ( i in options.xhrFields ) { - xhr[ i ] = options.xhrFields[ i ]; - } - } - - // Override mime type if needed - if ( options.mimeType && xhr.overrideMimeType ) { - xhr.overrideMimeType( options.mimeType ); - } - - // X-Requested-With header - // For cross-domain requests, seeing as conditions for a preflight are - // akin to a jigsaw puzzle, we simply never set it to be sure. - // (it can always be set on a per-request basis or even using ajaxSetup) - // For same-domain requests, won't change header if already provided. - if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { - headers[ "X-Requested-With" ] = "XMLHttpRequest"; - } - - // Set headers - for ( i in headers ) { - xhr.setRequestHeader( i, headers[ i ] ); - } - - // Callback - callback = function( type ) { - return function() { - if ( callback ) { - callback = errorCallback = xhr.onload = - xhr.onerror = xhr.onabort = xhr.onreadystatechange = null; - - if ( type === "abort" ) { - xhr.abort(); - } else if ( type === "error" ) { - - // Support: IE <=9 only - // On a manual native abort, IE9 throws - // errors on any property access that is not readyState - if ( typeof xhr.status !== "number" ) { - complete( 0, "error" ); - } else { - complete( - - // File: protocol always yields status 0; see #8605, #14207 - xhr.status, - xhr.statusText - ); - } - } else { - complete( - xhrSuccessStatus[ xhr.status ] || xhr.status, - xhr.statusText, - - // Support: IE <=9 only - // IE9 has no XHR2 but throws on binary (trac-11426) - // For XHR2 non-text, let the caller handle it (gh-2498) - ( xhr.responseType || "text" ) !== "text" || - typeof xhr.responseText !== "string" ? - { binary: xhr.response } : - { text: xhr.responseText }, - xhr.getAllResponseHeaders() - ); - } - } - }; - }; - - // Listen to events - xhr.onload = callback(); - errorCallback = xhr.onerror = callback( "error" ); - - // Support: IE 9 only - // Use onreadystatechange to replace onabort - // to handle uncaught aborts - if ( xhr.onabort !== undefined ) { - xhr.onabort = errorCallback; - } else { - xhr.onreadystatechange = function() { - - // Check readyState before timeout as it changes - if ( xhr.readyState === 4 ) { - - // Allow onerror to be called first, - // but that will not handle a native abort - // Also, save errorCallback to a variable - // as xhr.onerror cannot be accessed - window.setTimeout( function() { - if ( callback ) { - errorCallback(); - } - } ); - } - }; - } - - // Create the abort callback - callback = callback( "abort" ); - - try { - - // Do send the request (this may raise an exception) - xhr.send( options.hasContent && options.data || null ); - } catch ( e ) { - - // #14683: Only rethrow if this hasn't been notified as an error yet - if ( callback ) { - throw e; - } - } - }, - - abort: function() { - if ( callback ) { - callback(); - } - } - }; - } -} ); - - - - -// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) -jQuery.ajaxPrefilter( function( s ) { - if ( s.crossDomain ) { - s.contents.script = false; - } -} ); - -// Install script dataType -jQuery.ajaxSetup( { - accepts: { - script: "text/javascript, application/javascript, " + - "application/ecmascript, application/x-ecmascript" - }, - contents: { - script: /\b(?:java|ecma)script\b/ - }, - converters: { - "text script": function( text ) { - jQuery.globalEval( text ); - return text; - } - } -} ); - -// Handle cache's special case and crossDomain -jQuery.ajaxPrefilter( "script", function( s ) { - if ( s.cache === undefined ) { - s.cache = false; - } - if ( s.crossDomain ) { - s.type = "GET"; - } -} ); - -// Bind script tag hack transport -jQuery.ajaxTransport( "script", function( s ) { - - // This transport only deals with cross domain requests - if ( s.crossDomain ) { - var script, callback; - return { - send: function( _, complete ) { - script = jQuery( " - - - - - - -
- - - - -
- - - - - -
- -
- - - - - - - - - - - - - - - - - -
- -
    - -
  • Docs »
  • - -
  • Index
  • - - -
  • - - - -
  • - -
- - -
-
- - - -
-
- -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/old_docs/tiler_docs/_build/html/index.html b/old_docs/tiler_docs/_build/html/index.html deleted file mode 100644 index 069e3b88..00000000 --- a/old_docs/tiler_docs/_build/html/index.html +++ /dev/null @@ -1,989 +0,0 @@ - - - - - - - - - - - CosmiQ Works Tiler (cw-tiler) Documentation — cw-tiler 0.2.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -
- - - - - -
- -
- - - - - - - - - - - - - - - - - -
- - - - -
-
-
-
- -
-

CosmiQ Works Tiler (cw-tiler) Documentation

- --- - - - - - - - - - -
Author:CosmiQ Works
Version:
Copyright:2018, CosmiQ Works
License:This work is licensed under the BSD 3-Clause license.
- -
-

Tiling functions

-
-
-cw_tiler.main.calculate_analysis_grid(utm_bounds, stride_size_meters=300, cell_size_meters=400, quad_space=False, snapToGrid=False)[source]
-

Wrapper for calculate_anchor_points() and calculate_cells().

-

Based on UTM boundaries of an image tile, stride size, and cell size, -output a dictionary of boundary lists for analysis chips.

- --- - - - - - - - -
Parameters:
    -
  • utm_bounds (list-like of shape (W, S, E, N)) – UTM coordinate limits of the input tile.
  • -
  • stride_size_meters (int, optional) – Step size in both X and Y directions between cells in units of meters. -Defaults to 300 .
  • -
  • cell_size_meters (int, optional) – Extent of each cell in both X and Y directions in units of meters. -Defaults to 400 .
  • -
  • quad_space (bool, optional) – See calculate_anchor_points() . quad_space . Defaults to -False .
  • -
  • snapToGrid (bool, optional) –
  • -
-
Returns:

cells_list_dict – A dict whose keys are either 0 or [0, 1, 2, 3] (see -calculate_anchor_points() . quad_space ), and whose values are -list s of boundaries in the shape [W, S, E, N] . Boundaries -are in UTM coordinates.

-
Return type:

dict of list(s) of lists

-
-
- -
-
-cw_tiler.main.calculate_anchor_points(utm_bounds, stride_size_meters=400, extend=False, quad_space=False)[source]
-

Get anchor point (lower left corner of bbox) for chips from a tile.

- --- - - - - - -
Parameters:
    -
  • utm_bounds (tuple of 4 floats) – A tuple of shape (min_x, min_y, max_x, max_y) that defines -the spatial extent of the tile to be split. Coordinates should be in -UTM.
  • -
  • stride_size_meters (int, optional) – Stride size in both X and Y directions for generating chips. Defaults -to 400.
  • -
  • extend (bool, optional) – Defines whether UTM boundaries should be rounded to the nearest integer -outward from utm_bounds (extend == True) or -inward from utm_bounds (extend == False). Defaults -to False (inward).
  • -
  • quad_space (bool, optional) – If tiles will overlap by no more than half their X and/or Y extent in -each direction, quad_space can be used to split chip -anchors into four non-overlapping subsets. For example, if anchor -points are 400m apart and each chip will be 800m by 800m, quad_space -will generate four sets which do not internally overlap; -however, this would fail if tiles are 900m by 900m. Defaults to -False, in which case the returned anchor_point_list_dict will -comprise a single list of anchor points.
  • -
-
Returns:

    -
  • anchor_point_list_dict (dict of list(s) of lists)
  • -
  • If quad_space==True , anchor_point_list_dict is a
  • -
  • dict with four keys [0, 1, 2, 3] corresponding to the four
  • -
  • subsets of chips generated (see quad_space ). If
  • -
  • quad_space==False , anchor_point_list_dict is a
  • -
  • dict with a single key, 0 , that corresponds to a list of all
  • -
  • of the generated anchor points. Each anchor point in the list(s) is an
  • -
  • [x, y] pair of UTM coordinates denoting the SW corner of a chip.
  • -
-

-
-
- -
-
-cw_tiler.main.calculate_cells(anchor_point_list_dict, cell_size_meters, utm_bounds=[])[source]
-

Calculate boundaries for image cells (chips) from anchor points.

-

This function takes the output from calculate_anchor_points() as well -as a desired cell size (cell_size_meters) and outputs -(W, S, E, N) tuples for generating cells.

- --- - - - - - - - -
Parameters:
    -
  • anchor_point_list_dict (dict) – Output of calculate_anchor_points(). See that function for -details.
  • -
  • cell_size_meters (int or float) – Desired width and height of each cell in meters.
  • -
  • utm_bounds (list -like of float s, optional) – A list-like of shape (W, S, E, N) that defines the limits -of an input image tile in UTM coordinates to ensure that no cells -extend beyond those limits. If not provided, all cells will be included -even if they extend beyond the UTM limits of the source imagery.
  • -
-
Returns:

cells_list_dict – A dict whose keys are either 0 or [0, 1, 2, 3] (see -calculate_anchor_points() . quad_space ), and whose values are -list s of boundaries in the shape [W, S, E, N] . Boundaries -are in UTM coordinates.

-
Return type:

dict of list(s) of lists

-
-
- -
-
-cw_tiler.main.get_chip(source, ll_x, ll_y, gsd, utm_crs='', indexes=None, tilesize=256, nodata=None, alpha=None)[source]
-

Get an image tile of specific pixel size.

-

This wrapper function permits passing of ll_x, ll_y, gsd, and -tile_size_pixels in place of boundary coordinates to extract an image -region of defined pixel extent.

- --- - - - - - - - -
Parameters:
    -
  • source (rasterio.Dataset) – Source imagery dataset to tile.
  • -
  • ll_x (int or float) – Lower left x position (i.e. Western bound).
  • -
  • ll_y (int or float) – Lower left y position (i.e. Southern bound).
  • -
  • gsd (float) – Ground sample distance of the source imagery in meter/pixel units.
  • -
  • utm_crs (rasterio.crs.CRS, optional) – UTM coordinate reference system string for the imagery. If not -provided, this is calculated using -cw_tiler.utils.get_wgs84_bounds() and -cw_tiler.utils.calculate_UTM_crs() .
  • -
  • indexes (tuple of 3 ints, optional) – Band indexes for the output. By default, extracts all of the -indexes from source.
  • -
  • tilesize (int, optional) – Output image X and Y pixel extent. Defaults to 256 .
  • -
  • nodata (int or float, optional) – Value to use for nodata pixels during tiling. By default, uses -the existing nodata value in source.
  • -
  • alpha (int, optional) – Alpha band index for tiling. By default, uses the same band as -specified by source.
  • -
-
Returns:

-
data : numpy.ndarray
-

int pixel values. Shape is (C, Y, X) if retrieving multiple -channels, (Y, X) otherwise.

-
-
mask : numpy.ndarray
-

int mask indicating which pixels contain information and which are -nodata. Pixels containing data have value 255, nodata pixels -have value 0.

-
-
window : rasterio.windows.Window
-

rasterio.windows.Window object indicating the raster -location of the dataset subregion being returned in data.

-
-
window_transform : affine.Affine
-

Affine transformation for the window.

-
-
-

-
Return type:

(data, mask, window, window_transform tuple.

-
-
- -
-
-cw_tiler.main.tile_utm(source, ll_x, ll_y, ur_x, ur_y, indexes=None, tilesize=256, nodata=None, alpha=None, dst_crs='epsg:4326')[source]
-

Create a UTM tile from a file or a rasterio.Dataset in memory.

-

This function is a wrapper around tile_utm_source() to enable passing -of file paths instead of pre-loaded rasterio.Dataset s.

- --- - - - - - - - -
Parameters:
    -
  • source (rasterio.Dataset) – Source imagery dataset to tile.
  • -
  • ll_x (int or float) – Lower left x position (i.e. Western bound).
  • -
  • ll_y (int or float) – Lower left y position (i.e. Southern bound).
  • -
  • ur_x (int or float) – Upper right x position (i.e. Eastern bound).
  • -
  • ur_y (int or float) – Upper right y position (i.e. Northern bound).
  • -
  • indexes (tuple of 3 ints, optional) – Band indexes for the output. By default, extracts all of the -indexes from source .
  • -
  • tilesize (int, optional) – Output image X and Y pixel extent. Defaults to 256.
  • -
  • nodata (int or float, optional) – Value to use for nodata pixels during tiling. By default, uses -the existing nodata value in src.
  • -
  • alpha (int, optional) – Alpha band index for tiling. By default, uses the same band as -specified by src.
  • -
  • dst_crs (str, optional) – Coordinate reference system for output. Defaults to "epsg:4326".
  • -
-
Returns:

-
data : numpy.ndarray
-

int pixel values. Shape is (C, Y, X) if retrieving multiple -channels, (Y, X) otherwise.

-
-
mask : numpy.ndarray
-

int mask indicating which pixels contain information and which are -nodata. Pixels containing data have value 255, nodata pixels -have value 0.

-
-
window : rasterio.windows.Window
-

rasterio.windows.Window object indicating the raster -location of the dataset subregion being returned in data.

-
-
window_transform : affine.Affine
-

Affine transformation for the window.

-
-
-

-
Return type:

(data, mask, window, window_transform tuple.

-
-
- -
-
-cw_tiler.main.tile_utm_source(src, ll_x, ll_y, ur_x, ur_y, indexes=None, tilesize=256, nodata=None, alpha=None, dst_crs='epsg:4326')[source]
-

Create a UTM tile from a rasterio.Dataset in memory.

- --- - - - - - - - -
Parameters:
    -
  • src (rasterio.Dataset) – Source imagery dataset to tile.
  • -
  • ll_x (int or float) – Lower left x position (i.e. Western bound).
  • -
  • ll_y (int or f) –
  • -
  • loat – Lower left y position (i.e. Southern bound).
  • -
  • ur_x (int or float) – Upper right x position (i.e. Eastern bound).
  • -
  • ur_y (int or float) – Upper right y position (i.e. Northern bound).
  • -
  • indexes (tuple of 3 ints, optional) – Band indexes for the output. By default, extracts all of the -indexes from src.
  • -
  • tilesize (int, optional) – Output image X and Y pixel extent. Defaults to 256.
  • -
  • nodata (int or float, optional) – Value to use for nodata pixels during tiling. By default, uses -the existing nodata value in src.
  • -
  • alpha (int, optional) – Alpha band index for tiling. By default, uses the same band as -specified by src.
  • -
  • dst_crs (str, optional) – Coordinate reference system for output. Defaults to "epsg:4326".
  • -
-
Returns:

-
data : numpy.ndarray
-

int pixel values. Shape is (C, Y, X) if retrieving multiple -channels, (Y, X) otherwise.

-
-
mask : numpy.ndarray
-

int mask indicating which pixels contain information and which are -nodata. Pixels containing data have value 255, nodata -pixels have value 0.

-
-
window : rasterio.windows.Window
-

rasterio.windows.Window object indicating the raster -location of the dataset subregion being returned in data.

-
-
window_transform : affine.Affine
-

Affine transformation for the window.

-
-
-

-
Return type:

(data, mask, window, window_transform) tuple.

-
-
- -
-
-

Utility functions

-
-

Raster utilities

-

cw_tiler.utils: utility functions for raster files.

-
-
-cw_tiler.utils.calculate_UTM_crs(coords)[source]
-

Calculate UTM Projection String.

- --- - - - - - - - -
Parameters:coords (list) – [longitude, latitude] or -[min_longitude, min_latitude, max_longitude, max_latitude] .
Returns:out – returns proj4 projection string
Return type:str
-
- -
-
-cw_tiler.utils.get_utm_bounds(source, utm_EPSG)[source]
-

Transform bounds from source crs to a UTM crs.

- --- - - - - - - - -
Parameters: -
Returns:

utm_bounds – Bounding box limits in utm_EPSG crs coordinates with shape -(W, S, E, N).

-
Return type:

tuple

-
-
- -
-
-cw_tiler.utils.get_utm_vrt(source, crs='EPSG:3857', resampling=<Resampling.bilinear: 1>, src_nodata=None, dst_nodata=None)[source]
-

Get a rasterio.vrt.WarpedVRT projection of a dataset.

- --- - - - - - - - -
Parameters:
    -
  • source (rasterio.io.DatasetReader) – The dataset to virtually warp using rasterio.vrt.WarpedVRT.
  • -
  • crs (rasterio.crs.CRS, optional) – Coordinate reference system for the VRT. Defaults to ‘EPSG:3857’ -(Web Mercator).
  • -
  • resampling (rasterio.enums.Resampling method, optional) – Resampling method to use. Defaults to -rasterio.enums.Resampling.bilinear(). Alternatives include -rasterio.enums.Resampling.average(), -rasterio.enums.Resampling.cubic(), and others. See docs for -rasterio.enums.Resampling for more information.
  • -
  • src_nodata (int or float, optional) – Source nodata value which will be ignored for interpolation. Defaults -to None (all data used in interpolation).
  • -
  • dst_nodata (int or float, optional) – Destination nodata value which will be ignored for interpolation. -Defaults to None, in which case the value of src_nodata will be -used if provided, or 0 otherwise.
  • -
-
Returns:

-
Return type:

A rasterio.vrt.WarpedVRT instance with the transformation.

-
-
- -
-
-cw_tiler.utils.get_utm_vrt_profile(source, crs='EPSG:3857', resampling=<Resampling.bilinear: 1>, src_nodata=None, dst_nodata=None)[source]
-

Get a rasterio.profiles.Profile for projection of a VRT.

- --- - - - - - -
Parameters:
    -
  • source (rasterio.io.DatasetReader) – The dataset to virtually warp using rasterio.vrt.WarpedVRT.
  • -
  • crs (rasterio.crs.CRS, optional) – Coordinate reference system for the VRT. Defaults to "EPSG:3857" -(Web Mercator).
  • -
  • resampling (rasterio.enums.Resampling method, optional) – Resampling method to use. Defaults to -rasterio.enums.Resampling.bilinear. Alternatives include -rasterio.enums.Resampling.average, -rasterio.enums.Resampling.cubic, and others. See docs for -rasterio.enums.Resampling for more information.
  • -
  • src_nodata (int or float, optional) – Source nodata value which will be ignored for interpolation. Defaults -to None (all data used in interpolation).
  • -
  • dst_nodata (int or float, optional) – Destination nodata value which will be ignored for interpolation. -Defaults to None, in which case the value of src_nodata -will be used if provided, or 0 otherwise.
  • -
-
Returns:

-

-
-
- -
-
-cw_tiler.utils.get_wgs84_bounds(source)[source]
-

Transform dataset bounds from source crs to wgs84.

- --- - - - - - - - -
Parameters:source (str or rasterio.io.DatasetReader) – Source dataset to get bounds transformation for. Can either be a string -path to a dataset file or an opened -rasterio.io.DatasetReader.
Returns:wgs_bounds – Bounds tuple for source in wgs84 crs with shape (W, S, E, N).
Return type:tuple
-
- -
-
-cw_tiler.utils.tile_exists_utm(boundsSrc, boundsTile)[source]
-

Check if suggested tile is within bounds.

- --- - - - - - - - -
Parameters:
    -
  • boundsSrc (list-like) – Bounding box limits for the source data in the shape (W, S, E, N).
  • -
  • boundsTile (list-like) – Bounding box limits for the target tile in the shape (W, S, E, N).
  • -
-
Returns:

Do the boundsSrc and boundsTile bounding boxes overlap?

-
Return type:

bool

-
-
- -
-
-cw_tiler.utils.tile_read_utm(source, bounds, tilesize, indexes=[1], nodata=None, alpha=None, dst_crs='EPSG:3857', verbose=False, boundless=False)[source]
-

Read data and mask.

- --- - - - - - -
Parameters:
    -
  • source (str or rasterio.io.DatasetReader) – input file path or rasterio.io.DatasetReader object.
  • -
  • bounds ((W, S, E, N) tuple) – bounds in dst_crs .
  • -
  • tilesize (int) – Length of one edge of the output tile in pixels.
  • -
  • indexes (list of ints or int, optional) – Channel index(es) to output. Returns a 3D np.ndarray of -shape (C, Y, X) if indexes is a list, or a 2D array if indexes is -an int channel index. Defaults to 1.
  • -
  • nodata (int or float, optional) – nodata value to use in rasterio.vrt.WarpedVRT. -Defaults to None (use all data in warping).
  • -
  • alpha (int, optional) – Force alphaband if not present in the dataset metadata. Defaults to -None (don’t force).
  • -
  • dst_crs (str, optional) – Destination coordinate reference system. Defaults to "EPSG:3857" -(Web Mercator)
  • -
  • verbose (bool, optional) – Verbose text output. Defaults to False.
  • -
  • boundless (bool, optional) – This argument is deprecated and should never be used.
  • -
-
Returns:

    -
  • data (np.ndarray) – int pixel values. Shape is (C, Y, X) if retrieving multiple -channels, (Y, X) otherwise.
  • -
  • mask (np.ndarray) – int mask indicating which pixels contain information and which are -nodata. Pixels containing data have value 255, nodata -pixels have value 0.
  • -
  • window (rasterio.windows.Window) – rasterio.windows.Window object indicating the raster -location of the dataset subregion being returned in data.
  • -
  • window_transform (affine.Affine) – Affine transformation for window .
  • -
-

-
-
- -
-
-cw_tiler.utils.utm_getZone(longitude)[source]
-

Calculate UTM Zone from Longitude.

- --- - - - - - - - -
Parameters:longitude (float) – longitude coordinate (Degrees.decimal degrees)
Returns:out – UTM Zone number.
Return type:int
-
- -
-
-cw_tiler.utils.utm_isNorthern(latitude)[source]
-

Determine if a latitude coordinate is in the northern hemisphere.

- --- - - - - - - - -
Parameters:latitude (float) – latitude coordinate (Deg.decimal degrees)
Returns:outTrue if latitude is in the northern hemisphere, False -otherwise.
Return type:bool
-
- -
-
-

Vector utilities

-
-
-cw_tiler.vector_utils.clip_gdf(gdf, poly_to_cut, min_partial_perc=0.0, geom_type='Polygon', use_sindex=True)[source]
-

Clip GDF to a provided polygon.

-
-

Note

-

Clips objects within gdf to the region defined by -poly_to_cut. Also adds several columns to the output:

-
-
origarea
-
The original area of the polygons (only used if geom_type == -"Polygon").
-
origlen
-
The original length of the objects (only used if geom_type == -"LineString").
-
partialDec
-
The fraction of the object that remains after clipping -(fraction of area for Polygons, fraction of length for -LineStrings.) Can filter based on this by using min_partial_perc.
-
truncated
-
Boolean indicator of whether or not an object was clipped.
-
-
- --- - - - - - - - -
Parameters:
    -
  • gdf (geopandas.GeoDataFrame) – A geopandas.GeoDataFrame of polygons to clip.
  • -
  • poly_to_cut (shapely.geometry.Polygon) – The polygon to clip objects in gdf to.
  • -
  • min_partial_perc (float, optional) – The minimum fraction of an object in gdf that must be -preserved. Defaults to 0.0 (include any object if any part remains -following clipping).
  • -
  • geom_type (str, optional) – Type of objects in gdf. Can be one of -["Polygon", "LineString"] . Defaults to "Polygon" .
  • -
  • use_sindex (bool, optional) – Use the gdf sindex be used for searching. Improves efficiency -but requires libspatialindex .
  • -
-
Returns:

cutGeoDFgdf with all contained objects clipped to poly_to_cut . -See notes above for details on additional clipping columns added.

-
Return type:

geopandas.GeoDataFrame

-
-
- -
-
-cw_tiler.vector_utils.rasterize_gdf(gdf, src_shape, burn_value=1, src_transform=Affine(1.0, 0.0, 0.0, 0.0, 1.0, 0.0))[source]
-

Convert a GeoDataFrame to a binary image (array) mask.

-

Uses rasterio.features.rasterize() to generate a raster mask from -object geometries in gdf .

- --- - - - - - - - -
Parameters:
    -
  • gdf (geopandas.GeoDataFrame) – A geopandas.GeoDataFrame of objects to convert into a mask.
  • -
  • src_shape (list-like of 2 ints) – Shape of the output array in (Y, X) pixel units.
  • -
  • burn_value (int in range(0, 255), optional) – Integer value for pixels corresponding to objects from gdf . -Defaults to 1.
  • -
  • src_transform (affine.Affine, optional) – Affine transformation for the output raster. If not provided, defaults -to arbitrary pixel units.
  • -
-
Returns:

img – A NumPy array of integers with 0s where no pixels from objects in -gdf exist, and burn_value where they do. Shape is -defined by src_shape.

-
Return type:

np.ndarray, dtype uint8

-
-
- -
-
-cw_tiler.vector_utils.read_vector_file(geoFileName)[source]
-

Read Fiona-Supported Files into GeoPandas GeoDataFrame.

-
-

Warning

-

This will raise an exception for empty GeoJSON files, which GDAL and Fiona -cannot read. try/except the Fiona.errors.DriverError or -Fiona._err.CPLE_OpenFailedError if you must use this.

-
-
- -
-
-cw_tiler.vector_utils.search_gdf_bounds(gdf, tile_bounds)[source]
-

Use tile_bounds to subset gdf and return the intersect.

- --- - - - - - - - -
Parameters: -
Returns:

smallGdf – The subset of gdf that overlaps with tile_bounds .

-
Return type:

geopandas.GeoDataFrame

-
-
- -
-
-cw_tiler.vector_utils.search_gdf_polygon(gdf, tile_polygon)[source]
-

Find polygons in a GeoDataFrame that overlap with tile_polygon .

- --- - - - - - - - -
Parameters: -
Returns:

precise_matches – The subset of gdf that overlaps with tile_polygon . If -there are no overlaps, this will return an empty -geopandas.GeoDataFrame.

-
Return type:

geopandas.GeoDataFrame

-
-
- -
-
-cw_tiler.vector_utils.transformToUTM(gdf, utm_crs, estimate=True, calculate_sindex=True)[source]
-

Transform GeoDataFrame to UTM coordinate reference system.

- --- - - - - - - - -
Parameters: -
Returns:

gdf – The input geopandas.GeoDataFrame converted to -utm_crs coordinate reference system.

-
Return type:

geopandas.GeoDataFrame

-
-
- -
-
-cw_tiler.vector_utils.vector_tile_utm(gdf, tile_bounds, min_partial_perc=0.1, geom_type='Polygon', use_sindex=True)[source]
-

Wrapper for clip_gdf() that converts tile_bounds to a polygon.

- --- - - - - - - - -
Parameters:
    -
  • gdf (geopandas.GeoDataFrame) – A geopandas.GeoDataFrame of polygons to clip.
  • -
  • tile_bounds (list-like of floats) – list of shape (W, S, E, N) denoting the boundaries of an -imagery tile. Converted to a polygon for clip_gdf().
  • -
  • min_partial_perc (float) – The minimum fraction of an object in gdf that must be -preserved. Defaults to 0.0 (include any object if any part remains -following clipping).
  • -
  • use_sindex (bool, optional) – Use the gdf sindex be used for searching. Improves efficiency -but requires libspatialindex .
  • -
-
Returns:

small_gdfgdf with all contained objects clipped to tile_bounds. -See notes above for details on additional clipping columns added.

-
Return type:

geopandas.GeoDataFrame

-
-
- -
-
-
-
-
-

Indices and tables

- -
-
- - -
- -
- - -
-
- -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/old_docs/tiler_docs/_build/html/objects.inv b/old_docs/tiler_docs/_build/html/objects.inv deleted file mode 100644 index bfecb4b3..00000000 --- a/old_docs/tiler_docs/_build/html/objects.inv +++ /dev/null @@ -1,7 +0,0 @@ -# Sphinx inventory version 2 -# Project: cw-tiler -# Version: -# The remainder of this file is compressed using zlib. -xڝ=O0 H0 -$ - $(6"8(&'j2]yvVnSl eX?uXSuދr+)pz$E>HаE{TQS0pC)C >VW\ZJIW7r9[\OGӓh]X N&}Sh Stֵ`B ެusԪMYYAl_ V5ˤG=63Yah[8|Y-j#Wil :vYҋfsNݲ'V$v 7e(]:0Ni~؏ Z^E㒔?.]oiDzKXLtoDLSn< e"fQ;u? \ No newline at end of file diff --git a/old_docs/tiler_docs/_build/html/py-modindex.html b/old_docs/tiler_docs/_build/html/py-modindex.html deleted file mode 100644 index 9f2faef7..00000000 --- a/old_docs/tiler_docs/_build/html/py-modindex.html +++ /dev/null @@ -1,225 +0,0 @@ - - - - - - - - - - - Python Module Index — cw-tiler 0.2.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -
- - - - - -
- -
- - - - - - - - - - - - - - - - - -
- -
    - -
  • Docs »
  • - -
  • Python Module Index
  • - - -
  • - -
  • - -
- - -
-
-
-
- - -

Python Module Index

- -
- c -
- - - - - - - - - - - - - - - - -
 
- c
- cw_tiler -
    - cw_tiler.main -
    - cw_tiler.utils -
    - cw_tiler.vector_utils -
- - -
- -
- - -
-
- -
- -
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/old_docs/tiler_docs/_build/html/search.html b/old_docs/tiler_docs/_build/html/search.html deleted file mode 100644 index 15ef9cb4..00000000 --- a/old_docs/tiler_docs/_build/html/search.html +++ /dev/null @@ -1,211 +0,0 @@ - - - - - - - - - - - Search — cw-tiler 0.2.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -
- - - - - -
- -
- - - - - - - - - - - - - - - - - -
- -
    - -
  • Docs »
  • - -
  • Search
  • - - -
  • - - - -
  • - -
- - -
-
-
-
- - - - -
- -
- -
- -
- - -
-
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/old_docs/tiler_docs/_build/html/searchindex.js b/old_docs/tiler_docs/_build/html/searchindex.js deleted file mode 100644 index 4c306fd3..00000000 --- a/old_docs/tiler_docs/_build/html/searchindex.js +++ /dev/null @@ -1 +0,0 @@ -Search.setIndex({docnames:["index"],envversion:{"sphinx.domains.c":1,"sphinx.domains.changeset":1,"sphinx.domains.cpp":1,"sphinx.domains.javascript":1,"sphinx.domains.math":2,"sphinx.domains.python":1,"sphinx.domains.rst":1,"sphinx.domains.std":1,"sphinx.ext.intersphinx":1,"sphinx.ext.todo":1,"sphinx.ext.viewcode":1,sphinx:55},filenames:["index.rst"],objects:{"cw_tiler.main":{calculate_analysis_grid:[0,1,1,""],calculate_anchor_points:[0,1,1,""],calculate_cells:[0,1,1,""],get_chip:[0,1,1,""],tile_utm:[0,1,1,""],tile_utm_source:[0,1,1,""]},"cw_tiler.utils":{calculate_UTM_crs:[0,1,1,""],get_utm_bounds:[0,1,1,""],get_utm_vrt:[0,1,1,""],get_utm_vrt_profile:[0,1,1,""],get_wgs84_bounds:[0,1,1,""],tile_exists_utm:[0,1,1,""],tile_read_utm:[0,1,1,""],utm_getZone:[0,1,1,""],utm_isNorthern:[0,1,1,""]},"cw_tiler.vector_utils":{clip_gdf:[0,1,1,""],rasterize_gdf:[0,1,1,""],read_vector_file:[0,1,1,""],search_gdf_bounds:[0,1,1,""],search_gdf_polygon:[0,1,1,""],transformToUTM:[0,1,1,""],vector_tile_utm:[0,1,1,""]},cw_tiler:{main:[0,0,0,"-"],utils:[0,0,0,"-"],vector_utils:[0,0,0,"-"]}},objnames:{"0":["py","module","Python module"],"1":["py","function","Python function"]},objtypes:{"0":"py:module","1":"py:function"},terms:{"400m":0,"800m":0,"900m":0,"boolean":0,"case":0,"class":[],"default":0,"enum":0,"float":0,"int":0,"return":0,"true":0,"try":0,CRS:0,For:0,The:0,Use:0,Uses:0,__note:[],_err:0,abov:0,add:0,added:0,addit:0,affin:0,after:0,all:0,alpha:0,alphaband:0,also:0,altern:0,analysi:0,anchor:0,anchor_point_list_dict:0,ani:0,apart:0,appli:0,arbitrari:0,area:0,argument:0,around:0,arrai:0,author:0,averag:0,band:0,base:0,bbox:0,being:0,between:0,beyond:0,bilinear:0,binari:0,bool:0,both:0,bound:0,boundari:0,boundless:0,boundssrc:0,boundstil:0,box:0,bsd:0,burn_valu:0,calcul:0,calculate_analysis_grid:0,calculate_anchor_point:0,calculate_cel:0,calculate_sindex:0,calculate_utm_cr:0,can:0,cannot:0,cell:0,cell_size_met:0,cells_list_dict:0,channel:0,check:0,chip:0,claus:0,clip:0,clip_gdf:0,column:0,compris:0,contain:0,convert:0,coord:0,coordin:0,copyright:0,corner:0,correspond:0,cple_openfailederror:0,creat:0,crs:0,cubic:0,cutgeodf:0,cw_tiler:0,data:0,dataset:0,datasetread:0,decim:0,defin:0,deg:0,degre:0,denot:0,deprec:0,desir:0,destin:0,detail:0,determin:0,dict:0,dictionari:0,direct:0,distanc:0,doc:0,don:0,drivererror:0,dst_cr:0,dst_nodata:0,dtype:0,dure:0,each:0,eastern:0,edg:0,effici:0,either:0,empti:0,enabl:0,ensur:0,epsg:0,error:0,estim:0,even:0,exampl:0,except:0,exist:0,extend:0,extent:0,extract:0,fail:0,fals:0,featur:0,file:0,filter:0,find:0,fiona:0,follow:0,forc:0,four:0,fraction:0,from:0,func:[],gdal:0,gdf:0,gener:0,geodatafram:0,geofilenam:0,geojson:0,geom_typ:0,geometri:0,geopanda:0,geotiff:0,get:0,get_chip:0,get_utm_bound:0,get_utm_vrt:0,get_utm_vrt_profil:0,get_wgs84_bound:0,ground:0,gsd:0,half:0,have:0,height:0,hemispher:0,howev:0,ignor:0,imag:0,imageri:0,img:0,improv:0,includ:0,index:0,inform:0,input:0,instanc:0,instead:0,integ:0,intern:0,interpol:0,intersect:0,inward:0,kei:0,latitud:0,left:0,length:0,libspatialindex:0,licens:0,like:0,limit:0,linestr:0,list:0,ll_x:0,ll_y:0,load:0,loat:0,locat:0,longer:0,longitud:0,lower:0,main:0,mask:0,max_i:0,max_latitud:0,max_longitud:0,max_x:0,memori:0,mercat:0,metadata:0,meter:0,method:0,min_i:0,min_latitud:0,min_longitud:0,min_partial_perc:0,min_x:0,minimum:0,modul:0,more:0,multipl:0,must:0,ndarrai:0,nearest:0,never:0,nodata:0,non:0,none:0,northern:0,note:0,number:0,numpi:0,obj:[],object:0,one:0,onli:0,open:0,option:0,origarea:0,origin:0,origlen:0,other:0,otherwis:0,out:0,output:0,outward:0,overlap:0,page:0,pair:0,param:[],paramet:0,part:0,partialdec:0,pass:0,path:0,permit:0,pixel:0,place:0,point:0,poly_to_cut:0,polygon:0,posit:0,pre:0,precise_match:0,present:0,preserv:0,profil:0,proj4:0,project:0,provid:0,quad_spac:0,rais:0,rang:0,rasterio:0,rasterize_gdf:0,read:0,read_vector_fil:0,ref:[],refer:0,region:0,remain:0,requir:0,resampl:0,retriev:0,right:0,round:0,same:0,sampl:0,search:0,search_gdf_bound:0,search_gdf_polygon:0,see:0,set:0,sever:0,shape:0,should:0,sinc:0,sindex:0,singl:0,size:0,small_gdf:0,smallgdf:0,snaptogrid:0,sourc:0,southern:0,spatial:0,specif:0,specifi:0,split:0,src:0,src_nodata:0,src_shape:0,src_transform:0,step:0,str:0,stride:0,stride_size_met:0,string:0,subregion:0,subset:0,suggest:0,support:0,system:0,take:0,target:0,text:0,than:0,thei:0,thi:0,those:0,tile_bound:0,tile_exists_utm:0,tile_polygon:0,tile_read_utm:0,tile_size_pixel:0,tile_utm:0,tile_utm_sourc:0,tiles:0,transform:0,transformtoutm:0,truncat:0,tupl:0,type:0,uint8:0,under:0,unit:0,upper:0,ur_i:0,ur_x:0,use:0,use_sindex:0,used:0,uses:0,using:0,utm:0,utm_bound:0,utm_cr:0,utm_epsg:0,utm_getzon:0,utm_isnorthern:0,valu:0,vector_tile_utm:0,vector_util:0,verbos:0,version:0,virtual:0,vrt:0,warp:0,warpedvrt:0,web:0,well:0,western:0,wgs84:0,wgs_bound:0,where:0,whether:0,which:0,whose:0,width:0,window:0,window_transform:0,within:0,would:0,wrapper:0,you:0,zone:0},titles:["CosmiQ Works Tiler (cw-tiler) Documentation"],titleterms:{"function":0,cosmiq:0,document:0,indic:0,raster:0,section:0,tabl:0,tile:0,tiler:0,util:0,vector:0,work:0}}) \ No newline at end of file diff --git a/old_docs/tiler_docs/conf.py b/old_docs/tiler_docs/conf.py deleted file mode 100644 index 5dee51a5..00000000 --- a/old_docs/tiler_docs/conf.py +++ /dev/null @@ -1,204 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Configuration file for the Sphinx documentation builder. -# -# This file does only contain a selection of the most common options. For a -# full list see the documentation: -# http://www.sphinx-doc.org/en/master/config - -# -- 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('.')) - - -# -- Project information ----------------------------------------------------- - -project = 'cw-tiler' -copyright = '2018, CosmiQ Works' -author = 'David Lindenbaum, Nick Weir' - -# The short X.Y version -version = '0.2' -# The full version, including alpha/beta/rc tags -release = '0.2.0' - - -# -- 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.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.intersphinx', - 'sphinx.ext.todo', - 'sphinx.ext.coverage', - 'sphinx.ext.mathjax', - 'sphinx.ext.viewcode', - 'sphinx.ext.githubpages', - '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 master toctree document. -master_doc = 'index' - -# 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 - -# 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 = ['_build', 'Thumbs.db', '.DS_Store'] - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = None - - -# -- 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' - -# 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 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'] - -# Custom sidebar templates, must be a dictionary that maps document names -# to template names. -# -# The default sidebars (for documents that don't match any pattern) are -# defined by theme itself. Builtin themes are using these templates by -# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', -# 'searchbox.html']``. -# -# html_sidebars = {} - - -# -- Options for HTMLHelp output --------------------------------------------- - -# Output file base name for HTML help builder. -htmlhelp_basename = 'cw-tilerdoc' - - -# -- 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, 'cw-tiler.tex', 'cw-tiler Documentation', - 'David Lindenbaum, Nick Weir', 'manual'), -] - - -# -- 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, 'cw-tiler', 'cw-tiler Documentation', - [author], 1) -] - - -# -- 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, 'cw-tiler', 'cw-tiler Documentation', - author, 'cw-tiler', 'One line description of project.', - 'Miscellaneous'), -] - - -# -- Options for Epub output ------------------------------------------------- - -# Bibliographic Dublin Core info. -epub_title = project - -# The unique identifier of the text. This can be a ISBN number -# or the project homepage. -# -# epub_identifier = '' - -# A unique identification for the text. -# -# epub_uid = '' - -# A list of files that should not be packed into the epub file. -epub_exclude_files = ['search.html'] - - -# -- Extension configuration ------------------------------------------------- - -# -- Options for intersphinx extension --------------------------------------- - -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = { - "python": ('https://docs.python.org/', None), - "rasterio": ('https://rasterio.readthedocs.io/en/latest/', None), - "pandas": ('http://pandas.pydata.org/pandas-docs/stable/', None), - "geopandas": ('http://geopandas.org/', None), - "rtree": ('http://toblerity.org/rtree/', None), - "shapely": ('https://shapely.readthedocs.io/en/stable/', None), - "numpy": ('http://docs.scipy.org/doc/numpy/', None) - } - - -# -- Options for todo extension ---------------------------------------------- - -# If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = True diff --git a/old_docs/tiler_docs/index.rst b/old_docs/tiler_docs/index.rst deleted file mode 100644 index ae3618aa..00000000 --- a/old_docs/tiler_docs/index.rst +++ /dev/null @@ -1,51 +0,0 @@ -CosmiQ Works Tiler (`cw-tiler `__) Documentation -==================================================================================== -:Author: `CosmiQ Works `__ -:Version: |version| -:Copyright: 2018, CosmiQ Works -:License: This work is licensed under the `BSD 3-Clause license`__. - -.. __: https://opensource.org/licenses/BSD-3-Clause - -Sections --------- -* :ref:`tiling-functions` -* :ref:`raster-utilities` -* :ref:`vector-utilities` - -.. _tiling-functions: - -Tiling functions ----------------- -.. automodule:: cw_tiler.main - :members: - -.. _utility-functions: - -Utility functions ------------------ - -.. _raster-utilities: - -Raster utilities -^^^^^^^^^^^^^^^^ -.. automodule:: cw_tiler.utils - :members: - -.. _vector-utilities: - -Vector utilities -^^^^^^^^^^^^^^^^ -.. automodule:: cw_tiler.vector_utils - :members: - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - -Indices and tables ------------------- - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/old_docs/tiler_docs/make.bat b/old_docs/tiler_docs/make.bat deleted file mode 100644 index 27f573b8..00000000 --- a/old_docs/tiler_docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=. -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% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% - -:end -popd diff --git a/old_setup/geodata_setup.py b/old_setup/geodata_setup.py deleted file mode 100644 index 75bfb80f..00000000 --- a/old_setup/geodata_setup.py +++ /dev/null @@ -1,47 +0,0 @@ -from setuptools import setup, find_packages -version = '0.1.0' - -# Runtime requirements. -inst_reqs = ["shapely==1.6.4", - "rtree==0.8.3", - "geopandas==0.4.0", - "pandas==0.23.4", - "networkx==2.2", - "rasterio==1.0.18", - "tqdm==4.28.1", - "numpy==1.15.4", - "scipy==1.2.0", - "scikit-image==0.14", - "affine==2.2.1"] - -extra_reqs = { - 'test': ['mock', 'pytest', 'pytest-cov', 'codecov']} - -console_scripts = [ - 'geotransform_footprints=cw_geodata.bin.geotransform_footprints:main', - 'make_masks=cw_geodata.bin.make_masks:main', - 'make_graphs=cw_geodata.bin.make_graphs:main' - ] - -setup(name='cw_geodata', - version=version, - description=u"""Geospatial raster and vector data processing for ML""", - classifiers=[ - 'Intended Audience :: Information Technology', - 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: BSD License', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 2.7', - 'Topic :: Scientific/Engineering :: GIS'], - keywords='spacenet machinelearning gis geojson', - author=u"Nicholas Weir", - author_email='nweir@iqt.org', - url='https://github.com/CosmiQ/cw-geodata', - license='Apache-2.0', - packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), - zip_safe=False, - include_package_data=True, - install_requires=inst_reqs, - extras_require=extra_reqs, - entry_points={'console_scripts': console_scripts} - ) diff --git a/readthedocs-environment.yml b/readthedocs-environment.yml deleted file mode 100644 index c2c632b8..00000000 --- a/readthedocs-environment.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: solaris -dependencies: - - python=3.6 - - pip=19.0 - - shapely=1.6.4 - - fiona=1.8.6 - - pandas=0.23.4 - - geopandas=0.4.0 - - opencv=4.1.0 - - numpy=1.15.4 - - tqdm=4.28.1 - - rtree=0.8.3 - - networkx=2.2 - - nbsphinx=0.4.2 - - rasterio=1.0.18 - - scipy=1.2.0 - - scikit-image=0.15.dev0 - - tensorflow=1.13.1 - - pytorch=1.0.1 - - pyyaml=5.1 - - pip: - - affine=2.2.1 - - albumentations=0.2.2 - - rio-tiler=1.2.4 diff --git a/readthedocs.yml b/readthedocs.yml deleted file mode 100644 index f6c7b0a1..00000000 --- a/readthedocs.yml +++ /dev/null @@ -1,27 +0,0 @@ -language: python -sudo: true -python: - - "3.6" - -# command to install dependencies -install: - - sudo apt-get update - - wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh; - - bash miniconda.sh -b -p $HOME/miniconda - - export PATH="$HOME/miniconda/bin:$PATH" - - hash -r - - conda config --set always_yes yes --set changeps1 no - - conda update -q conda - # Useful for debugging any issues with conda - - conda info -a - - conda env create -n solaris --file=environment.yml - - source activate solaris - - python --version - - pip install -q -e .[test] - - pip install codecov pytest pytest-cov -# command to run tests -script: - - pytest --cov=./ - -after_success: - - codecov diff --git a/renovate.json b/renovate.json index 4571a71b..3ea65c78 100644 --- a/renovate.json +++ b/renovate.json @@ -7,7 +7,7 @@ }, "assignees": ["nrweir"], "baseBranches": ["dev"], - "ignoreDeps": ["GDAL"], + "ignoreDeps": ["GDAL", "tensorflow"], "labels": ["dependencies"], "reviewers": ["nrweir"] } diff --git a/requirements.txt b/requirements.txt index a9092595..77653291 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,9 +4,10 @@ pandas==0.24.2 geopandas==0.5.0 opencv-python==4.1.0.25 numpy==1.16.4 -tqdm==4.32.1 -GDAL==2.4.0 # pyup: ignore +tqdm==4.32.2 +GDAL==2.4.0 rtree==0.8.3 +matplotlib==3.1.0 networkx==2.3 rasterio==1.0.23 scipy==1.3.0 @@ -17,4 +18,7 @@ torchvision==0.3.0 affine==2.2.2 albumentations==0.2.3 rio-tiler==1.2.7 -pyyaml==5.1 +urllib3==1.24.3 +pyyaml==5.1.1 +pyproj==2.2.0 +rio-cogeo==1.0.0 diff --git a/setup.py b/setup.py index c41ae043..d6d2fa76 100644 --- a/setup.py +++ b/setup.py @@ -56,28 +56,34 @@ def check_output(cmd): "environment first, then installing solaris there.") - - -inst_reqs = ['shapely>=1.6.4', - 'fiona>=1.8.6', - 'pandas>=0.23.4', - 'geopandas>=0.4.0', - 'opencv-python==4.1.0.25', - 'numpy>=1.15.4', - 'tqdm>=4.28.1', - 'rtree>=0.8.3', - 'networkx>=2.2', - 'rasterio>=1.0.18', - 'scipy>=1.2.0', - 'scikit-image>=0.14.0', - 'tensorflow>=1.13.1', - 'torch>=1.1.0', - 'affine>=2.2.2', - 'albumentations>=0.2.3', - 'rio-tiler>=1.2.7', - 'pyyaml>=5.1', - 'torchvision>=0.3.0' - ] +on_rtd = os.environ.get('READTHEDOCS') == 'True' + +if on_rtd: + inst_reqs = ['sphinx_bootstrap_theme'] +else: + inst_reqs = ['shapely>=1.6.4', + 'fiona>=1.8.6', + 'pandas>=0.23.4', + 'geopandas>=0.4.0', + 'opencv-python==4.1.0.25', + 'numpy>=1.15.4', + 'tqdm>=4.32.2', + 'GDAL>=2.4.0', + 'rtree>=0.8.3', + 'networkx>=2.2', + 'rasterio>=1.0.18', + 'rio-cogeo>-1.0.0', + 'scipy>=1.2.0', + 'urllib3==1.24.3', + 'scikit-image>=0.14.0', + 'tensorflow==1.13.1', + 'torch>=1.1.0', + 'matplotlib>=3.1.0', + 'affine>=2.2.2', + 'albumentations>=0.2.3', + 'rio-tiler>=1.2.7', + 'pyyaml>=5.1', + 'torchvision>=0.3.0'] extra_reqs = { @@ -107,7 +113,8 @@ def check_output(cmd): 'make_graphs = solaris.bin.make_graphs:main', 'make_masks = solaris.bin.make_masks:main', 'mask_to_polygons = solaris.bin.mask_to_polygons:main', - 'spacenet_eval = solaris.bin.spacenet_eval:main' + 'spacenet_eval = solaris.bin.spacenet_eval:main', + 'solaris_run_ml = solaris.bin.solaris_run_ml:main' ] } ) diff --git a/solaris/__init__.py b/solaris/__init__.py index 906d596c..c26ab1c7 100644 --- a/solaris/__init__.py +++ b/solaris/__init__.py @@ -1,3 +1,3 @@ from . import bin, data, eval, nets, raster, tile, utils, vector -__version__ = "0.0.3" +__version__ = "0.1.0" diff --git a/solaris/bin/geotransform_footprints.py b/solaris/bin/geotransform_footprints.py index 0f27593a..f6249961 100644 --- a/solaris/bin/geotransform_footprints.py +++ b/solaris/bin/geotransform_footprints.py @@ -134,8 +134,8 @@ def main(): return result else: with Pool(processes=args.workers) as pool: - result = tqdm(pool.map(_func_wrapper, zip(repeat(func_to_call), - arg_dict_list))) + result = tqdm(pool.starmap(_func_wrapper, zip(repeat(func_to_call), + arg_dict_list))) pool.close() diff --git a/solaris/bin/make_graphs.py b/solaris/bin/make_graphs.py index de255a70..07bc2e07 100644 --- a/solaris/bin/make_graphs.py +++ b/solaris/bin/make_graphs.py @@ -101,8 +101,9 @@ def main(): else: with Pool(processes=args.workers) as pool: - result = tqdm(pool.map(_func_wrapper, zip(repeat(geojson_to_graph), - arg_dict_list))) + result = tqdm(pool.starmap(_func_wrapper, + zip(repeat(geojson_to_graph), + arg_dict_list))) pool.close() diff --git a/solaris/bin/make_masks.py b/solaris/bin/make_masks.py index 0eb0545a..99c3254f 100644 --- a/solaris/bin/make_masks.py +++ b/solaris/bin/make_masks.py @@ -16,65 +16,69 @@ def main(): parser.add_argument('--source_file', '-s', type=str, help='Full path to file to create mask from.') parser.add_argument('--reference_image', '-r', type=str, - help='Full path to a georegistered image in the same' + - ' coordinate system (for conversion to pixels) or in' + - ' the target coordinate system (for conversion to a' + + help='Full path to a georegistered image in the same' + ' coordinate system (for conversion to pixels) or in' + ' the target coordinate system (for conversion to a' ' geographic coordinate reference system).') parser.add_argument('--output_path', '-o', type=str, - help='Full path to the output file for the converted' + + help='Full path to the output file for the converted' 'footprints.') parser.add_argument('--geometry_column', '-g', type=str, - default='geometry', help='The column containing' + - ' footprint polygons to transform. If not provided,' + + default='geometry', help='The column containing' + ' footprint polygons to transform. If not provided,' ' defaults to "geometry".') parser.add_argument('--transform', '-t', action='store_true', - default=False, help='Use this flag if the geometries' + - ' are in a georeferenced coordinate system and' + + default=False, help='Use this flag if the geometries' + ' are in a georeferenced coordinate system and' ' need to be converted to pixel coordinates.') parser.add_argument('--value', '-v', type=int, default=255, - help='The value to set for labeled pixels in the' + + help='The value to set for labeled pixels in the' ' mask. Defaults to 255.') parser.add_argument('--footprint', '-f', action='store_true', - default=False, help='If this flag is set, the mask' + - ' will include filled-in building footprints as a' + + default=False, help='If this flag is set, the mask' + ' will include filled-in building footprints as a' ' channel.') parser.add_argument('--edge', '-e', action='store_true', - default=False, help='If this flag is set, the mask' + + default=False, help='If this flag is set, the mask' ' will include the building edges as a channel.') parser.add_argument('--edge_width', '-ew', type=int, default=3, - help='Pixel thickness of the edges in the edge mask.' + + help='Pixel thickness of the edges in the edge mask.' ' Defaults to 3 if not provided.') parser.add_argument('--edge_type', '-et', type=str, default='inner', - help='Type of edge: either inner or outer. Defaults' + + help='Type of edge: either inner or outer. Defaults' ' to inner if not provided.') parser.add_argument('--contact', '-c', action='store_true', - default=False, help='If this flag is set, the mask' + - ' will include contact points between buildings as a' + + default=False, help='If this flag is set, the mask' + ' will include contact points between buildings as a' ' channel.') parser.add_argument('--contact_spacing', '-cs', type=int, default=10, - help='Sets the maximum distance between two' + - ' buildings, in source file units, that will be' + + help='Sets the maximum distance between two' + ' buildings, in source file units, that will be' ' identified as a contact. Defaults to 10.') + parser.add_argument('--metric_widths', '-m', action='store_true', + default=False, help='Use this flag if any widths ' + '(--contact-spacing specifically) should be in metric ' + 'units instead of pixel units.') parser.add_argument('--batch', '-b', action='store_true', default=False, - help='Use this flag if you wish to operate on' + - ' multiple files in batch. In this case,' + - ' --argument-csv must be provided. See help' + - ' for --argument_csv and the codebase docs at' + - ' https://cw-geodata.readthedocs.io for more info.') + help='Use this flag if you wish to operate on' + ' multiple files in batch. In this case,' + ' --argument-csv must be provided. See help' + ' for --argument_csv and the codebase docs at' + ' https://solaris.readthedocs.io for more info.') parser.add_argument('--argument_csv', '-a', type=str, - help='The reference file for variable values for' + - ' batch processing. It must contain columns to pass' + - ' the source_file and reference_image arguments, and' + - ' can additionally contain columns providing the' + - ' footprint_column and decimal_precision arguments' + - ' if you wish to define them differently for items' + - ' in the batch. These columns must have the same' + - ' names as the corresponding arguments. See the ' + - ' usage recipes at https://cw-geodata.readthedocs.io' + + help='The reference file for variable values for' + ' batch processing. It must contain columns to pass' + ' the source_file and reference_image arguments, and' + ' can additionally contain columns providing the' + ' footprint_column and decimal_precision arguments' + ' if you wish to define them differently for items' + ' in the batch. These columns must have the same' + ' names as the corresponding arguments. See the ' + ' usage recipes at https://solaris.readthedocs.io' ' for examples.') parser.add_argument('--workers', '-w', type=int, default=1, - help='The number of parallel processing workers to' + - ' use. This should not exceed the number of CPU' + + help='The number of parallel processing workers to' + ' use. This should not exceed the number of CPU' ' cores available.') args = parser.parse_args() @@ -140,6 +144,7 @@ def main(): arg_df['output_path'] = [args.output_path] arg_df['geometry_column'] = [args.geometry_column] arg_df['transform'] = [args.transform] + arg_df['metric'] = [args.metric_widths] arg_df['value'] = [args.value] arg_df['edge_width'] = [args.edge_width] arg_df['edge_type'] = [args.edge_type] @@ -167,8 +172,9 @@ def main(): else: with Pool(processes=args.workers) as pool: - result = tqdm(pool.map(_func_wrapper, zip(repeat(df_to_px_mask), - arg_dict_list))) + result = tqdm(pool.starmap(_func_wrapper, + zip(repeat(df_to_px_mask), + arg_dict_list))) pool.close() diff --git a/solaris/bin/solaris_run_ml.py b/solaris/bin/solaris_run_ml.py new file mode 100644 index 00000000..1bedb2d7 --- /dev/null +++ b/solaris/bin/solaris_run_ml.py @@ -0,0 +1,38 @@ +import argparse +import os +import pandas as pd +from ..utils.config import parse +from ..nets.train import Trainer +from ..nets.infer import Inferer + + +def main(): + + parser = argparse.ArgumentParser( + description='Run a Solaris ML pipeline based on a config YAML', + argument_default=None) + + parser.add_argument('--config', '-c', type=str, required=True, + help="Full path to a YAML-formatted config file " + "specifying parameters for model training and/or " + "inference.") + + args = parser.parse_args() + + if not os.path.exists(args.config): + raise ValueError('The configuration file cannot be found at the path ' + 'specified.') + + config = parse(args.config) + + if config['train']: + trainer = Trainer(config) + trainer.train() + if config['infer']: + inferer = Inferer(config) + inf_df = pd.read_csv(config['inference_data_csv']) + inferer(inf_df) + + +if __name__ == '__main__': + main() diff --git a/solaris/bin/spacenet_eval.py b/solaris/bin/spacenet_eval.py index e42f7563..2b09c921 100644 --- a/solaris/bin/spacenet_eval.py +++ b/solaris/bin/spacenet_eval.py @@ -1,10 +1,9 @@ """Script for executing eval for SpaceNet challenges.""" -from __future__ import print_function, with_statement, division -from ..eval.challenges import off_nadir_dataset -from ..eval.challenges import spacenet_buildings2_dataset +from ..eval.challenges import off_nadir_buildings +from ..eval.challenges import spacenet_buildings_2 import argparse import pandas as pd -supported_challenges = ['off-nadir', 'spaceNet-buildings2'] +supported_challenges = ['off-nadir', 'spacenet-buildings2'] # , 'spaceNet-buildings1', 'spacenet-roads1', 'buildings', 'roads'] @@ -29,14 +28,13 @@ def main(): if args.challenge.lower() == 'off-nadir': evalSettings = {'miniou': 0.5, - 'minArea': 20} - results_DF, results_DF_Full = off_nadir_dataset.eval_off_nadir( + 'min_area': 20} + results_DF, results_DF_Full = off_nadir_buildings( prop_csv=prop_file, truth_csv=truth_file, **evalSettings) elif args.challenge.lower() == 'spaceNet-buildings2'.lower(): evalSettings = {'miniou': 0.5, - 'minArea': 20} - results_DF, results_DF_Full = spacenet_buildings2_dataset \ - .eval_spacenet_buildings2( + 'min_area': 20} + results_DF, results_DF_Full = spacenet_buildings_2( prop_csv=prop_file, truth_csv=truth_file, **evalSettings) with pd.option_context('display.max_rows', None, diff --git a/solaris/data/datagen_sample/expected_im.tif b/solaris/data/datagen_sample/expected_im.tif index ed16832c..c0e6ef84 100644 Binary files a/solaris/data/datagen_sample/expected_im.tif and b/solaris/data/datagen_sample/expected_im.tif differ diff --git a/solaris/data/gt_epsg4326.json b/solaris/data/gt_epsg4326.json new file mode 100644 index 00000000..550d2c7d --- /dev/null +++ b/solaris/data/gt_epsg4326.json @@ -0,0 +1,34 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 112379, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 391.91383579611608, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.4487639, 33.6156071 ], [ -84.4487679, 33.6156964 ], [ -84.4487156, 33.6157489 ], [ -84.4485654, 33.6157612 ], [ -84.4485534, 33.615673 ], [ -84.4485762, 33.6156428 ], [ -84.448489, 33.6155747 ], [ -84.4485238, 33.6155322 ], [ -84.4486258, 33.615597 ], [ -84.4486472, 33.6155769 ], [ -84.4487371, 33.6155725 ], [ -84.4487438, 33.6156071 ], [ -84.4487639, 33.6156071 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 386, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 2624.7975056988498, "origlen": 0, "partialDec": 0.19529179922156292, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.448079059714743, 33.615642603932443 ], [ -84.4482179, 33.6156434 ], [ -84.4482221, 33.6159843 ], [ -84.448069001991314, 33.615983640258932 ], [ -84.448079059714743, 33.615642603932443 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 112358, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 58.470938413055272, "origlen": 0, "partialDec": 0.84938424165096427, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.451892819685412, 33.615526769304253 ], [ -84.4518939, 33.6155733 ], [ -84.4517919, 33.6155733 ], [ -84.4517906, 33.615542 ], [ -84.4517946, 33.6155264 ], [ -84.451804874697416, 33.615524956448077 ], [ -84.451892819685412, 33.615526769304253 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 112363, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 16.648158736312929, "origlen": 0, "partialDec": 0.98964372574588277, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.4509598, 33.6155136 ], [ -84.4509611, 33.615543 ], [ -84.4509164, 33.6155457 ], [ -84.4509164, 33.6155256 ], [ -84.4509164, 33.6155072 ], [ -84.450924492884155, 33.615506805225834 ], [ -84.450947904289066, 33.615507287990347 ], [ -84.4509598, 33.6155136 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 94297, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 183.20412136950409, "origlen": 0, "partialDec": 0.93523287290728463, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.4522486, 33.6156905 ], [ -84.452248221065886, 33.615534094741641 ], [ -84.452363623633815, 33.615536473168348 ], [ -84.4523638, 33.6156206 ], [ -84.4523457, 33.6156207 ], [ -84.4523458, 33.6156904 ], [ -84.4522486, 33.6156905 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 112359, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 176.34374484678875, "origlen": 0, "partialDec": 0.96463496551767225, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.451678045206066, 33.615522341935012 ], [ -84.4516847, 33.6156793 ], [ -84.4515916, 33.615682 ], [ -84.4515883, 33.6156063 ], [ -84.4515691, 33.6156068 ], [ -84.451565411362395, 33.615520019947454 ], [ -84.451678045206066, 33.615522341935012 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 112362, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 179.02465416204927, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.4511477, 33.6155133 ], [ -84.4511477, 33.6155815 ], [ -84.4511668, 33.6155855 ], [ -84.4511684, 33.6156493 ], [ -84.4511573, 33.6156626 ], [ -84.4511573, 33.6156785 ], [ -84.4511333, 33.6156812 ], [ -84.4511142, 33.6156851 ], [ -84.4510536, 33.6156838 ], [ -84.451044, 33.61557890000001 ], [ -84.4510632, 33.615559 ], [ -84.4510743, 33.615531099999984 ], [ -84.4510743, 33.615513799999988 ], [ -84.4511477, 33.6155133 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 112365, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 7.4751673398446687, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.4508194, 33.6156157 ], [ -84.4508194, 33.6156447 ], [ -84.4508012, 33.6156444 ], [ -84.4507957, 33.615636 ], [ -84.4507881, 33.6156234 ], [ -84.4507929, 33.6156171 ], [ -84.4508194, 33.6156157 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 94343, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 151.28750827276107, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.45278110000001, 33.6157521 ], [ -84.452780300000015, 33.6156019 ], [ -84.4528781, 33.6156015 ], [ -84.4528789, 33.6157517 ], [ -84.45278110000001, 33.6157521 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 112364, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 21.963567885446508, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.450883, 33.6157245 ], [ -84.4508849, 33.6157979 ], [ -84.4508687, 33.6157987 ], [ -84.4508602, 33.6157814 ], [ -84.4508526, 33.6157648 ], [ -84.4508507, 33.6157537 ], [ -84.4508488, 33.6157387 ], [ -84.4508526, 33.6157245 ], [ -84.450883, 33.6157245 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 112378, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 1978.5412509861878, "origlen": 0, "partialDec": 0.95008661457365517, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.449267985989621, 33.615472635281179 ], [ -84.4492638, 33.61550110000001 ], [ -84.4492925, 33.6155038 ], [ -84.449286200000017, 33.6155808 ], [ -84.4492543, 33.6155782 ], [ -84.4492431, 33.6156286 ], [ -84.4492718, 33.6156366 ], [ -84.4492559, 33.6157044 ], [ -84.449230300000011, 33.6157004 ], [ -84.4491968, 33.6159009 ], [ -84.4491681, 33.6159009 ], [ -84.4491649, 33.6159208 ], [ -84.449149, 33.6159262 ], [ -84.449177700000021, 33.6159952 ], [ -84.4489528, 33.6160563 ], [ -84.4489608, 33.6160802 ], [ -84.448877900000014, 33.6160975 ], [ -84.4488667, 33.6160789 ], [ -84.4488029, 33.6161001 ], [ -84.4488141, 33.6161214 ], [ -84.4487343, 33.616144 ], [ -84.4487152, 33.6161187 ], [ -84.4486434, 33.6161413 ], [ -84.4486052, 33.6160404 ], [ -84.4485749, 33.616051 ], [ -84.4485621, 33.616023099999985 ], [ -84.4485892, 33.6160138 ], [ -84.4485844, 33.615988599999987 ], [ -84.4487391, 33.6159487 ], [ -84.4487296, 33.6159222 ], [ -84.4488061, 33.6159022 ], [ -84.4488141, 33.6159288 ], [ -84.4489991, 33.615877 ], [ -84.4490485, 33.6156167 ], [ -84.4490182, 33.6156114 ], [ -84.4490342, 33.615545 ], [ -84.4490661, 33.6155463 ], [ -84.449079681173004, 33.615468749578028 ], [ -84.449267985989621, 33.615472635281179 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 94296, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 126.17363166362102, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.4521879, 33.6158747 ], [ -84.4522541, 33.6157606 ], [ -84.4523411, 33.6157956 ], [ -84.4522749, 33.6159098 ], [ -84.4521879, 33.6158747 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 112360, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 180.75176621340333, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.4516838, 33.615769 ], [ -84.4517711, 33.6159111 ], [ -84.4516895, 33.6159506 ], [ -84.4516459, 33.615882699999986 ], [ -84.4516269, 33.6158906 ], [ -84.4515776, 33.6158085 ], [ -84.4516838, 33.615769 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 112361, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 177.29104209284444, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.4511419, 33.6157821 ], [ -84.451059, 33.6159439 ], [ -84.450969, 33.6159119 ], [ -84.4510519, 33.6157501 ], [ -84.4511419, 33.6157821 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 94295, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 159.47798255248392, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.4527581, 33.616004 ], [ -84.4527666, 33.6158301 ], [ -84.4528555, 33.6158331 ], [ -84.452847, 33.616007 ], [ -84.4527581, 33.616004 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 112373, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 494.45818962400131, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.4521379, 33.6162535 ], [ -84.4521357, 33.6163418 ], [ -84.4515922, 33.6163324 ], [ -84.4515944, 33.61624410000001 ], [ -84.4521379, 33.6162535 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 112376, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 255.97436340611333, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.4503467, 33.616335 ], [ -84.4503453, 33.616438600000016 ], [ -84.4501053, 33.6164364 ], [ -84.4501067, 33.61633290000001 ], [ -84.4503467, 33.616335 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 94241, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 529.29603533983379, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.4526111, 33.6166753 ], [ -84.4526179, 33.6162548 ], [ -84.4527401, 33.6162562 ], [ -84.45273330000002, 33.6166767 ], [ -84.4526111, 33.6166753 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 112371, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 502.08685271218383, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.4513705, 33.6162409 ], [ -84.4513641, 33.6166644 ], [ -84.451249, 33.6166632 ], [ -84.4512554, 33.6162397 ], [ -84.4513705, 33.6162409 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 94240, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 514.86470663648481, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.4522002, 33.6167558 ], [ -84.4522057, 33.6162938 ], [ -84.4523139, 33.6162947 ], [ -84.4523084, 33.6167567 ], [ -84.4522002, 33.6167558 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 112375, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 694.47253909490576, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.4510997, 33.61644230000001 ], [ -84.4510927, 33.6165868 ], [ -84.4506268, 33.616571 ], [ -84.4506338, 33.6164265 ], [ -84.4510997, 33.61644230000001 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 112372, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 360.54645618916322, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.4517571, 33.6163755 ], [ -84.45175, 33.6166985 ], [ -84.4516417, 33.6166968 ], [ -84.451648700000021, 33.6163738 ], [ -84.4517571, 33.6163755 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 112377, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 289.94182610795599, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.4504242, 33.6166052 ], [ -84.4504042, 33.6167453 ], [ -84.450401, 33.6167626 ], [ -84.4503628, 33.616766599999984 ], [ -84.4503357, 33.6167838 ], [ -84.4503277, 33.6167971 ], [ -84.45024, 33.6167865 ], [ -84.4502511, 33.6167294 ], [ -84.4502439, 33.616720799999989 ], [ -84.4502511, 33.6166769 ], [ -84.4502703, 33.616678299999982 ], [ -84.4502838, 33.6165906 ], [ -84.4504242, 33.6166052 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 94239, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 430.56656045400211, "origlen": 0, "partialDec": 0.61880994996342276, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.4526284, 33.6168801 ], [ -84.452628800000014, 33.6167824 ], [ -84.452894755393373, 33.616783146366444 ], [ -84.452891877787877, 33.616880901019456 ], [ -84.4526284, 33.6168801 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 112374, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 489.52426447714623, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.4517581, 33.6168779 ], [ -84.4517375, 33.6169698 ], [ -84.4512377, 33.6168921 ], [ -84.4512582, 33.6168002 ], [ -84.4517581, 33.6168779 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 94237, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 525.7717667099605, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.451824, 33.6169854 ], [ -84.4518426, 33.6168873 ], [ -84.4523501, 33.616954 ], [ -84.4523315, 33.61705220000001 ], [ -84.451824, 33.6169854 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 94238, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 498.71535465444168, "origlen": 0, "partialDec": 0.8949086745538668, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.45288390594277, 33.617151708758335 ], [ -84.4524268, 33.6170859 ], [ -84.4524462, 33.6169921 ], [ -84.452886735185913, 33.617055598307978 ], [ -84.45288390594277, 33.617151708758335 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 94236, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 152.8742559217601, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -84.4523028, 33.6184157 ], [ -84.4523014, 33.6183139 ], [ -84.4524472, 33.618312499999981 ], [ -84.4524486, 33.6184143 ], [ -84.4523028, 33.6184157 ] ] ] } } +] +} diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733601_3724734.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733601_3724734.tif new file mode 100644 index 00000000..e1421d9d Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733601_3724734.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733601_3724779.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733601_3724779.tif new file mode 100644 index 00000000..818f36de Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733601_3724779.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733601_3724824.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733601_3724824.tif new file mode 100644 index 00000000..a25a4888 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733601_3724824.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733601_3724869.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733601_3724869.tif new file mode 100644 index 00000000..fcc54f37 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733601_3724869.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733601_3724914.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733601_3724914.tif new file mode 100644 index 00000000..4b144681 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733601_3724914.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733601_3724959.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733601_3724959.tif new file mode 100644 index 00000000..98a0f461 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733601_3724959.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733601_3725004.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733601_3725004.tif new file mode 100644 index 00000000..3d786d83 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733601_3725004.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733601_3725049.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733601_3725049.tif new file mode 100644 index 00000000..c769585e Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733601_3725049.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733601_3725094.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733601_3725094.tif new file mode 100644 index 00000000..ad0df649 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733601_3725094.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733601_3725139.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733601_3725139.tif new file mode 100644 index 00000000..25565fc3 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733601_3725139.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733646_3724734.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733646_3724734.tif new file mode 100644 index 00000000..1c750ff2 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733646_3724734.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733646_3724779.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733646_3724779.tif new file mode 100644 index 00000000..20b601fb Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733646_3724779.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733646_3724824.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733646_3724824.tif new file mode 100644 index 00000000..1c116bee Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733646_3724824.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733646_3724869.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733646_3724869.tif new file mode 100644 index 00000000..288bb202 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733646_3724869.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733646_3724914.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733646_3724914.tif new file mode 100644 index 00000000..e1e97883 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733646_3724914.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733646_3724959.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733646_3724959.tif new file mode 100644 index 00000000..3b905f50 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733646_3724959.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733646_3725004.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733646_3725004.tif new file mode 100644 index 00000000..59412865 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733646_3725004.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733646_3725049.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733646_3725049.tif new file mode 100644 index 00000000..5b17f44e Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733646_3725049.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733646_3725094.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733646_3725094.tif new file mode 100644 index 00000000..55c8b78b Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733646_3725094.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733646_3725139.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733646_3725139.tif new file mode 100644 index 00000000..dfe05db2 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733646_3725139.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733691_3724734.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733691_3724734.tif new file mode 100644 index 00000000..7504c895 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733691_3724734.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733691_3724779.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733691_3724779.tif new file mode 100644 index 00000000..842c72d7 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733691_3724779.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733691_3724824.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733691_3724824.tif new file mode 100644 index 00000000..0ad72d67 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733691_3724824.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733691_3724869.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733691_3724869.tif new file mode 100644 index 00000000..451dce71 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733691_3724869.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733691_3724914.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733691_3724914.tif new file mode 100644 index 00000000..f0f7553b Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733691_3724914.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733691_3724959.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733691_3724959.tif new file mode 100644 index 00000000..aa3cc74b Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733691_3724959.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733691_3725004.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733691_3725004.tif new file mode 100644 index 00000000..3cf1e826 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733691_3725004.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733691_3725049.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733691_3725049.tif new file mode 100644 index 00000000..55182ccb Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733691_3725049.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733691_3725094.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733691_3725094.tif new file mode 100644 index 00000000..9fac53c6 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733691_3725094.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733691_3725139.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733691_3725139.tif new file mode 100644 index 00000000..6cb139fa Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733691_3725139.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733736_3724734.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733736_3724734.tif new file mode 100644 index 00000000..024cbed7 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733736_3724734.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733736_3724779.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733736_3724779.tif new file mode 100644 index 00000000..5d4c4c00 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733736_3724779.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733736_3724824.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733736_3724824.tif new file mode 100644 index 00000000..6f68f8d3 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733736_3724824.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733736_3724869.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733736_3724869.tif new file mode 100644 index 00000000..2c0a475d Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733736_3724869.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733736_3724914.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733736_3724914.tif new file mode 100644 index 00000000..d4e71b4d Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733736_3724914.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733736_3724959.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733736_3724959.tif new file mode 100644 index 00000000..a21cee96 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733736_3724959.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733736_3725004.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733736_3725004.tif new file mode 100644 index 00000000..1bfadb0f Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733736_3725004.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733736_3725049.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733736_3725049.tif new file mode 100644 index 00000000..c4676f78 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733736_3725049.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733736_3725094.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733736_3725094.tif new file mode 100644 index 00000000..423d80e9 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733736_3725094.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733736_3725139.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733736_3725139.tif new file mode 100644 index 00000000..f0ca7f5a Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733736_3725139.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733781_3724734.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733781_3724734.tif new file mode 100644 index 00000000..ffec0496 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733781_3724734.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733781_3724779.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733781_3724779.tif new file mode 100644 index 00000000..2b4464c5 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733781_3724779.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733781_3724824.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733781_3724824.tif new file mode 100644 index 00000000..ed7f06f1 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733781_3724824.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733781_3724869.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733781_3724869.tif new file mode 100644 index 00000000..fe2e4935 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733781_3724869.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733781_3724914.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733781_3724914.tif new file mode 100644 index 00000000..94a17f11 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733781_3724914.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733781_3724959.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733781_3724959.tif new file mode 100644 index 00000000..89068782 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733781_3724959.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733781_3725004.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733781_3725004.tif new file mode 100644 index 00000000..6056a49d Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733781_3725004.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733781_3725049.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733781_3725049.tif new file mode 100644 index 00000000..dfd4b09d Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733781_3725049.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733781_3725094.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733781_3725094.tif new file mode 100644 index 00000000..1c54d990 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733781_3725094.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733781_3725139.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733781_3725139.tif new file mode 100644 index 00000000..da01b6df Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733781_3725139.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733826_3724734.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733826_3724734.tif new file mode 100644 index 00000000..c0ddada2 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733826_3724734.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733826_3724779.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733826_3724779.tif new file mode 100644 index 00000000..7a0870d6 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733826_3724779.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733826_3724824.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733826_3724824.tif new file mode 100644 index 00000000..ab5e175c Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733826_3724824.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733826_3724869.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733826_3724869.tif new file mode 100644 index 00000000..18109898 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733826_3724869.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733826_3724914.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733826_3724914.tif new file mode 100644 index 00000000..0c7fdb05 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733826_3724914.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733826_3724959.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733826_3724959.tif new file mode 100644 index 00000000..7db46392 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733826_3724959.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733826_3725004.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733826_3725004.tif new file mode 100644 index 00000000..9d082c19 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733826_3725004.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733826_3725049.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733826_3725049.tif new file mode 100644 index 00000000..a59583ca Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733826_3725049.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733826_3725094.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733826_3725094.tif new file mode 100644 index 00000000..1610be5c Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733826_3725094.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733826_3725139.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733826_3725139.tif new file mode 100644 index 00000000..f50651bb Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733826_3725139.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733871_3724734.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733871_3724734.tif new file mode 100644 index 00000000..8c58c0e2 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733871_3724734.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733871_3724779.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733871_3724779.tif new file mode 100644 index 00000000..cc03cd90 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733871_3724779.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733871_3724824.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733871_3724824.tif new file mode 100644 index 00000000..fa748243 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733871_3724824.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733871_3724869.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733871_3724869.tif new file mode 100644 index 00000000..2f701da4 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733871_3724869.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733871_3724914.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733871_3724914.tif new file mode 100644 index 00000000..930c7343 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733871_3724914.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733871_3724959.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733871_3724959.tif new file mode 100644 index 00000000..9a79ecb1 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733871_3724959.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733871_3725004.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733871_3725004.tif new file mode 100644 index 00000000..501ed72c Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733871_3725004.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733871_3725049.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733871_3725049.tif new file mode 100644 index 00000000..df007605 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733871_3725049.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733871_3725094.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733871_3725094.tif new file mode 100644 index 00000000..2f654052 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733871_3725094.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733871_3725139.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733871_3725139.tif new file mode 100644 index 00000000..2f5186ce Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733871_3725139.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733916_3724734.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733916_3724734.tif new file mode 100644 index 00000000..76e0420b Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733916_3724734.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733916_3724779.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733916_3724779.tif new file mode 100644 index 00000000..3c0cafde Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733916_3724779.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733916_3724824.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733916_3724824.tif new file mode 100644 index 00000000..5ae4321d Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733916_3724824.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733916_3724869.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733916_3724869.tif new file mode 100644 index 00000000..2e089c95 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733916_3724869.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733916_3724914.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733916_3724914.tif new file mode 100644 index 00000000..40528897 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733916_3724914.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733916_3724959.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733916_3724959.tif new file mode 100644 index 00000000..57891ceb Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733916_3724959.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733916_3725004.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733916_3725004.tif new file mode 100644 index 00000000..6f64b0a3 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733916_3725004.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733916_3725049.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733916_3725049.tif new file mode 100644 index 00000000..e7d2287d Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733916_3725049.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733916_3725094.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733916_3725094.tif new file mode 100644 index 00000000..d91a95a9 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733916_3725094.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733916_3725139.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733916_3725139.tif new file mode 100644 index 00000000..a6ca086b Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733916_3725139.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733961_3724734.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733961_3724734.tif new file mode 100644 index 00000000..c1fabe07 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733961_3724734.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733961_3724779.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733961_3724779.tif new file mode 100644 index 00000000..2399e421 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733961_3724779.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733961_3724824.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733961_3724824.tif new file mode 100644 index 00000000..46ed483a Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733961_3724824.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733961_3724869.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733961_3724869.tif new file mode 100644 index 00000000..12c001c4 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733961_3724869.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733961_3724914.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733961_3724914.tif new file mode 100644 index 00000000..b34a3620 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733961_3724914.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733961_3724959.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733961_3724959.tif new file mode 100644 index 00000000..b39b9cc4 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733961_3724959.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733961_3725004.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733961_3725004.tif new file mode 100644 index 00000000..589b8e6b Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733961_3725004.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733961_3725049.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733961_3725049.tif new file mode 100644 index 00000000..fa11a8a3 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733961_3725049.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733961_3725094.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733961_3725094.tif new file mode 100644 index 00000000..92d17596 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733961_3725094.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_733961_3725139.tif b/solaris/data/rastertile_test_expected/sample_geotiff_733961_3725139.tif new file mode 100644 index 00000000..fec575ad Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_733961_3725139.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_734006_3724734.tif b/solaris/data/rastertile_test_expected/sample_geotiff_734006_3724734.tif new file mode 100644 index 00000000..317a2006 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_734006_3724734.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_734006_3724779.tif b/solaris/data/rastertile_test_expected/sample_geotiff_734006_3724779.tif new file mode 100644 index 00000000..5c7443b2 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_734006_3724779.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_734006_3724824.tif b/solaris/data/rastertile_test_expected/sample_geotiff_734006_3724824.tif new file mode 100644 index 00000000..44be8bd5 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_734006_3724824.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_734006_3724869.tif b/solaris/data/rastertile_test_expected/sample_geotiff_734006_3724869.tif new file mode 100644 index 00000000..60883342 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_734006_3724869.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_734006_3724914.tif b/solaris/data/rastertile_test_expected/sample_geotiff_734006_3724914.tif new file mode 100644 index 00000000..ab2a9ca9 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_734006_3724914.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_734006_3724959.tif b/solaris/data/rastertile_test_expected/sample_geotiff_734006_3724959.tif new file mode 100644 index 00000000..09fc3149 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_734006_3724959.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_734006_3725004.tif b/solaris/data/rastertile_test_expected/sample_geotiff_734006_3725004.tif new file mode 100644 index 00000000..ece13bd1 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_734006_3725004.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_734006_3725049.tif b/solaris/data/rastertile_test_expected/sample_geotiff_734006_3725049.tif new file mode 100644 index 00000000..be81dcad Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_734006_3725049.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_734006_3725094.tif b/solaris/data/rastertile_test_expected/sample_geotiff_734006_3725094.tif new file mode 100644 index 00000000..99796408 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_734006_3725094.tif differ diff --git a/solaris/data/rastertile_test_expected/sample_geotiff_734006_3725139.tif b/solaris/data/rastertile_test_expected/sample_geotiff_734006_3725139.tif new file mode 100644 index 00000000..e19d36e6 Binary files /dev/null and b/solaris/data/rastertile_test_expected/sample_geotiff_734006_3725139.tif differ diff --git a/solaris/data/vectortile_test_expected/geoms_733601_3724689.json b/solaris/data/vectortile_test_expected/geoms_733601_3724689.json new file mode 100644 index 00000000..da9ac4c9 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733601_3724689.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 134696, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 160.8812060779413, "origlen": 0, "partialDec": 0.33276744108985457, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733631.017092985799536, 3724696.563397473655641 ], [ 733637.916874720831402, 3724689.0 ], [ 733626.758141876780428, 3724689.0 ], [ 733624.904465531464666, 3724691.031963157467544 ], [ 733631.017092985799536, 3724696.563397473655641 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733601_3724734.json b/solaris/data/vectortile_test_expected/geoms_733601_3724734.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733601_3724734.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733601_3724779.json b/solaris/data/vectortile_test_expected/geoms_733601_3724779.json new file mode 100644 index 00000000..4cb854c0 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733601_3724779.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 117300, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 56.69138110426168, "origlen": 0, "partialDec": 0.82298268439083644, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733637.712568847462535, 3724812.644189648330212 ], [ 733637.935811243369244, 3724818.720185527112335 ], [ 733637.983977672527544, 3724821.69559862697497 ], [ 733645.835462932707742, 3724821.387535042595118 ], [ 733646.0, 3724821.378123895265162 ], [ 733646.0, 3724817.318846063688397 ], [ 733644.187826864537783, 3724817.38542799977586 ], [ 733642.700126820825972, 3724817.105018282774836 ], [ 733640.485648880014196, 3724816.174316430930048 ], [ 733639.697913700132631, 3724812.692572094965726 ], [ 733637.712568847462535, 3724812.644189648330212 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733601_3724824.json b/solaris/data/vectortile_test_expected/geoms_733601_3724824.json new file mode 100644 index 00000000..497d5474 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733601_3724824.json @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 102939, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 263.99532490416749, "origlen": 0, "partialDec": 0.22027170560859002, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733646.0, 3724863.492486663162708 ], [ 733636.398522177478299, 3724862.375186257530004 ], [ 733636.446408499730751, 3724866.504775203764439 ], [ 733636.346274557523429, 3724869.0 ], [ 733646.0, 3724869.0 ], [ 733646.0, 3724863.492486663162708 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 102938, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 241.49355060420999, "origlen": 0, "partialDec": 0.88389656023284158, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733645.532881900900975, 3724858.180826778523624 ], [ 733646.0, 3724854.806816860102117 ], [ 733646.0, 3724834.694837557617575 ], [ 733643.618271068204194, 3724833.519004478584975 ], [ 733637.692060588742606, 3724833.674225233960897 ], [ 733636.89257507189177, 3724836.007498409133404 ], [ 733637.419333599274978, 3724844.865368209313601 ], [ 733637.096330924076028, 3724858.119497096166015 ], [ 733645.532881900900975, 3724858.180826778523624 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733601_3724869.json b/solaris/data/vectortile_test_expected/geoms_733601_3724869.json new file mode 100644 index 00000000..be089ed1 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733601_3724869.json @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 102932, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 250.90410248692208, "origlen": 0, "partialDec": 0.8735442193360704, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733643.912090259022079, 3724914.0 ], [ 733643.061781426658854, 3724892.15789200225845 ], [ 733632.952742423396558, 3724892.544111663941294 ], [ 733633.78803826845251, 3724914.0 ], [ 733643.912090259022079, 3724914.0 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 102939, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 263.99532490416749, "origlen": 0, "partialDec": 0.70369949427404388, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733644.666782932821661, 3724887.62466867826879 ], [ 733646.0, 3724884.255406009964645 ], [ 733646.0, 3724869.0 ], [ 733636.346274557523429, 3724869.0 ], [ 733636.068220987217501, 3724875.928781074937433 ], [ 733635.770354353357106, 3724888.151417502202094 ], [ 733644.666782932821661, 3724887.62466867826879 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733601_3724914.json b/solaris/data/vectortile_test_expected/geoms_733601_3724914.json new file mode 100644 index 00000000..989c4297 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733601_3724914.json @@ -0,0 +1,9 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 102932, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 250.90410248692208, "origlen": 0, "partialDec": 0.12645578066316202, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733633.917563493945636, 3724917.327059258706868 ], [ 733644.026576642645523, 3724916.940842032898217 ], [ 733643.912090259022079, 3724914.0 ], [ 733633.78803826845251, 3724914.0 ], [ 733633.917563493945636, 3724917.327059258706868 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 102940, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 247.85698155347222, "origlen": 0, "partialDec": 0.46588867952968854, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733646.0, 3724923.920079745352268 ], [ 733642.8083045554813, 3724925.412150491029024 ], [ 733636.785855265101418, 3724926.852379398420453 ], [ 733638.080213227891363, 3724931.256500165909529 ], [ 733642.490936725749634, 3724931.197525009978563 ], [ 733643.028865780681372, 3724937.691814653109759 ], [ 733641.684800133458339, 3724942.564334524329752 ], [ 733641.276853826944716, 3724944.829464028123766 ], [ 733643.267351940623485, 3724949.61678282963112 ], [ 733646.0, 3724949.535827656742185 ], [ 733646.0, 3724923.920079745352268 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 135783, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 236.74072382805585, "origlen": 0, "partialDec": 7.6890011379658681e-06, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733646.0, 3724957.285058092791587 ], [ 733645.967119043460116, 3724957.295747282914817 ], [ 733646.0, 3724957.395778697449714 ], [ 733646.0, 3724957.285058092791587 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733601_3724959.json b/solaris/data/vectortile_test_expected/geoms_733601_3724959.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733601_3724959.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733601_3725004.json b/solaris/data/vectortile_test_expected/geoms_733601_3725004.json new file mode 100644 index 00000000..d7783644 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733601_3725004.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 135943, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 293.80215091143663, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733614.692407695925795, 3725025.917704849969596 ], [ 733618.232930953730829, 3725025.759830723050982 ], [ 733618.361258932505734, 3725028.493043820839375 ], [ 733623.40737501042895, 3725028.260883178096265 ], [ 733623.226638415828347, 3725024.25013302732259 ], [ 733629.22064378275536, 3725023.974487836007029 ], [ 733628.499683096189983, 3725008.231178386602551 ], [ 733624.467038923874497, 3725005.347326850984246 ], [ 733613.770959213492461, 3725005.830230219755322 ], [ 733614.692407695925795, 3725025.917704849969596 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733601_3725049.json b/solaris/data/vectortile_test_expected/geoms_733601_3725049.json new file mode 100644 index 00000000..25196dfc --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733601_3725049.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 135941, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 208.00350114029035, "origlen": 0, "partialDec": 0.075357171616733137, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733646.0, 3725089.934429133776575 ], [ 733638.289124431437813, 3725094.0 ], [ 733646.0, 3725094.0 ], [ 733646.0, 3725089.934429133776575 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733601_3725094.json b/solaris/data/vectortile_test_expected/geoms_733601_3725094.json new file mode 100644 index 00000000..1e78fc70 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733601_3725094.json @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 102923, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 371.9610521071786, "origlen": 0, "partialDec": 0.40890572019922689, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733601.0, 3725137.577448081690818 ], [ 733604.893619947950356, 3725135.093213116750121 ], [ 733604.174474695930257, 3725128.416942054405808 ], [ 733603.743797679431736, 3725124.377905528992414 ], [ 733610.68987981416285, 3725120.074717226438224 ], [ 733610.059207651182078, 3725110.148913879878819 ], [ 733601.0, 3725111.934446161147207 ], [ 733601.0, 3725137.577448081690818 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 135941, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 208.00350114029035, "origlen": 0, "partialDec": 0.59749945752301825, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733644.636568528716452, 3725102.534991428721696 ], [ 733646.0, 3725101.754577273502946 ], [ 733646.0, 3725094.0 ], [ 733638.289124431437813, 3725094.0 ], [ 733627.684122170670889, 3725099.591503564734012 ], [ 733630.799799439031631, 3725105.438350759446621 ], [ 733640.795360824326053, 3725100.177396830171347 ], [ 733644.636568528716452, 3725102.534991428721696 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733646_3724689.json b/solaris/data/vectortile_test_expected/geoms_733646_3724689.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733646_3724689.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733646_3724734.json b/solaris/data/vectortile_test_expected/geoms_733646_3724734.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733646_3724734.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733646_3724779.json b/solaris/data/vectortile_test_expected/geoms_733646_3724779.json new file mode 100644 index 00000000..58db95a9 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733646_3724779.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 117300, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 56.69138110426168, "origlen": 0, "partialDec": 0.17701731560750186, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733646.0, 3724821.378123895265162 ], [ 733648.828701670165174, 3724821.216328571550548 ], [ 733648.656602984643541, 3724817.993740823119879 ], [ 733646.185421441216022, 3724817.312033404130489 ], [ 733646.0, 3724817.318846063688397 ], [ 733646.0, 3724821.378123895265162 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733646_3724824.json b/solaris/data/vectortile_test_expected/geoms_733646_3724824.json new file mode 100644 index 00000000..b9d1bad1 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733646_3724824.json @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 102939, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 263.99532490416749, "origlen": 0, "partialDec": 0.029826769182349354, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733647.285931088379584, 3724869.0 ], [ 733647.617427871096879, 3724863.680702774319798 ], [ 733646.0, 3724863.492486663162708 ], [ 733646.0, 3724869.0 ], [ 733647.285931088379584, 3724869.0 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 102938, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 241.49355060420999, "origlen": 0, "partialDec": 0.11610343976548934, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733646.0, 3724854.806816860102117 ], [ 733646.794668328016996, 3724849.066900496836752 ], [ 733648.42761501041241, 3724842.625518082175404 ], [ 733647.709180656238459, 3724835.538641034159809 ], [ 733646.0, 3724834.694837557617575 ], [ 733646.0, 3724854.806816860102117 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733646_3724869.json b/solaris/data/vectortile_test_expected/geoms_733646_3724869.json new file mode 100644 index 00000000..4c2999ad --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733646_3724869.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 102939, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 263.99532490416749, "origlen": 0, "partialDec": 0.046202030937300832, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733646.0, 3724884.255406009964645 ], [ 733646.397882855031639, 3724883.249889441765845 ], [ 733647.285931088379584, 3724869.0 ], [ 733646.0, 3724869.0 ], [ 733646.0, 3724884.255406009964645 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733646_3724914.json b/solaris/data/vectortile_test_expected/geoms_733646_3724914.json new file mode 100644 index 00000000..24e4d42e --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733646_3724914.json @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 102940, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 247.85698155347222, "origlen": 0, "partialDec": 0.53411132047277343, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733653.132692800136283, 3724949.324520394671708 ], [ 733648.855268120998517, 3724922.585283832624555 ], [ 733646.0, 3724923.920079745352268 ], [ 733646.0, 3724949.535827656742185 ], [ 733653.132692800136283, 3724949.324520394671708 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 135783, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 236.74072382805585, "origlen": 0, "partialDec": 0.17080870764493319, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733658.305873904144391, 3724959.0 ], [ 733656.60582423210144, 3724953.837236555293202 ], [ 733646.0, 3724957.285058092791587 ], [ 733646.0, 3724957.395778697449714 ], [ 733646.527317655156367, 3724959.0 ], [ 733658.305873904144391, 3724959.0 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733646_3724959.json b/solaris/data/vectortile_test_expected/geoms_733646_3724959.json new file mode 100644 index 00000000..7693d3e5 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733646_3724959.json @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 135783, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 236.74072382805585, "origlen": 0, "partialDec": 0.82918360335723085, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733653.939914708724245, 3724982.094132591970265 ], [ 733664.747478068107739, 3724978.562062832526863 ], [ 733658.305873904144391, 3724959.0 ], [ 733646.527317655156367, 3724959.0 ], [ 733649.078211254440248, 3724966.760403643827885 ], [ 733654.418122929870151, 3724970.852504897862673 ], [ 733657.441431801649742, 3724975.920252304058522 ], [ 733657.77753429254517, 3724979.268915843218565 ], [ 733653.939914708724245, 3724982.094132591970265 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 102924, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 151.59457935772951, "origlen": 0, "partialDec": 0.67253016461917026, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733678.195862984284759, 3725004.0 ], [ 733682.16668538027443, 3725001.970434435643256 ], [ 733678.337399649550207, 3724994.552469162270427 ], [ 733663.362111695343629, 3725002.26673267967999 ], [ 733663.028553360374644, 3725004.0 ], [ 733678.195862984284759, 3725004.0 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733646_3725004.json b/solaris/data/vectortile_test_expected/geoms_733646_3725004.json new file mode 100644 index 00000000..73f1bfd2 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733646_3725004.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 102924, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 151.59457935772951, "origlen": 0, "partialDec": 0.3274698353796876, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733665.773527625598945, 3725010.34930036123842 ], [ 733678.195862984284759, 3725004.0 ], [ 733663.028553360374644, 3725004.0 ], [ 733662.883814595406875, 3725004.752105239313096 ], [ 733665.773527625598945, 3725010.34930036123842 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733646_3725049.json b/solaris/data/vectortile_test_expected/geoms_733646_3725049.json new file mode 100644 index 00000000..4cfff6dc --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733646_3725049.json @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 135941, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 208.00350114029035, "origlen": 0, "partialDec": 0.12153410671769142, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733652.800486668245867, 3725094.0 ], [ 733649.850147562567145, 3725088.956138697918504 ], [ 733646.575522312079556, 3725089.630984119605273 ], [ 733646.0, 3725089.934429133776575 ], [ 733646.0, 3725094.0 ], [ 733652.800486668245867, 3725094.0 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 102920, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 226.41975901786444, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733667.977394764660858, 3725090.163731965236366 ], [ 733676.199185358826071, 3725084.937261826358736 ], [ 733677.677698997664265, 3725082.16552930790931 ], [ 733680.178836030769162, 3725080.473026779480278 ], [ 733682.073956778040156, 3725075.081243939697742 ], [ 733686.535091507947072, 3725072.570883402135223 ], [ 733684.217532261973247, 3725066.732374109327793 ], [ 733679.064045730396174, 3725068.704256920609623 ], [ 733676.880473150406033, 3725071.458802872337401 ], [ 733671.348778790328652, 3725074.475772328674793 ], [ 733667.915225020376965, 3725078.243055400904268 ], [ 733663.415438750991598, 3725082.339483730494976 ], [ 733667.977394764660858, 3725090.163731965236366 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733646_3725094.json b/solaris/data/vectortile_test_expected/geoms_733646_3725094.json new file mode 100644 index 00000000..e65ef72e --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733646_3725094.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 135941, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 208.00350114029035, "origlen": 0, "partialDec": 0.2056092641335639, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733646.0, 3725101.754577273502946 ], [ 733654.492900374345481, 3725096.893328425474465 ], [ 733652.800486668245867, 3725094.0 ], [ 733646.0, 3725094.0 ], [ 733646.0, 3725101.754577273502946 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733691_3724689.json b/solaris/data/vectortile_test_expected/geoms_733691_3724689.json new file mode 100644 index 00000000..75b152a5 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733691_3724689.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 134694, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 98.143961268411516, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733707.923785710008815, 3724706.18411343311891 ], [ 733716.751506693311967, 3724697.820616477634758 ], [ 733709.214741862844676, 3724689.402245204430073 ], [ 733706.274902248056605, 3724694.235860752407461 ], [ 733708.154135095421225, 3724696.734307382255793 ], [ 733707.539887185208499, 3724700.22627462958917 ], [ 733702.747399608721025, 3724701.85182610200718 ], [ 733707.923785710008815, 3724706.18411343311891 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733691_3724734.json b/solaris/data/vectortile_test_expected/geoms_733691_3724734.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733691_3724734.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733691_3724779.json b/solaris/data/vectortile_test_expected/geoms_733691_3724779.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733691_3724779.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733691_3724824.json b/solaris/data/vectortile_test_expected/geoms_733691_3724824.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733691_3724824.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733691_3724869.json b/solaris/data/vectortile_test_expected/geoms_733691_3724869.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733691_3724869.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733691_3724914.json b/solaris/data/vectortile_test_expected/geoms_733691_3724914.json new file mode 100644 index 00000000..e8c3dce5 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733691_3724914.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 102925, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 235.62354171707437, "origlen": 0, "partialDec": 0.19134224537340172, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733723.705273776431568, 3724959.0 ], [ 733721.837239123298787, 3724956.803474905434996 ], [ 733713.769853547797538, 3724952.64484176505357 ], [ 733711.143068718956783, 3724957.585964888799936 ], [ 733713.499046904733405, 3724959.0 ], [ 733723.705273776431568, 3724959.0 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733691_3724959.json b/solaris/data/vectortile_test_expected/geoms_733691_3724959.json new file mode 100644 index 00000000..57600998 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733691_3724959.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 102925, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 235.62354171707437, "origlen": 0, "partialDec": 0.80865775462712375, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733716.606242132140324, 3724973.75564177101478 ], [ 733725.094902680953965, 3724960.633992579299957 ], [ 733723.705273776431568, 3724959.0 ], [ 733713.499046904733405, 3724959.0 ], [ 733714.034097261959687, 3724959.32113200193271 ], [ 733712.084855253109708, 3724961.981503693852574 ], [ 733706.699992214213125, 3724959.353198084048927 ], [ 733702.652436110540293, 3724965.458262465894222 ], [ 733716.606242132140324, 3724973.75564177101478 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733691_3725004.json b/solaris/data/vectortile_test_expected/geoms_733691_3725004.json new file mode 100644 index 00000000..62b2dc60 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733691_3725004.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 102919, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 376.96788638895055, "origlen": 0, "partialDec": 0.43161612025689933, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733736.0, 3725046.363777188584208 ], [ 733735.121561629115604, 3725043.957477799151093 ], [ 733732.919629639131017, 3725040.230379342567176 ], [ 733731.032719954149798, 3725038.43090241169557 ], [ 733727.129203135380521, 3725039.390032827854156 ], [ 733715.550556391477585, 3725045.233793378807604 ], [ 733711.973885777289979, 3725047.255200332496315 ], [ 733712.888976000715047, 3725049.0 ], [ 733736.0, 3725049.0 ], [ 733736.0, 3725046.363777188584208 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733691_3725049.json b/solaris/data/vectortile_test_expected/geoms_733691_3725049.json new file mode 100644 index 00000000..4cb415d4 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733691_3725049.json @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 117299, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 17.925629600560526, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733714.078736086841673, 3725075.905853402335197 ], [ 733714.823168934090063, 3725080.019134919159114 ], [ 733719.046768112573773, 3725079.256469215732068 ], [ 733718.293059892137535, 3725075.14296110207215 ], [ 733714.078736086841673, 3725075.905853402335197 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 102919, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 376.96788638895055, "origlen": 0, "partialDec": 0.54836180845584703, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733719.886159604880959, 3725062.341508098412305 ], [ 733722.739838666282594, 3725060.657610790804029 ], [ 733723.763353180838749, 3725062.080902691464871 ], [ 733727.644603378139436, 3725061.653928931802511 ], [ 733727.184225898468867, 3725058.834930552169681 ], [ 733736.0, 3725054.352273083757609 ], [ 733736.0, 3725049.0 ], [ 733712.888976000715047, 3725049.0 ], [ 733719.886159604880959, 3725062.341508098412305 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733691_3725094.json b/solaris/data/vectortile_test_expected/geoms_733691_3725094.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733691_3725094.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733736_3724689.json b/solaris/data/vectortile_test_expected/geoms_733736_3724689.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733736_3724689.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733736_3724734.json b/solaris/data/vectortile_test_expected/geoms_733736_3724734.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733736_3724734.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733736_3724779.json b/solaris/data/vectortile_test_expected/geoms_733736_3724779.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733736_3724779.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733736_3724824.json b/solaris/data/vectortile_test_expected/geoms_733736_3724824.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733736_3724824.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733736_3724869.json b/solaris/data/vectortile_test_expected/geoms_733736_3724869.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733736_3724869.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733736_3724914.json b/solaris/data/vectortile_test_expected/geoms_733736_3724914.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733736_3724914.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733736_3724959.json b/solaris/data/vectortile_test_expected/geoms_733736_3724959.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733736_3724959.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733736_3725004.json b/solaris/data/vectortile_test_expected/geoms_733736_3725004.json new file mode 100644 index 00000000..594f229c --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733736_3725004.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 102919, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 376.96788638895055, "origlen": 0, "partialDec": 0.0031608728154630481, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733736.803118143696338, 3725049.0 ], [ 733736.609508816385642, 3725048.033399943262339 ], [ 733736.0, 3725046.363777188584208 ], [ 733736.0, 3725049.0 ], [ 733736.803118143696338, 3725049.0 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733736_3725049.json b/solaris/data/vectortile_test_expected/geoms_733736_3725049.json new file mode 100644 index 00000000..485e6f92 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733736_3725049.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 102919, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 376.96788638895055, "origlen": 0, "partialDec": 0.016861198470121515, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733736.0, 3725054.352273083757609 ], [ 733737.701844487339258, 3725053.486916756257415 ], [ 733736.803118143696338, 3725049.0 ], [ 733736.0, 3725049.0 ], [ 733736.0, 3725054.352273083757609 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733736_3725094.json b/solaris/data/vectortile_test_expected/geoms_733736_3725094.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733736_3725094.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733781_3724689.json b/solaris/data/vectortile_test_expected/geoms_733781_3724689.json new file mode 100644 index 00000000..798f51e6 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733781_3724689.json @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 93019, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 223.11504089232326, "origlen": 0, "partialDec": 0.71792570273177214, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733808.825040770112537, 3724703.694553496781737 ], [ 733812.694460124941543, 3724699.57170610036701 ], [ 733813.810271165333688, 3724692.263191219884902 ], [ 733809.834030322846957, 3724689.0 ], [ 733793.844497522106394, 3724689.0 ], [ 733808.825040770112537, 3724703.694553496781737 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 93018, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 189.51651421072748, "origlen": 0, "partialDec": 0.024760218872170288, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733824.194954353850335, 3724717.886300034821033 ], [ 733826.0, 3724718.606595266610384 ], [ 733826.0, 3724713.40731359552592 ], [ 733824.194954353850335, 3724717.886300034821033 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733781_3724734.json b/solaris/data/vectortile_test_expected/geoms_733781_3724734.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733781_3724734.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733781_3724779.json b/solaris/data/vectortile_test_expected/geoms_733781_3724779.json new file mode 100644 index 00000000..50e2003f --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733781_3724779.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 93146, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 85.499318876340681, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733797.19361286086496, 3724803.425375143066049 ], [ 733809.65190442930907, 3724803.396256368607283 ], [ 733810.314092590706423, 3724796.798043267335743 ], [ 733797.529922557529062, 3724796.486277254763991 ], [ 733797.19361286086496, 3724803.425375143066049 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733781_3724824.json b/solaris/data/vectortile_test_expected/geoms_733781_3724824.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733781_3724824.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733781_3724869.json b/solaris/data/vectortile_test_expected/geoms_733781_3724869.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733781_3724869.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733781_3724914.json b/solaris/data/vectortile_test_expected/geoms_733781_3724914.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733781_3724914.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733781_3724959.json b/solaris/data/vectortile_test_expected/geoms_733781_3724959.json new file mode 100644 index 00000000..0b9db42a --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733781_3724959.json @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86007, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 288.75968082249199, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733804.608246845542453, 3724992.315301816910505 ], [ 733814.888789982767776, 3724991.744788535870612 ], [ 733813.477104315534234, 3724966.273909805342555 ], [ 733801.90457292238716, 3724966.912798856850713 ], [ 733803.019650391303003, 3724987.038337231613696 ], [ 733804.311609080410562, 3724986.969964746385813 ], [ 733804.608246845542453, 3724992.315301816910505 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86008, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 231.57048190271922, "origlen": 0, "partialDec": 0.078160449292484446, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733815.287342350929976, 3725004.0 ], [ 733815.140452889609151, 3725001.217458509840071 ], [ 733808.102239650906995, 3725001.678382671438158 ], [ 733808.159776426036842, 3725004.0 ], [ 733815.287342350929976, 3725004.0 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733781_3725004.json b/solaris/data/vectortile_test_expected/geoms_733781_3725004.json new file mode 100644 index 00000000..458fe867 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733781_3725004.json @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86008, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 231.57048190271922, "origlen": 0, "partialDec": 0.92183955070685608, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733817.060308103798889, 3725026.023760433774441 ], [ 733816.30381879361812, 3725016.316818532999605 ], [ 733814.319134745746851, 3725016.623545987997204 ], [ 733813.591841582325287, 3725009.525329754687846 ], [ 733815.572198782116175, 3725009.396063346415758 ], [ 733815.287342350929976, 3725004.0 ], [ 733808.159776426036842, 3725004.0 ], [ 733808.237916652811691, 3725007.152969629038125 ], [ 733806.608725623344071, 3725011.152880643028766 ], [ 733803.99797402555123, 3725014.673837821930647 ], [ 733804.473099919268861, 3725017.648571964353323 ], [ 733806.2293102737749, 3725019.478170173708349 ], [ 733806.057821782305837, 3725022.703484487254173 ], [ 733804.33324929792434, 3725023.38278932031244 ], [ 733804.607062857598066, 3725026.61896403087303 ], [ 733817.060308103798889, 3725026.023760433774441 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86009, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 259.07434426271021, "origlen": 0, "partialDec": 0.71855108760884145, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733817.00581841240637, 3725049.0 ], [ 733816.334671155898832, 3725030.65609390148893 ], [ 733806.096146776108071, 3725031.027864479925483 ], [ 733806.753711150959134, 3725049.0 ], [ 733817.00581841240637, 3725049.0 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733781_3725049.json b/solaris/data/vectortile_test_expected/geoms_733781_3725049.json new file mode 100644 index 00000000..c628b883 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733781_3725049.json @@ -0,0 +1,9 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86009, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 259.07434426271021, "origlen": 0, "partialDec": 0.28144891239269454, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733807.020737620303407, 3725056.298199352808297 ], [ 733817.2592352849897, 3725055.926431227475405 ], [ 733817.00581841240637, 3725049.0 ], [ 733806.753711150959134, 3725049.0 ], [ 733807.020737620303407, 3725056.298199352808297 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86013, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 168.5733291901596, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733819.335845608613454, 3725081.535611427389085 ], [ 733818.589080677134916, 3725066.102373758796602 ], [ 733815.155530380317941, 3725061.113343438133597 ], [ 733811.214473494095728, 3725061.327962883748114 ], [ 733810.384644210571423, 3725062.239949475973845 ], [ 733810.523116171592847, 3725064.174366268794984 ], [ 733811.440289434045553, 3725067.293057959526777 ], [ 733810.916349143604748, 3725070.887105803471059 ], [ 733809.174980869051069, 3725073.397157665342093 ], [ 733809.349399076891132, 3725081.469605537131429 ], [ 733819.335845608613454, 3725081.535611427389085 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86012, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 284.46838311737099, "origlen": 0, "partialDec": 0.091442241875016386, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733824.826035436824895, 3725094.0 ], [ 733824.310515563818626, 3725090.701761959586293 ], [ 733814.219372081570327, 3725092.264592101797462 ], [ 733814.490603572456166, 3725094.0 ], [ 733824.826035436824895, 3725094.0 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733781_3725094.json b/solaris/data/vectortile_test_expected/geoms_733781_3725094.json new file mode 100644 index 00000000..a349aad4 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733781_3725094.json @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86012, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 284.46838311737099, "origlen": 0, "partialDec": 0.75634556221898208, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733826.0, 3725101.510892950929701 ], [ 733824.826035436824895, 3725094.0 ], [ 733814.490603572456166, 3725094.0 ], [ 733818.105646131793037, 3725117.12995953951031 ], [ 733826.0, 3725115.904065827373415 ], [ 733826.0, 3725101.510892950929701 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86014, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 269.2410676574849, "origlen": 0, "partialDec": 0.015222220907925837, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733826.0, 3725124.38049762416631 ], [ 733824.230409230804071, 3725125.192175134085119 ], [ 733826.0, 3725129.012582149822265 ], [ 733826.0, 3725124.38049762416631 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733826_3724689.json b/solaris/data/vectortile_test_expected/geoms_733826_3724689.json new file mode 100644 index 00000000..cdcd40e7 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733826_3724689.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 93018, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 189.51651421072748, "origlen": 0, "partialDec": 0.9752397811307828, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733826.0, 3724718.606595266610384 ], [ 733841.671712412848137, 3724724.860320621170104 ], [ 733845.427343191811815, 3724715.507587827276438 ], [ 733827.959576514316723, 3724708.544878867920488 ], [ 733826.0, 3724713.40731359552592 ], [ 733826.0, 3724718.606595266610384 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733826_3724734.json b/solaris/data/vectortile_test_expected/geoms_733826_3724734.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733826_3724734.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733826_3724779.json b/solaris/data/vectortile_test_expected/geoms_733826_3724779.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733826_3724779.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733826_3724824.json b/solaris/data/vectortile_test_expected/geoms_733826_3724824.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733826_3724824.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733826_3724869.json b/solaris/data/vectortile_test_expected/geoms_733826_3724869.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733826_3724869.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733826_3724914.json b/solaris/data/vectortile_test_expected/geoms_733826_3724914.json new file mode 100644 index 00000000..89db0316 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733826_3724914.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86006, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 300.47300947479948, "origlen": 0, "partialDec": 0.98819774517170944, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733848.800145414541475, 3724959.0 ], [ 733848.919941028230824, 3724958.981422422919422 ], [ 733847.980877452413552, 3724952.954536063130945 ], [ 733864.939495211467147, 3724950.327385626267642 ], [ 733863.365486120572314, 3724940.267557054758072 ], [ 733847.021852035541087, 3724942.798733331263065 ], [ 733846.740707991877571, 3724941.005108900833875 ], [ 733839.323592993430793, 3724942.155940910801291 ], [ 733841.955895113293082, 3724959.0 ], [ 733848.800145414541475, 3724959.0 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733826_3724959.json b/solaris/data/vectortile_test_expected/geoms_733826_3724959.json new file mode 100644 index 00000000..44f29499 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733826_3724959.json @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86006, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 300.47300947479948, "origlen": 0, "partialDec": 0.011802254828676802, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733842.117838621605188, 3724960.036273914854974 ], [ 733848.800145414541475, 3724959.0 ], [ 733841.955895113293082, 3724959.0 ], [ 733842.117838621605188, 3724960.036273914854974 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86010, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 309.6189312678942, "origlen": 0, "partialDec": 0.58115918743729356, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733871.0, 3724981.793367408216 ], [ 733859.904205449391156, 3724982.077810946386307 ], [ 733860.067030058125965, 3724988.341016149614006 ], [ 733863.800775169045664, 3724988.254532156512141 ], [ 733864.206809974275529, 3725004.0 ], [ 733871.0, 3725004.0 ], [ 733871.0, 3724981.793367408216 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733826_3725004.json b/solaris/data/vectortile_test_expected/geoms_733826_3725004.json new file mode 100644 index 00000000..29a97b57 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733826_3725004.json @@ -0,0 +1,9 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 134680, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 101.16730006427393, "origlen": 0, "partialDec": 0.20008370299942965, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733869.672710774117149, 3725049.0 ], [ 733867.962342513957992, 3725047.874265642371029 ], [ 733866.364284909097478, 3725046.792074438650161 ], [ 733863.243467739550397, 3725044.585138092748821 ], [ 733863.183629220700823, 3725042.852400838397443 ], [ 733862.012820417294279, 3725042.901524438522756 ], [ 733862.195312751107849, 3725047.97773150773719 ], [ 733863.279007132863626, 3725049.0 ], [ 733869.672710774117149, 3725049.0 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86011, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 255.33402454110015, "origlen": 0, "partialDec": 0.81644336831442044, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733862.708252763142809, 3725039.888753946404904 ], [ 733871.0, 3725039.569324703887105 ], [ 733871.0, 3725014.132537124678493 ], [ 733862.252876899787225, 3725014.796318253036588 ], [ 733861.065796526381746, 3725019.317511857487261 ], [ 733860.456022532889619, 3725022.2435885919258 ], [ 733864.300192805239931, 3725024.47926747566089 ], [ 733865.408540649805218, 3725028.512659384403378 ], [ 733862.343076531891711, 3725032.788266675546765 ], [ 733862.708252763142809, 3725039.888753946404904 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86010, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 309.6189312678942, "origlen": 0, "partialDec": 0.090266316781838496, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733864.315949483308941, 3725008.232279121875763 ], [ 733871.0, 3725008.06125667039305 ], [ 733871.0, 3725004.0 ], [ 733864.206809974275529, 3725004.0 ], [ 733864.315949483308941, 3725008.232279121875763 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733826_3725049.json b/solaris/data/vectortile_test_expected/geoms_733826_3725049.json new file mode 100644 index 00000000..d1bdd2bb --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733826_3725049.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 134680, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 101.16730006427393, "origlen": 0, "partialDec": 0.79991629700049249, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733869.037673469400033, 3725063.914816930424422 ], [ 733868.948807091452181, 3725060.328016446903348 ], [ 733869.699439586838707, 3725057.716118558309972 ], [ 733869.12773916113656, 3725056.037481418810785 ], [ 733870.693983497098088, 3725049.672183774411678 ], [ 733869.672710774117149, 3725049.0 ], [ 733863.279007132863626, 3725049.0 ], [ 733863.91026226838585, 3725049.595474375877529 ], [ 733866.030543527216651, 3725053.242929659318179 ], [ 733865.282733293948695, 3725054.978159906808287 ], [ 733863.307564386399463, 3725056.417098031844944 ], [ 733863.502039127284661, 3725064.046122215222567 ], [ 733869.037673469400033, 3725063.914816930424422 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733826_3725094.json b/solaris/data/vectortile_test_expected/geoms_733826_3725094.json new file mode 100644 index 00000000..f2bf7065 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733826_3725094.json @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86012, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 284.46838311737099, "origlen": 0, "partialDec": 0.15221219590616589, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733830.582235559704714, 3725115.192502366378903 ], [ 733828.823811884387396, 3725103.940700557082891 ], [ 733826.438338553067297, 3725104.315333605743945 ], [ 733826.0, 3725101.510892950929701 ], [ 733826.0, 3725115.904065827373415 ], [ 733830.582235559704714, 3725115.192502366378903 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86014, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 269.2410676574849, "origlen": 0, "partialDec": 0.66304888315714261, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733843.10121820657514, 3725139.0 ], [ 733840.877073246985674, 3725134.199127878062427 ], [ 733839.731750019127503, 3725134.726086068432778 ], [ 733833.373904321575537, 3725120.998228457290679 ], [ 733826.0, 3725124.38049762416631 ], [ 733826.0, 3725129.012582149822265 ], [ 733830.626115061459132, 3725139.0 ], [ 733843.10121820657514, 3725139.0 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733871_3724689.json b/solaris/data/vectortile_test_expected/geoms_733871_3724689.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733871_3724689.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733871_3724734.json b/solaris/data/vectortile_test_expected/geoms_733871_3724734.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733871_3724734.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733871_3724779.json b/solaris/data/vectortile_test_expected/geoms_733871_3724779.json new file mode 100644 index 00000000..dafeb67b --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733871_3724779.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 85995, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 208.27750543142528, "origlen": 0, "partialDec": 0.01344511640873805, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733916.0, 3724819.008678769227117 ], [ 733914.877926233806647, 3724824.0 ], [ 733916.0, 3724824.0 ], [ 733916.0, 3724819.008678769227117 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733871_3724824.json b/solaris/data/vectortile_test_expected/geoms_733871_3724824.json new file mode 100644 index 00000000..cdee9782 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733871_3724824.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 85995, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 208.27750543142528, "origlen": 0, "partialDec": 0.29052810106459787, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733914.877926233806647, 3724824.0 ], [ 733913.923271550564095, 3724828.246590849943459 ], [ 733907.995921526453458, 3724826.925592739600688 ], [ 733906.674282207386568, 3724832.775251807179302 ], [ 733916.0, 3724834.853403809480369 ], [ 733916.0, 3724824.0 ], [ 733914.877926233806647, 3724824.0 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733871_3724869.json b/solaris/data/vectortile_test_expected/geoms_733871_3724869.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733871_3724869.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733871_3724914.json b/solaris/data/vectortile_test_expected/geoms_733871_3724914.json new file mode 100644 index 00000000..30042a7f --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733871_3724914.json @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86005, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 263.65732906731142, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733873.500091013731435, 3724936.93015677202493 ], [ 733873.77284804196097, 3724947.058124471455812 ], [ 733881.724138652090915, 3724946.841479253023863 ], [ 733881.803844684036449, 3724949.662298649549484 ], [ 733887.590841862838715, 3724949.503837334923446 ], [ 733887.518520563142374, 3724946.760883453302085 ], [ 733898.116790315601975, 3724946.486752765718848 ], [ 733897.845682443701662, 3724936.292234980501235 ], [ 733873.500091013731435, 3724936.93015677202493 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 134689, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 28.431725003743587, "origlen": 0, "partialDec": 0.11793113430313892, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733898.702365371049382, 3724959.0 ], [ 733899.437801164225675, 3724957.783386053517461 ], [ 733896.328320293803699, 3724957.77410851046443 ], [ 733896.323598282644525, 3724959.0 ], [ 733898.702365371049382, 3724959.0 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733871_3724959.json b/solaris/data/vectortile_test_expected/geoms_733871_3724959.json new file mode 100644 index 00000000..28711397 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733871_3724959.json @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86010, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 309.6189312678942, "origlen": 0, "partialDec": 0.29487274175170175, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733873.539359263842925, 3725004.0 ], [ 733873.247122836532071, 3724992.580114854499698 ], [ 733877.046077472623438, 3724992.48412902886048 ], [ 733876.762816092930734, 3724981.645636139903218 ], [ 733871.0, 3724981.793367408216 ], [ 733871.0, 3725004.0 ], [ 733873.539359263842925, 3725004.0 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 134689, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 28.431725003743587, "origlen": 0, "partialDec": 0.88206886569721232, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733898.297767688753083, 3724968.365196610335261 ], [ 733897.317986879032105, 3724965.910844214726239 ], [ 733898.783516068593599, 3724963.294194012414664 ], [ 733898.249386645853519, 3724961.216946474276483 ], [ 733898.518854100489989, 3724959.303578331600875 ], [ 733898.702365371049382, 3724959.0 ], [ 733896.323598282644525, 3724959.0 ], [ 733896.316138221649453, 3724960.936722508631647 ], [ 733895.295106926350854, 3724960.934007437899709 ], [ 733895.271784850629047, 3724968.35795633494854 ], [ 733898.297767688753083, 3724968.365196610335261 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733871_3725004.json b/solaris/data/vectortile_test_expected/geoms_733871_3725004.json new file mode 100644 index 00000000..214b537a --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733871_3725004.json @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86011, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 255.33402454110015, "origlen": 0, "partialDec": 0.18355663168738637, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733871.0, 3725039.569324703887105 ], [ 733873.467645566677675, 3725039.474261729512364 ], [ 733872.213034232496284, 3725014.040485232602805 ], [ 733871.0, 3725014.132537124678493 ], [ 733871.0, 3725039.569324703887105 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86010, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 309.6189312678942, "origlen": 0, "partialDec": 0.033701754027512569, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733871.0, 3725008.06125667039305 ], [ 733873.641557777300477, 3725007.993668059818447 ], [ 733873.539359263842925, 3725004.0 ], [ 733871.0, 3725004.0 ], [ 733871.0, 3725008.06125667039305 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733871_3725049.json b/solaris/data/vectortile_test_expected/geoms_733871_3725049.json new file mode 100644 index 00000000..479358af --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733871_3725049.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86015, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 251.33309650398371, "origlen": 0, "partialDec": 0.86354391451294721, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733892.678339317324571, 3725094.0 ], [ 733892.969486426794901, 3725093.91931293066591 ], [ 733895.139970269752666, 3725093.606038105674088 ], [ 733877.763453568681143, 3725074.182422619313002 ], [ 733875.624623022042215, 3725075.861519815400243 ], [ 733873.833603138453327, 3725081.167029351461679 ], [ 733874.96624890586827, 3725088.008793785702437 ], [ 733880.208773081074469, 3725094.0 ], [ 733892.678339317324571, 3725094.0 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733871_3725094.json b/solaris/data/vectortile_test_expected/geoms_733871_3725094.json new file mode 100644 index 00000000..b40efe91 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733871_3725094.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86015, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 251.33309650398371, "origlen": 0, "partialDec": 0.13645608548626761, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733884.809803572017699, 3725099.258101164363325 ], [ 733887.820667246589437, 3725100.263786776456982 ], [ 733887.97904269839637, 3725096.81619278434664 ], [ 733889.399402834707871, 3725094.908708232920617 ], [ 733892.678339317324571, 3725094.0 ], [ 733880.208773081074469, 3725094.0 ], [ 733884.809803572017699, 3725099.258101164363325 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733916_3724689.json b/solaris/data/vectortile_test_expected/geoms_733916_3724689.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733916_3724689.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733916_3724734.json b/solaris/data/vectortile_test_expected/geoms_733916_3724734.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733916_3724734.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733916_3724779.json b/solaris/data/vectortile_test_expected/geoms_733916_3724779.json new file mode 100644 index 00000000..ad1bfa6a --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733916_3724779.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 85995, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 208.27750543142528, "origlen": 0, "partialDec": 0.26662346769845036, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733925.038212854298763, 3724824.0 ], [ 733926.087521769106388, 3724819.343201762996614 ], [ 733916.41197619389277, 3724817.176084883511066 ], [ 733916.0, 3724819.008678769227117 ], [ 733916.0, 3724824.0 ], [ 733925.038212854298763, 3724824.0 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733916_3724824.json b/solaris/data/vectortile_test_expected/geoms_733916_3724824.json new file mode 100644 index 00000000..30aaa0c5 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733916_3724824.json @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 85995, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 208.27750543142528, "origlen": 0, "partialDec": 0.42940331482458827, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733922.277427969733253, 3724836.252271949779242 ], [ 733925.038212854298763, 3724824.0 ], [ 733916.0, 3724824.0 ], [ 733916.0, 3724834.853403809480369 ], [ 733922.277427969733253, 3724836.252271949779242 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 85996, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 147.78075500471959, "origlen": 0, "partialDec": 0.91029046907941491, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733960.773279143380933, 3724839.677568545099348 ], [ 733961.0, 3724837.512521461583674 ], [ 733961.0, 3724829.087063139304519 ], [ 733960.194102098466828, 3724829.175883492454886 ], [ 733957.964056476601399, 3724830.786153580993414 ], [ 733952.526077670161612, 3724831.474698279984295 ], [ 733949.761529997806065, 3724829.121061434503645 ], [ 733946.739561799215153, 3724828.947437366005033 ], [ 733946.682754538487643, 3724839.644461845047772 ], [ 733960.773279143380933, 3724839.677568545099348 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733916_3724869.json b/solaris/data/vectortile_test_expected/geoms_733916_3724869.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733916_3724869.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733916_3724914.json b/solaris/data/vectortile_test_expected/geoms_733916_3724914.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733916_3724914.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733916_3724959.json b/solaris/data/vectortile_test_expected/geoms_733916_3724959.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733916_3724959.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733916_3725004.json b/solaris/data/vectortile_test_expected/geoms_733916_3725004.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733916_3725004.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733916_3725049.json b/solaris/data/vectortile_test_expected/geoms_733916_3725049.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733916_3725049.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733916_3725094.json b/solaris/data/vectortile_test_expected/geoms_733916_3725094.json new file mode 100644 index 00000000..2f6d9d6f --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733916_3725094.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 134690, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 75.233357615263444, "origlen": 0, "partialDec": 0.5426079647770381, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733933.053643652936444, 3725139.0 ], [ 733933.981720102136023, 3725136.448536148294806 ], [ 733932.724451179732569, 3725136.617617703508586 ], [ 733929.566822279826738, 3725137.062165400478989 ], [ 733928.701306592440233, 3725138.295111690182239 ], [ 733927.683588467072695, 3725137.016209401190281 ], [ 733927.95271526533179, 3725134.736601110547781 ], [ 733928.741625818191096, 3725132.458578986581415 ], [ 733926.893847053404897, 3725130.959658264648169 ], [ 733925.461097773630172, 3725129.570747075602412 ], [ 733925.616007869830355, 3725131.971682267263532 ], [ 733924.303450533887371, 3725134.02606556750834 ], [ 733924.030880918144248, 3725136.827193052973598 ], [ 733923.15020942944102, 3725138.681254582013935 ], [ 733923.05790709448047, 3725139.0 ], [ 733933.053643652936444, 3725139.0 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733961_3724689.json b/solaris/data/vectortile_test_expected/geoms_733961_3724689.json new file mode 100644 index 00000000..8c93eb06 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733961_3724689.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 92642, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 272.76725138138551, "origlen": 0, "partialDec": 0.50582372917952123, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 734006.0, 3724733.314095525071025 ], [ 734005.036672754795291, 3724732.308777935802937 ], [ 734004.700672700069845, 3724728.960097201168537 ], [ 733985.17671379854437, 3724727.518070545978844 ], [ 733980.793273733812384, 3724729.49750511161983 ], [ 733981.172765030758455, 3724734.0 ], [ 734006.0, 3724734.0 ], [ 734006.0, 3724733.314095525071025 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733961_3724734.json b/solaris/data/vectortile_test_expected/geoms_733961_3724734.json new file mode 100644 index 00000000..58a1b216 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733961_3724734.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 92642, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 272.76725138138551, "origlen": 0, "partialDec": 0.49174194516610154, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733998.172171951970086, 3724738.955371181480587 ], [ 734005.087085773120634, 3724738.990968229714781 ], [ 734006.0, 3724735.776267854962498 ], [ 734006.0, 3724734.0 ], [ 733981.172765030758455, 3724734.0 ], [ 733981.333166613709182, 3724735.903093201573938 ], [ 733988.48206047678832, 3724740.80528543330729 ], [ 733992.534389728447422, 3724740.981871583964676 ], [ 733996.584479778190143, 3724740.86985838599503 ], [ 733998.172171951970086, 3724738.955371181480587 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733961_3724779.json b/solaris/data/vectortile_test_expected/geoms_733961_3724779.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733961_3724779.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733961_3724824.json b/solaris/data/vectortile_test_expected/geoms_733961_3724824.json new file mode 100644 index 00000000..b1f9321a --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733961_3724824.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 85996, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 147.78075500471959, "origlen": 0, "partialDec": 0.089709530921962946, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733961.0, 3724837.512521461583674 ], [ 733961.184794776490889, 3724835.747843090444803 ], [ 733963.223460652050562, 3724833.61130030779168 ], [ 733963.607368887402117, 3724830.812890837434679 ], [ 733962.502454621368088, 3724828.921473243273795 ], [ 733961.0, 3724829.087063139304519 ], [ 733961.0, 3724837.512521461583674 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733961_3724869.json b/solaris/data/vectortile_test_expected/geoms_733961_3724869.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733961_3724869.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733961_3724914.json b/solaris/data/vectortile_test_expected/geoms_733961_3724914.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733961_3724914.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733961_3724959.json b/solaris/data/vectortile_test_expected/geoms_733961_3724959.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733961_3724959.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_733961_3725004.json b/solaris/data/vectortile_test_expected/geoms_733961_3725004.json new file mode 100644 index 00000000..46900b41 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733961_3725004.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86607, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 109.09963485201257, "origlen": 0, "partialDec": 1.0, "truncated": 0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733984.247434898978099, 3725029.170712447725236 ], [ 733984.469720786786638, 3725037.177755209617317 ], [ 733988.266932822414674, 3725039.434536231681705 ], [ 733990.833932525943965, 3725040.751259885728359 ], [ 733991.039521950762719, 3725035.751105143688619 ], [ 733997.483796045300551, 3725035.664246738888323 ], [ 733997.398187489830889, 3725028.903508787043393 ], [ 733984.247434898978099, 3725029.170712447725236 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733961_3725049.json b/solaris/data/vectortile_test_expected/geoms_733961_3725049.json new file mode 100644 index 00000000..d18e6357 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733961_3725049.json @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86606, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 298.78711714810817, "origlen": 0, "partialDec": 0.86838371522454494, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 734006.0, 3725065.888844077009708 ], [ 734002.152090000803582, 3725065.742649260908365 ], [ 734002.463489620480686, 3725057.548868575133383 ], [ 733989.952479137689807, 3725057.088115018326789 ], [ 733989.289290938875638, 3725074.750953383278102 ], [ 734006.0, 3725075.374068238772452 ], [ 734006.0, 3725065.888844077009708 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86605, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 238.82858297724087, "origlen": 0, "partialDec": 0.22456785130355603, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 734006.0, 3725086.290827450808138 ], [ 733999.194156195735559, 3725086.14615699602291 ], [ 733999.022649019025266, 3725094.0 ], [ 734006.0, 3725094.0 ], [ 734006.0, 3725086.290827450808138 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_733961_3725094.json b/solaris/data/vectortile_test_expected/geoms_733961_3725094.json new file mode 100644 index 00000000..94159dcb --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_733961_3725094.json @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86604, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 243.05210583905563, "origlen": 0, "partialDec": 0.64773908923557733, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733998.347550055012107, 3725137.941802813205868 ], [ 734006.0, 3725138.167202132754028 ], [ 734006.0, 3725114.898354505188763 ], [ 734003.339624392450787, 3725114.824559542350471 ], [ 733998.844389547826722, 3725120.630035975016654 ], [ 733998.347550055012107, 3725137.941802813205868 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86605, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 238.82858297724087, "origlen": 0, "partialDec": 0.4327024436850192, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 733999.022649019025266, 3725094.0 ], [ 733998.721341388998553, 3725107.797806410584599 ], [ 734002.757127131801099, 3725107.885221907868981 ], [ 734004.217231130925938, 3725109.674339018762112 ], [ 734006.0, 3725109.713915592059493 ], [ 734006.0, 3725094.0 ], [ 733999.022649019025266, 3725094.0 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_734006_3724689.json b/solaris/data/vectortile_test_expected/geoms_734006_3724689.json new file mode 100644 index 00000000..00dc9b12 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_734006_3724689.json @@ -0,0 +1,9 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 92642, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 272.76725138138551, "origlen": 0, "partialDec": 0.000791905883559901, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 734006.504426542669535, 3724734.0 ], [ 734006.539364616852254, 3724733.876970435027033 ], [ 734006.0, 3724733.314095525071025 ], [ 734006.0, 3724734.0 ], [ 734006.504426542669535, 3724734.0 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 92641, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 127.89418152890968, "origlen": 0, "partialDec": 0.45229163370845532, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 734031.973043444682844, 3724734.0 ], [ 734033.127172784181312, 3724732.295262097381055 ], [ 734018.804850079119205, 3724729.581800156738609 ], [ 734016.222719809040427, 3724732.304357184097171 ], [ 734011.883401251398027, 3724732.475890559609979 ], [ 734011.748591097886674, 3724734.0 ], [ 734031.973043444682844, 3724734.0 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 92640, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 140.04971670918377, "origlen": 0, "partialDec": 0.19622806628268313, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 734044.256298960419372, 3724728.538379620760679 ], [ 734044.193610417540185, 3724734.0 ], [ 734051.0, 3724734.0 ], [ 734051.0, 3724729.625226940959692 ], [ 734048.398788033635356, 3724730.725917739327997 ], [ 734045.49420094571542, 3724729.545217602048069 ], [ 734044.256298960419372, 3724728.538379620760679 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_734006_3724734.json b/solaris/data/vectortile_test_expected/geoms_734006_3724734.json new file mode 100644 index 00000000..6ffaf9fd --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_734006_3724734.json @@ -0,0 +1,9 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 92642, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 272.76725138138551, "origlen": 0, "partialDec": 0.0016424197706948571, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 734006.0, 3724735.776267854962498 ], [ 734006.504426542669535, 3724734.0 ], [ 734006.0, 3724734.0 ], [ 734006.0, 3724735.776267854962498 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 92641, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 127.89418152890968, "origlen": 0, "partialDec": 0.54770836629094588, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 734011.306465386645868, 3724738.998495681677014 ], [ 734020.362710424815305, 3724739.918715209700167 ], [ 734021.868912066216581, 3724735.638380894903094 ], [ 734030.806304456898943, 3724735.723363324068487 ], [ 734031.973043444682844, 3724734.0 ], [ 734011.748591097886674, 3724734.0 ], [ 734011.306465386645868, 3724738.998495681677014 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 92640, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 140.04971670918377, "origlen": 0, "partialDec": 0.19012403421520796, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 734044.193610417540185, 3724734.0 ], [ 734044.149200432701036, 3724737.8691356764175 ], [ 734051.0, 3724737.92929164506495 ], [ 734051.0, 3724734.0 ], [ 734044.193610417540185, 3724734.0 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_734006_3724779.json b/solaris/data/vectortile_test_expected/geoms_734006_3724779.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_734006_3724779.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_734006_3724824.json b/solaris/data/vectortile_test_expected/geoms_734006_3724824.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_734006_3724824.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_734006_3724869.json b/solaris/data/vectortile_test_expected/geoms_734006_3724869.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_734006_3724869.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_734006_3724914.json b/solaris/data/vectortile_test_expected/geoms_734006_3724914.json new file mode 100644 index 00000000..b20c417c --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_734006_3724914.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86004, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 245.87085671707746, "origlen": 0, "partialDec": 0.20134007160948594, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 734039.951275395345874, 3724957.361701209563762 ], [ 734020.141815275885165, 3724955.846008125692606 ], [ 734019.899857406038791, 3724959.0 ], [ 734041.972572033060715, 3724959.0 ], [ 734039.951275395345874, 3724957.361701209563762 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_734006_3724959.json b/solaris/data/vectortile_test_expected/geoms_734006_3724959.json new file mode 100644 index 00000000..7e8f51b3 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_734006_3724959.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86004, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 245.87085671707746, "origlen": 0, "partialDec": 0.79865992839011157, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 734019.899857406038791, 3724959.0 ], [ 734019.51552347233519, 3724964.009905667975545 ], [ 734026.204987838980742, 3724964.528337045572698 ], [ 734026.000307232956402, 3724967.20904346043244 ], [ 734028.35346275055781, 3724967.399663022719324 ], [ 734028.161849318654276, 3724969.925317186396569 ], [ 734033.76750397705473, 3724970.350708946119994 ], [ 734033.998593332478777, 3724967.348806756082922 ], [ 734043.10642178892158, 3724968.048365659546107 ], [ 734043.692446619272232, 3724960.393990571144968 ], [ 734041.972572033060715, 3724959.0 ], [ 734019.899857406038791, 3724959.0 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_734006_3725004.json b/solaris/data/vectortile_test_expected/geoms_734006_3725004.json new file mode 100644 index 00000000..c7751493 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_734006_3725004.json @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG:32616"}}, "features": []} \ No newline at end of file diff --git a/solaris/data/vectortile_test_expected/geoms_734006_3725049.json b/solaris/data/vectortile_test_expected/geoms_734006_3725049.json new file mode 100644 index 00000000..dd27eb2d --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_734006_3725049.json @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86606, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 298.78711714810817, "origlen": 0, "partialDec": 0.13161628477513404, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 734010.322496132925153, 3725066.053069988265634 ], [ 734006.0, 3725065.888844077009708 ], [ 734006.0, 3725075.374068238772452 ], [ 734009.97067632782273, 3725075.522128228098154 ], [ 734010.322496132925153, 3725066.053069988265634 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86605, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 238.82858297724087, "origlen": 0, "partialDec": 0.11780849332891749, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 734009.584178030490875, 3725094.0 ], [ 734009.752270760363899, 3725086.37058871705085 ], [ 734006.0, 3725086.290827450808138 ], [ 734006.0, 3725094.0 ], [ 734009.584178030490875, 3725094.0 ] ] ] } } +] +} diff --git a/solaris/data/vectortile_test_expected/geoms_734006_3725094.json b/solaris/data/vectortile_test_expected/geoms_734006_3725094.json new file mode 100644 index 00000000..d4531a24 --- /dev/null +++ b/solaris/data/vectortile_test_expected/geoms_734006_3725094.json @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::32616" } }, +"features": [ +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86604, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 243.05210583905563, "origlen": 0, "partialDec": 0.35226091076888344, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 734006.0, 3725138.167202132754028 ], [ 734009.348738314933144, 3725138.265837647020817 ], [ 734010.00927426526323, 3725115.009565961547196 ], [ 734006.0, 3725114.898354505188763 ], [ 734006.0, 3725138.167202132754028 ] ] ] } }, +{ "type": "Feature", "properties": { "access": "", "addr_house": "", "addr_hou_1": "", "addr_inter": "", "admin_leve": "", "aerialway": "", "aeroway": "", "amenity": "", "area": "", "barrier": "", "bicycle": "", "boundary": "", "brand": "", "bridge": "", "building": "yes", "constructi": "", "covered": "", "culvert": "", "cutting": "", "denominati": "", "disused": "", "embankment": "", "foot": "", "generator_": "", "harbour": "", "highway": "", "historic": "", "horse": "", "intermitte": "", "junction": "", "landuse": "", "layer": "", "leisure": "", "lock": "", "man_made": "", "military": "", "motorcar": "", "name": "Occlusion", "natural": "", "office": "", "oneway": "", "operator": "", "osm_id": 86605, "place": "", "population": "", "power": "", "power_sour": "", "public_tra": "", "railway": "", "ref": "", "religion": "", "route": "", "service": "", "shop": "", "sport": "", "surface": "", "tags": "\"security:classification\"=>\"UNCLASSIFIED\",\"source\"=>\"Unknown\"", "toll": "", "tourism": "", "tower_type": "", "tunnel": "", "water": "", "waterway": "", "wetland": "", "width": "", "wood": "", "z_order": -999999, "tracktype": "", "way_area": -999999.0, "origarea": 238.82858297724087, "origlen": 0, "partialDec": 0.22492121168800522, "truncated": 1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 734009.23638294194825, 3725109.785761667881161 ], [ 734009.584178030490875, 3725094.0 ], [ 734006.0, 3725094.0 ], [ 734006.0, 3725109.713915592059493 ], [ 734009.23638294194825, 3725109.785761667881161 ] ] ] } } +] +} diff --git a/solaris/eval/__init__.py b/solaris/eval/__init__.py index dc572ce4..3a4c7a02 100644 --- a/solaris/eval/__init__.py +++ b/solaris/eval/__init__.py @@ -1 +1 @@ -from . import baseeval, evalfunctions +from . import base, iou, challenges, pixel diff --git a/solaris/eval/baseeval.py b/solaris/eval/base.py similarity index 68% rename from solaris/eval/baseeval.py rename to solaris/eval/base.py index 22c20463..633b1a35 100644 --- a/solaris/eval/baseeval.py +++ b/solaris/eval/base.py @@ -1,18 +1,30 @@ -from __future__ import print_function, with_statement, division - import shapely.wkt import geopandas as gpd import pandas as pd from tqdm import tqdm import os -from . import evalfunctions as eF +from . import iou from fiona.errors import DriverError from fiona._err import CPLE_OpenFailedError -class EvalBase(): +class Evaluator(): """Object to test IoU for predictions and ground truth polygons. + Attributes + ---------- + ground_truth_fname : str + The filename for the ground truth CSV or JSON. + ground_truth_GDF : :class:`geopandas.GeoDataFrame` + A :class:`geopandas.GeoDataFrame` containing the ground truth vector + labels. + ground_truth_GDF_Edit : :class:`geopandas.GeoDataFrame` + A copy of ``ground_truth_GDF`` which will be manipulated during + processing. + proposal_GDF : :class:`geopandas.GeoDataFrame` + The proposal :class:`geopandas.GeoDataFrame`, added using + ``load_proposal()``. + Arguments --------- ground_truth_vector_file : str @@ -37,7 +49,8 @@ def __init__(self, ground_truth_vector_file): self.proposal_GDF = gpd.GeoDataFrame([]) # initialize proposal GDF def __repr__(self): - return 'EvalBase {}'.format(os.path.split(self.ground_truth_fname)[-1]) + return 'Evaluator {}'.format(os.path.split( + self.ground_truth_fname)[-1]) def get_iou_by_building(self): """Returns a copy of the ground truth table, which includes a @@ -48,7 +61,7 @@ def get_iou_by_building(self): return output_ground_truth_GDF def eval_iou_spacenet_csv(self, miniou=0.5, iou_field_prefix="iou_score", - imageIDField="ImageId", debug=False, minArea=0): + imageIDField="ImageId", debug=False, min_area=0): """Evaluate IoU between the ground truth and proposals in CSVs. Arguments @@ -65,7 +78,7 @@ def eval_iou_spacenet_csv(self, miniou=0.5, iou_field_prefix="iou_score", debug : bool , optional Argument for verbose execution during debugging. Defaults to ``False`` (silent execution). - minArea : float or int , optional + min_area : float or int , optional Minimum area of a ground truth polygon to be considered during evaluation. Often set to ``20`` in SpaceNet competitions. Defaults to ``0`` (consider all ground truth polygons). @@ -81,8 +94,7 @@ def eval_iou_spacenet_csv(self, miniou=0.5, iou_field_prefix="iou_score", 'Precision', 'Recall', 'F1Score') """ - - # Get List of all ImageIDs + # Get List of all ImageID in both ground truth and proposals imageIDList = [] imageIDList.extend(list(self.ground_truth_GDF[imageIDField].unique())) if not self.proposal_GDF.empty: @@ -100,12 +112,12 @@ def eval_iou_spacenet_csv(self, miniou=0.5, iou_field_prefix="iou_score", self.ground_truth_GDF[imageIDField] == imageID ].copy(deep=True) self.ground_truth_GDF_Edit = self.ground_truth_GDF_Edit[ - self.ground_truth_GDF_Edit.area >= minArea + self.ground_truth_GDF_Edit.area >= min_area ] proposal_GDF_copy = self.proposal_GDF[self.proposal_GDF[ imageIDField] == imageID].copy(deep=True) proposal_GDF_copy = proposal_GDF_copy[proposal_GDF_copy.area - > minArea] + > min_area] if debug: print(iou_field) for _, pred_row in proposal_GDF_copy.iterrows(): @@ -113,23 +125,20 @@ def eval_iou_spacenet_csv(self, miniou=0.5, iou_field_prefix="iou_score", print(pred_row.name) if pred_row.geometry.area > 0: pred_poly = pred_row.geometry - iou_GDF = eF.calculate_iou(pred_poly, - self.ground_truth_GDF_Edit) + iou_GDF = iou.calculate_iou(pred_poly, + self.ground_truth_GDF_Edit) # Get max iou if not iou_GDF.empty: - max_iou_row = iou_GDF.loc[ - iou_GDF['iou_score'].idxmax(axis=0, skipna=True) - ] - # Find corresponding entry in full ground truth table - max_iou_row_id = max_iou_row[:id_cols] - match_flags = ground_truth_ids.eq( - max_iou_row_id, axis=1).all(1) - truth_index = match_flags[match_flags].index[0] - if max_iou_row[iou_field] > \ - self.ground_truth_GDF.iloc[truth_index, iou_index]: - self.ground_truth_GDF.iloc[truth_index, - iou_index] \ - = max_iou_row[iou_field] + max_index = iou_GDF['iou_score'].idxmax(axis=0, + skipna=True) + max_iou_row = iou_GDF.loc[max_index] + # Update entry in full ground truth table + previous_iou = self.ground_truth_GDF.iloc[ + max_index, iou_index] + new_iou = max_iou_row[iou_field] + if new_iou > previous_iou: + self.ground_truth_GDF.iloc[max_index, iou_index] \ + = new_iou if max_iou_row['iou_score'] > miniou: self.proposal_GDF.loc[pred_row.name, iou_field] \ = max_iou_row['iou_score'] @@ -152,7 +161,7 @@ def eval_iou_spacenet_csv(self, miniou=0.5, iou_field_prefix="iou_score", proposal_GDF_copy = self.proposal_GDF[ self.proposal_GDF[imageIDField] == imageID].copy(deep=True) proposal_GDF_copy = proposal_GDF_copy[ - proposal_GDF_copy.area > minArea] + proposal_GDF_copy.area > min_area] if not proposal_GDF_copy.empty: if iou_field in proposal_GDF_copy.columns: TruePos = proposal_GDF_copy[ @@ -260,8 +269,8 @@ def eval_iou(self, miniou=0.5, iou_field_prefix='iou_score', if pred_row['__max_conf_class'] == class_id \ or class_id == 'all': pred_poly = pred_row.geometry - iou_GDF = eF.calculate_iou(pred_poly, - self.ground_truth_GDF_Edit) + iou_GDF = iou.calculate_iou(pred_poly, + self.ground_truth_GDF_Edit) # Get max iou if not iou_GDF.empty: max_iou_row = iou_GDF.loc[iou_GDF['iou_score'].idxmax( @@ -320,9 +329,139 @@ def eval_iou(self, miniou=0.5, iou_field_prefix='iou_score', return scoring_dict_list + def eval_iou_return_GDFs(self, miniou=0.5, iou_field_prefix='iou_score', + ground_truth_class_field='', calculate_class_scores=True, + class_list=['all']): + """Evaluate IoU between the ground truth and proposals. + Arguments + --------- + miniou : float, optional + Minimum intersection over union score to qualify as a successful + object detection event. Defaults to ``0.5``. + iou_field_prefix : str, optional + The name of the IoU score column in ``self.proposal_GDF``. Defaults + to ``"iou_score"``. + ground_truth_class_field : str, optional + The column in ``self.ground_truth_GDF`` that indicates the class of + each polygon. Required if using ``calculate_class_scores``. + calculate_class_scores : bool, optional + Should class-by-class scores be calculated? Defaults to ``True``. + class_list : list, optional + List of classes to be scored. Defaults to ``['all']`` (score all + classes). + Returns + ------- + scoring_dict_list : list + list of score output dicts for each image in the ground + truth and evaluated image datasets. The dicts contain + the following keys: :: + ('class_id', 'iou_field', 'TruePos', 'FalsePos', 'FalseNeg', + 'Precision', 'Recall', 'F1Score') + True_Pos_gdf : gdf + A geodataframe containing only true positive predictions + False_Neg_gdf : gdf + A geodataframe containing only false negative predictions + False_Pos_gdf : gdf + A geodataframe containing only false positive predictions + """ + + scoring_dict_list = [] + + if calculate_class_scores: + if not ground_truth_class_field: + raise ValueError('Must provide ground_truth_class_field if using calculate_class_scores.') + if class_list == ['all']: + class_list = list( + self.ground_truth_GDF[ground_truth_class_field].unique()) + if not self.proposal_GDF.empty: + class_list.extend( + list(self.proposal_GDF['__max_conf_class'].unique())) + class_list = list(set(class_list)) + + for class_id in class_list: + iou_field = "{}_{}".format(iou_field_prefix, class_id) + if class_id is not 'all': # this is probably unnecessary now + self.ground_truth_GDF_Edit = self.ground_truth_GDF[ + self.ground_truth_GDF[ + ground_truth_class_field] == class_id].copy(deep=True) + else: + self.ground_truth_GDF_Edit = self.ground_truth_GDF.copy( + deep=True) + + for _, pred_row in tqdm(self.proposal_GDF.iterrows()): + if pred_row['__max_conf_class'] == class_id or class_id == 'all': + pred_poly = pred_row.geometry + iou_GDF = eF.calculate_iou(pred_poly, + self.ground_truth_GDF_Edit) + # Get max iou + if not iou_GDF.empty: + max_iou_row = iou_GDF.loc[iou_GDF['iou_score'].idxmax( + axis=0, skipna=True)] + if max_iou_row['iou_score'] > miniou: + self.proposal_GDF.loc[pred_row.name, iou_field] = max_iou_row['iou_score'] + self.ground_truth_GDF_Edit = self.ground_truth_GDF_Edit.drop(max_iou_row.name, axis=0) + else: + self.proposal_GDF.loc[pred_row.name, iou_field] = 0 + else: + self.proposal_GDF.loc[pred_row.name, iou_field] = 0 + + if self.proposal_GDF.empty: + TruePos = 0 + FalsePos = 0 + else: + try: + True_Pos_gdf = self.proposal_GDF[ + self.proposal_GDF[iou_field] >= miniou] + TruePos = True_Pos_gdf.shape[0] + if TruePos == 0: + True_Pos_gdf = None + False_Pos_gdf = self.proposal_GDF[ + self.proposal_GDF[iou_field] < miniou] + FalsePos = False_Pos_gdf.shape[0] + if FalsePos == 0: + False_Pos_gdf = None + except KeyError: # handle missing iou_field + print("iou field {} missing") + TruePos = 0 + FalsePos = 0 + False_Pos_gdf = None + True_Pos_gdf = None + + # number of remaining rows in ground_truth_gdf_edit after removing + # matches is number of false negatives + False_Neg_gdf = self.ground_truth_GDF_Edit + FalseNeg = False_Neg_gdf.shape[0] + if FalseNeg == 0: + False_Neg_gdf = None + if float(TruePos + FalsePos) > 0: + Precision = TruePos / float(TruePos + FalsePos) + else: + Precision = 0 + if float(TruePos + FalseNeg) > 0: + Recall = TruePos / float(TruePos + FalseNeg) + else: + Recall = 0 + if Recall * Precision > 0: + F1Score = 2 * Precision * Recall / (Precision + Recall) + else: + F1Score = 0 + + score_calc = {'class_id': class_id, + 'iou_field': iou_field, + 'TruePos': TruePos, + 'FalsePos': FalsePos, + 'FalseNeg': FalseNeg, + 'Precision': Precision, + 'Recall': Recall, + 'F1Score': F1Score + } + scoring_dict_list.append(score_calc) + + return scoring_dict_list, True_Pos_gdf, False_Neg_gdf, False_Pos_gdf + def load_proposal(self, proposal_vector_file, conf_field_list=['conf'], proposalCSV=False, pred_row_geo_value='PolygonWKT_Pix', - conf_field_mapping=[]): + conf_field_mapping=None): """Load in a proposal geojson or CSV. Arguments @@ -352,7 +491,7 @@ def load_proposal(self, proposal_vector_file, conf_field_list=['conf'], ----- Loads in a .geojson or .csv-formatted file of proposal polygons for comparison to the ground truth and stores it as part of the - ``EvalBase`` instance. This method assumes the geometry contained in + ``Evaluator`` instance. This method assumes the geometry contained in the proposal file is in WKT format. """ @@ -386,7 +525,7 @@ def load_proposal(self, proposal_vector_file, conf_field_list=['conf'], self.proposal_GDF['__total_conf'] = 1.0 self.proposal_GDF['__max_conf_class'] = 1 - if conf_field_mapping: + if conf_field_mapping is not None: self.proposal_GDF['__max_conf_class'] = [ conf_field_mapping[item] for item in self.proposal_GDF['__max_conf_class'].values] @@ -417,7 +556,7 @@ def load_truth(self, ground_truth_vector_file, truthCSV=False, Notes ----- - Loads the ground truth vector data into the ``EvalBase`` instance. + Loads the ground truth vector data into the ``Evaluator`` instance. """ if truthCSV: @@ -444,9 +583,9 @@ def eval(self, type='iou'): def eval_base(ground_truth_vector_file, csvFile=False, truth_geo_value='PolygonWKT_Pix'): - """Deprecated API to EvalBase. + """Deprecated API to Evaluator. .. deprecated:: 0.3 - Use :class:`EvalBase` instead.""" + Use :class:`Evaluator` instead.""" - return EvalBase(ground_truth_vector_file) + return Evaluator(ground_truth_vector_file) diff --git a/solaris/eval/challenges.py b/solaris/eval/challenges.py new file mode 100644 index 00000000..5d6dcfca --- /dev/null +++ b/solaris/eval/challenges.py @@ -0,0 +1,229 @@ +import pandas as pd +from .base import Evaluator +import re + + +def spacenet_buildings_2(prop_csv, truth_csv, miniou=0.5, min_area=20): + """Evaluate a SpaceNet2 building footprint competition proposal csv. + + Uses :class:`Evaluator` to evaluate SpaceNet2 challenge proposals. + + Arguments + --------- + prop_csv : str + Path to the proposal polygon CSV file. + truth_csv : str + Path to the ground truth polygon CSV file. + miniou : float, optional + Minimum IoU score between a region proposal and ground truth to define + as a successful identification. Defaults to 0.5. + min_area : float or int, optional + Minimum area of ground truth regions to include in scoring calculation. + Defaults to ``20``. + + Returns + ------- + + results_DF, results_DF_Full + + results_DF : :py:class:`pd.DataFrame` + Summary :py:class:`pd.DataFrame` of score outputs grouped by nadir + angle bin, along with the overall score. + + results_DF_Full : :py:class:`pd.DataFrame` + :py:class:`pd.DataFrame` of scores by individual image chip across + the ground truth and proposal datasets. + + """ + + evaluator = Evaluator(ground_truth_vector_file=truth_csv) + evaluator.load_proposal(prop_csv, + conf_field_list=['Confidence'], + proposalCSV=True + ) + results = evaluator.eval_iou_spacenet_csv(miniou=miniou, + iou_field_prefix="iou_score", + imageIDField="ImageId", + min_area=min_area + ) + results_DF_Full = pd.DataFrame(results) + + results_DF_Full['AOI'] = [get_chip_id(imageID, challenge='spacenet_2') + for imageID in results_DF_Full['imageID'].values] + + results_DF = results_DF_Full.groupby(['AOI']).sum() + + # Recalculate Values after Summation of AOIs + for indexVal in results_DF.index: + rowValue = results_DF[results_DF.index == indexVal] + # Precision = TruePos / float(TruePos + FalsePos) + if float(rowValue['TruePos'] + rowValue['FalsePos']) > 0: + Precision = float( + rowValue['TruePos'] / float( + rowValue['TruePos'] + rowValue['FalsePos'])) + else: + Precision = 0 + # Recall = TruePos / float(TruePos + FalseNeg) + if float(rowValue['TruePos'] + rowValue['FalseNeg']) > 0: + Recall = float(rowValue['TruePos'] / float( + rowValue['TruePos'] + rowValue['FalseNeg'])) + else: + Recall = 0 + if Recall * Precision > 0: + F1Score = 2 * Precision * Recall / (Precision + Recall) + else: + F1Score = 0 + results_DF.loc[results_DF.index == indexVal, 'Precision'] = Precision + results_DF.loc[results_DF.index == indexVal, 'Recall'] = Recall + results_DF.loc[results_DF.index == indexVal, 'F1Score'] = F1Score + + return results_DF, results_DF_Full + + +def off_nadir_buildings(prop_csv, truth_csv, image_columns={}, miniou=0.5, + min_area=20, verbose=False): + """Evaluate an off-nadir competition proposal csv. + + Uses :class:`Evaluator` to evaluate off-nadir challenge proposals. See + ``image_columns`` in the source code for how collects are broken into + Nadir, Off-Nadir, and Very-Off-Nadir bins. + + Arguments + --------- + prop_csv : str + Path to the proposal polygon CSV file. + truth_csv : str + Path to the ground truth polygon CSV file. + image_columns : dict, optional + dict of ``(collect: nadir bin)`` pairs used to separate collects into + sets. Nadir bin values must be one of + ``["Nadir", "Off-Nadir", "Very-Off-Nadir"]`` . See source code for + collect name options. + miniou : float, optional + Minimum IoU score between a region proposal and ground truth to define + as a successful identification. Defaults to 0.5. + min_area : float or int, optional + Minimum area of ground truth regions to include in scoring calculation. + Defaults to ``20``. + + Returnss + ------- + + results_DF, results_DF_Full + + results_DF : :py:class:`pd.DataFrame` + Summary :py:class:`pd.DataFrame` of score outputs grouped by nadir + angle bin, along with the overall score. + + results_DF_Full : :py:class:`pd.DataFrame` + :py:class:`pd.DataFrame` of scores by individual image chip across + the ground truth and proposal datasets. + + """ + + evaluator = Evaluator(ground_truth_vector_file=truth_csv) + evaluator.load_proposal(prop_csv, + conf_field_list=['Confidence'], + proposalCSV=True + ) + results = evaluator.eval_iou_spacenet_csv(miniou=miniou, + iou_field_prefix="iou_score", + imageIDField="ImageId", + min_area=min_area + ) + results_DF_Full = pd.DataFrame(results) + + if not image_columns: + image_columns = { + 'Atlanta_nadir7_catid_1030010003D22F00': "Nadir", + 'Atlanta_nadir8_catid_10300100023BC100': "Nadir", + 'Atlanta_nadir10_catid_1030010003993E00': "Nadir", + 'Atlanta_nadir10_catid_1030010003CAF100': "Nadir", + 'Atlanta_nadir13_catid_1030010002B7D800': "Nadir", + 'Atlanta_nadir14_catid_10300100039AB000': "Nadir", + 'Atlanta_nadir16_catid_1030010002649200': "Nadir", + 'Atlanta_nadir19_catid_1030010003C92000': "Nadir", + 'Atlanta_nadir21_catid_1030010003127500': "Nadir", + 'Atlanta_nadir23_catid_103001000352C200': "Nadir", + 'Atlanta_nadir25_catid_103001000307D800': "Nadir", + 'Atlanta_nadir27_catid_1030010003472200': "Off-Nadir", + 'Atlanta_nadir29_catid_1030010003315300': "Off-Nadir", + 'Atlanta_nadir30_catid_10300100036D5200': "Off-Nadir", + 'Atlanta_nadir32_catid_103001000392F600': "Off-Nadir", + 'Atlanta_nadir34_catid_1030010003697400': "Off-Nadir", + 'Atlanta_nadir36_catid_1030010003895500': "Off-Nadir", + 'Atlanta_nadir39_catid_1030010003832800': "Off-Nadir", + 'Atlanta_nadir42_catid_10300100035D1B00': "Very-Off-Nadir", + 'Atlanta_nadir44_catid_1030010003CCD700': "Very-Off-Nadir", + 'Atlanta_nadir46_catid_1030010003713C00': "Very-Off-Nadir", + 'Atlanta_nadir47_catid_10300100033C5200': "Very-Off-Nadir", + 'Atlanta_nadir49_catid_1030010003492700': "Very-Off-Nadir", + 'Atlanta_nadir50_catid_10300100039E6200': "Very-Off-Nadir", + 'Atlanta_nadir52_catid_1030010003BDDC00': "Very-Off-Nadir", + 'Atlanta_nadir53_catid_1030010003193D00': "Very-Off-Nadir", + 'Atlanta_nadir53_catid_1030010003CD4300': "Very-Off-Nadir", + } + + results_DF_Full['nadir-category'] = [ + image_columns[get_chip_id(imageID, challenge='spacenet_off_nadir')] + for imageID in results_DF_Full['imageID'].values] + + results_DF = results_DF_Full.groupby(['nadir-category']).sum() + + # Recalculate Values after Summation of AOIs + for indexVal in results_DF.index: + if verbose: + print(indexVal) + rowValue = results_DF[results_DF.index == indexVal] + # Precision = TruePos / float(TruePos + FalsePos) + if float(rowValue['TruePos'] + rowValue['FalsePos']) > 0: + Precision = float( + rowValue['TruePos'] / float(rowValue['TruePos'] + + rowValue['FalsePos']) + ) + else: + Precision = 0 + # Recall = TruePos / float(TruePos + FalseNeg) + if float(rowValue['TruePos'] + rowValue['FalseNeg']) > 0: + Recall = float(rowValue['TruePos'] / float(rowValue['TruePos'] + + rowValue['FalseNeg'])) + else: + Recall = 0 + if Recall * Precision > 0: + F1Score = 2 * Precision * Recall / (Precision + Recall) + else: + F1Score = 0 + results_DF.loc[results_DF.index == indexVal, 'Precision'] = Precision + results_DF.loc[results_DF.index == indexVal, 'Recall'] = Recall + results_DF.loc[results_DF.index == indexVal, 'F1Score'] = F1Score + + return results_DF, results_DF_Full + + +def get_chip_id(chip_name, challenge="spacenet_2"): + """Get the unique identifier for a chip location from SpaceNet images. + + Arguments + --------- + chip_name: str + The name of the chip to extract the identifier from. + challenge: str, optional + One of + ``['spacenet_buildings_2', 'spacenet_roads', 'spacenet_off_nadir']``. + The name of the challenge that `chip_name` came from. Defaults to + ``'spacenet_2'``. + + Returns + ------- + chip_id : str + The unique identifier for the chip location. + """ + # AS NEW CHALLENGES ARE ADDED, ADD THE CHIP MATCHING FUNCTIONALITY WITHIN + # THIS FUNCTION. + if challenge in ['spacenet_2', 'spacenet_3']: + chip_id = '_'.join(chip_name.split('_')[:-1]) + elif challenge == 'spacenet_off_nadir': + chip_id = re.findall('Atlanta_nadir[0-9]{1,2}_catid_[0-9A-Z]{16}', + chip_name)[0] + + return chip_id diff --git a/solaris/eval/challenges/__init__.py b/solaris/eval/challenges/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/solaris/eval/challenges/off_nadir_dataset.py b/solaris/eval/challenges/off_nadir_dataset.py deleted file mode 100644 index ee08ce5d..00000000 --- a/solaris/eval/challenges/off_nadir_dataset.py +++ /dev/null @@ -1,136 +0,0 @@ -from __future__ import print_function, with_statement, division - -import pandas as pd -from .. import baseeval as bF -import re - -# Note, for mac osx compatability import something from shapely.geometry before -# importing fiona or geopandas -# https://github.com/Toblerity/Shapely/issues/553 * Import shapely before -# rasterio or fiona - - -def eval_off_nadir(prop_csv, truth_csv, imageColumns={}, miniou=0.5, - minArea=20): - """Evaluate an off-nadir competition proposal csv. - - Uses :class:`EvalBase` to evaluate off-nadir challenge proposals. See - ``imageColumns`` in the source code for how collects are broken into - Nadir, Off-Nadir, and Very-Off-Nadir bins. - - Arguments - --------- - prop_csv : str - Path to the proposal polygon CSV file. - truth_csv : str - Path to the ground truth polygon CSV file. - imageColumns : dict, optional - dict of ``(collect: nadir bin)`` pairs used to separate collects into - sets. Nadir bin values must be one of - ``["Nadir", "Off-Nadir", "Very-Off-Nadir"]`` . See source code for - collect name options. - miniou : float, optional - Minimum IoU score between a region proposal and ground truth to define - as a successful identification. Defaults to 0.5. - minArea : float or int, optional - Minimum area of ground truth regions to include in scoring calculation. - Defaults to ``20``. - - Returnss - ------- - - results_DF, results_DF_Full - - results_DF : :py:class:`pd.DataFrame` - Summary :py:class:`pd.DataFrame` of score outputs grouped by nadir - angle bin, along with the overall score. - - results_DF_Full : :py:class:`pd.DataFrame` - :py:class:`pd.DataFrame` of scores by individual image chip across - the ground truth and proposal datasets. - - """ - - evalObject = bF.EvalBase(ground_truth_vector_file=truth_csv) - evalObject.load_proposal(prop_csv, - conf_field_list=['Confidence'], - proposalCSV=True - ) - results = evalObject.eval_iou_spacenet_csv(miniou=miniou, - iou_field_prefix="iou_score", - imageIDField="ImageId", - minArea=minArea - ) - results_DF_Full = pd.DataFrame(results) - - if not imageColumns: - imageColumns = { - 'Atlanta_nadir7_catid_1030010003D22F00': "Nadir", - 'Atlanta_nadir8_catid_10300100023BC100': "Nadir", - 'Atlanta_nadir10_catid_1030010003993E00': "Nadir", - 'Atlanta_nadir10_catid_1030010003CAF100': "Nadir", - 'Atlanta_nadir13_catid_1030010002B7D800': "Nadir", - 'Atlanta_nadir14_catid_10300100039AB000': "Nadir", - 'Atlanta_nadir16_catid_1030010002649200': "Nadir", - 'Atlanta_nadir19_catid_1030010003C92000': "Nadir", - 'Atlanta_nadir21_catid_1030010003127500': "Nadir", - 'Atlanta_nadir23_catid_103001000352C200': "Nadir", - 'Atlanta_nadir25_catid_103001000307D800': "Nadir", - 'Atlanta_nadir27_catid_1030010003472200': "Off-Nadir", - 'Atlanta_nadir29_catid_1030010003315300': "Off-Nadir", - 'Atlanta_nadir30_catid_10300100036D5200': "Off-Nadir", - 'Atlanta_nadir32_catid_103001000392F600': "Off-Nadir", - 'Atlanta_nadir34_catid_1030010003697400': "Off-Nadir", - 'Atlanta_nadir36_catid_1030010003895500': "Off-Nadir", - 'Atlanta_nadir39_catid_1030010003832800': "Off-Nadir", - 'Atlanta_nadir42_catid_10300100035D1B00': "Very-Off-Nadir", - 'Atlanta_nadir44_catid_1030010003CCD700': "Very-Off-Nadir", - 'Atlanta_nadir46_catid_1030010003713C00': "Very-Off-Nadir", - 'Atlanta_nadir47_catid_10300100033C5200': "Very-Off-Nadir", - 'Atlanta_nadir49_catid_1030010003492700': "Very-Off-Nadir", - 'Atlanta_nadir50_catid_10300100039E6200': "Very-Off-Nadir", - 'Atlanta_nadir52_catid_1030010003BDDC00': "Very-Off-Nadir", - 'Atlanta_nadir53_catid_1030010003193D00': "Very-Off-Nadir", - 'Atlanta_nadir53_catid_1030010003CD4300': "Very-Off-Nadir", - } - - results_DF_Full['nadir-category'] = [ - imageColumns[get_collect_id(imageID)] - for imageID in results_DF_Full['imageID'].values] - - results_DF = results_DF_Full.groupby(['nadir-category']).sum() - - # Recalculate Values after Summation of AOIs - for indexVal in results_DF.index: - rowValue = results_DF[results_DF.index == indexVal] - # Precision = TruePos / float(TruePos + FalsePos) - if float(rowValue['TruePos'] + rowValue['FalsePos']) > 0: - Precision = float( - rowValue['TruePos'] / float(rowValue['TruePos'] + - rowValue['FalsePos']) - ) - else: - Precision = 0 - # Recall = TruePos / float(TruePos + FalseNeg) - if float(rowValue['TruePos'] + rowValue['FalseNeg']) > 0: - Recall = float(rowValue['TruePos'] / float(rowValue['TruePos'] + - rowValue['FalseNeg'])) - else: - Recall = 0 - if Recall * Precision > 0: - F1Score = 2 * Precision * Recall / (Precision + Recall) - else: - F1Score = 0 - results_DF.loc[results_DF.index == indexVal, 'Precision'] = Precision - results_DF.loc[results_DF.index == indexVal, 'Recall'] = Recall - results_DF.loc[results_DF.index == indexVal, 'F1Score'] = F1Score - - return results_DF, results_DF_Full - - -def get_collect_id(imageID): - """Get the collect ID for an image name using a regex.""" - collect_id = re.findall('Atlanta_nadir[0-9]{1,2}_catid_[0-9A-Z]{16}', - imageID)[0] - - return collect_id diff --git a/solaris/eval/challenges/spacenet_buildings2_dataset.py b/solaris/eval/challenges/spacenet_buildings2_dataset.py deleted file mode 100644 index 6b66f9e7..00000000 --- a/solaris/eval/challenges/spacenet_buildings2_dataset.py +++ /dev/null @@ -1,93 +0,0 @@ -from __future__ import print_function, with_statement, division - -import pandas as pd -from .. import baseeval as bF -import re - -# Note, for mac osx compatability import something from shapely.geometry before -# importing fiona or geopandas -# https://github.com/Toblerity/Shapely/issues/553 * Import shapely before -# rasterio or fiona - - -def eval_spacenet_buildings2(prop_csv, truth_csv, miniou=0.5, minArea=20): - """Evaluate a SpaceNet2 building footprint competition proposal csv. - - Uses :class:`EvalBase` to evaluate SpaceNet2 challenge proposals. - - Arguments - --------- - prop_csv : str - Path to the proposal polygon CSV file. - truth_csv : str - Path to the ground truth polygon CSV file. - miniou : float, optional - Minimum IoU score between a region proposal and ground truth to define - as a successful identification. Defaults to 0.5. - minArea : float or int, optional - Minimum area of ground truth regions to include in scoring calculation. - Defaults to ``20``. - - Returns - ------- - - results_DF, results_DF_Full - - results_DF : :py:class:`pd.DataFrame` - Summary :py:class:`pd.DataFrame` of score outputs grouped by nadir - angle bin, along with the overall score. - - results_DF_Full : :py:class:`pd.DataFrame` - :py:class:`pd.DataFrame` of scores by individual image chip across - the ground truth and proposal datasets. - - """ - - evalObject = bF.EvalBase(ground_truth_vector_file=truth_csv) - evalObject.load_proposal(prop_csv, - conf_field_list=['Confidence'], - proposalCSV=True - ) - results = evalObject.eval_iou_spacenet_csv(miniou=miniou, - iou_field_prefix="iou_score", - imageIDField="ImageId", - minArea=minArea - ) - results_DF_Full = pd.DataFrame(results) - - results_DF_Full['AOI'] = [get_aoi(imageID) for imageID - in results_DF_Full['imageID'].values] - - results_DF = results_DF_Full.groupby(['AOI']).sum() - - # Recalculate Values after Summation of AOIs - for indexVal in results_DF.index: - rowValue = results_DF[results_DF.index == indexVal] - # Precision = TruePos / float(TruePos + FalsePos) - if float(rowValue['TruePos'] + rowValue['FalsePos']) > 0: - Precision = float( - rowValue['TruePos'] / float(rowValue['TruePos'] + - rowValue['FalsePos']) - ) - else: - Precision = 0 - # Recall = TruePos / float(TruePos + FalseNeg) - if float(rowValue['TruePos'] + rowValue['FalseNeg']) > 0: - Recall = float(rowValue['TruePos'] / float(rowValue['TruePos'] + - rowValue['FalseNeg'])) - else: - Recall = 0 - if Recall * Precision > 0: - F1Score = 2 * Precision * Recall / (Precision + Recall) - else: - F1Score = 0 - results_DF.loc[results_DF.index == indexVal, 'Precision'] = Precision - results_DF.loc[results_DF.index == indexVal, 'Recall'] = Recall - results_DF.loc[results_DF.index == indexVal, 'F1Score'] = F1Score - - return results_DF, results_DF_Full - - -def get_aoi(imageID): - """Get the AOI from an image name""" - return '_'.join(imageID.split('_')[:-1]) diff --git a/solaris/eval/evalfunctions.py b/solaris/eval/iou.py similarity index 100% rename from solaris/eval/evalfunctions.py rename to solaris/eval/iou.py diff --git a/solaris/eval/pixel.py b/solaris/eval/pixel.py new file mode 100644 index 00000000..34f0d0a9 --- /dev/null +++ b/solaris/eval/pixel.py @@ -0,0 +1,344 @@ +import time +import cv2 +import numpy as np +import matplotlib +import matplotlib.pyplot as plt + + +def iou(truth_mask, prop_mask, prop_threshold=0.5, verbose=False): + """Compute pixel-wise intersection over union. + + Multiplies truth_mask by 2, and subtract. Make sure arrays are clipped + so that overlapping regions don't cause problems + + Arguments + --------- + truth_mask : :class:`numpy.ndarray` + 2-D binary array of ground truth pixels. + prop_mask : :class:`numpy.ndarray` + 2-D array of proposals. + prop_threshold : float, optional + The threshold for proposal values to be defined as positive (``1``) or + negative (``0``) predictions. Values >= `prop_threshold` will be set to + ``1``, values < `prop_threshold` will be set to ``0``. + verbose : bool, optional + Switch to print relevant values. + + Returns + ------- + iou : float + Intersection over union of ground truth and proposal + """ + if truth_mask.shape != prop_mask.shape: + raise ValueError("The shape of `truth_mask` and `prop_mask` must " + "be the same.") + truth_mask_clip = np.clip(truth_mask, 0, 1).astype(float) + prop_mask_clip = (np.clip(prop_mask, 0, 1) >= prop_threshold).astype(float) + # subtract array + sub_mask = 2*prop_mask_clip - truth_mask_clip + add_mask = prop_mask_clip + truth_mask_clip + + # true pos = 1, false_pos = 2, true_neg = 0, false_neg = -1 + tp_count = np.sum(sub_mask == 1) + union = np.sum(add_mask > 0) + intersection = tp_count + + iou = 1. * intersection / union + + if verbose: + print("intersection:", intersection) + print("union:", union) + print("iou:", iou) + + return iou + + +def f1(truth_mask, prop_mask, prop_threshold=0.5, show_plot=False, im_file='', + show_colorbar=False, plot_file='', dpi=200, verbose=False): + """Compute pixel-wise precision, recall, and f1 score. + + Find true pos, false pos, true neg, false neg as well as f1 score. + Multiply truth_mask by 2, and subtract. Make sure arrays are clipped + so that overlapping regions don't cause problems. + + Arguments + --------- + truth_mask : :class:`numpy.ndarray` + 2-D binary array of ground truth pixels. + prop_mask : :class:`numpy.ndarray` + 2-D array of proposals. + prop_threshold : float, optional + The threshold for proposal values to be defined as positive (``1``) or + negative (``0``) predictions. Values >= `prop_threshold` will be set to + ``1``, values < `prop_threshold` will be set to ``0``. + show_plot : bool, optional + Switch to plot the outputs. Defaults to ``False``. + im_file : str, optional + Image file corresponding to the masks. Ignored if + ``show_plot == False``. Defaults to ``''``. + show_colorbar : bool, optional + Switch to show colorbar. Ignored if ``show_plot == False``. + Defaults to ``False``. + plot_file : str, optional + Output file if plotting. Ignored if ``show_plot == False``. + Defaults to ``''``. + dpi : int, optional + Dots per inch for plotting. Ignored if ``show_plot == False``. + Defaults to ``200``. + verbose : bool, optional + Switch to print relevant values. + + Returns + ------- + f1 : float + Pixel-wise F1 score. + precision : float + Pixel-wise precision. + recall : float + Pixel-wise recall. + """ + + truth_mask_clip = np.clip(truth_mask, 0, 1).astype(float) + prop_mask_clip = (np.clip(prop_mask, 0, 1) >= prop_threshold).astype(float) + # subtract array + sub_mask = 2*prop_mask_clip - truth_mask_clip + # sub_mask2 = prop_mask_clip - truth_mask_clip + + # true pos = 1, false_pos = 2, true_neg = 0, false_neg = -1 + n_pos = len(np.where(truth_mask_clip == 1)[0]) + tp_count = len(np.where(sub_mask == 1)[0]) + fp_count = len(np.where(sub_mask == 2)[0]) + tn_count = len(np.where(sub_mask == 0)[0]) + fn_count = len(np.where(sub_mask == -1)[0]) + + if (n_pos > 0) and (tp_count > 0): + precision = float(tp_count) / float(tp_count + fp_count) + recall = float(tp_count) / float(tp_count + fn_count) + f1 = 2 * precision * recall / (precision + recall) + else: + precision, recall, f1 = 0, 0, 0 + + if verbose: + print("mask.shape:\t", truth_mask.shape) + print("num pixels:\t", truth_mask.size) + print("false_neg:\t", fn_count) + print("false_pos:\t", fp_count) + print("true_neg:\t", tn_count) + print("true_pos:\t", tp_count) + print("precision:\t", precision) + print("recall:\t\t", recall) + print("F1 score:\t", f1) + + # TODO: split this out into a separate function + if show_plot: + + fontsize = 6 + t0 = time.time() + title = "Precision: " + str(np.round(precision, 3)) \ + + " Recall: " + str(np.round(recall, 3)) \ + + " F1: " + str(np.round(f1, 3)) + + if show_colorbar: + fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, sharex=True, + sharey=True, + figsize=(6, 6)) + else: + fig, (ax1, ax2, ax3) = plt.subplots(1, 3, sharex=True, + sharey=True, + figsize=(9.5, 3)) + # fig, ((ax1, ax2, ax3)) = plt.subplots(1, 3, sharex=True, + # sharey=True, figsize=(13,4)) + plt.suptitle(title, fontsize=fontsize) + + # ground truth + if len(im_file) > 0: + # raw image + ax1.imshow(cv2.imread(im_file, 1)) + # ground truth + # set zeros to nan + palette = plt.cm.gray + palette.set_over('orange', 1.0) + palette.set_over('orange', 1.0) + + z = truth_mask.astype(float) + z[z == 0] = np.nan + ax1.imshow(z, cmap=palette, alpha=0.5, + norm=matplotlib.colors.Normalize( + vmin=0.5, vmax=0.9, clip=False)) + ax1.set_title('truth_mask_clip + image', fontsize=fontsize) + else: + ax1.imshow(truth_mask_clip) + ax1.set_title('truth_mask_clip', fontsize=fontsize) + ax1.axis('off') + + # proposal mask + ax2.imshow(prop_mask_clip) + ax2.axis('off') + ax2.set_title('prop_mask_clip', fontsize=fontsize) + + # mask + if show_colorbar: + z = ax3.pcolor(sub_mask) + fig.colorbar(z) + ax4.axis('off') + + else: + ax3.imshow(sub_mask) + # z = ax3.pcolor(sub_mask2) + ax3.axis('off') + ax3.set_title('subtract_mask', fontsize=fontsize) + + # plt.tight_layout() + # fig.tight_layout(rect=[0, 0.03, 1, 0.95]) + plt.subplots_adjust(top=0.8) + + if len(plot_file) > 0: + plt.savefig(plot_file, dpi=dpi) + print("Time to create and save F1 plots:", time.time() - t0, "seconds") + + plt.show() + + return f1, precision, recall + + +def _get_neighborhood_limits(row, col, h, w, rho=3): + '''Get neighbors of point p with pixel coords row, col''' + + rowmin = max(0, row-rho) + rowmax = min(h, row + rho) + colmin = max(0, col-rho) + colmax = min(w, col + rho) + + return rowmin, rowmax, colmin, colmax + + +def relaxed_f1(truth_mask, prop_mask, radius=3, verbose=False): + """ + Compute relaxed f1 score + + Notes + ----- + Also find relaxed precision, recall, f1. + http://www.cs.toronto.edu/~fritz/absps/road_detection.pdf + "completenetess represents the fraction of true road pixels that are + within ρ pixels of a predicted road pixel, while correctness measures + the fraction of predicted road pixels that are within ρ pixels of a + true road pixel." + https://arxiv.org/pdf/1711.10684.pdf + The relaxed precision is defined as the fraction of number of pixels + predicted as road + within a range of ρ pixels from pixels labeled as road. The + relaxed recall is the fraction of number of pixels labeled as + road that are within a range of ρ pixels from pixels predicted + as road. + http://ceur-ws.org/Vol-156/paper5.pdf + + Arguments + --------- + truth_mask : np array + 2-D array of ground truth. + prop_mask : np array + 2-D array of proposals. + radius : int + Radius in pixels to use for relaxed f1. + verbose : bool + Switch to print relevant values + + Returns + ------- + output : tuple + Tuple containing [relaxed_f1, relaxed_precision, relaxed_recall] + + Examples + -------- + + >>> truth_mask = np.zeros(shape=(10, 10)) + >>> prop_mask = np.zeros(shape=(10, 10)) + + >>> truth_mask[5, :] = 1 + >>> prop_mask[5, :] = 1 + >>> prop_mask[:, 2] = 0 + >>> prop_mask[:, 3] = 1 + >>> prop_mask[6:8, :] = 0 + >>> prop_mask + array([[0., 0., 0., 1., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.], + [1., 1., 0., 1., 1., 1., 1., 1., 1., 1.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.]]) + >>>truth_mask + array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [1., 1., 1., 1., 1., 1., 1., 1., 1., 1.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]]) + >>> relaxed_f1(truth_mask, prop_mask, radius=3) + (0.8571428571428571, 0.75, 1.0) + """ + + truth_mask_clip = np.clip(truth_mask, 0, 1).astype(float) + prop_mask_clip = np.clip(prop_mask, 0, 1).astype(float) + + # true pos = 1, false_pos = 2, true_neg = 0, false_neg = -1 + n_truth = len(np.where(truth_mask_clip == 1)[0]) + n_prop = len(np.where(prop_mask_clip == 1)[0]) + + # iterate through truth pixels + precision_count = 0 + recall_count = 0 + h, w = truth_mask.shape + for row in range(h): + for col in range(w): + truth_val = truth_mask_clip[row][col] + prop_val = prop_mask_clip[row][col] + # get window limits + rowmin, rowmax, colmin, colmax = _get_neighborhood_limits( + row, col, h, w, rho=radius) + # get windows + truth_win = truth_mask_clip[rowmin:rowmax, colmin:colmax] + prop_win = prop_mask_clip[rowmin:rowmax, colmin:colmax] + + # add precision_count if proposal is within the radius of a gt node + if prop_val == 1: + if np.max(truth_win) > 0: + precision_count += 1 + + if truth_val == 1: + if np.max(prop_win) > 0: + recall_count += 1 + + # get fractions + if n_truth == 0: + relaxed_recall = 0 + else: + relaxed_recall = 1. * recall_count / n_truth + if n_prop == 0: + relaxed_precision = 0 + else: + relaxed_precision = 1. * precision_count / n_prop + + if (relaxed_recall > 0) and (relaxed_precision > 0): + relaxed_f1 = 2 * relaxed_precision * relaxed_recall \ + / (relaxed_precision + relaxed_recall) + else: + relaxed_f1 = 0 + + if verbose: + print("mask.shape:\t", truth_mask.shape) + print("num pixels:\t", truth_mask.size) + print("precision:\t", relaxed_precision) + print("recall:\t\t", relaxed_recall) + print("rF1 score:\t", f1) + + output = (relaxed_f1, relaxed_precision, relaxed_recall) + return output diff --git a/solaris/nets/_keras_losses.py b/solaris/nets/_keras_losses.py index c27c8c67..229f4e3b 100644 --- a/solaris/nets/_keras_losses.py +++ b/solaris/nets/_keras_losses.py @@ -116,3 +116,107 @@ def tf_lovasz_grad(gt_sorted): 'jaccard': k_jaccard_loss, 'dice': k_dice_loss } + + +def k_weighted_bce(y_true, y_pred, weight): + """Weighted binary cross-entropy for Keras. + + Arguments: + ---------- + y_true : ``tf.Tensor`` + passed silently by Keras during model training. + y_pred : ``tf.Tensor`` + passed silently by Keras during model training. + weight : :class:`float` or :class:`int` + Weight to assign to mask foreground pixels. Use values + >1 to over-weight foreground or 0 1: + class_two = class_two*(weight-1) + final_mask = weight_mask + class_two # foreground pixels weighted + return K.binary_crossentropy(y_pred, y_true) * final_mask + + +def k_layered_weighted_bce(y_true, y_pred, weights): + """Binary cross-entropy function with different weights for mask channels. + + Arguments: + ---------- + y_true (tensor): passed silently by Keras during model training. + y_pred (tensor): passed silently by Keras during model training. + weights (list-like): Weights to assign to mask foreground pixels for each + channel in the 3rd axis of the mask. + + Returns: + -------- + The binary cross-entropy loss function output multiplied by a weighting + mask. + + Usage: + ------ + See implementation instructions for `weighted_bce`. + + This loss function is intended to allow different weighting of different + segmentation outputs - for example, if a model outputs a 3D image mask, + where the first channel corresponds to foreground objects and the second + channel corresponds to object edges. `weights` must be a list of length + equal to the depth of the output mask. The output mask's "z-axis" + corresponding to the mask channel must be the third axis in the output + array. + + """ + weight_mask = K.ones_like(y_true) + submask_list = [] + for i in range(len(weights)): + class_two = K.equal(y_true[:, :, :, i], weight_mask[:, :, :, i]) + class_two = K.cast(class_two, 'float32') + if weights[i] < 1: + class_two = class_two*(1-weights[i]) + layer_mask = weight_mask[:, :, :, i] - class_two + elif weights[i] > 1: + class_two = class_two*(weights[i]-1) + layer_mask = weight_mask[:, :, :, i] + class_two + else: + layer_mask = weight_mask[:, :, :, i] + submask_list.append(layer_mask) + final_mask = K.stack(submask_list, axis=-1) + return K.binary_crossentropy(y_pred, y_true) * final_mask diff --git a/solaris/nets/_torch_losses.py b/solaris/nets/_torch_losses.py index a0927adc..ba20fd02 100644 --- a/solaris/nets/_torch_losses.py +++ b/solaris/nets/_torch_losses.py @@ -143,6 +143,25 @@ def flatten_binary_scores(scores, labels, ignore=None): return vscores, vlabels +class TorchJaccardLoss(torch.nn.modules.Module): + # modified from XD_XD's implementation + def __init__(self): + super(TorchJaccardLoss, self).__init__() + + def forward(self, outputs, targets): + eps = 1e-15 + + jaccard_target = (targets == 1).float() + jaccard_output = torch.sigmoid(outputs) + intersection = (jaccard_output * jaccard_target).sum() + union = jaccard_output.sum() + jaccard_target.sum() + jaccard_score = ((intersection + eps) / (union - intersection + eps)) + self._stash_jaccard = jaccard_score + loss = 1. - jaccard_score + + return loss + + class TorchStableBCELoss(torch.nn.modules.Module): def __init__(self): super(TorchStableBCELoss, self).__init__() @@ -273,6 +292,8 @@ def mean(l, ignore_nan=False, empty=0): 'binary_crossentropy': nn.BCELoss, 'bce': nn.BCELoss, 'bceloss': nn.BCELoss, + 'bcewithlogits': nn.BCEWithLogitsLoss, + 'bcewithlogitsloss': nn.BCEWithLogitsLoss, 'hinge': nn.HingeEmbeddingLoss, 'hingeembeddingloss': nn.HingeEmbeddingLoss, 'multiclass_hinge': nn.MultiMarginLoss, @@ -286,5 +307,7 @@ def mean(l, ignore_nan=False, empty=0): 'cosineembeddingloss': nn.CosineEmbeddingLoss, 'lovaszhinge': torch_lovasz_hinge, 'focalloss': TorchFocalLoss, - 'focal': TorchFocalLoss + 'focal': TorchFocalLoss, + 'jaccard': TorchJaccardLoss, + 'jaccardloss': TorchJaccardLoss } diff --git a/solaris/nets/callbacks.py b/solaris/nets/callbacks.py index 9866878f..d1833e3a 100644 --- a/solaris/nets/callbacks.py +++ b/solaris/nets/callbacks.py @@ -1,6 +1,7 @@ import numpy as np from tensorflow import keras from tensorflow.keras.callbacks import Callback +from .torch_callbacks import torch_callback_dict import torch @@ -40,36 +41,52 @@ def get_callbacks(framework, config): else: callbacks.append(keras_callbacks[callback](**params)) elif framework == 'torch': - for callback in config['training']['callbacks'].keys(): + for callback, params in config['training']['callbacks'].items(): if callback == 'lr_schedule': callbacks.append(get_lr_schedule(framework, config)) else: - callbacks.append(callback) + callbacks.append(torch_callback_dict[callback](**params)) return callbacks - + class KerasTerminateOnMetricNaN(Callback): """Callback to stop training if a metric has value NaN or infinity. - Arguments: + Notes + ----- + Instantiate as you would any other keras callback. For example, to end + training if a validation metric called `f1_score` reaches value NaN:: + + m = Model(inputs, outputs) + m.compile() + m.fit(X, y, callbacks=[TerminateOnMetricNaN('val_f1_score')]) + + + Attributes ---------- - metric (str): The name of the metric to be tested for NaN value. - checkpoint (['epoch', 'batch']): Should the metric be checked at the end of - every epoch (default) or every batch? + metric : str, optional + Name of the metric being monitored. + checkpoint : str, optional + One of ``['epoch', 'batch']``: Should the metric be checked at the end + of every epoch (default) or every batch? - Usage: - ------ - Instantiate as you would any other keras callback. For example, to end - training if a validation metric called `f1_score` reaches value NaN: - ``` - m = Model(inputs, outputs) - m.compile() - m.fit(X, y, callbacks=[TerminateOnMetricNaN('val_f1_score')]) - ``` + Methods + ------- + on_epoch_end : operations to complete at the end of each epoch. + on_batch_end : operations to complete at the end of each batch. """ def __init__(self, metric=None, checkpoint='epoch'): + """ + + Parameters + ---------- + metric (str): The name of the metric to be tested for NaN value. + checkpoint (['epoch', 'batch']): Should the metric be checked at the end of + every epoch (default) or every batch? + + """ super(KerasTerminateOnMetricNaN, self).__init__() self.metric = metric self.ckpt = checkpoint @@ -119,7 +136,7 @@ def get_lr_schedule(framework, config): Returns ------- lr_scheduler : :class:`tensorflow.keras.callbacks.LearningRateScheduler` or - :module:`torch.optim.lr_schedule` scheduler class + ``torch.optim.lr_schedule`` scheduler class A scheduler to provide during training. For Keras, this takes the form of a callback passed to the optimizer; for PyTorch, it's a class object that wraps the optimizer. Because the torch version must wrap the @@ -170,8 +187,7 @@ def keras_lr_schedule(schedule_type, initial_lr=0.001, update_frequency=1, The initial learning rate to use. Defaults to ``0.001`` . update_frequency : int, optional How frequently should learning rate be reduced (or increased)? Defaults - to ``1`` (every epoch). Has no effect if ``schedule_type='arbitrary'`` - . + to ``1`` (every epoch). Has no effect if ``schedule_type='arbitrary'``. factor : float, optional The magnitude by which learning rate should be changed at each update. Use a positive number to increase learning rate and a negative number @@ -190,13 +206,18 @@ def keras_lr_schedule(schedule_type, initial_lr=0.001, update_frequency=1, Usage ----- - ``schedule_type='arbitrary' usage is documented in the arguments above. + ``schedule_type='arbitrary'`` usage is documented in the arguments above. For ``schedule_type='exponential'``, the following equation is applied to determine the learning rate at each epoch: + .. math:: + lr = initial_lr*e^{factor\times(floor(epoch/update_frequency))} - For ``schedule_type='linear', the following equation is applied: + + For ``schedule_type='linear'``, the following equation is applied: + .. math:: + lr = initial_lr\times(1+factor\times(floor(epoch/update_frequency))) """ diff --git a/solaris/data/config_skeleton.yml b/solaris/nets/configs/config_skeleton.yml similarity index 100% rename from solaris/data/config_skeleton.yml rename to solaris/nets/configs/config_skeleton.yml diff --git a/solaris/nets/configs/xdxd_spacenet4.yml b/solaris/nets/configs/xdxd_spacenet4.yml new file mode 100644 index 00000000..9d632c03 --- /dev/null +++ b/solaris/nets/configs/xdxd_spacenet4.yml @@ -0,0 +1,157 @@ +################################################################################ +################# SOLARIS MODEL CONFIGURATION SKELETON ######################### +################################################################################ + +# This skeleton lays out the required instructions for running a model using +# solaris. See the full documentation at [INCLUDE DOC LINK HERE] for details on +# options, required arguments, and sample usage. + +model_name: xdxd_spacenet4 + +model_src_path: # leave this blank unless you're using a custom model not + # native to solaris. solaris will automatically find your + # model. +train: true # set to false for inference only +infer: false # set to false for training only + +pretrained: false # use pretrained weights associated with the model? +nn_framework: torch +batch_size: 4 + +data_specs: + width: 512 + height: 512 + image_type: zscore # format of images read into the neural net. options + # are 'normalized', 'zscore', '8bit', '16bit'. + rescale: false # should image pixel values be rescaled before pre-processing? + # If so, the image will be rescaled to the pixel range defined + # by rescale_min and rescale_max below. + rescale_minima: auto # the minimum values to use in rescaling (if + # rescale=true). If 'auto', the minimum pixel intensity + # in each channel of the image will be subtracted. If + # a single value is provided, that value will be set to + # zero for each channel in the input image. + # if a list of values are provided, then minima in the + # separate channels (in that order) will be set to that + # value PRIOR to any augmentation. + rescale_maxima: auto # same as rescale_minima, but for the maximum value for + # each channel in the image. + channels: 4 # number of channels in the input imagery. + label_type: mask # one of ['mask', 'bbox'] + is_categorical: false # are the labels binary (default) or categorical? + mask_channels: 1 # number of channels in the training mask + val_holdout_frac: # if empty, assumes that separate data ref files define the + # training and validation dataset. If a float between 0 and + # 1, indicates the fraction of training data that's held + # out for validation (and validation_data_csv will be + # ignored) + data_workers: # number of cpu threads to use for loading and preprocessing + # input images. +# other_inputs: # this can provide a list of additional inputs to pass to the + # neural net for training. These inputs should be specified in + # extra columns of the csv files (denoted below), either as + # filepaths to additional data to load or as values to include. + # NOTE: This is not currently implemented. +training_data_csv: # TODO # path to the reference csv that defines training data. + # see the documentation for the specifications of this file. +validation_data_csv: # TODO # path to the validation ref csv. See the docs. If + # val_holdout_frac is specified (under data_specs), then + # this argument will be ignored. +inference_data_csv: # TODO # path to the reference csv that defines inference data. + # see the documentation for the specs of this file. + +training_augmentation: # augmentations for use with training data + augmentations: + DropChannel: + idx: 3 + axis: 2 + HorizontalFlip: + p: 0.5 + RandomRotate90: + p: 0.5 + RandomCrop: + height: 512 + width: 512 + p: 1.0 + Normalize: + mean: # mean values after dividing by max pixel value + - 0.006479 + - 0.009328 + - 0.01123 + std: # std after dividing by max pixel value + - 0.004986 + - 0.004964 + - 0.004950 + p: 1.0 + max_pixel_value: 65535.0 + p: 1.0 # probability of applying the entire training augmentation pipeline. + shuffle: true # should the image order be shuffled in each epoch. +validation_augmentation: # augmentations for use with validation data + augmentations: + CenterCrop: + height: 512 + width: 512 + p: 1.0 + Normalize: + p: 1.0 + # include augmentations here + p: 1.0 # probability of applying the full validation augmentation pipeline. +inference_augmentation: # this is optional. If not provided, + # validation_augmentation will be used instead. + +training: + epochs: 50 # number of epochs. A list can also be provided here indicating + # distinct sets of epochs at different learning rates, etc; if so, + # a list of equal length must be provided in the parameter that varies + # with the values for each set of epochs. + steps_per_epoch: # optional argument defining # steps/epoch. If not provided, + # each epoch will include the number of steps needed to go + # through the entire training dataset. + optimizer: Adam # optimizer function name. see docs for options. + lr: 1e-4 # learning rate. + opt_args: # dictionary of values (e.g. alpha, gamma, momentum) specific to + # the optimizer. + loss: + bcewithlogits: + jaccard: + + loss_weights: + - bcewithlogits: 1 # (optional) weights to use for each loss function if using + - jaccard: 0.25 # loss: composite. This must be a set of key:value pairs where + # defining the weight for each sub-loss within the composite. + # If using a composite and a value isn't provided here, all + # losses will be weighted equally. + metrics: # metrics to monitor on the training and validation sets. + training: # training set metrics. + - f1_score + - recall + - precision + + validation: # validation set metrics. + - f1_score + - recall + - precision + checkpoint_frequency: 10 # how frequently should checkpoints be saved? + # this can be an int, in which case every n epochs + # a checkpoint will be made, or a list, in which case + # checkpoints will be saved on those specific epochs. + # if not provided, only the final model is saved. + callbacks: + model_checkpoint: + filepath: 'xdxd_sn4_best.pth' + monitor: val_loss + model_dest_path: 'xdxd_sn4.pth' # path to save the trained model output and checkpoint(s) + # to. Should be a filename ending in .h5, .hdf5 for keras + # or .pth, .pt for torch. Epoch numbers will be appended + # for checkpoints. + verbose: true # verbose text output during training + +inference: + window_step_size_x: # size of each step for the sliding window for inference. + # set to the same size as the input image size for zero + # overlap; to average predictions across multiple images, + # use a smaller step size. + window_step_size_y: # size of each step for the sliding window for inference. + # set to the same size as the input image size for zero + # overlap; to average predictions across multiple images, + # use a smaller step size. diff --git a/solaris/nets/datagen.py b/solaris/nets/datagen.py index 7f34a99a..5bbf9724 100644 --- a/solaris/nets/datagen.py +++ b/solaris/nets/datagen.py @@ -3,7 +3,7 @@ from torch.utils.data import Dataset, DataLoader from .transform import process_aug_dict from ..utils.core import _check_df_load -from ..utils.io import imread, scale_for_model +from ..utils.io import imread, scale_for_model, _check_channel_order def make_data_generator(framework, config, df, stage='train'): @@ -126,6 +126,7 @@ def _data_generation(self, image_idxs): pass # TODO: IMPLEMENT BBOX LABEL SETUP HERE! for i in range(self.batch_size): im = imread(self.df['image'].iloc[image_idxs[i]]) + im = _check_channel_order(im, 'keras') if self.config['data_specs']['label_type'] == 'mask': label = imread(self.df['label'].iloc[image_idxs[i]]) if not self.config['data_specs']['is_categorical']: @@ -195,10 +196,13 @@ def __getitem__(self, idx): mask = imread(self.df['label'].iloc[idx]) if not self.config['data_specs']['is_categorical']: mask[mask != 0] = 1 - sample = {'image': image, 'label': mask} + if len(mask.shape) == 2: + mask = mask[:, :, np.newaxis] + sample = {'image': image, 'mask': mask} if self.aug: sample = self.aug(**sample) - + sample['image'] = _check_channel_order(sample['image'], 'torch').astype(np.float32) + sample['mask'] = _check_channel_order(sample['mask'], 'torch').astype(np.float32) return sample @@ -244,7 +248,7 @@ def __call__(self, im): """ # read in the image if it's a path if isinstance(im, str): - im = imread(im) + im = imread(im, make_8bit=True) # determine how many samples will be generated with the sliding window src_im_height = im.shape[0] src_im_width = im.shape[1] diff --git a/solaris/nets/losses.py b/solaris/nets/losses.py index ff264e8b..61d46866 100644 --- a/solaris/nets/losses.py +++ b/solaris/nets/losses.py @@ -2,8 +2,6 @@ from tensorflow.keras import backend as K from ._keras_losses import keras_losses, k_focal_loss from ._torch_losses import torch_losses -import tensorflow as tf -import torch from torch import nn @@ -39,10 +37,10 @@ def get_loss(framework, config): return get_single_loss(framework, loss_name, loss_dict) -def get_single_loss(framework, loss_name, loss_dict): +def get_single_loss(framework, loss_name, params_dict): if framework == 'keras': if loss_name.lower() == 'focal': - return k_focal_loss(**loss_dict) + return k_focal_loss(**params_dict) else: # keras_losses in the next line is a matching dict # TODO: the next line doesn't handle non-focal loss functions that @@ -50,7 +48,10 @@ def get_single_loss(framework, loss_name, loss_dict): # refactor this to handle that possibility. return keras_losses.get(loss_name.lower(), None) elif framework in ['torch', 'pytorch']: - return torch_losses.get(loss_name.lower(), None)(**loss_dict) + if params_dict is None: + return torch_losses.get(loss_name.lower(), None)() + else: + return torch_losses.get(loss_name.lower(), None)(**params_dict) def keras_composite_loss(loss_dict, weight_dict): @@ -85,108 +86,3 @@ def forward(self, outputs, targets): loss += weight*self.values[func_name] return loss - - -def k_weighted_bce(y_true, y_pred, weight): - """Weighted binary cross-entropy for Keras. - - Arguments: - ---------- - y_true (tensor): passed silently by Keras during model training. - y_pred (tensor): passed silently by Keras during model training. - weight (numeric): Weight to assign to mask foreground pixels. Use values - >1 to over-weight foreground or 0 1: - class_two = class_two*(weight-1) - final_mask = weight_mask + class_two # foreground pixels weighted - return K.binary_crossentropy(y_pred, y_true) * final_mask - - -def k_layered_weighted_bce(y_true, y_pred, weights): - """Binary cross-entropy function with different weights for mask channels. - - Arguments: - ---------- - y_true (tensor): passed silently by Keras during model training. - y_pred (tensor): passed silently by Keras during model training. - weights (list-like): Weights to assign to mask foreground pixels for each - channel in the 3rd axis of the mask. - - Returns: - -------- - The binary cross-entropy loss function output multiplied by a weighting - mask. - - Usage: - ------ - See implementation instructions for `weighted_bce`. - - This loss function is intended to allow different weighting of different - segmentation outputs - for example, if a model outputs a 3D image mask, - where the first channel corresponds to foreground objects and the second - channel corresponds to object edges. `weights` must be a list of length - equal to the depth of the output mask. The output mask's "z-axis" - corresponding to the mask channel must be the third axis in the output - array. - - """ - weight_mask = K.ones_like(y_true) - submask_list = [] - for i in range(len(weights)): - class_two = K.equal(y_true[:, :, :, i], weight_mask[:, :, :, i]) - class_two = K.cast(class_two, 'float32') - if weights[i] < 1: - class_two = class_two*(1-weights[i]) - layer_mask = weight_mask[:, :, :, i] - class_two - elif weights[i] > 1: - class_two = class_two*(weights[i]-1) - layer_mask = weight_mask[:, :, :, i] + class_two - else: - layer_mask = weight_mask[:, :, :, i] - submask_list.append(layer_mask) - final_mask = K.stack(submask_list, axis=-1) - return K.binary_crossentropy(y_pred, y_true) * final_mask diff --git a/solaris/nets/model_io.py b/solaris/nets/model_io.py index 594c63ec..7bb08736 100644 --- a/solaris/nets/model_io.py +++ b/solaris/nets/model_io.py @@ -9,14 +9,22 @@ # model_name in the config file. -def get_model(model_name, framework, model_path=None, pretrained=False): +def get_model(model_name, framework, model_path=None, pretrained=False, + custom_model_dict=None): """Load a model from a file based on its name.""" - md = model_dict.get(model_name) - if md is not None: # if the model's in the dict - if model_path is None: - model_path = md.get('weight_path') - model = md.get('arch')() + md = model_dict.get(model_name, None) + if md is None: # if the model's not provided by solaris + if custom_model_dict is None: + raise ValueError(f"{model_name} can't be found in solaris and no " + "custom_model_dict was provided. Check your " + "model_name in the config file and/or provide a " + "custom_model_dict argument to Trainer().") + else: + md = custom_model_dict + if model_path is None: + model_path = md.get('weight_path') + model = md.get('arch')() if model is not None and pretrained: try: model = _load_model_weights(model, model_path, framework) diff --git a/solaris/nets/optimizers.py b/solaris/nets/optimizers.py index 19c0d085..1af1c3cd 100644 --- a/solaris/nets/optimizers.py +++ b/solaris/nets/optimizers.py @@ -46,6 +46,6 @@ def get_optimizer(framework, config): 'file.') if framework in ['torch', 'pytorch']: - return torch_optimizers.get(config['training']['optimizer'], None) + return torch_optimizers.get(config['training']['optimizer'].lower()) elif framework == 'keras': - return keras_optimizers.get(config['training']['optimizer'], None) + return keras_optimizers.get(config['training']['optimizer'].lower()) diff --git a/solaris/nets/torch_callbacks.py b/solaris/nets/torch_callbacks.py index 1084bfcc..85e8335d 100644 --- a/solaris/nets/torch_callbacks.py +++ b/solaris/nets/torch_callbacks.py @@ -225,10 +225,18 @@ def save(self, model, weights_only=False): entire model must be saved to resume training without re-defining the model architecture, optimizer, and loss function. """ - save_name = os.splitext(self.filepath)[0] + '_{}+{}'.format( + save_name = os.path.splitext(self.filepath)[0] + '_{}+{}'.format( self.epoch, np.round(self.last_saved_value, 3)) - save_name = save_name + os.splitext(self.filepath)[1] + save_name = save_name + os.path.splitext(self.filepath)[1] if weights_only: torch.save(model.state_dict(), save_name) else: torch.save(model, save_name) + + +torch_callback_dict = { + "early_stopping": TorchEarlyStopping, + "model_checkpoint": TorchModelCheckpoint, + "terminate_on_nan": TorchTerminateOnNaN, + "terminate_on_metric_nan": TorchTerminateOnMetricNaN +} diff --git a/solaris/nets/train.py b/solaris/nets/train.py index 048472d4..0936b678 100644 --- a/solaris/nets/train.py +++ b/solaris/nets/train.py @@ -12,25 +12,26 @@ from ..utils.core import get_data_paths import torch from torch.optim.lr_scheduler import _LRScheduler +import tensorflow as tf class Trainer(object): """Object for training `solaris` models using PyTorch or Keras.""" - def __init__(self, config): + def __init__(self, config, custom_model_dict=None): self.config = config self.pretrained = self.config['pretrained'] self.batch_size = self.config['batch_size'] self.framework = self.config['nn_framework'] self.model_name = self.config['model_name'] - self.model_path = self.config['model_path'] - self.model = get_model(self.model_name, self.nn_framework, - self.model_path) + self.model_path = self.config.get('model_path', None) + self.model = get_model(self.model_name, self.framework, + self.model_path, custom_model_dict) self.train_df, self.val_df = get_train_val_dfs(self.config) self.train_datagen = make_data_generator(self.framework, self.config, self.train_df, stage='train') self.val_datagen = make_data_generator(self.framework, self.config, - self.train_df, stage='train') + self.val_df, stage='train') self.epochs = self.config['training']['epochs'] self.optimizer = get_optimizer(self.framework, self.config) self.lr = self.config['training']['lr'] @@ -38,6 +39,10 @@ def __init__(self, config): self.callbacks = get_callbacks(self.framework, self.config) self.metrics = get_metrics(self.framework, self.config) self.verbose = self.config['training']['verbose'] + if self.framework in ['torch', 'pytorch']: + self.gpu_available = torch.cuda.is_available() + elif self.framework == 'keras': + self.gpu_available = tf.test.is_gpu_available() self.is_initialized = False self.stop = False @@ -55,12 +60,19 @@ def initialize_model(self): metrics=self.metrics) elif self.framework == 'torch': + if self.gpu_available: + self.model = self.model.cuda() # create optimizer - self.optimizer = self.optimizer( - self.model.parameters(), lr=self.lr, - **self.config['training']['opt_args'] + if self.config['training']['opt_args'] is not None: + self.optimizer = self.optimizer( + self.model.parameters(), lr=self.lr, + **self.config['training']['opt_args'] ) - # wrap in lr_scheduler if one was created + else: + self.optimizer = self.optimizer( + self.model.parameters(), lr=self.lr + ) + # wrap in lr_scheduler if one was created for cb in self.callbacks: if isinstance(cb, _LRScheduler): self.optimizer = cb( @@ -85,16 +97,17 @@ def train(self): callbacks=self.callbacks) elif self.framework == 'torch': +# tf_sess = tf.Session() for epoch in range(self.epochs): if self.verbose: print('Beginning training epoch {}'.format(epoch)) # TRAINING self.model.train() - for batch_idx, (data, target) in enumerate(self.train_datagen): - data, target = data.cuda(), target.cuda() + for batch_idx, batch in enumerate(self.train_datagen): + data = batch['image'].cuda() + target = batch['mask'].cuda().float() self.optimizer.zero_grad() output = self.model(data) - loss = self.loss(output, target) loss.backward() self.optimizer.step() @@ -102,25 +115,35 @@ def train(self): if self.verbose and batch_idx % 10 == 0: print(' loss at batch {}: {}'.format( - batch_idx, np.round(loss, 3))) + batch_idx, loss)) # calculate metrics - for metric in self.metrics: - print('{} score: {}'.format( - metric, metric(target, output))) +# for metric in self.metrics['train']: +# with tf_sess.as_default(): +# print('{} score: {}'.format( +# metric, metric(tf.convert_to_tensor(target.detach().cpu().numpy(), dtype='float64'), tf.convert_to_tensor(output.detach().cpu().numpy(), dtype='float64')).eval())) # VALIDATION - self.model.eval() - val_loss = [] - for batch_idx, (data, - target) in enumerate(self.val_datagen): - val_output = self.model(data) - val_loss.append(self.loss(val_output, target)) - val_loss = np.mean(val_loss) + with torch.no_grad(): + self.model.eval() + torch.cuda.empty_cache() + val_loss = [] + for batch_idx, batch in enumerate(self.val_datagen): + data = batch['image'].cuda() + target = batch['mask'].cuda().float() + val_output = self.model(data) + val_loss.append(self.loss(val_output, target)) + val_loss = torch.mean(torch.stack(val_loss)) if self.verbose: print() print(' Validation loss at epoch {}: {}'.format( epoch, val_loss)) print() - check_continue = self._run_torch_callbacks(loss, val_loss) +# for metric in self.metrics['val']: +# with tf_sess.as_default(): +# print('validation {} score: {}'.format( +# metric, metric(tf.convert_to_tensor(target.detach().cpu().numpy(), dtype='float64'), tf.convert_to_tensor(output.detach().cpu().numpy(), dtype='float64')).eval())) + check_continue = self._run_torch_callbacks( + loss.detach().cpu().numpy(), + val_loss.detach().cpu().numpy()) if not check_continue: break @@ -153,7 +176,7 @@ def _run_torch_callbacks(self, loss, val_loss): elif cb.monitor == 'periodic': cb(self.model) - return True + return True def save_model(self): """Save the final model output.""" diff --git a/solaris/nets/transform.py b/solaris/nets/transform.py index 84c04e98..8c0128a8 100644 --- a/solaris/nets/transform.py +++ b/solaris/nets/transform.py @@ -28,6 +28,7 @@ - Normalize - HueSaturationValue # NOTE: CAN ONLY HANDLE RGB 3-CHANNEL! - RGBShift # NOTE: CAN ONLY HANDLE RGB 3-CHANNEL! +- RandomRotate90 - RandomBrightnessContrast - Blur - MotionBlur @@ -57,7 +58,8 @@ HorizontalFlip, Flip, Transpose, Resize, CenterCrop, RandomCrop, Cutout, \ RandomSizedCrop, OpticalDistortion, GridDistortion, ElasticTransform, \ Normalize, HueSaturationValue, RGBShift, RandomBrightnessContrast, \ - Blur, MotionBlur, MedianBlur, GaussNoise, CLAHE, RandomGamma, ToFloat + Blur, MotionBlur, MedianBlur, GaussNoise, CLAHE, RandomGamma, ToFloat, \ + RandomRotate90 from albumentations.core.composition import Compose, OneOf, OneOrOther @@ -66,9 +68,37 @@ 'OpticalDistortion', 'GridDistortion', 'ElasticTransform', 'Normalize', 'HueSaturationValue', 'RGBShift', 'RandomBrightnessContrast', 'Blur', 'MotionBlur', 'MedianBlur', - 'GaussNoise', 'CLAHE', 'RandomGamma', 'ToFloat', 'Rotate', + 'GaussNoise', 'CLAHE', 'RandomGamma', 'ToFloat', 'Rotate', 'RandomRotate90', 'RandomScale', 'Cutout', 'Compose', 'OneOf', 'OneOrOther', 'NoOp', - 'process_aug_dict', 'get_augs', 'build_pipeline'] + 'RandomRotate90', 'process_aug_dict', 'get_augs', 'build_pipeline'] + + +class DropChannel(ImageOnlyTransform): + """Drop a channel from an input image. + + Arguments + --------- + idx : int + The channel index to drop. + axis : int, optional (default: 1) + The axis to drop the channel from. Defaults to ``1`` (torch channel + axis). Set to ``3`` for TF models where the channel is the last axis + of an image. + always_apply : bool, optional (default: False) + Apply this transformation to every image? Defaults to no (``False``). + p : float [0, 1], optional (default: 1.0) + Probability that the augmentation is performed to each image. Defaults + to ``1.0``. + """ + + def __init__(self, idx, axis=1, always_apply=False, p=1.0): + super().__init__(always_apply, p) + + self.idx = idx + self.axis = axis + + def apply(self, im_arr, **params): + return np.delete(im_arr, self.idx, self.axis) class Rotate(DualTransform): @@ -417,5 +447,6 @@ def _get_aug(aug, params): 'blur': Blur, 'motionblur': MotionBlur, 'medianblur': MedianBlur, 'gaussnoise': GaussNoise, 'clahe': CLAHE, 'randomgamma': RandomGamma, 'tofloat': ToFloat, 'rotate': Rotate, 'randomscale': RandomScale, - 'cutout': Cutout, 'oneof': OneOf, 'oneorother': OneOrOther, 'noop': NoOp + 'cutout': Cutout, 'oneof': OneOf, 'oneorother': OneOrOther, 'noop': NoOp, + 'randomrotate90': RandomRotate90, 'dropchannel': DropChannel } diff --git a/solaris/tile/__init__.py b/solaris/tile/__init__.py index 9a8d6202..08743baa 100644 --- a/solaris/tile/__init__.py +++ b/solaris/tile/__init__.py @@ -1 +1 @@ -from . import main, vector_utils +from . import raster_tile, vector_tile diff --git a/solaris/tile/main.py b/solaris/tile/main.py deleted file mode 100644 index 921b5758..00000000 --- a/solaris/tile/main.py +++ /dev/null @@ -1,400 +0,0 @@ -import os -import rasterio -from rasterio.warp import transform_bounds -from rasterio.io import DatasetReader -import math -from rio_tiler.errors import TileOutsideBounds -from ..utils import tile -import numpy as np - - -def tile_utm_source(src, ll_x, ll_y, ur_x, ur_y, indexes=None, tilesize=256, - nodata=None, alpha=None, dst_crs='epsg:4326'): - """ - Create a UTM tile from a :py:class:`rasterio.Dataset` in memory. - - Arguments - --------- - src : :py:class:`rasterio.Dataset` - Source imagery dataset to tile. - ll_x : int or float - Lower left x position (i.e. Western bound). - ll_y : int or f - loat - Lower left y position (i.e. Southern bound). - ur_x : int or float - Upper right x position (i.e. Eastern bound). - ur_y : int or float - Upper right y position (i.e. Northern bound). - indexes : tuple of 3 ints, optional - Band indexes for the output. By default, extracts all of the - indexes from `src`. - tilesize : int, optional - Output image X and Y pixel extent. Defaults to ``256``. - nodata : int or float, optional - Value to use for `nodata` pixels during tiling. By default, uses - the existing `nodata` value in `src`. - alpha : int, optional - Alpha band index for tiling. By default, uses the same band as - specified by `src`. - dst_crs : str, optional - Coordinate reference system for output. Defaults to ``"epsg:4326"``. - - Returns - ------- - ``(data, mask, window, window_transform)`` tuple. - data : :py:class:`numpy.ndarray` - int pixel values. Shape is ``(C, Y, X)`` if retrieving multiple - channels, ``(Y, X)`` otherwise. - mask : :py:class:`numpy.ndarray` - int mask indicating which pixels contain information and which are - `nodata`. Pixels containing data have value ``255``, `nodata` - pixels have value ``0``. - window : :py:class:`rasterio.windows.Window` - :py:class:`rasterio.windows.Window` object indicating the raster - location of the dataset subregion being returned in `data`. - window_transform : :py:class:`affine.Affine` - Affine transformation for the window. - - """ - - wgs_bounds = transform_bounds( - *[src.crs, dst_crs] + list(src.bounds), densify_pts=21) - - indexes = indexes if indexes is not None else src.indexes - tile_bounds = (ll_x, ll_y, ur_x, ur_y) - if not utils.tile_exists_utm(wgs_bounds, tile_bounds): - raise TileOutsideBounds( - 'Tile {}/{}/{}/{} is outside image bounds'.format(tile_bounds)) - - return utils.tile_read_utm(src, tile_bounds, tilesize, indexes=indexes, - nodata=nodata, alpha=alpha, dst_crs=dst_crs) - - -def tile_utm(source, ll_x, ll_y, ur_x, ur_y, indexes=None, tilesize=256, - nodata=None, alpha=None, dst_crs='epsg:4326'): - """ - Create a UTM tile from a file or a :py:class:`rasterio.Dataset` in memory. - - This function is a wrapper around :func:`tile_utm_source` to enable passing - of file paths instead of pre-loaded :py:class:`rasterio.Dataset` s. - - Arguments - --------- - source : :py:class:`rasterio.Dataset` - Source imagery dataset to tile. - ll_x : int or float - Lower left x position (i.e. Western bound). - ll_y : int or float - Lower left y position (i.e. Southern bound). - ur_x : int or float - Upper right x position (i.e. Eastern bound). - ur_y : int or float - Upper right y position (i.e. Northern bound). - indexes : tuple of 3 ints, optional - Band indexes for the output. By default, extracts all of the - indexes from `source` . - tilesize : :obj:`int`, optional - Output image X and Y pixel extent. Defaults to ``256``. - nodata : int or float, optional - Value to use for ``nodata`` pixels during tiling. By default, uses - the existing ``nodata`` value in `src`. - alpha : :obj:`int`, optional - Alpha band index for tiling. By default, uses the same band as - specified by `src`. - dst_crs : str, optional - Coordinate reference system for output. Defaults to ``"epsg:4326"``. - - Returns - ------- - ``(data, mask, window, window_transform`` tuple. - data : :py:class:`numpy.ndarray` - int pixel values. Shape is ``(C, Y, X)`` if retrieving multiple - channels, ``(Y, X)`` otherwise. - mask : :class:`numpy.ndarray` - int mask indicating which pixels contain information and which are - `nodata`. Pixels containing data have value ``255``, `nodata` pixels - have value ``0``. - window : :py:class:`rasterio.windows.Window` - :py:class:`rasterio.windows.Window` object indicating the raster - location of the dataset subregion being returned in `data`. - window_transform : :py:class:`affine.Affine` - Affine transformation for the window. - - """ - - if isinstance(source, DatasetReader): - src = source - elif os.path.exists(source): - src = rasterio.open(source) # read in the file - else: - raise ValueError('Source is not a rasterio.Dataset or a valid path.') - - return tile_utm_source(src, ll_x, ll_y, ur_x, ur_y, indexes=indexes, - tilesize=tilesize, nodata=nodata, alpha=alpha, - dst_crs=dst_crs) - - -def get_chip(source, ll_x, ll_y, gsd, - utm_crs='', - indexes=None, - tilesize=256, - nodata=None, - alpha=None): - """Get an image tile of specific pixel size. - - This wrapper function permits passing of `ll_x`, `ll_y`, `gsd`, and - `tile_size_pixels` in place of boundary coordinates to extract an image - region of defined pixel extent. - - Arguments - --------- - source : :py:class:`rasterio.Dataset` - Source imagery dataset to tile. - ll_x : int or float - Lower left x position (i.e. Western bound). - ll_y : int or float - Lower left y position (i.e. Southern bound). - gsd : float - Ground sample distance of the source imagery in meter/pixel units. - utm_crs : :py:class:`rasterio.crs.CRS`, optional - UTM coordinate reference system string for the imagery. If not - provided, this is calculated using - :func:`cw_tiler.utils.get_wgs84_bounds` and - :func:`cw_tiler.utils.calculate_UTM_crs` . - indexes : tuple of 3 ints, optional - Band indexes for the output. By default, extracts all of the - indexes from `source`. - tilesize : int, optional - Output image X and Y pixel extent. Defaults to ``256`` . - nodata : int or float, optional - Value to use for `nodata` pixels during tiling. By default, uses - the existing `nodata` value in `source`. - alpha : int, optional - Alpha band index for tiling. By default, uses the same band as - specified by `source`. - - Returns - ------- - ``(data, mask, window, window_transform`` tuple. - data : :class:`numpy.ndarray` - int pixel values. Shape is ``(C, Y, X)`` if retrieving multiple - channels, ``(Y, X)`` otherwise. - mask : :class:`numpy.ndarray` - int mask indicating which pixels contain information and which are - `nodata`. Pixels containing data have value ``255``, `nodata` pixels - have value ``0``. - window : :py:class:`rasterio.windows.Window` - :py:class:`rasterio.windows.Window` object indicating the raster - location of the dataset subregion being returned in `data`. - window_transform : :py:class:`affine.Affine` - Affine transformation for the window. - """ - - ur_x = ll_x + gsd * tilesize - ur_y = ll_y + gsd * tilesize - - if isinstance(source, DatasetReader): - src = source - else: - src = rasterio.open(source) - - if not utm_crs: - wgs_bounds = utils.get_wgs84_bounds(src) - utm_crs = utils.calculate_UTM_crs(wgs_bounds) - - return tile_utm(source, ll_x, ll_y, ur_x, ur_y, indexes=indexes, - tilesize=tilesize, nodata=nodata, alpha=alpha, - dst_crs=utm_crs) - - -def calculate_anchor_points(utm_bounds, stride_size_meters=400, extend=False, - quad_space=False): - """Get anchor point (lower left corner of bbox) for chips from a tile. - - Arguments - --------- - utm_bounds : tuple of 4 floats - A :obj:`tuple` of shape ``(min_x, min_y, max_x, max_y)`` that defines - the spatial extent of the tile to be split. Coordinates should be in - UTM. - stride_size_meters : int, optional - Stride size in both X and Y directions for generating chips. Defaults - to ``400``. - extend : bool, optional - Defines whether UTM boundaries should be rounded to the nearest integer - outward from `utm_bounds` (`extend` == ``True``) or - inward from `utm_bounds` (`extend` == ``False``). Defaults - to ``False`` (inward). - quad_space : bool, optional - If tiles will overlap by no more than half their X and/or Y extent in - each direction, `quad_space` can be used to split chip - anchors into four non-overlapping subsets. For example, if anchor - points are 400m apart and each chip will be 800m by 800m, `quad_space` - will generate four sets which do not internally overlap; - however, this would fail if tiles are 900m by 900m. Defaults to - ``False``, in which case the returned ``anchor_point_list_dict`` will - comprise a single list of anchor points. - - Returns - ------- - anchor_point_list_dict : dict of list(s) of lists - - If ``quad_space==True`` , `anchor_point_list_dict` is a - :obj:`dict` with four keys ``[0, 1, 2, 3]`` corresponding to the four - subsets of chips generated (see `quad_space` ). If - ``quad_space==False`` , `anchor_point_list_dict` is a - :obj:`dict` with a single key, ``0`` , that corresponds to a list of all - of the generated anchor points. Each anchor point in the list(s) is an - ``[x, y]`` pair of UTM coordinates denoting the SW corner of a chip. - - """ - if extend: - min_x = math.floor(utm_bounds[0]) - min_y = math.floor(utm_bounds[1]) - max_x = math.ceil(utm_bounds[2]) - max_y = math.ceil(utm_bounds[3]) - else: - print("NoExtend") - print('UTM_Bounds: {}'.format(utm_bounds)) - min_x = math.ceil(utm_bounds[0]) - min_y = math.ceil(utm_bounds[1]) - max_x = math.floor(utm_bounds[2]) - max_y = math.floor(utm_bounds[3]) - - if quad_space: - print("quad_space") - row_cell = np.asarray([[0, 1], [2, 3]]) - anchor_point_list_dict = {0: [], 1: [], 2: [], 3: []} - else: - anchor_point_list_dict = {0: []} - - for rowidx, x in enumerate(np.arange(min_x, max_x, stride_size_meters)): - for colidx, y in enumerate(np.arange(min_y, max_y, - stride_size_meters)): - if quad_space: - anchor_point_list_dict[ - row_cell[rowidx % 2, colidx % 2]].append([x, y]) - else: - anchor_point_list_dict[0].append([x, y]) - - return anchor_point_list_dict - - -def calculate_cells(anchor_point_list_dict, cell_size_meters, utm_bounds=[]): - """ Calculate boundaries for image cells (chips) from anchor points. - - This function takes the output from :func:`calculate_anchor_points` as well - as a desired cell size (`cell_size_meters`) and outputs - ``(W, S, E, N)`` tuples for generating cells. - - Arguments - --------- - anchor_point_list_dict : dict - Output of :func:`calculate_anchor_points`. See that function for - details. - cell_size_meters : int or float - Desired width and height of each cell in meters. - utm_bounds : list -like of float s, optional - A :obj:`list`-like of shape ``(W, S, E, N)`` that defines the limits - of an input image tile in UTM coordinates to ensure that no cells - extend beyond those limits. If not provided, all cells will be included - even if they extend beyond the UTM limits of the source imagery. - - Returns - ------- - cells_list_dict : dict of list(s) of lists - A dict whose keys are either ``0`` or ``[0, 1, 2, 3]`` (see - :func:`calculate_anchor_points` . ``quad_space`` ), and whose values are - :obj:`list` s of boundaries in the shape ``[W, S, E, N]`` . Boundaries - are in UTM coordinates. - - """ - cells_list_dict = {} - for anchor_point_list_id, anchor_point_list in anchor_point_list_dict.items(): - cells_list = [] - for anchor_point in anchor_point_list: - if utm_bounds: - if (anchor_point[0] + cell_size_meters < utm_bounds[2]) and ( - anchor_point[1] + cell_size_meters < utm_bounds[3]): - cells_list.append([anchor_point[0], anchor_point[1], - anchor_point[0] + cell_size_meters, - anchor_point[1] + cell_size_meters]) - else: - pass - else: # include it regardless of extending beyond bounds - cells_list.append([anchor_point[0], anchor_point[1], - anchor_point[0] + cell_size_meters, - anchor_point[1] + cell_size_meters]) - - cells_list_dict[anchor_point_list_id] = cells_list - - return cells_list_dict - - -def calculate_analysis_grid(utm_bounds, stride_size_meters=300, - cell_size_meters=400, quad_space=False, - snapToGrid=False): - """Wrapper for :func:`calculate_anchor_points` and :func:`calculate_cells`. - - Based on UTM boundaries of an image tile, stride size, and cell size, - output a dictionary of boundary lists for analysis chips. - - Arguments - --------- - utm_bounds : list-like of shape ``(W, S, E, N)`` - UTM coordinate limits of the input tile. - stride_size_meters : int, optional - Step size in both X and Y directions between cells in units of meters. - Defaults to ``300`` . - cell_size_meters : int, optional - Extent of each cell in both X and Y directions in units of meters. - Defaults to ``400`` . - quad_space : bool, optional - See :func:`calculate_anchor_points` . ``quad_space`` . Defaults to - ``False`` . - snapToGrid : bool, optional - .. :deprecated: 0.2.0 - This argument is deprecated and no longer has any effect. - - Returns - ------- - cells_list_dict : dict of list(s) of lists - A dict whose keys are either ``0`` or ``[0, 1, 2, 3]`` (see - :func:`calculate_anchor_points` . ``quad_space`` ), and whose values are - :obj:`list` s of boundaries in the shape ``[W, S, E, N]`` . Boundaries - are in UTM coordinates. - - """ - anchor_point_list_dict = calculate_anchor_points( - utm_bounds, stride_size_meters=stride_size_meters, - quad_space=quad_space) - cells_list_dict = calculate_cells(anchor_point_list_dict, cell_size_meters, - utm_bounds=utm_bounds) - return cells_list_dict - - -if __name__ == '__main__': - utmX, utmY = 658029, 4006947 - cll_x = utmX - cur_x = utmX + 500 - cll_y = utmY - cur_y = utmY + 500 - stride_size_meters = 300 - cell_size_meters = 400 - ctile_size_pixels = 1600 - spacenetPath = "s3://spacenet-dataset/AOI_2_Vegas/srcData/rasterData/AOI_2_Vegas_MUL-PanSharpen_Cloud.tif" - address = spacenetPath - - with rasterio.open(address) as src: - cwgs_bounds = utils.get_wgs84_bounds(src) - cutm_crs = utils.calculate_UTM_crs(cwgs_bounds) - cutm_bounds = utils.get_utm_bounds(src, cutm_crs) - - #ccells_list = calculate_analysis_grid(cutm_bounds, stride_size_meters=stride_size_meters, - # cell_size_meters=cell_size_meters) - - #random_cell = random.choice(ccells_list) - #cll_x, cll_y, cur_x, cur_y = random_cell - tile, mask, window, window_transform = tile_utm(src, cll_x, cll_y, cur_x, cur_y, indexes=None, tilesize=ctile_size_pixels, nodata=None, alpha=None, - dst_crs=cutm_crs) diff --git a/solaris/tile/raster_tile.py b/solaris/tile/raster_tile.py new file mode 100644 index 00000000..e2784d13 --- /dev/null +++ b/solaris/tile/raster_tile.py @@ -0,0 +1,402 @@ +import os +import rasterio +from rasterio.warp import transform_bounds, Resampling +from rasterio.io import DatasetReader +from rasterio.vrt import WarpedVRT +from rasterio.crs import CRS +from rasterio import transform +from shapely.geometry import box +import math +from rio_cogeo.cogeo import cog_validate, cog_translate +from ..utils.core import _check_crs, _check_rasterio_im_load +from ..utils.tile import read_cog_tile +from ..utils.geo import latlon_to_utm_epsg, reproject_geometry, reproject +from ..utils.geo import raster_get_projection_unit +from tqdm import tqdm +import numpy as np + + +class RasterTiler(object): + """An object to tile geospatial image strips into smaller pieces. + + Arguments + --------- + dest_dir : str, optional + Path to save output files to. If not specified here, this + must be provided when ``Tiler.tile_generator()`` is called. + src_tile_size : `tuple` of `int`s, optional + The size of the output tiles in ``(y, x)`` coordinates. By default, + this is in pixel units; this can be changed to metric units using the + `src_metric_size` argument. + src_metric_size : bool, optional + Is `src_tile_size` in pixel units (default) or metric? To set to metric + use ``src_metric_size=True``. + dest_tile_size : `tuple` of `int`s, optional + The size of the output tiles in ``(y, x)`` coordinates in pixel units. + dest_crs : int, optional + The EPSG code for the CRS that output tiles are in. If not provided, + tiles use the crs of `src` by default. + nodata : int, optional + The value in `src` that specifies nodata. If this value is not + provided, solaris will attempt to infer the nodata value from the `src` + metadata. + alpha : int, optional + The band to specify as alpha. If not provided, solaris will attempt to + infer if an alpha band is present from the `src` metadata. + force_load_cog : bool, optional + If `src` is a cloud-optimized geotiff, use this argument to force + loading in the entire image at once. + aoi_bounds : list, optional + A :class:`list` -like of shape + ``[left_bound, bottom_bound, right_bound, top_bound]`` defining the + extent of the area of interest to be tiled, in the same units as + defined by `src_metric_size`. If not provided either upon + initialization or when an image is loaded, the image bounds will be + used; if provided, this value will override image metadata. + verbose : bool, optional + Verbose text output. By default, verbose text is not printed. + + Attributes + ---------- + src : :class:`rasterio.io.DatasetReader` + The source dataset to tile. + src_path : `str` + The path or URL to the source dataset. Used for calling + ``rio_cogeo.cogeo.cog_validate()``. + dest_dir : `str` + The directory to save the output tiles to. If not + dest_crs : int + The EPSG code for the output images. If not provided, outputs will + keep the same CRS as the source image when ``Tiler.make_tile_images()`` + is called. + tile_size: tuple + A ``(y, x)`` :class:`tuple` storing the dimensions of the output. + These are in pixel units unless ``size_in_meters=True``. + size_in_meters : bool + If ``True``, the units of `tile_size` are in meters instead of pixels. + is_cog : bool + Indicates whether or not the image being tiled is a Cloud-Optimized + GeoTIFF (COG). Determined by checking COG validity using + `rio-cogeo `_. + nodata : `int` + The value for nodata in the outputs. Will be set to zero in outputs if + ``None``. + alpha : `int` + The band index corresponding to an alpha channel (if one exists). + ``None`` if there is no alpha channel. + tile_bounds : list + A :class:`list` containing ``[left, bottom, right, top]`` bounds + sublists for each tile created. + resampling : str + The resampling method for any resizing. Possible values are + ``['bilinear', 'cubic', 'nearest', 'lanczos', 'average']`` (or any + other option from :class:`rasterio.warp.Resampling`). + aoi_bounds : :class:`shapely.geometry.Polygon` + A :class:`shapely.geometry.Polygon` defining the bounds of the AOI that + tiles will be created for. If a tile will extend beyond the boundary, + the "extra" pixels will have the value `nodata`. Can be provided at + initialization of the :class:`Tiler` instance or when the input is + loaded. + """ + + def __init__(self, dest_dir=None, dest_crs=None, channel_idxs=None, + src_tile_size=(900, 900), src_metric_size=False, + dest_tile_size=None, dest_metric_size=False, + aoi_bounds=None, nodata=None, alpha=None, + force_load_cog=False, resampling='bilinear', + verbose=False): + # set up attributes + if verbose: + print("Initializing Tiler...") + self.dest_dir = dest_dir + if not os.path.exists(self.dest_dir): + os.makedirs(self.dest_dir) + if dest_crs is not None: + self.dest_crs = _check_crs(dest_crs) + else: + self.dest_crs = None + self.src_tile_size = src_tile_size + self.src_metric_size = src_metric_size + if dest_tile_size is None: + self.dest_tile_size = src_tile_size + else: + self.dest_tile_size = dest_tile_size + self.resampling = resampling + self.force_load_cog = force_load_cog + self.nodata = nodata + self.alpha = alpha + self.aoi_bounds = aoi_bounds +# self.cog_output = cog_output + self.verbose = verbose + if self.verbose: + print('Tiler initialized.') + print('dest_dir: {}'.format(self.dest_dir)) + if dest_crs is not None: + print('dest_crs: EPSG:{}'.format(self.dest_crs)) + else: + print('dest_crs will be inferred from source data.') + print('src_tile_size: {}'.format(self.src_tile_size)) + print('tile size units metric: {}'.format(self.src_metric_size)) + + def tile(self, src, dest_dir=None, channel_idxs=None, nodata=None, + alpha=None, aoi_bounds=None, restrict_to_aoi=False): + + tile_gen = self.tile_generator(src, dest_dir, channel_idxs, nodata, + alpha, aoi_bounds, restrict_to_aoi) + + if self.verbose: + print('Beginning tiling...') + for tile_data, mask, profile in tqdm(tile_gen): + self.save_tile(tile_data, mask, profile) + if self.verbose: + print('Tiling complete. Cleaning up...') + self.src.close() + if os.path.exists(os.path.join(self.dest_dir, 'tmp.tif')) and \ + self.src_crs != _check_crs(self.src.crs): + os.remove(os.path.join(self.dest_dir, 'tmp.tif')) + if self.verbose: + print("Done.") + + def tile_generator(self, src, dest_dir=None, channel_idxs=None, + nodata=None, alpha=None, aoi_bounds=None, + restrict_to_aoi=False): + """Create the tiled output imagery from input tiles. + + Uses the arguments provided at initialization to generate output tiles. + First, tile locations are generated based on `Tiler.tile_size` and + `Tiler.size_in_meters` given the bounds of the input image. + + Arguments + --------- + src : `str` or :class:`Rasterio.DatasetReader` + The source data to tile from. If this is a "classic" + (non-cloud-optimized) GeoTIFF, the whole image will be loaded in; + if it's cloud-optimized, only the required portions will be loaded + during tiling unless ``force_load_cog=True`` was specified upon + initialization. + dest_dir : str, optional + The path to the destination directory to output images to. If the + path doesn't exist, it will be created. This argument is required + if it wasn't provided during initialization. + channel_idxs : list, optional + The list of channel indices to be included in the output array. + If not provided, all channels will be included. *Note:* per + ``rasterio`` convention, indexing starts at ``1``, not ``0``. + nodata : int, optional + The value in `src` that specifies nodata. If this value is not + provided, solaris will attempt to infer the nodata value from the + `src` metadata. + alpha : int, optional + The band to specify as alpha. If not provided, solaris will attempt + to infer if an alpha band is present from the `src` metadata. + aoi_bounds : `list`-like or :class:`shapely.geometry.Polygon`, optional + AOI bounds can be provided either as a + ``[left, bottom, right, top]`` :class:`list`-like or as a + :class:`shapely.geometry.Polygon`. + restrict_to_aoi : bool, optional + Should output tiles be restricted to the limits of the AOI? If + ``True``, any tile that partially extends beyond the limits of the + AOI will not be returned. This is the inverse of the ``boundless`` + argument for :class:`rasterio.io.DatasetReader` 's ``.read()`` + method. + + Yields + ------ + tile_data, mask, tile_bounds + tile_data : :class:`numpy.ndarray` + A list of lists of each tile's bounds in the order they were + created, to be used in tiling vector data. These data are also + stored as an attribute of the :class:`Tiler` instance named + `tile_bounds`. + + """ + # parse arguments + if self.verbose: + print("Checking input data...") + if isinstance(src, str): + self.is_cog = cog_validate(src) + else: + self.is_cog = cog_validate(src.name) + if self.verbose: + print('COG: {}'.format(self.is_cog)) + self.src = _check_rasterio_im_load(src) + if channel_idxs is None: # if not provided, include them all + channel_idxs = list(range(1, self.src.count + 1)) + print(channel_idxs) + self.src_crs = _check_crs(self.src.crs) + if self.verbose: + print('Source CRS: EPSG:{}'.format(self.src_crs)) + if self.dest_crs is None: + self.dest_crs = self.src_crs + if self.verbose: + print('Destination CRS: EPSG:{}'.format(self.dest_crs)) + self.src_path = self.src.name + self.proj_unit = raster_get_projection_unit( + self.src).strip('"').strip("'") + if self.verbose: + print("Inputs OK.") + if self.src_metric_size: + if self.verbose: + print("Checking if inputs are in metric units...") + if self.proj_unit not in ['meter', 'metre']: + if self.verbose: + print("Input CRS is not metric. " + "Reprojecting the input to UTM.") + self.src = reproject(self.src, + resampling_method=self.resampling, + dest_path=os.path.join(self.dest_dir, + 'tmp.tif')) + if self.verbose: + print('Done reprojecting.') + if nodata is None and self.nodata is None: + self.nodata = self.src.nodata + else: + self.nodata = nodata + # get index of alpha channel + if alpha is None and self.alpha is None: + mf_list = [rasterio.enums.MaskFlags.alpha in i for i in + self.src.mask_flag_enums] # list with True at idx of alpha c + try: + self.alpha = np.where(mf_list)[0] + 1 + except IndexError: # if there isn't a True + self.alpha = None + else: + self.alpha = alpha + + if getattr(self, 'tile_bounds', None) is None: + self.get_tile_bounds() + + for tb in self.tile_bounds: + if not self.is_cog or self.force_load_cog: + vrt = self.load_src_vrt() + window = vrt.window(*tb) + if self.src.count != 1: + tile_data = vrt.read(window=window, + resampling=getattr(Resampling, + self.resampling), + indexes=channel_idxs) + else: + tile_data = vrt.read(window=window, + resampling=getattr(Resampling, + self.resampling)) + # get the affine xform between src and dest for the tile + aff_xform = transform.from_bounds(*tb, + self.dest_tile_size[1], + self.dest_tile_size[0]) + if self.nodata: + mask = np.all(tile_data != nodata, + axis=0).astype(np.uint8) * 255 + elif self.alpha: + mask = vrt.read(self.alpha, window=window, + resampling=getattr(Resampling, + self.resampling)) + else: + mask = None # placeholder + + else: + tile_data, mask, window, aff_xform = read_cog_tile( + src=self.src, + bounds=tb, + tile_size=self.dest_tile_size, + indexes=channel_idxs, + nodata=self.nodata, + resampling_method=self.resampling + ) + profile = self.src.profile + profile.update(width=self.dest_tile_size[1], + height=self.dest_tile_size[0], + crs=CRS.from_epsg(self.dest_crs), + transform=aff_xform) + if len(tile_data.shape) == 2: # if there's no channel band + profile.update(count=1) + else: + profile.update(count=tile_data.shape[0]) + + yield tile_data, mask, profile + + def save_tile(self, tile_data, mask, profile): + """Save a tile created by ``Tiler.tile_generator()``.""" + dest_fname_root = os.path.splitext(os.path.split(self.src_path)[1])[0] + if self.proj_unit not in ['meter', 'metre']: + dest_fname = '{}_{}_{}.tif'.format( + dest_fname_root, + np.round(profile['transform'][2], 3), + np.round(profile['transform'][5], 3)) + else: + dest_fname = '{}_{}_{}.tif'.format( + dest_fname_root, + int(profile['transform'][2]), + int(profile['transform'][5])) + # if self.cog_output: + # dest_path = os.path.join(self.dest_dir, 'tmp.tif') + #else: + dest_path = os.path.join(self.dest_dir, dest_fname) + + with rasterio.open(dest_path, 'w', + **profile) as dest: + if profile['count'] == 1: + dest.write(tile_data[0, :, :], 1) + else: + for band in range(1, profile['count'] + 1): + # base-1 vs. base-0 indexing...bleh + dest.write(tile_data[band-1, :, :], band) + if self.alpha: + # write the mask if there's an alpha band + dest.write(mask, profile['count'] + 1) + + dest.close() + + # if self.cog_output: + # self._create_cog(os.path.join(self.dest_dir, 'tmp.tif'), + # os.path.join(self.dest_dir, dest_fname)) + # os.remove(os.path.join(self.dest_dir, 'tmp.tif')) + + def _create_cog(self, src_path, dest_path): + """Overwrite non-cloud-optimized GeoTIFF with a COG.""" + cog_translate(src_path=src_path, dst_path=dest_path, + dst_kwargs={'crs': CRS.from_epsg(self.dest_crs)}, + resampling=self.resampling, + latitude_adjustment=False) + + def get_tile_bounds(self): + """Get tile bounds for each tile to be created in the input CRS.""" + if not self.aoi_bounds: + if not self.src: + raise ValueError('aoi_bounds and/or a source file must be ' + 'provided.') + else: + # set to the bounds of the image + self.aoi_bounds = self.src.bounds + if not self.src_metric_size: + xform = self.src.transform + # convert pixel units to CRS units to use during image tiling. + # NOTE: This will be imperfect for large AOIs where there isn't + # a constant relationship between the src CRS units and src pixel + # units. + tmp_tile_size = [self.src_tile_size[0]*xform[0], + self.src_tile_size[1]*-xform[4]] + else: + tmp_tile_size = self.src_tile_size + + x_extent = self.aoi_bounds.right - self.aoi_bounds.left + y_extent = self.aoi_bounds.top - self.aoi_bounds.bottom + x_steps = np.ceil(x_extent/tmp_tile_size[1]) + y_steps = np.ceil(y_extent/tmp_tile_size[0]) + x_mins = np.arange(self.aoi_bounds.left, + self.aoi_bounds.left + tmp_tile_size[1]*x_steps, + tmp_tile_size[1]) + y_mins = np.arange(self.aoi_bounds.bottom, + self.aoi_bounds.bottom + tmp_tile_size[0]*y_steps, + tmp_tile_size[0]) + self.tile_bounds = [(i, + j, + i+tmp_tile_size[1], + j+tmp_tile_size[0]) + for i in x_mins for j in y_mins] + + def load_src_vrt(self): + """Load a source dataset's VRT into the destination CRS.""" + vrt_params = dict(crs=CRS.from_epsg(self.dest_crs), + resampling=getattr(Resampling, self.resampling), + src_nodata=self.nodata, dst_nodata=self.nodata) + return WarpedVRT(self.src, **vrt_params) diff --git a/solaris/tile/vector_tile.py b/solaris/tile/vector_tile.py new file mode 100644 index 00000000..c64b98dc --- /dev/null +++ b/solaris/tile/vector_tile.py @@ -0,0 +1,242 @@ +import os +import numpy as np +from shapely.geometry import box, Polygon +import geopandas as gpd +from ..utils.core import _check_gdf_load, _check_crs +from ..utils.tile import save_empty_geojson +from ..utils.geo import gdf_get_projection_unit +from tqdm import tqdm + + +class VectorTiler(object): + """An object to tile geospatial vector data into smaller pieces. + + Arguments + --------- + + + Attributes + ---------- + """ + + def __init__(self, dest_dir=None, dest_crs=None, output_format='GeoJSON', + verbose=False): + if verbose: + print('Preparing the tiler...') + self.dest_dir = dest_dir + if not os.path.isdir(self.dest_dir): + os.makedirs(self.dest_dir) + if dest_crs is not None: + self.dest_crs = _check_crs(dest_crs) + self.output_format = output_format + self.verbose = verbose + if self.verbose: + print('Initialization done.') + + def tile(self, src, tile_bounds, geom_type='Polygon', + split_multi_geometries=True, min_partial_perc=0.0, + dest_fname_base='geoms'): + """Tile `src` into vector data tiles bounded by `tile_bounds`. + + Arguments + --------- + src : `str` or :class:`geopandas.GeoDataFrame` + The source vector data to tile. Must either be a path to a GeoJSON + or a :class:`geopandas.GeoDataFrame`. + tile_bounds : list + A :class:`list` made up of ``[left, bottom, right, top]`` sublists + (this can be extracted from + :class:`solaris.tile.raster_tile.RasterTiler` after tiling imagery) + geom_type : str, optional (default: "Polygon") + The type of geometries contained within `src`. Defaults to + ``"Polygon"``, can also be ``"LineString"``. + split_multi_geometries : bool, optional (default: True) + Should multi-polygons or multi-linestrings generated by clipping + a geometry into discontinuous pieces be separated? Defaults to yes + (``True``). + min_partial_perc : float, optional (default: 0.0) + The minimum percentage of a :class:`shapely.geometry.Polygon` 's + area or :class:`shapely.geometry.LineString` 's length that must + be retained within a tile's bounds to be included in the output. + Defaults to ``0.0``, meaning that the contained portion of a + clipped geometry will be included, no matter how small. + dest_fname_base : str, optional (default: 'geoms') + The base filename to use when creating outputs. The lower left + corner coordinates of the tile's bounding box will be appended + when saving. + """ + tile_gen = self.tile_generator(src, tile_bounds, geom_type, + split_multi_geometries, + min_partial_perc) + for tile_gdf, tb in tqdm(tile_gen): + if self.proj_unit not in ['meter', 'metre']: + out_path = os.path.join( + self.dest_dir, '{}_{}_{}.json'.format(dest_fname_base, + np.round(tb[0], 3), + np.round(tb[1], 3))) + else: + out_path = os.path.join( + self.dest_dir, '{}_{}_{}.json'.format(dest_fname_base, + int(tb[0]), + int(tb[1]))) + if len(tile_gdf) > 0: + tile_gdf.to_file(out_path, driver='GeoJSON') + else: + save_empty_geojson(out_path, self.dest_crs) + + def tile_generator(self, src, tile_bounds, geom_type='Polygon', + split_multi_geometries=True, min_partial_perc=0.0): + """Generate `src` vector data tiles bounded by `tile_bounds`. + + Arguments + --------- + src : `str` or :class:`geopandas.GeoDataFrame` + The source vector data to tile. Must either be a path to a GeoJSON + or a :class:`geopandas.GeoDataFrame`. + tile_bounds : list + A :class:`list` made up of ``[left, bottom, right, top]`` sublists + (this can be extracted from + :class:`solaris.tile.raster_tile.RasterTiler` after tiling imagery) + geom_type : str, optional (default: "Polygon") + The type of geometries contained within `src`. Defaults to + ``"Polygon"``, can also be ``"LineString"``. + split_multi_geometries : bool, optional (default: True) + Should multi-polygons or multi-linestrings generated by clipping + a geometry into discontinuous pieces be separated? Defaults to yes + (``True``). + min_partial_perc : float, optional (default: 0.0) + The minimum percentage of a :class:`shapely.geometry.Polygon` 's + area or :class:`shapely.geometry.LineString` 's length that must + be retained within a tile's bounds to be included in the output. + Defaults to ``0.0``, meaning that the contained portion of a + clipped geometry will be included, no matter how small. + """ + self.src = _check_gdf_load(src) + self.src_crs = _check_crs(self.src.crs) + self.proj_unit = gdf_get_projection_unit( + self.src).strip('"').strip("'") + if getattr(self, 'dest_crs', None) is None: + self.dest_crs = self.src_crs + for tb in tile_bounds: + tile_gdf = clip_gdf(self.src, tb, min_partial_perc, + geom_type) + if self.src_crs != self.dest_crs: + tile_gdf = tile_gdf.to_crs(epsg=self.dest_crs) + yield tile_gdf, tb + + +def search_gdf_polygon(gdf, tile_polygon): + """Find polygons in a GeoDataFrame that overlap with `tile_polygon` . + + Arguments + --------- + gdf : :py:class:`geopandas.GeoDataFrame` + A :py:class:`geopandas.GeoDataFrame` of polygons to search. + tile_polygon : :py:class:`shapely.geometry.Polygon` + A :py:class:`shapely.geometry.Polygon` denoting a tile's bounds. + + Returns + ------- + precise_matches : :py:class:`geopandas.GeoDataFrame` + The subset of `gdf` that overlaps with `tile_polygon` . If + there are no overlaps, this will return an empty + :py:class:`geopandas.GeoDataFrame`. + + """ + + sindex = gdf.sindex + possible_matches_index = list(sindex.intersection(tile_polygon.bounds)) + possible_matches = gdf.iloc[possible_matches_index] + precise_matches = possible_matches[ + possible_matches.intersects(tile_polygon) + ] + if precise_matches.empty: + precise_matches = gpd.GeoDataFrame(geometry=[]) + return precise_matches + + +def clip_gdf(gdf, tile_bounds, min_partial_perc=0.0, geom_type="Polygon", + use_sindex=True): + """Clip GDF to a provided polygon. + + Clips objects within `gdf` to the region defined by + `poly_to_cut`. Also adds several columns to the output:: + + `origarea` + The original area of the polygons (only used if `geom_type` == + ``"Polygon"``). + `origlen` + The original length of the objects (only used if `geom_type` == + ``"LineString"``). + `partialDec` + The fraction of the object that remains after clipping + (fraction of area for Polygons, fraction of length for + LineStrings.) Can filter based on this by using `min_partial_perc`. + `truncated` + Boolean indicator of whether or not an object was clipped. + + Arguments + --------- + gdf : :py:class:`geopandas.GeoDataFrame` + A :py:class:`geopandas.GeoDataFrame` of polygons to clip. + tile_bounds : `list` or :class:`shapely.geometry.Polygon` + The geometry to clip objects in `gdf` to. This can either be a + ``[left, bottom, right, top]`` bounds list or a + :class:`shapely.geometry.Polygon` object defining the area to keep. + min_partial_perc : float, optional + The minimum fraction of an object in `gdf` that must be + preserved. Defaults to 0.0 (include any object if any part remains + following clipping). + geom_type : str, optional + Type of objects in `gdf`. Can be one of + ``["Polygon", "LineString"]`` . Defaults to ``"Polygon"`` . + use_sindex : bool, optional + Use the `gdf` sindex be used for searching. Improves efficiency + but requires `libspatialindex `__ . + + Returns + ------- + cut_gdf : :py:class:`geopandas.GeoDataFrame` + `gdf` with all contained objects clipped to `poly_to_cut` . + See notes above for details on additional clipping columns added. + + """ + if isinstance(tile_bounds, tuple): + tb = box(*tile_bounds) + elif isinstance(tile_bounds, Polygon): + tb = tile_bounds + if use_sindex: + gdf = search_gdf_polygon(gdf, tb) + + # if geom_type == "LineString": + if 'origarea' in gdf.columns: + pass + else: + if "geom_type" == "LineString": + gdf['origarea'] = 0 + else: + gdf['origarea'] = gdf.area + if 'origlen' in gdf.columns: + pass + else: + if "geom_type" == "LineString": + gdf['origlen'] = gdf.length + else: + gdf['origlen'] = 0 + # TODO must implement different case for lines and for spatialIndex + # (Assume RTree is already performed) + + cut_gdf = gdf.copy() + cut_gdf.geometry = gdf.intersection(tb) + + if geom_type == 'Polygon': + cut_gdf['partialDec'] = cut_gdf.area / cut_gdf['origarea'] + cut_gdf = cut_gdf.loc[cut_gdf['partialDec'] > min_partial_perc, :] + cut_gdf['truncated'] = (cut_gdf['partialDec'] != 1.0).astype(int) + else: + cut_gdf = cut_gdf[cut_gdf.geom_type != "GeometryCollection"] + cut_gdf['partialDec'] = 1 + cut_gdf['truncated'] = 0 + # TODO: IMPLEMENT TRUNCATION MEASUREMENT FOR LINESTRINGS + + return cut_gdf diff --git a/solaris/tile/vector_utils.py b/solaris/tile/vector_utils.py deleted file mode 100644 index a19260d8..00000000 --- a/solaris/tile/vector_utils.py +++ /dev/null @@ -1,228 +0,0 @@ -from shapely.geometry import box -import geopandas as gpd -from rasterio import features -from rasterio import Affine -import numpy as np - - -def read_vector_file(geoFileName): - """Read Fiona-Supported Files into GeoPandas GeoDataFrame. - - Warning - ---- - This will raise an exception for empty GeoJSON files, which GDAL and Fiona - cannot read. ``try/except`` the :py:exc:`Fiona.errors.DriverError` or - :py:exc:`Fiona._err.CPLE_OpenFailedError` if you must use this. - - """ - - return gpd.read_file(geoFileName) - - -def transformToUTM(gdf, utm_crs, estimate=True, calculate_sindex=True): - """Transform GeoDataFrame to UTM coordinate reference system. - - Arguments - --------- - gdf : :py:class:`geopandas.GeoDataFrame` - :py:class:`geopandas.GeoDataFrame` to transform. - utm_crs : str - :py:class:`rasterio.crs.CRS` string for destination UTM CRS. - estimate : bool, optional - .. deprecated:: 0.2.0 - This argument is no longer used. - calculate_sindex : bool, optional - .. deprecated:: 0.2.0 - This argument is no longer used. - - Returns - ------- - gdf : :py:class:`geopandas.GeoDataFrame` - The input :py:class:`geopandas.GeoDataFrame` converted to - `utm_crs` coordinate reference system. - - """ - - gdf = gdf.to_crs(utm_crs) - return gdf - - -def search_gdf_bounds(gdf, tile_bounds): - """Use `tile_bounds` to subset `gdf` and return the intersect. - - Arguments - --------- - gdf : :py:class:`geopandas.GeoDataFrame` - A :py:class:`geopandas.GeoDataFrame` of polygons to subset. - tile_bounds : tuple - A tuple of shape ``(W, S, E, N)`` that denotes the boundaries of a - tile. - - Returns - ------- - smallGdf : :py:class:`geopandas.GeoDataFrame` - The subset of `gdf` that overlaps with `tile_bounds` . - - """ - - tile_polygon = box(tile_bounds) - smallGdf = search_gdf_polygon(gdf, tile_polygon) - - return smallGdf - - -def search_gdf_polygon(gdf, tile_polygon): - """Find polygons in a GeoDataFrame that overlap with `tile_polygon` . - - Arguments - --------- - gdf : :py:class:`geopandas.GeoDataFrame` - A :py:class:`geopandas.GeoDataFrame` of polygons to search. - tile_polygon : :py:class:`shapely.geometry.Polygon` - A :py:class:`shapely.geometry.Polygon` denoting a tile's bounds. - - Returns - ------- - precise_matches : :py:class:`geopandas.GeoDataFrame` - The subset of `gdf` that overlaps with `tile_polygon` . If - there are no overlaps, this will return an empty - :py:class:`geopandas.GeoDataFrame`. - - """ - - sindex = gdf.sindex - possible_matches_index = list(sindex.intersection(tile_polygon.bounds)) - possible_matches = gdf.iloc[possible_matches_index] - precise_matches = possible_matches[ - possible_matches.intersects(tile_polygon) - ] - if precise_matches.empty: - precise_matches = gpd.GeoDataFrame(geometry=[]) - return precise_matches - - -def vector_tile_utm(gdf, tile_bounds, min_partial_perc=0.1, - geom_type="Polygon", use_sindex=True): - """Wrapper for :func:`clip_gdf` that converts `tile_bounds` to a polygon. - - Arguments - --------- - gdf : :class:`geopandas.GeoDataFrame` - A :py:class:`geopandas.GeoDataFrame` of polygons to clip. - tile_bounds : list-like of floats - :obj:`list` of shape ``(W, S, E, N)`` denoting the boundaries of an - imagery tile. Converted to a polygon for :func:`clip_gdf`. - min_partial_perc : float - The minimum fraction of an object in `gdf` that must be - preserved. Defaults to 0.0 (include any object if any part remains - following clipping). - use_sindex : bool, optional - Use the `gdf` sindex be used for searching. Improves efficiency - but requires `libspatialindex `__ . - - Returns - ------- - small_gdf : :py:class:`geopandas.GeoDataFrame` - `gdf` with all contained objects clipped to `tile_bounds`. - See notes above for details on additional clipping columns added. - """ - tile_polygon = box(*tile_bounds) - small_gdf = clip_gdf(gdf, tile_polygon, - min_partial_perc=min_partial_perc, - geom_type=geom_type - ) - - return small_gdf - - -def getCenterOfGeoFile(gdf, estimate=True): - - #TODO implement calculate UTM from gdf see osmnx implementation - - pass - - -def clip_gdf(gdf, poly_to_cut, min_partial_perc=0.0, geom_type="Polygon", - use_sindex=True): - """Clip GDF to a provided polygon. - - Note - ---- - Clips objects within `gdf` to the region defined by - `poly_to_cut`. Also adds several columns to the output: - - `origarea` - The original area of the polygons (only used if `geom_type` == - ``"Polygon"``). - `origlen` - The original length of the objects (only used if `geom_type` == - ``"LineString"``). - `partialDec` - The fraction of the object that remains after clipping - (fraction of area for Polygons, fraction of length for - LineStrings.) Can filter based on this by using `min_partial_perc`. - `truncated` - Boolean indicator of whether or not an object was clipped. - - Arguments - --------- - gdf : :py:class:`geopandas.GeoDataFrame` - A :py:class:`geopandas.GeoDataFrame` of polygons to clip. - poly_to_cut : :py:class:`shapely.geometry.Polygon` - The polygon to clip objects in `gdf` to. - min_partial_perc : float, optional - The minimum fraction of an object in `gdf` that must be - preserved. Defaults to 0.0 (include any object if any part remains - following clipping). - geom_type : str, optional - Type of objects in `gdf`. Can be one of - ``["Polygon", "LineString"]`` . Defaults to ``"Polygon"`` . - use_sindex : bool, optional - Use the `gdf` sindex be used for searching. Improves efficiency - but requires `libspatialindex `__ . - - Returns - ------- - cutGeoDF : :py:class:`geopandas.GeoDataFrame` - `gdf` with all contained objects clipped to `poly_to_cut` . - See notes above for details on additional clipping columns added. - - """ - - # check if geoDF has origAreaField - - if use_sindex: - gdf = search_gdf_polygon(gdf, poly_to_cut) - - # if geom_type == "LineString": - if 'origarea' in gdf.columns: - pass - else: - if "geom_type" == "LineString": - gdf['origarea'] = 0 - else: - gdf['origarea'] = gdf.area - if 'origlen' in gdf.columns: - pass - else: - if "geom_type" == "LineString": - gdf['origlen'] = gdf.length - else: - gdf['origlen'] = 0 - # TODO must implement different case for lines and for spatialIndex - # (Assume RTree is already performed) - - cutGeoDF = gdf.copy() - cutGeoDF.geometry = gdf.intersection(poly_to_cut) - - if geom_type == 'Polygon': - cutGeoDF['partialDec'] = cutGeoDF.area / cutGeoDF['origarea'] - cutGeoDF = cutGeoDF.loc[cutGeoDF['partialDec'] > min_partial_perc, :] - cutGeoDF['truncated'] = (cutGeoDF['partialDec'] != 1.0).astype(int) - else: - cutGeoDF = cutGeoDF[cutGeoDF.geom_type != "GeometryCollection"] - cutGeoDF['partialDec'] = 1 - cutGeoDF['truncated'] = 0 - # TODO: IMPLEMENT TRUNCATION MEASUREMENT FOR LINESTRINGS - - return cutGeoDF diff --git a/solaris/utils/config.py b/solaris/utils/config.py index 285e323c..8090470e 100644 --- a/solaris/utils/config.py +++ b/solaris/utils/config.py @@ -19,17 +19,14 @@ def parse(path): with open(path, 'r') as f: config = yaml.safe_load(f) f.close() - if config['model_name'] not in zoo.models: - raise ValueError('{} is not a valid model name.'.format( - config['model_name'])) if not config['train'] and not config['infer']: raise ValueError('"train", "infer", or both must be true.') - if config['train'] and config['data']['train_im_src'] is None: - raise ValueError('"train_im_src" must be provided if training.') - if config['train'] and config['data']['train_label_src'] is None: - raise ValueError('"train_label_src" must be provided if training.') - if config['infer'] and config['data']['infer_im_src'] is None: - raise ValueError('"infer_im_src" must be provided if "infer".') + if config['train'] and config['training_data_csv'] is None: + raise ValueError('"training_data_csv" must be provided if training.') + if config['infer'] and config['inference_data_csv'] is None: + raise ValueError('"inference_data_csv" must be provided if "infer".') + if config['training']['lr'] is not None: + config['training']['lr'] = float(config['training']['lr']) # TODO: IMPLEMENT UPDATING VALUES BASED ON EMPTY ELEMENTS HERE! return config diff --git a/solaris/utils/core.py b/solaris/utils/core.py index 079cc481..9ff64d04 100644 --- a/solaris/utils/core.py +++ b/solaris/utils/core.py @@ -1,10 +1,14 @@ import os import numpy as np +from shapely.wkt import loads +from shapely.geometry import Point +from shapely.geometry.base import BaseGeometry import pandas as pd import geopandas as gpd import rasterio import skimage - +from fiona._err import CPLE_OpenFailedError +from fiona.errors import DriverError def _check_rasterio_im_load(im): """Check if `im` is already loaded in; if not, load it in.""" @@ -44,7 +48,10 @@ def _check_df_load(df): def _check_gdf_load(gdf): """Check if `gdf` is already loaded in, if not, load from geojson.""" if isinstance(gdf, str): - return gpd.read_file(gdf) + try: + return gpd.read_file(gdf) + except (DriverError, CPLE_OpenFailedError): + return gpd.GeoDataFrame() elif isinstance(gdf, gpd.GeoDataFrame): return gdf else: @@ -52,6 +59,34 @@ def _check_gdf_load(gdf): "{} is not an accepted GeoDataFrame format.".format(gdf)) +def _check_geom(geom): + """Check if a geometry is loaded in. + + Returns the geometry if it's a shapely geometry object. If it's a wkt + string or a list of coordinates, convert to a shapely geometry. + """ + if isinstance(geom, BaseGeometry): + return geom + elif isinstance(geom, str): # assume it's a wkt + return loads(geom) + elif isinstance(geom, list) and len(geom) == 2: # coordinates + return Point(geom) + + +def _check_crs(input_crs): + """Convert CRS to the integer format passed by ``solaris``.""" + if isinstance(input_crs, dict): + # assume it's an {'init': 'epsgxxxx'} dict + out_crs = int(input_crs['init'].lower().strip('epsg:')) + elif isinstance(input_crs, str): + # handle PROJ4 strings, epsg strings, wkt strings + out_crs = rasterio.crs.CRS.from_string(input_crs).to_epsg() + elif isinstance(input_crs, rasterio.crs.CRS): + out_crs = input_crs.to_epsg() + elif isinstance(input_crs, int): + out_crs = input_crs + return out_crs + def get_data_paths(path, infer=False): """Get a pandas dataframe of images and labels from a csv. @@ -79,7 +114,6 @@ def get_data_paths(path, infer=False): case only the `image` column is returned.) """ - df = pd.read_csv(path) if infer: return df[['image']] # no labels in those files diff --git a/solaris/utils/geo.py b/solaris/utils/geo.py index b74d39f7..4a328e0c 100644 --- a/solaris/utils/geo.py +++ b/solaris/utils/geo.py @@ -1,16 +1,405 @@ -from .core import _check_gdf_load +import os +from .core import _check_df_load, _check_gdf_load, _check_rasterio_im_load +from .core import _check_geom, _check_crs import numpy as np import pandas as pd -from affine import Affine import geopandas as gpd +from affine import Affine import rasterio +from rasterio.crs import CRS +from rasterio.vrt import WarpedVRT +from rasterio.warp import calculate_default_transform, Resampling +from rasterio.warp import transform_bounds +from shapely.affinity import affine_transform from shapely.errors import WKTReadingError from shapely.wkt import loads +from shapely.geometry import Point, Polygon, LineString from shapely.geometry import MultiLineString, MultiPolygon, mapping, shape from shapely.ops import cascaded_union +from fiona.transform import transform +import osr +import gdal from warnings import warn +def reproject(input_object, input_crs=None, + target_crs=None, target_object=None, dest_path=None, + resampling_method='cubic'): + """Reproject a dataset (df, gdf, or image) to a new coordinate system. + + This function takes a georegistered image or a dataset of vector geometries + and converts them to a new coordinate reference system. If no target CRS + is provided, the data will be converted to the appropriate UTM zone by + default. To convert a pixel-coordinate dataset to geographic coordinates or + vice versa, use :func:`solaris.vector.polygon.georegister_px_df` or + :func:`solaris.vector.polygon.geojson_to_px_gdf` instead. + + Arguments + --------- + input_object : `str` or :class:`rasterio.DatasetReader` or :class:`gdal.Dataset` or :class:`geopandas.GeoDataFrame` + An object to transform to a new CRS. If a string, it must be a path + to a georegistered image or vector dataset (e.g. a .GeoJSON). If the + object itself does not contain georeferencing information, the + coordinate reference system can be provided with `input_crs`. + input_crs : int, optional + The EPSG code integer for the input data's CRS. If provided and a CRS + is also associated with `input_object`, this argument's value has + precedence. + target_crs : int, optional + The EPSG code for the output projection. If values are not provided + for this argument or `target_object`, the input data will be + re-projected into the appropriate UTM zone. If both `target_crs` and + `target_object` are provided, `target_crs` takes precedence (and a + warning is raised). + target_object : str, optional + An object in the desired destination CRS. If neither this argument nor + `target_crs` is provided, the input will be projected into the + appropriate UTM zone. `target_crs` takes precedence if both it and + `target_object` are provided. + dest_path : str, optional + The path to save the output to (if desired). This argument is only + required if the input is a :class:`gdal.Dataset`; otherwise, it is + optional. + resampling_method : str, optional + The resampling method to use during reprojection of raster data. **Only + has an effect if the input is a :class:`rasterio.DatasetReader` !** + Possible values are + ``['cubic' (default), 'bilinear', 'nearest', 'average']``. + + Returns + ------- + output : :class:`rasterio.DatasetReader` or :class:`gdal.Dataset` or :class:`geopandas.GeoDataFrame` + An output in the same format as `input_object`, but reprojected + into the destination CRS. + """ + input_data, input_type = _parse_geo_data(input_object) + if input_crs is None: + input_crs = get_crs(input_data) + else: + input_crs = _check_crs(input_crs) + if target_object is not None: + target_data, _ = _parse_geo_data(target_object) + else: + target_data = None + # get CRS from target_object if it's not provided + if target_crs is None and target_data is not None: + target_crs = get_crs(target_data) + + if target_crs is not None: + target_crs = _check_crs(target_crs) + output = _reproject(input_data, input_type, input_crs, target_crs, + dest_path, resampling_method) + else: + output = reproject_to_utm(input_data, input_type, input_crs, + dest_path, resampling_method) + return output + + +def _reproject(input_data, input_type, input_crs, target_crs, dest_path, + resampling_method='bicubic'): + + if input_type == 'vector': + output = input_data.to_crs(epsg=target_crs) + if dest_path is not None: + output.to_file(dest_path, driver='GeoJSON') + + elif input_type == 'raster': + + if isinstance(input_data, rasterio.DatasetReader): + transform, width, height = calculate_default_transform( + CRS.from_epsg(input_crs), CRS.from_epsg(target_crs), + input_data.width, input_data.height, *input_data.bounds + ) + kwargs = input_data.meta.copy() + kwargs.update({'crs': target_crs, + 'transform': transform, + 'width': width, + 'height': height}) + + if dest_path is not None: + with rasterio.open(dest_path, 'w', **kwargs) as dst: + for band_idx in range(1, input_data.count + 1): + rasterio.warp.reproject( + source=rasterio.band(input_data, band_idx), + destination=rasterio.band(dst, band_idx), + src_transform=input_data.transform, + src_crs=input_data.crs, + dst_transform=transform, + dst_crs=CRS.from_epsg(target_crs), + resampling=getattr(Resampling, resampling_method) + ) + output = rasterio.open(dest_path) + input_data.close() + + else: + output = np.zeros(shape=(height, width, input_data.count)) + for band_idx in range(1, input_data.count + 1): + rasterio.warp.reproject( + source=rasterio.band(input_data, band_idx), + destination=output[:, :, band_idx-1], + src_transform=input_data.transform, + src_crs=input_data.crs, + dst_transform=transform, + dst_crs=CRS.from_epsg(target_crs), + resampling=getattr(Resampling, resampling_method) + ) + + elif isinstance(input_data, gdal.Dataset): + if dest_path is not None: + gdal.Warp(dest_path, input_data, + dstSRS='EPSG:' + str(target_crs)) + output = gdal.Open(dest_path) + else: + raise ValueError('An output path must be provided for ' + 'reprojecting GDAL datasets.') + return output + + +def reproject_to_utm(input_data, input_type, input_crs=None, dest_path=None, + resampling_method='bicubic'): + """Convert an input to a UTM CRS (after determining the correct UTM zone). + + """ + if input_crs is None: + input_crs = get_crs(input_data) + if input_crs is None: + raise ValueError('An input CRS must be provided by input_data or' + ' input_crs.') + input_crs = _check_crs(input_crs) + + bounds = get_bounds(input_data, crs=4326) # need in wkt84 for UTM zone + midpoint = [(bounds[1] + bounds[3])/2., (bounds[0] + bounds[2])/2.] + utm_epsg = latlon_to_utm_epsg(*midpoint) + + output = _reproject(input_data, input_type=input_type, input_crs=input_crs, + target_crs=utm_epsg, dest_path=dest_path, + resampling_method=resampling_method) + # cleanup + if os.path.isfile('tmp'): + os.remove('tmp') + + return output + + +def get_bounds(input, crs=None): + """Get the ``[left, bottom, right, top]`` bounds in any CRS. + + Arguments + --------- + geo_obj : a georeferenced raster or vector dataset. + crs : int, optional + The EPSG code for the CRS the bounds should be returned in. If not + provided, the bounds will be returned in the same crs as `geo_obj`. + + Returns + ------- + bounds : list + ``[left, bottom, right, top]`` bounds in the input crs (if `crs` is + ``None``) or in `crs` if it was provided. + """ + input_data, input_type = _parse_geo_data(input) + if input_type == 'vector': + bounds = list(input_data.geometry.total_bounds) + elif input_type == 'raster': + if isinstance(input_data, rasterio.DatasetReader): + bounds = list(input_data.bounds) + elif isinstance(input_data, gdal.Dataset): + input_gt = input_data.GetGeoTransform() + min_x = input_gt[0] + max_x = min_x + input_gt[1]*input_data.RasterXSize + max_y = input_gt[3] + min_y = max_y + input_gt[5]*input_data.RasterYSize + + bounds = [min_x, min_y, max_x, max_y] + + if crs is not None: + crs = _check_crs(crs) + src_crs = get_crs(input_data) + # transform bounds to desired CRS + bounds = transform_bounds(CRS.from_epsg(src_crs), + CRS.from_epsg(crs), + *bounds) + + return bounds + + +def get_crs(obj): + """Get a coordinate reference system from any georegistered object.""" + if isinstance(obj, gpd.GeoDataFrame): + return int(obj.crs['init'].lstrip('epsg:')) + elif isinstance(obj, rasterio.DatasetReader): + return int(obj.crs['init'].lstrip('epsg:')) + elif isinstance(obj, gdal.Dataset): + # rawr + return int(osr.SpatialReference(wkt=obj.GetProjection()).GetAttrValue( + 'AUTHORITY', 1)) + else: + raise TypeError("solaris doesn't know how to extract a crs from an " + "object of type {}".format(type(obj))) + + +def _parse_geo_data(input): + if isinstance(input, str): + if input.lower().endswith('json') or input.lower().endswith('csv'): + input_type = 'vector' + input_data = _check_df_load(input) + elif input.lower().endswith('tif') or input.lower().endswith('tiff'): + input_type = 'raster' + input_data = _check_rasterio_im_load(input) + else: + input_data = input + if isinstance(input_data, pd.DataFrame): + input_type = 'vector' + elif isinstance( + input_data, rasterio.DatasetReader + ) or isinstance( + input_data, gdal.Dataset + ): + input_type = 'raster' + else: + raise ValueError('The input format {} is not compatible with ' + 'solaris.'.format(type(input))) + return input_data, input_type + + +def reproject_geometry(input_geom, input_crs=None, target_crs=None, + affine_obj=None): + """Reproject a geometry or coordinate into a new CRS. + + Arguments + --------- + input_geom : `str`, `list`, or `Shapely `_ geometry + A geometry object to re-project. This can be a 2-member ``list``, in + which case `input_geom` is assumed to coorespond to ``[x, y]`` + coordinates in `input_crs`. It can also be a Shapely geometry object or + a wkt string. + input_crs : int, optional + The coordinate reference system for `input_geom`'s coordinates, as an + EPSG :class:`int`. Required unless `affine_transform` is provided. + target_crs : int, optional + The target coordinate reference system to re-project the geometry into. + If not provided, the appropriate UTM zone will be selected by default, + unless `affine_transform` is provided (and therefore CRSs are ignored.) + affine_transform : :class:`affine.Affine`, optional + An :class:`affine.Affine` object (or a ``[a, b, c, d, e, f]`` list to + convert to that format) to use for transformation. Has no effect unless + `input_crs` **and** `target_crs` are not provided. + + Returns + ------- + output_geom : Shapely geometry + A shapely geometry object: + - in `target_crs`, if one was provided; + - in the appropriate UTM zone, if `input_crs` was provided and + `target_crs` was not; + - with `affine_transform` applied to it if neither `input_crs` nor + `target_crs` were provided. + """ + input_geom = _check_geom(input_geom) + + input_coords = _get_coords(input_geom) + + if input_crs is not None: + input_crs = _check_crs(input_crs) + if target_crs is None: + latlon = reproject_geometry(input_geom, input_crs, target_crs=4326) + target_crs = latlon_to_utm_epsg(latlon.y, latlon.x) + target_crs = _check_crs(target_crs) + xformed_coords = transform('EPSG:' + str(input_crs), + 'EPSG:' + str(target_crs), + *input_coords) + # create a new instance of the same geometry class as above with the + # new coordinates + output_geom = input_geom.__class__(list(zip(*xformed_coords))) + + else: + if affine_obj is None: + raise ValueError('If an input CRS is not provided, ' + 'affine_transform is required to complete the ' + 'transformation.') + elif isinstance(affine_obj, Affine): + affine_obj = affine_to_list(affine_obj) + + output_geom = affine_transform(input_geom, affine_obj) + + return output_geom + + +def gdf_get_projection_unit(vector_file): + """Get the projection unit for a vector_file or gdf. + + Arguments + --------- + vector_file : :py:class:`geopandas.GeoDataFrame` or geojson/shapefile + A vector file or gdf with georeferencing + + Notes + ----- + If vector file is already in UTM coords, the projection WKT is complex: + https://www.spatialreference.org/ref/epsg/wgs-84-utm-zone-11n/html/ + In this case, return the second instance of 'UNIT'. + + Returns + ------- + unit : String + The unit i.e. meter, metre, or degree, of the projection + """ + c = _check_gdf_load(vector_file) + crs = _check_crs(c.crs) + srs = osr.SpatialReference() + srs.ImportFromEPSG(crs) + WKT = srs.ExportToWkt() + # get count of 'UNIT' + if WKT.count('UNIT') == 1: + # simple geo format + unit = WKT.split("UNIT[")[1].split(",")[0] + elif WKT.count('UNIT') == 2: + # complex geo format, return the second instance of 'UNIT' + unit = WKT.split("UNIT[")[2].split(",")[0] + else: + print("Unknown units in {}".format(vector_file)) + return + + return unit + + +def raster_get_projection_unit(image): + """Get the projection unit for a vector_file. + + Arguments + --------- + image : raster image, GeoTIFF or other format + A raster file with georeferencing + + Notes + ----- + If raster is already in UTM coords, the projection WKT is complex: + https://www.spatialreference.org/ref/epsg/wgs-84-utm-zone-11n/html/ + In this case, return the second instance of 'UNIT'. + + Returns + ------- + unit : String + The unit i.e. meters or degrees, of the projection + """ + c = _check_rasterio_im_load(image) + crs = _check_crs(c.crs) + srs = osr.SpatialReference() + srs.ImportFromEPSG(crs) + WKT = srs.ExportToWkt() + # get count of 'UNIT' + if WKT.count('UNIT') == 1: + # simple geo format + unit = WKT.split("UNIT[")[1].split(",")[0] + elif WKT.count('UNIT') == 2: + # complex geo format, return the second instance of 'UNIT' + unit = WKT.split("UNIT[")[2].split(",")[0] + else: + print("Unknown units in {}".format(image)) + return + return unit + + def list_to_affine(xform_mat): """Create an Affine from a list or array-formatted [a, b, d, e, xoff, yoff] @@ -33,6 +422,13 @@ def list_to_affine(xform_mat): return Affine(*xform_mat) +def affine_to_list(affine_obj): + """Convert a :class:`affine.Affine` instance to a list for Shapely.""" + return [affine_obj.a, affine_obj.b, + affine_obj.d, affine_obj.e, + affine_obj.xoff, affine_obj.yoff] + + def geometries_internal_intersection(polygons): """Get the intersection geometries between all geometries in a set. @@ -206,263 +602,111 @@ def _reduce_geom_precision(geom, precision=2): return shape(geojson) -def _check_wkt_load(x): - """Check if an object is a loaded polygon or not. If not, load it.""" - if isinstance(x, str): - try: - x = loads(x) - except WKTReadingError: - warn('{} is not a WKT-formatted string.'.format(x)) - - return x - - - -# PRETEND THIS ISN'T HERE AT THE MOMENT -# class CoordTransformer(object): -# """A transformer class to change coordinate space using affine transforms. -# -# Notes -# ----- -# This class will take in an image or geometric object (Shapely or GDAL) -# and transform its coordinate space based on `dest_obj` . `dest_obj` -# should be an instance of :class:`rasterio.DatasetReader` . -# -# Arguments -# --------- -# src_obj -# A source image or geometric object to transform. The function will -# first try to extract georegistration information from this object -# if it exists; if it doesn't, it will assume unit (pixel) coords. -# dest_obj -# Object with a destination coordinate reference system to apply to -# `src_obj` . This can be in the form of an ``[a, b, d, e, xoff, yoff]`` -# `list` , an :class:`affine.Affine` instance, or a source -# :class:`geopandas.GeoDataFrame` or geotiff with `crs` metadata to -# produce the transform from, or even just a crs string. -# src_crs : optional -# Source coordinate reference in the form of a :class:`rasterio.crs.CRS` -# object or an epsg string. Only needed if the source object provided -# does not have CRS metadata attached to it. -# src_transform : :class:`affine.Affine` or :class:`list` -# The source affine transformation matrix as a :class:`affine.Affine` -# object or in an ``[a, b, c, d, xoff, yoff]`` `list`. Required if -# `src_obj` is a :class:`numpy.array` . -# dest_transform : :class:`affine.Affine` or :class:`list` -# The destination affine transformation matrix as a -# :class:`affine.Affine` object or in an ``[a, b, c, d, xoff, yoff]`` -# `list` . Required if `dest_obj` is a :class:`numpy.array` . -# """ -# def __init__(self, src_obj=None, dest_obj=None, src_crs=None, -# src_transform=None, dest_transform=None): -# self.src_obj = src_obj -# self.src_type = None -# self.dest_obj = dest_obj -# self.dest_type = None -# self.get_obj_types() # replaces the None values above -# self.src_crs = src_crs -# if isinstance(self.src_crs, dict): -# self.src_crs = self.src_crs['init'] -# if not self.src_crs: -# self.src_crs = self._get_crs(self.src_obj, self.src_type) -# self.dest_crs = self._get_crs(self.dest_obj, self.dest_type) -# self.src_transform = src_transform -# self.dest_transform = dest_transform -# -# def __repr__(self): -# print('CoordTransformer for {}'.format(self.src_obj)) -# -# def load_src_obj(self, src_obj, src_crs=None): -# """Load in a new source object for transformation.""" -# self.src_obj = src_obj -# self.src_type = None # replaced in self._get_src_crs() -# self.src_type = self._get_type(self.src_obj) -# self.src_crs = src_crs -# if self.src_crs is None: -# self.src_crs = self._get_crs(self.src_obj, self.src_type) -# -# def load_dest_obj(self, dest_obj): -# """Load in a new destination object for transformation.""" -# self.dest_obj = dest_obj -# self.dest_type = None -# self.dest_type = self._get_type(self.dest_obj) -# self.dest_crs = self._get_crs(self.dest_obj, self.dest_type) -# -# def load_src_crs(self, src_crs): -# """Load in a new source coordinate reference system.""" -# self.src_crs = self._get_crs(src_crs) -# -# def get_obj_types(self): -# if self.src_obj is not None: -# self.src_type = self._get_type(self.src_obj) -# if self.src_type is None: -# warn('The src_obj type is not compatible with this package.') -# if self.dest_obj is not None: -# self.dest_type = self._get_type(self.dest_obj) -# if self.dest_type is None: -# warn('The dest_obj type is not compatible with this package.') -# elif self.dest_type == 'shapely Geometry': -# warn('Shapely geometries cannot provide a destination CRS.') -# -# @staticmethod -# def _get_crs(obj, obj_type): -# """Get the destination coordinate reference system.""" -# # get the affine transformation out of dest_obj -# if obj_type == "transform matrix": -# return Affine(obj) -# elif obj_type == 'Affine': -# return obj -# elif obj_type == 'GeoTIFF': -# return rasterio.open(obj).crs -# elif obj_type == 'GeoDataFrame': -# if isinstance(obj, str): # if it's a path to a gdf -# return gpd.read_file(obj).crs -# else: # assume it's a GeoDataFrame object -# return obj.crs -# elif obj_type == 'epsg string': -# if obj.startswith('{init'): -# return rasterio.crs.CRS.from_string( -# obj.lstrip('{init: ').rstrip('}')) -# elif obj.lower().startswith('epsg'): -# return rasterio.crs.CRS.from_string(obj) -# elif obj_type == 'OGR Geometry': -# return get_crs_from_ogr(obj) -# elif obj_type == 'shapely Geometry': -# raise TypeError('Cannot extract a coordinate system from a ' + -# 'shapely.Geometry') -# else: -# raise TypeError('Cannot extract CRS from this object type.') -# -# @staticmethod -# def _get_type(obj): -# if isinstance(obj, gpd.GeoDataFrame): -# return 'GeoDataFrame' -# elif isinstance(obj, str): -# if os.path.isfile(obj): -# if os.path.splitext(obj)[1].lower() in ['tif', 'tiff', -# 'geotiff']: -# return 'GeoTIFF' -# elif os.path.splitext(obj)[1] in ['csv', 'geojson']: -# # assume it can be loaded as a geodataframe -# return 'GeoDataFrame' -# else: # assume it's a crs string -# if obj.startswith('{init'): -# return "epsg string" -# elif obj.lower().startswith('epsg'): -# return "epsg string" -# else: -# raise ValueError('{} is not an accepted crs type.'.format( -# obj)) -# elif isinstance(obj, ogr.Geometry): -# # ugh. Try to get the EPSG code out. -# return 'OGR Geometry' -# elif isinstance(obj, shapely.Geometry): -# return "shapely Geometry" -# elif isinstance(obj, list): -# return "transform matrix" -# elif isinstance(obj, Affine): -# return "Affine transform" -# elif isinstance(obj, np.array): -# return "numpy array" -# else: -# return None -# -# def transform(self, output_loc): -# """Transform `src_obj` from `src_crs` to `dest_crs`. -# -# Arguments -# --------- -# output_loc : `str` or `var` -# Object or location to output transformed src_obj to. If it's a -# string, it's assumed to be a path. -# """ -# if not self.src_crs or not self.dest_crs: -# raise AttributeError('The source or destination CRS is missing.') -# if not self.src_obj: -# raise AttributeError('The source object to transform is missing.') -# if isinstance(output_loc, str): -# out_file = True -# if self.src_type == 'GeoTIFF': -# return rasterio.warp.reproject(rasterio.open(self.src_obj), -# output_loc, -# src_transform=self.src_transform, -# src_crs=self.src_crs, -# dst_trasnform=self.dest_transform, -# dst_crs=self.dest_crs, -# resampling=Resampling.bilinear) -# elif self.src_type == 'GeoDataFrame': -# if isinstance(self.src_obj, str): -# # load the gdf and transform it -# tmp_src = gpd.read_file(self.src_obj).to_crs(self.dest_crs) -# else: -# # just transform it -# tmp_src = self.src_obj.to_crs(self.dest_crs) -# if out_file: -# # save to file -# if output_loc.lower().endswith('json'): -# tmp_src.to_file(output_loc, driver="GeoJSON") -# else: -# tmp_src.to_file(output_loc) # ESRI shapefile -# return -# else: -# # assign to the variable and return -# output_loc = tmp_src -# return output_loc -# elif self.src_type == 'OGR Geometry': -# dest_sr = ogr.SpatialReference().ImportFromEPSG( -# int(self.dest_crs.lstrip('epsg'))) -# output_loc = self.src_obj.TransformTo(dest_sr) -# return output_loc -# elif self.src_type == 'shapely Geometry': -# if self.dest_type not in [ -# 'Affine transform', 'transform matrix' -# ] and not self.dest_transform: -# raise ValueError('Transforming shapely objects requires ' + -# 'an affine transformation matrix.') -# elif self.dest_type == 'Affine transform': -# output_loc = shapely.affinity.affine_transform( -# self.src_obj, [self.dest_obj.a, self.dest_obj.b, -# self.dest_obj.d, self.dest_obj.e, -# self.dest_obj.xoff, self.dest_obj.yoff] -# ) -# return output_loc -# elif self.dest_type == 'transform matrix': -# output_loc = shapely.affinity.affine_transform(self.src_obj, -# self.dest_obj) -# return output_loc -# else: -# if isinstance(self.dest_transform, Affine): -# xform_mat = [self.dest_transform.a, self.dest_transform.b, -# self.dest_transform.d, self.dest_transform.e, -# self.dest_transform.xoff, -# self.dest_transform.yoff] -# else: -# xform_mat = self.dest_transform -# output_loc = shapely.affinity.affine_transform(self.src_obj, -# xform_mat) -# return output_loc -# elif self.src_type == 'numpy array': -# return rasterio.warp.reproject( -# self.src_obj, output_loc, src_transform=self.src_transform, -# src_crs=self.src_crs, dst_transform=self.dest_transform, -# dst_crs=self.dest_crs) -# -# -# def get_crs_from_ogr(annoying_OGR_geometry): -# """Get a CRS from an :class:`osgeo.ogr.Geometry` object. -# -# Arguments -# --------- -# annoying_OGR_geometry: :class:`osgeo.ogr.Geometry` -# An OGR object which stores crs information in an annoying fashion. -# -# Returns -# ------- -# An extremely clear, easy to work with ``'epsg[number]'`` string. -# """ -# srs = annoying_OGR_geometry.GetSpatialReference() -# result_of_ID = srs.AutoIdentifyEPSG() # if success, returns 0 -# if result_of_ID == 0: -# return 'epsg:' + str(srs.GetAuthorityCode(None)) -# else: -# raise ValueError('Could not determine EPSG code.') +def latlon_to_utm_epsg(latitude, longitude, return_proj4=False): + """Get the WGS84 UTM EPSG code based on a latitude and longitude value. + + Arguments + --------- + latitude : numeric + The latitude value for the coordinate. + longitude : numeric + The longitude value for the coordinate. + return_proj4 : bool, optional + Should the proj4 string be returned as well as the EPSG code? Defaults + to no (``False``) + + Returns + ------- + epsg : int + The integer corresponding to the EPSG code for the relevant UTM zone + in WGS 84. + proj4 : str + The proj4 string for the CRS. Only returned if ``return_proj4=True``. + """ + zone_number, zone_letter = _latlon_to_utm_zone(latitude, longitude) + + if return_proj4: + if zone_letter == 'N': + direction_indicator = '+north' + elif zone_letter == 'S': + direction_indicator = '+south' + proj = "+proj=utm +zone={} {}".format(zone_number, + direction_indicator) + proj += " +ellps=WGS84 +datum=WGS84 +units=m +no_defs" + + if zone_letter == 'N': + epsg = 32600 + zone_number + elif zone_letter == 'S': + epsg = 32700 + zone_number + + return (epsg, proj) if return_proj4 else epsg + + +def _latlon_to_utm_zone(latitude, longitude, ns_only=True): + """Convert latitude and longitude to a UTM zone ID. + + This function modified from + `the python utm library `_. + + Arguments + --------- + latitude : numeric or :class:`numpy.ndarray` + The latitude value of a coordinate. + longitude : numeric or :class:`numpy.ndarray` + The longitude value of a coordinate. + ns_only : bool, optional + Should the full list of possible zone numbers be used or just the N/S + options? Defaults to N/S only (``True``). + + Returns + ------- + zone_number : int + The numeric portion of the UTM zone ID. + zone_letter : str + The string portion of the UTM zone ID. Note that by default this + function uses only the N/S designation rather than the full range of + possible letters. + """ + + # If the input is a numpy array, just use the first element + # User responsibility to make sure that all points are in one zone + if isinstance(latitude, np.ndarray): + latitude = latitude.flat[0] + if isinstance(longitude, np.ndarray): + longitude = longitude.flat[0] + + utm_val = None + + if 56 <= latitude < 64 and 3 <= longitude < 12: + utm_val = 32 + + elif 72 <= latitude <= 84 and longitude >= 0: + if longitude < 9: + utm_val = 31 + elif longitude < 21: + utm_val = 33 + elif longitude < 33: + utm_val = 35 + elif longitude < 42: + utm_val = 37 + + if latitude < 0: + zone_letter = "S" + else: + zone_letter = "N" + + if not -80 <= latitude <= 84: + warn('Warning: UTM projections not recommended for ' + 'latitude {}'.format(latitude)) + if utm_val is None: + utm_val = int((longitude + 180) / 6) + 1 + + return utm_val, zone_letter + + +def _get_coords(geom): + """Get coordinates from various shapely geometry types.""" + if isinstance(geom, Point) or isinstance(geom, LineString): + return geom.coords.xy + elif isinstance(geom, Polygon): + return geom.exterior.coords.xy diff --git a/solaris/utils/io.py b/solaris/utils/io.py index e1e4bdf8..584b8947 100644 --- a/solaris/utils/io.py +++ b/solaris/utils/io.py @@ -3,7 +3,8 @@ import skimage -def imread(path, rescale=False, rescale_min='auto', rescale_max='auto'): +def imread(path, make_8bit=False, rescale=False, + rescale_min='auto', rescale_max='auto'): """Read in an image file and rescale pixel values (if applicable). Note @@ -20,6 +21,8 @@ def imread(path, rescale=False, rescale_min='auto', rescale_max='auto'): --------- path : str Path to the image file to load. + make_8bit : bool, optional + Should the image be converted to an 8-bit format? Defaults to False. rescale : bool, optional Should pixel intensities be rescaled? Defaults to no (False). rescale_min : ``'auto'`` or :class:`int` or :class:`float` or :class:`list` @@ -65,10 +68,10 @@ def imread(path, rescale=False, rescale_min='auto', rescale_max='auto'): raise TypeError('The loaded image array is an unexpected dtype.') else: raise TypeError('The loaded image array is an unexpected dtype.') - - im_arr = preprocess_im_arr(im_arr, dtype, rescale=rescale, - rescale_min=rescale_min, - rescale_max=rescale_max) + if make_8bit: + im_arr = preprocess_im_arr(im_arr, dtype, rescale=rescale, + rescale_min=rescale_min, + rescale_max=rescale_max) return im_arr @@ -280,3 +283,23 @@ def rescale_arr(im_arr, im_format, rescale_min='auto', rescale_max='auto'): scale_factor/(rescale_max-rescale_min)) return im_arr + + +def _check_channel_order(im_arr, framework): + im_shape = im_arr.shape + if len(im_shape) == 3: # doesn't matter for 1-channel images + if im_shape[0] > im_shape[2] and framework in ['torch', 'pytorch']: + # in [Y, X, C], needs to be in [C, Y, X] + im_arr = np.moveaxis(im_arr, 2, 0) + elif im_shape[2] > im_shape[0] and framework == 'keras': + # in [C, Y, X], needs to be in [Y, X, C] + im_arr = np.moveaxis(im_arr, 0, 2) + elif len(im_shape) == 4: # for a whole minibatch + if im_shape[1] > im_shape[3] and framework in ['torch', 'pytorch']: + # in [Y, X, C], needs to be in [C, Y, X] + im_arr = np.moveaxis(im_arr, 3, 1) + elif im_shape[3] > im_shape[1] and framework == 'keras': + # in [C, Y, X], needs to be in [Y, X, C] + im_arr = np.moveaxis(im_arr, 1, 3) + + return im_arr diff --git a/solaris/utils/tile.py b/solaris/utils/tile.py index 34987c58..07bfce0d 100644 --- a/solaris/utils/tile.py +++ b/solaris/utils/tile.py @@ -1,654 +1,155 @@ -from rasterio import Affine -from rasterio import features import geopandas as gpd -import numpy as np -from shapely.geometry import box -import rasterio +import json +from affine import Affine +from rasterio.windows import Window from rasterio.vrt import WarpedVRT from rasterio.enums import Resampling -from rasterio.io import DatasetReader -from rasterio.warp import transform_bounds -from rio_tiler.errors import RioTilerError -from rasterio import transform -import osr -from solaris.utils.core import _check_gdf_load -from solaris.utils.core import _check_rasterio_im_load - - -def utm_getZone(longitude): - """Calculate UTM Zone from Longitude. - - Arguments - --------- - longitude: float - longitude coordinate (Degrees.decimal degrees) - - Returns - ------- - out: int - UTM Zone number. - - """ - - return (int(1+(longitude+180.0)/6.0)) - - -def utm_isNorthern(latitude): - """Determine if a latitude coordinate is in the northern hemisphere. - - Arguments - --------- - latitude: float - latitude coordinate (Deg.decimal degrees) - - Returns - ------- - out: bool - ``True`` if `latitude` is in the northern hemisphere, ``False`` - otherwise. - - """ - - return (latitude > 0.0) - - -def calculate_UTM_crs(coords): - """Calculate UTM Projection String. - - Arguments - --------- - coords: list - ``[longitude, latitude]`` or - ``[min_longitude, min_latitude, max_longitude, max_latitude]`` . - - Returns - ------- - out: str - returns `proj4 projection string `__ - - """ - - if len(coords) == 2: - longitude, latitude = coords - elif len(coords) == 4: - longitude = np.mean([coords[0], coords[2]]) - latitude = np.mean([coords[1], coords[3]]) - - utm_zone = utm_getZone(longitude) - - utm_isNorthern(latitude) - - if utm_isNorthern(latitude): - direction_indicator = "+north" - else: - direction_indicator = "+south" - - utm_crs = "+proj=utm +zone={} {} +ellps=WGS84 +datum=WGS84 +units=m +no_defs".format(utm_zone, - direction_indicator) - - return utm_crs - - -def get_utm_vrt(source, crs='EPSG:3857', resampling=Resampling.bilinear, - src_nodata=None, dst_nodata=None): - """Get a :py:class:`rasterio.vrt.WarpedVRT` projection of a dataset. - - Arguments - --------- - source : :py:class:`rasterio.io.DatasetReader` - The dataset to virtually warp using :py:class:`rasterio.vrt.WarpedVRT`. - crs : :py:class:`rasterio.crs.CRS`, optional - Coordinate reference system for the VRT. Defaults to 'EPSG:3857' - (Web Mercator). - resampling : :py:class:`rasterio.enums.Resampling` method, optional - Resampling method to use. Defaults to - :py:func:`rasterio.enums.Resampling.bilinear`. Alternatives include - :py:func:`rasterio.enums.Resampling.average`, - :py:func:`rasterio.enums.Resampling.cubic`, and others. See docs for - :py:class:`rasterio.enums.Resampling` for more information. - src_nodata : int or float, optional - Source nodata value which will be ignored for interpolation. Defaults - to ``None`` (all data used in interpolation). - dst_nodata : int or float, optional - Destination nodata value which will be ignored for interpolation. - Defaults to ``None``, in which case the value of `src_nodata` will be - used if provided, or ``0`` otherwise. - - Returns - ------- - A :py:class:`rasterio.vrt.WarpedVRT` instance with the transformation. - - """ - - vrt_params = dict( - crs=crs, - resampling=Resampling.bilinear, - src_nodata=src_nodata, - dst_nodata=dst_nodata) - - return WarpedVRT(source, **vrt_params) - - -def get_utm_vrt_profile(source, crs='EPSG:3857', - resampling=Resampling.bilinear, - src_nodata=None, dst_nodata=None): - """Get a :py:class:`rasterio.profiles.Profile` for projection of a VRT. - - Arguments - --------- - source : :py:class:`rasterio.io.DatasetReader` - The dataset to virtually warp using :py:class:`rasterio.vrt.WarpedVRT`. - crs : :py:class:`rasterio.crs.CRS`, optional - Coordinate reference system for the VRT. Defaults to ``"EPSG:3857"`` - (Web Mercator). - resampling : :py:class:`rasterio.enums.Resampling` method, optional - Resampling method to use. Defaults to - ``rasterio.enums.Resampling.bilinear``. Alternatives include - ``rasterio.enums.Resampling.average``, - ``rasterio.enums.Resampling.cubic``, and others. See docs for - :py:class:`rasterio.enums.Resampling` for more information. - src_nodata : int or float, optional - Source nodata value which will be ignored for interpolation. Defaults - to ``None`` (all data used in interpolation). - dst_nodata : int or float, optional - Destination nodata value which will be ignored for interpolation. - Defaults to ``None``, in which case the value of `src_nodata` - will be used if provided, or ``0`` otherwise. - - Returns - ------- - A :py:class:`rasterio.profiles.Profile` instance with the transformation - applied. - +from rio_tiler.utils import get_vrt_transform, has_alpha_band +from rio_tiler.utils import _requested_tile_aligned_with_internal_tile + + +def save_empty_geojson(path, crs): + empty_geojson_dict = { + "type": "FeatureCollection", + "crs": + { + "type": "name", + "properties": + { + "name": "urn:ogc:def:crs:EPSG:{}".format(crs) + } + }, + "features": + [] + } + + with open(path, 'w') as f: + json.dump(empty_geojson_dict, f) + f.close() + + +def read_cog_tile(src, + bounds, + tile_size, + indexes=None, + nodata=None, + resampling_method="bilinear", + tile_edge_padding=2): + """ + Read cloud-optimized geotiff tile. + + Notes + ----- + Modified from `rio-tiler `_. + License included below per terms of use. + + BSD 3-Clause License + (c) 2017 Mapbox + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Arguments + --------- + src : rasterio.io.DatasetReader + rasterio.io.DatasetReader object + bounds : list + Tile bounds (left, bottom, right, top) + tile_size : list + Output image size + indexes : list of ints or a single int, optional, (defaults: None) + If `indexes` is a list, the result is a 3D array, but is + a 2D array if it is a band index number. + nodata: int or float, optional (defaults: None) + resampling_method : str, optional (default: "bilinear") + Resampling algorithm + tile_edge_padding : int, optional (default: 2) + Padding to apply to each edge of the tile when retrieving data + to assist in reducing resampling artefacts along edges. + + Returns + ------- + out : array, int + returns pixel value. """ - - with get_utm_vrt(source, crs=crs, resampling=resampling, - src_nodata=src_nodata, dst_nodata=dst_nodata) as vrt: - vrt_profile = vrt.profile - - return vrt_profile - - -def tile_read_utm(source, bounds, tilesize, indexes=[1], nodata=None, - alpha=None, dst_crs='EPSG:3857', verbose=False, - boundless=False): - """Read data and mask. - - Arguments - --------- - source : str or :py:class:`rasterio.io.DatasetReader` - input file path or :py:class:`rasterio.io.DatasetReader` object. - bounds : ``(W, S, E, N)`` tuple - bounds in `dst_crs` . - tilesize : int - Length of one edge of the output tile in pixels. - indexes : list of ints or int, optional - Channel index(es) to output. Returns a 3D :py:class:`np.ndarray` of - shape (C, Y, X) if `indexes` is a list, or a 2D array if `indexes` is - an int channel index. Defaults to ``1``. - nodata: int or float, optional - nodata value to use in :py:class:`rasterio.vrt.WarpedVRT`. - Defaults to ``None`` (use all data in warping). - alpha: int, optional - Force alphaband if not present in the dataset metadata. Defaults to - ``None`` (don't force). - dst_crs: str, optional - Destination coordinate reference system. Defaults to ``"EPSG:3857"`` - (Web Mercator) - verbose : bool, optional - Verbose text output. Defaults to ``False``. - boundless : bool, optional - This argument is deprecated and should never be used. - - Returns - ------- - data : :py:class:`np.ndarray` - int pixel values. Shape is ``(C, Y, X)`` if retrieving multiple - channels, ``(Y, X)`` otherwise. - mask : :py:class:`np.ndarray` - int mask indicating which pixels contain information and which are - `nodata`. Pixels containing data have value ``255``, `nodata` - pixels have value ``0``. - window : :py:class:`rasterio.windows.Window` - :py:class:`rasterio.windows.Window` object indicating the raster - location of the dataset subregion being returned in `data`. - window_transform : :py:class:`affine.Affine` - Affine transformation for `window` . - - - - """ - w, s, e, n = bounds - if alpha is not None and nodata is not None: - raise RioTilerError('cannot pass alpha and nodata option') - if isinstance(indexes, int): indexes = [indexes] - out_shape = (len(indexes), tilesize, tilesize) - if verbose: - print(dst_crs) - vrt_params = dict(crs=dst_crs, resampling=Resampling.bilinear, - src_nodata=nodata, dst_nodata=nodata) - - if not isinstance(source, DatasetReader): - src = rasterio.open(source) - else: - src = source - with WarpedVRT(src, **vrt_params) as vrt: - window = vrt.window(w, s, e, n, precision=21) - if verbose: - print(window) - window_transform = transform.from_bounds(w, s, e, n, - tilesize, tilesize) - - data = vrt.read(window=window, - resampling=Resampling.bilinear, - out_shape=out_shape, - indexes=indexes) - if verbose: - print(bounds) - print(window) - print(out_shape) - print(indexes) - print(boundless) - print(window_transform) - - if nodata is not None: - mask = np.all(data != nodata, axis=0).astype(np.uint8) * 255 - elif alpha is not None: - mask = vrt.read(alpha, window=window, - out_shape=(tilesize, tilesize), - resampling=Resampling.bilinear) - else: - mask = vrt.read_masks(1, window=window, - out_shape=(tilesize, tilesize), - resampling=Resampling.bilinear) - return data, mask, window, window_transform - - -def tile_exists_utm(boundsSrc, boundsTile): - """Check if suggested tile is within bounds. - - Arguments - --------- - boundsSrc : list-like - Bounding box limits for the source data in the shape ``(W, S, E, N)``. - boundsTile : list-like - Bounding box limits for the target tile in the shape ``(W, S, E, N)``. - - Returns - ------- - bool - Do the `boundsSrc` and `boundsTile` bounding boxes overlap? - - """ - - boundsSrcBox = box(*boundsSrc) - boundsTileBox = box(*boundsTile) - - return boundsSrcBox.intersects(boundsTileBox) - - -def get_wgs84_bounds(source): - """Transform dataset bounds from source crs to wgs84. - - Arguments - --------- - source : str or :py:class:`rasterio.io.DatasetReader` - Source dataset to get bounds transformation for. Can either be a string - path to a dataset file or an opened - :py:class:`rasterio.io.DatasetReader`. - - Returns - ------- - wgs_bounds : tuple - Bounds tuple for `source` in wgs84 crs with shape ``(W, S, E, N)``. - - """ - if isinstance(source, DatasetReader): - src = source - else: - src = rasterio.open(source) - wgs_bounds = transform_bounds(*[src.crs, 'epsg:4326'] + - list(src.bounds), densify_pts=21) - return wgs_bounds - - -def get_utm_bounds(source, utm_EPSG): - """Transform bounds from source crs to a UTM crs. - - Arguments - --------- - source : str or :py:class:`rasterio.io.DatasetReader` - Source dataset. Can either be a string path to a dataset GeoTIFF or - a :py:class:`rasterio.io.DatasetReader` object. - utm_EPSG : str - :py:class:`rasterio.crs.CRS` string indicating the UTM crs to transform - into. + elif isinstance(indexes, tuple): + indexes = list(indexes) - Returns - ------- - utm_bounds : tuple - Bounding box limits in `utm_EPSG` crs coordinates with shape - ``(W, S, E, N)``. - - """ - if isinstance(source, DatasetReader): - src = source - else: - src = rasterio.open(source) - utm_bounds = transform_bounds(*[src.crs, utm_EPSG] + list(src.bounds), - densify_pts=21) - return utm_bounds - - -# Note, for mac osx compatability import something from shapely.geometry before -# importing fiona or geopandas: https://github.com/Toblerity/Shapely/issues/553 - - -def read_vector_file(geoFileName): - """Read Fiona-Supported Files into GeoPandas GeoDataFrame. - - Warning - ---- - This will raise an exception for empty GeoJSON files, which GDAL and Fiona - cannot read. ``try/except`` the :py:exc:`Fiona.errors.DriverError` or - :py:exc:`Fiona._err.CPLE_OpenFailedError` if you must use this. - - """ - - return gpd.read_file(geoFileName) - - -def transformToUTM(gdf, utm_crs, estimate=True, calculate_sindex=True): - """Transform GeoDataFrame to UTM coordinate reference system. - - Arguments - --------- - gdf : :py:class:`geopandas.GeoDataFrame` - :py:class:`geopandas.GeoDataFrame` to transform. - utm_crs : str - :py:class:`rasterio.crs.CRS` string for destination UTM CRS. - estimate : bool, optional - .. deprecated:: 0.2.0 - This argument is no longer used. - calculate_sindex : bool, optional - .. deprecated:: 0.2.0 - This argument is no longer used. - - Returns - ------- - gdf : :py:class:`geopandas.GeoDataFrame` - The input :py:class:`geopandas.GeoDataFrame` converted to - `utm_crs` coordinate reference system. - - """ - - gdf = gdf.to_crs(utm_crs) - return gdf - - -def search_gdf_bounds(gdf, tile_bounds): - """Use `tile_bounds` to subset `gdf` and return the intersect. - - Arguments - --------- - gdf : :py:class:`geopandas.GeoDataFrame` - A :py:class:`geopandas.GeoDataFrame` of polygons to subset. - tile_bounds : tuple - A tuple of shape ``(W, S, E, N)`` that denotes the boundaries of a - tile. - - Returns - ------- - smallGdf : :py:class:`geopandas.GeoDataFrame` - The subset of `gdf` that overlaps with `tile_bounds` . - - """ - - tile_polygon = box(tile_bounds) - smallGdf = search_gdf_polygon(gdf, tile_polygon) - - return smallGdf - - -def search_gdf_polygon(gdf, tile_polygon): - """Find polygons in a GeoDataFrame that overlap with `tile_polygon` . - - Arguments - --------- - gdf : :py:class:`geopandas.GeoDataFrame` - A :py:class:`geopandas.GeoDataFrame` of polygons to search. - tile_polygon : :py:class:`shapely.geometry.Polygon` - A :py:class:`shapely.geometry.Polygon` denoting a tile's bounds. - - Returns - ------- - precise_matches : :py:class:`geopandas.GeoDataFrame` - The subset of `gdf` that overlaps with `tile_polygon` . If - there are no overlaps, this will return an empty - :py:class:`geopandas.GeoDataFrame`. - - """ - - sindex = gdf.sindex - possible_matches_index = list(sindex.intersection(tile_polygon.bounds)) - possible_matches = gdf.iloc[possible_matches_index] - precise_matches = possible_matches[ - possible_matches.intersects(tile_polygon) - ] - if precise_matches.empty: - precise_matches = gpd.GeoDataFrame(geometry=[]) - return precise_matches - - -def vector_tile_utm(gdf, tile_bounds, min_partial_perc=0.1, - geom_type="Polygon", use_sindex=True): - """Wrapper for :func:`clip_gdf` that converts `tile_bounds` to a polygon. - - Arguments - --------- - gdf : :class:`geopandas.GeoDataFrame` - A :py:class:`geopandas.GeoDataFrame` of polygons to clip. - tile_bounds : list-like of floats - :obj:`list` of shape ``(W, S, E, N)`` denoting the boundaries of an - imagery tile. Converted to a polygon for :func:`clip_gdf`. - min_partial_perc : float - The minimum fraction of an object in `gdf` that must be - preserved. Defaults to 0.0 (include any object if any part remains - following clipping). - use_sindex : bool, optional - Use the `gdf` sindex be used for searching. Improves efficiency - but requires `libspatialindex `__ . - - Returns - ------- - small_gdf : :py:class:`geopandas.GeoDataFrame` - `gdf` with all contained objects clipped to `tile_bounds`. - See notes above for details on additional clipping columns added. - """ - tile_polygon = box(*tile_bounds) - small_gdf = clip_gdf(gdf, tile_polygon, - min_partial_perc=min_partial_perc, - geom_type=geom_type - ) - - return small_gdf - - -def getCenterOfGeoFile(gdf, estimate=True): - - # TODO implement calculate UTM from gdf see osmnx implementation - - pass - - -def clip_gdf(gdf, poly_to_cut, min_partial_perc=0.0, geom_type="Polygon", - use_sindex=True): - """Clip GDF to a provided polygon. - - Note - ---- - Clips objects within `gdf` to the region defined by - `poly_to_cut`. Also adds several columns to the output: - - `origarea` - The original area of the polygons (only used if `geom_type` == - ``"Polygon"``). - `origlen` - The original length of the objects (only used if `geom_type` == - ``"LineString"``). - `partialDec` - The fraction of the object that remains after clipping - (fraction of area for Polygons, fraction of length for - LineStrings.) Can filter based on this by using `min_partial_perc`. - `truncated` - Boolean indicator of whether or not an object was clipped. - - Arguments - --------- - gdf : :py:class:`geopandas.GeoDataFrame` - A :py:class:`geopandas.GeoDataFrame` of polygons to clip. - poly_to_cut : :py:class:`shapely.geometry.Polygon` - The polygon to clip objects in `gdf` to. - min_partial_perc : float, optional - The minimum fraction of an object in `gdf` that must be - preserved. Defaults to 0.0 (include any object if any part remains - following clipping). - geom_type : str, optional - Type of objects in `gdf`. Can be one of - ``["Polygon", "LineString"]`` . Defaults to ``"Polygon"`` . - use_sindex : bool, optional - Use the `gdf` sindex be used for searching. Improves efficiency - but requires `libspatialindex `__ . - - Returns - ------- - cutGeoDF : :py:class:`geopandas.GeoDataFrame` - `gdf` with all contained objects clipped to `poly_to_cut` . - See notes above for details on additional clipping columns added. - - """ - - # check if geoDF has origAreaField - - if use_sindex: - gdf = search_gdf_polygon(gdf, poly_to_cut) - - # if geom_type == "LineString": - if 'origarea' in gdf.columns: - pass - else: - if "geom_type" == "LineString": - gdf['origarea'] = 0 - else: - gdf['origarea'] = gdf.area - if 'origlen' in gdf.columns: - pass - else: - if "geom_type" == "LineString": - gdf['origlen'] = gdf.length - else: - gdf['origlen'] = 0 - # TODO must implement different case for lines and for spatialIndex - # (Assume RTree is already performed) - - cutGeoDF = gdf.copy() - cutGeoDF.geometry = gdf.intersection(poly_to_cut) - - if geom_type == 'Polygon': - cutGeoDF['partialDec'] = cutGeoDF.area / cutGeoDF['origarea'] - cutGeoDF = cutGeoDF.loc[cutGeoDF['partialDec'] > min_partial_perc, :] - cutGeoDF['truncated'] = (cutGeoDF['partialDec'] != 1.0).astype(int) - else: - cutGeoDF = cutGeoDF[cutGeoDF.geom_type != "GeometryCollection"] - cutGeoDF['partialDec'] = 1 - cutGeoDF['truncated'] = 0 - # TODO: IMPLEMENT TRUNCATION MEASUREMENT FOR LINESTRINGS - - return cutGeoDF - - -def rasterize_gdf(gdf, src_shape, burn_value=1, - src_transform=Affine(1.0, 0.0, 0.0, 0.0, 1.0, 0.0)): - """Convert a GeoDataFrame to a binary image (array) mask. - - Uses :py:func:`rasterio.features.rasterize` to generate a raster mask from - object geometries in `gdf` . - - Arguments - --------- - gdf : :py:class:`geopandas.GeoDataFrame` - A :py:class:`geopandas.GeoDataFrame` of objects to convert into a mask. - src_shape : list-like of 2 ints - Shape of the output array in ``(Y, X)`` pixel units. - burn_value : int in range(0, 255), optional - Integer value for pixels corresponding to objects from `gdf` . - Defaults to 1. - src_transform : :py:class:`affine.Affine`, optional - Affine transformation for the output raster. If not provided, defaults - to arbitrary pixel units. - - Returns - ------- - img : :class:`np.ndarray`, dtype ``uint8`` - A NumPy array of integers with 0s where no pixels from objects in - `gdf` exist, and `burn_value` where they do. Shape is - defined by `src_shape`. - """ - if not gdf.empty: - img = features.rasterize( - ((geom, burn_value) for geom in gdf.geometry), - out_shape=src_shape, - transform=src_transform + vrt_params = dict( + add_alpha=True, crs='epsg:' + str(src.crs.to_epsg()), + resampling=Resampling[resampling_method] + ) + + vrt_transform, vrt_width, vrt_height = get_vrt_transform( + src, bounds, bounds_crs='epsg:' + str(src.crs.to_epsg())) + out_window = Window(col_off=0, row_off=0, + width=vrt_width, height=vrt_height) + + if tile_edge_padding > 0 and not \ + _requested_tile_aligned_with_internal_tile(src, bounds, tile_size): + vrt_transform = vrt_transform * Affine.translation( + -tile_edge_padding, -tile_edge_padding + ) + orig__vrt_height = vrt_height + orig_vrt_width = vrt_width + vrt_height = vrt_height + 2 * tile_edge_padding + vrt_width = vrt_width + 2 * tile_edge_padding + out_window = Window( + col_off=tile_edge_padding, + row_off=tile_edge_padding, + width=orig_vrt_width, + height=orig__vrt_height, ) - else: - img = np.zeros(src_shape).astype(np.uint8) - - return img - - -def vector_gdf_get_projection_unit(vector_file): - """Get the projection unit for a vector_file or gdf. - Arguments - --------- - vector_file : :py:class:`geopandas.GeoDataFrame` or geojson/shapefile - A vector file or gdf with georeferencing + vrt_params.update(dict(transform=vrt_transform, + width=vrt_width, + height=vrt_height)) - Returns - ------- - unit : String - The unit i.e. meters or degrees, of the projection - """ - c = _check_gdf_load(vector_file) - crs = c.crs - srs = osr.SpatialReference() - x = (crs['init']).split(":")[1] - srs.ImportFromEPSG(int(x)) - WKT = srs.ExportToWkt() - unit = WKT.split("UNIT[")[1].split(",")[0] - return unit + indexes = indexes if indexes is not None else src.indexes + out_shape = (len(indexes), tile_size[1], tile_size[0]) + nodata = nodata if nodata is not None else src.nodata + if nodata is not None: + vrt_params.update(dict(nodata=nodata, + add_alpha=False, + src_nodata=nodata)) -def raster_get_projection_unit(image): - """Get the projection unit for a vector_file. + if has_alpha_band(src): + vrt_params.update(dict(add_alpha=False)) - Arguments - --------- - image : raster image, GeoTIFF or other format - A raster file with georeferencing + with WarpedVRT(src, **vrt_params) as vrt: + data = vrt.read( + out_shape=out_shape, + indexes=indexes, + window=out_window, + resampling=Resampling[resampling_method], + ) + mask = vrt.dataset_mask(out_shape=(tile_size[1], tile_size[0]), + window=out_window) - Returns - ------- - unit : String - The unit i.e. meters or degrees, of the projection - """ - c = _check_rasterio_im_load(image) - crs = c.crs - srs = osr.SpatialReference() - x = (crs['init']).split(":")[1] - srs.ImportFromEPSG(int(x)) - WKT = srs.ExportToWkt() - unit = WKT.split("UNIT[")[1].split(",")[0] - return unit + return data, mask, out_window, vrt_transform diff --git a/solaris/vector/graph.py b/solaris/vector/graph.py index 3ed493f8..051f1a26 100644 --- a/solaris/vector/graph.py +++ b/solaris/vector/graph.py @@ -5,6 +5,7 @@ import shapely from shapely.geometry import Point, LineString import networkx as nx +import rasterio as rio import fiona import pickle from multiprocessing import Pool @@ -449,10 +450,10 @@ def linestring_to_edges(linestring, node_gdf): return edges -def graph_to_geojson(G, output_path, encoding='utf-8', overwrite=False): +def graph_to_geojson(G, output_path, encoding='utf-8', overwrite=False, + verbose=False): """ Save graph to two geojsons: one containing nodes, the other edges. - Arguments --------- G : :class:`networkx.MultiDiGraph` @@ -466,31 +467,47 @@ def graph_to_geojson(G, output_path, encoding='utf-8', overwrite=False): overwrite : bool, optional Should files at ``output_path`` be overwritten? Defaults to no (``False``). + verbose : bool, optional + Switch to print relevant values. Defaults to no (``False``). Notes ----- This function is based on ``osmnx.save_load.save_graph_shapefile``, with tweaks to make it work with our graph objects. It will save two geojsons: a file containing all of the nodes and a file containing all of the edges. + When writing to geojson, must convert the coordinate reference system + (crs) to string if it's a dict, otherwise no crs will be appended to the + geojson. Returns ------- None - """ + # convert directed graph G to an undirected graph for saving as a shapefile G_to_save = G.copy().to_undirected() # create GeoDataFrame containing all of the nodes - gdf_nodes = gpd.GeoDataFrame( - {node: data for node, data in G_to_save.nodes(data=True)} - ) + nodes, data = zip(*G_to_save.nodes(data=True)) + gdf_nodes = gpd.GeoDataFrame(list(data), index=nodes) + + # get coordinate reference system + g_crs = G_to_save.graph['crs'] + if type(g_crs) == dict: + # convert from dict + g_crs = rio.crs.CRS.from_dict(g_crs) + gdf_nodes.crs = g_crs + if verbose: + print("crs:", g_crs) - gdf_nodes.crs = G_to_save.graph['crs'] gdf_nodes['geometry'] = gdf_nodes.apply( lambda row: Point(row['x'], row['y']), axis=1 ) - gdf_nodes = gdf_nodes.drop(columns=['x', 'y']) - gdf_nodes['node_idx'] = gdf_nodes['node_idx'].astype(np.int32) + gdf_nodes = gdf_nodes.drop(['x', 'y'], axis=1) + # gdf_nodes['node_idx'] = gdf_nodes['node_idx'].astype(np.int32) + + # # make everything but geometry column a string + # for col in [c for c in gdf_nodes.columns if not c == 'geometry']: + # gdf_nodes[col] = gdf_nodes[col].fillna('').map(make_str) # create GeoDataFrame containing all of the edges edges = [] @@ -505,7 +522,7 @@ def graph_to_geojson(G, output_path, encoding='utf-8', overwrite=False): edges.append(edge) gdf_edges = gpd.GeoDataFrame(edges) - gdf_edges.crs = G_to_save.graph['crs'] + gdf_edges.crs = g_crs for col in [c for c in gdf_nodes.columns if c != 'geometry']: gdf_nodes[col] = gdf_nodes[col].fillna('').apply(str) @@ -523,6 +540,7 @@ def graph_to_geojson(G, output_path, encoding='utf-8', overwrite=False): os.remove(edges_path) if os.path.exists(nodes_path): os.remove(nodes_path) + gdf_edges.to_file(edges_path, encoding=encoding, driver='GeoJSON') gdf_nodes.to_file(nodes_path, encoding=encoding, driver='GeoJSON') diff --git a/solaris/vector/mask.py b/solaris/vector/mask.py index 5b2d1c45..813de68b 100644 --- a/solaris/vector/mask.py +++ b/solaris/vector/mask.py @@ -1,7 +1,8 @@ -from ..utils.core import _check_df_load, _check_rasterio_im_load -from ..utils.core import _check_skimage_im_load -from ..utils.tile import vector_gdf_get_projection_unit, calculate_UTM_crs -from ..utils.geo import geometries_internal_intersection, _check_wkt_load +from ..utils.core import _check_df_load, _check_geom +from ..utils.core import _check_skimage_im_load, _check_rasterio_im_load +from ..utils.geo import gdf_get_projection_unit, reproject +from ..utils.geo import geometries_internal_intersection +from .polygon import georegister_px_df, geojson_to_px_gdf, affine_transform_gdf import numpy as np from shapely.geometry import shape import geopandas as gpd @@ -13,7 +14,7 @@ def df_to_px_mask(df, channels=['footprint'], out_file=None, reference_im=None, - geom_col='geometry', do_transform=False, affine_obj=None, + geom_col='geometry', do_transform=None, affine_obj=None, shape=(900, 900), out_type='int', burn_value=255, **kwargs): """Convert a dataframe of geometries to a pixel mask. @@ -50,7 +51,9 @@ def df_to_px_mask(df, channels=['footprint'], out_file=None, reference_im=None, The column containing geometries in `df`. Defaults to ``"geometry"``. do_transform : bool, optional Should the values in `df` be transformed from geospatial coordinates - to pixel coordinates? Defaults to no (``False``). If ``True``, either + to pixel coordinates? Defaults to ``None``, in which case the function + attempts to infer whether or not a transformation is required based on + the presence or absence of a CRS in `df`. If ``True``, either `reference_im` or `affine_obj` must be provided as a source for the the required affine transformation matrix. affine_obj : `list` or :class:`affine.Affine`, optional @@ -106,7 +109,8 @@ def df_to_px_mask(df, channels=['footprint'], out_file=None, reference_im=None, df=df, reference_im=reference_im, geom_col=geom_col, affine_obj=affine_obj, shape=shape, out_type=out_type, contact_spacing=kwargs.get('contact_spacing', 10), - burn_value=burn_value + burn_value=burn_value, + meters=kwargs.get('meters', False) ) output_arr = np.stack([mask_dict[c] for c in channels], axis=-1) @@ -126,7 +130,7 @@ def df_to_px_mask(df, channels=['footprint'], out_file=None, reference_im=None, def footprint_mask(df, out_file=None, reference_im=None, geom_col='geometry', - do_transform=False, affine_obj=None, shape=(900, 900), + do_transform=None, affine_obj=None, shape=(900, 900), out_type='int', burn_value=255, burn_field=None): """Convert a dataframe of geometries to a pixel mask. @@ -149,15 +153,16 @@ def footprint_mask(df, out_file=None, reference_im=None, geom_col='geometry', The column containing geometries in `df`. Defaults to ``"geometry"``. do_transform : bool, optional Should the values in `df` be transformed from geospatial coordinates - to pixel coordinates? Defaults to no (``False``). If ``True``, either + to pixel coordinates? Defaults to ``None``, in which case the function + attempts to infer whether or not a transformation is required based on + the presence or absence of a CRS in `df`. If ``True``, either `reference_im` or `affine_obj` must be provided as a source for the the required affine transformation matrix. affine_obj : `list` or :class:`affine.Affine`, optional Affine transformation to use to convert from geo coordinates to pixel space. Only provide this argument if `df` is a :class:`geopandas.GeoDataFrame` with coordinates in a georeferenced - coordinate space. Ignored if `reference_im` is provided or if - ``do_transform=False``. + coordinate space. Ignored if `reference_im` is provided. shape : tuple, optional An ``(x_size, y_size)`` tuple defining the pixel extent of the output mask. Ignored if `reference_im` is provided. @@ -177,13 +182,20 @@ def footprint_mask(df, out_file=None, reference_im=None, geom_col='geometry', pixels. `mask` dtype will coincide with `burn_value`. """ - # start with required checks and pre-population of values if out_file and not reference_im: raise ValueError( 'If saving output to file, `reference_im` must be provided.') df = _check_df_load(df) - df[geom_col] = df[geom_col].apply(_check_wkt_load) # load in geoms if wkt + + if len(df) == 0: + return np.zeros(shape=shape, dtype='uint8') + + if do_transform is None: + # determine whether or not transform should be done + do_transform = _check_do_transform(df, reference_im, affine_obj) + + df[geom_col] = df[geom_col].apply(_check_geom) # load in geoms if wkt if not do_transform: affine_obj = Affine(1, 0, 0, 0, 1, 0) # identity transform @@ -222,13 +234,16 @@ def boundary_mask(footprint_msk=None, out_file=None, reference_im=None, **kwargs): """Convert a dataframe of geometries to a pixel mask. - Notes - ----- + Note + ---- This function requires creation of a footprint mask before it can operate; therefore, if there is no footprint mask already present, it will create one. In that case, additional arguments for :func:`footprint_mask` (e.g. ``df``) must be passed. + By default, this function draws boundaries *within* the edges of objects. + To change this behavior, use the `boundary_type` argument. + Arguments --------- footprint_msk : :class:`numpy.array`, optional @@ -245,7 +260,7 @@ def boundary_mask(footprint_msk=None, out_file=None, reference_im=None, affine transformation matrix, the image extent, etc. If provided, `affine_obj` and `shape` are ignored boundary_width : int, optional - The width of the boundary to be created in pixels. Defaults to 3. + The width of the boundary to be created **in pixels.** Defaults to 3. boundary_type : ``"inner"`` or ``"outer"``, optional Where to draw the boundaries: within the object (``"inner"``) or outside of it (``"outer"``). Defaults to ``"inner"``. @@ -263,8 +278,6 @@ def boundary_mask(footprint_msk=None, out_file=None, reference_im=None, A pixel mask with 0s for non-object pixels and the same value as the footprint mask `burn_value` for the boundaries of each object. - Note: This function draws the boundaries within the edge of the object. - """ if out_file and not reference_im: raise ValueError( @@ -298,9 +311,10 @@ def boundary_mask(footprint_msk=None, out_file=None, reference_im=None, return output_arr -def contact_mask(df, out_file=None, reference_im=None, geom_col='geometry', - do_transform=False, affine_obj=None, shape=(900, 900), - out_type='int', contact_spacing=10, burn_value=255): +def contact_mask(df, contact_spacing=10, meters=False, out_file=None, + reference_im=None, geom_col='geometry', + do_transform=None, affine_obj=None, shape=(900, 900), + out_type='int', burn_value=255): """Create a pixel mask labeling closely juxtaposed objects. Notes @@ -315,6 +329,15 @@ def contact_mask(df, out_file=None, reference_im=None, geom_col='geometry', with a column containing geometries (identified by `geom_col`). If the geometries in `df` are not in pixel coordinates, then `affine` or `reference_im` must be passed to provide the transformation to convert. + contact_spacing : `int` or `float`, optional + The desired maximum distance between adjacent polygons to be labeled + as contact. Will be in pixel units unless ``meters=True`` is provided. + meters : bool, optional + Should `width` be defined in units of meters? Defaults to no + (``False``). If ``True`` and `df` is not in a CRS with metric units, + the function will attempt to transform to the relevant CRS using + ``df.to_crs()`` (if `df` is a :class:`geopandas.GeoDataFrame`) or + using the data provided in `reference_im` (if not). out_file : str, optional Path to an image file to save the output to. Must be compatible with :class:`rasterio.DatasetReader`. If provided, a `reference_im` must be @@ -327,7 +350,9 @@ def contact_mask(df, out_file=None, reference_im=None, geom_col='geometry', The column containing geometries in `df`. Defaults to ``"geometry"``. do_transform : bool, optional Should the values in `df` be transformed from geospatial coordinates - to pixel coordinates? Defaults to no (``False``). If ``True``, either + to pixel coordinates? Defaults to ``None``, in which case the function + attempts to infer whether or not a transformation is required based on + the presence or absence of a CRS in `df`. If ``True``, either `reference_im` or `affine_obj` must be provided as a source for the the required affine transformation matrix. affine_obj : `list` or :class:`affine.Affine`, optional @@ -339,25 +364,35 @@ def contact_mask(df, out_file=None, reference_im=None, geom_col='geometry', An ``(x_size, y_size)`` tuple defining the pixel extent of the output mask. Ignored if `reference_im` is provided. out_type : 'float' or 'int' - contact_spacing : `int` or `float`, optional - The desired maximum distance between adjacent polygons to be labeled - as contact. `contact_spacing` will be in the same units as `df` 's - geometries, not necessarily in pixel units. burn_value : `int` or `float`, optional The value to use for labeling objects in the mask. Defaults to 255 (the max value for ``uint8`` arrays). The mask array will be set to the same dtype as `burn_value`. + Returns + ------- + output_arr : :class:`numpy.array` + A pixel mask with `burn_value` at contact points between polygons. """ if out_file and not reference_im: raise ValueError( 'If saving output to file, `reference_im` must be provided.') df = _check_df_load(df) - df[geom_col] = df[geom_col].apply(_check_wkt_load) # load in geoms if wkt + + if len(df) == 0: + return np.zeros(shape=shape, dtype='uint8') + + if do_transform is None: + # determine whether or not transform should be done + do_transform = _check_do_transform(df, reference_im, affine_obj) + + df[geom_col] = df[geom_col].apply(_check_geom) # load in geoms if wkt if reference_im: reference_im = _check_rasterio_im_load(reference_im) - # grow geometries by half `contact_spacing` to find overlaps - buffered_geoms = df[geom_col].apply(lambda x: x.buffer(contact_spacing/2)) + buffered_geoms = buffer_df_geoms(df, contact_spacing/2., meters=meters, + reference_im=reference_im, + geom_col=geom_col, affine_obj=affine_obj) + buffered_geoms = buffered_geoms[geom_col] # create a single multipolygon that covers all of the intersections intersect_poly = geometries_internal_intersection(buffered_geoms) # create a small df containing the intersections to make a footprint from @@ -389,9 +424,231 @@ def contact_mask(df, out_file=None, reference_im=None, geom_col='geometry', return output_arr +def road_mask(df, width=4, meters=False, out_file=None, reference_im=None, + geom_col='geometry', do_transform=None, affine_obj=None, + shape=(900, 900), out_type='int', burn_value=255, + burn_field=None, min_background_value=None, verbose=False): + """Convert a dataframe of geometries to a pixel mask. + + Arguments + --------- + df : :class:`pandas.DataFrame` or :class:`geopandas.GeoDataFrame` + A :class:`pandas.DataFrame` or :class:`geopandas.GeoDataFrame` instance + with a column containing geometries (identified by `geom_col`). If the + geometries in `df` are not in pixel coordinates, then `affine` or + `reference_im` must be passed to provide the transformation to convert. + width : `float` or `int`, optional + The total width to make a road (i.e. twice x if using + road.buffer(x)). In pixel units unless `meters` is ``True``. + meters : bool, optional + Should `width` be defined in units of meters? Defaults to no + (``False``). If ``True`` and `df` is not in a CRS with metric units, + the function will attempt to transform to the relevant CRS using + ``df.to_crs()`` (if `df` is a :class:`geopandas.GeoDataFrame`) or + using the data provided in `reference_im` (if not). + out_file : str, optional + Path to an image file to save the output to. Must be compatible with + :class:`rasterio.DatasetReader`. If provided, a `reference_im` must be + provided (for metadata purposes). + reference_im : :class:`rasterio.DatasetReader` or `str`, optional + An image to extract necessary coordinate information from: the + affine transformation matrix, the image extent, etc. If provided, + `affine_obj` and `shape` are ignored. + geom_col : str, optional + The column containing geometries in `df`. Defaults to ``"geometry"``. + do_transform : bool, optional + Should the values in `df` be transformed from geospatial coordinates + to pixel coordinates? Defaults to ``None``, in which case the function + attempts to infer whether or not a transformation is required based on + the presence or absence of a CRS in `df`. If ``True``, either + `reference_im` or `affine_obj` must be provided as a source for the + the required affine transformation matrix. + affine_obj : `list` or :class:`affine.Affine`, optional + Affine transformation to use to convert from geo coordinates to pixel + space. Only provide this argument if `df` is a + :class:`geopandas.GeoDataFrame` with coordinates in a georeferenced + coordinate space. Ignored if `reference_im` is provided. + shape : tuple, optional + An ``(x_size, y_size)`` tuple defining the pixel extent of the output + mask. Ignored if `reference_im` is provided. + out_type : 'float' or 'int' + burn_value : `int` or `float`, optional + The value to use for labeling objects in the mask. Defaults to 255 (the + max value for ``uint8`` arrays). The mask array will be set to the same + dtype as `burn_value`. Ignored if `burn_field` is provided. + burn_field : str, optional + Name of a column in `df` that provides values for `burn_value` for each + independent object. If provided, `burn_value` is ignored. + min_background_val : int + Minimum value for mask background. Optional, ignore if ``None``. + Defaults to ``None``. + verbose : str, optional + Switch to print relevant values. Defaults to ``False``. + + Returns + ------- + mask : :class:`numpy.array` + A pixel mask with 0s for non-object pixels and `burn_value` at object + pixels. `mask` dtype will coincide with `burn_value`. + """ + + # start with required checks and pre-population of values + if out_file and not reference_im: + raise ValueError( + 'If saving output to file, `reference_im` must be provided.') + df = _check_df_load(df) + if do_transform is None: + # determine whether or not transform should be done + do_transform = _check_do_transform(df, reference_im, affine_obj) + df[geom_col] = df[geom_col].apply(_check_geom) # ensure WKTs are loaded + + buffered_df = buffer_df_geoms(df, width/2., meters=meters, + reference_im=reference_im, geom_col=geom_col, + affine_obj=affine_obj) + + if not do_transform: + affine_obj = Affine(1, 0, 0, 0, 1, 0) # identity transform + + if reference_im: + reference_im = _check_rasterio_im_load(reference_im) + shape = reference_im.shape + if do_transform: + affine_obj = reference_im.transform + + # extract geometries and pair them with burn values + if burn_field: + if out_type == 'int': + feature_list = list(zip(buffered_df[geom_col], + buffered_df[burn_field].astype('uint8'))) + else: + feature_list = list(zip(buffered_df[geom_col], + buffered_df[burn_field].astype('uint8'))) + else: + feature_list = list(zip(buffered_df[geom_col], + [burn_value] * len(buffered_df))) + + output_arr = features.rasterize(shapes=feature_list, out_shape=shape, + transform=affine_obj) + if min_background_value: + output_arr = np.clip(output_arr, min_background_value, + np.max(output_arr)) + + if out_file: + meta = reference_im.meta.copy() + meta.update(count=1) + if out_type == 'int': + meta.update(dtype='uint8') + with rasterio.open(out_file, 'w', **meta) as dst: + dst.write(output_arr, indexes=1) + + return output_arr + + +def buffer_df_geoms(df, buffer, meters=False, reference_im=None, + geom_col='geometry', affine_obj=None): + """Buffer geometries within a pd.DataFrame or gpd.GeoDataFrame. + + Arguments + --------- + df : :class:`pandas.DataFrame` or :class:`geopandas.GeoDataFrame` + A :class:`pandas.DataFrame` or :class:`geopandas.GeoDataFrame` instance + with a column containing geometries (identified by `geom_col`). If `df` + lacks a ``crs`` attribute (isn't a :class:`geopandas.GeoDataFrame` ) + and ``meters=True``, then `reference_im` must be provided for + georeferencing. + buffer : `int` or `float` + The amount to buffer the geometries in `df`. In pixel units unless + ``meters=True``. This corresponds to width/2 in mask creation + functions. + meters : bool, optional + Should buffers be in pixel units (default) or metric units (if `meters` + is ``True``)? + reference_im : `str` or :class:`rasterio.DatasetReader`, optional + The path to a reference image covering the same geographic extent as + the area labeled in `df`. Provided for georeferencing of pixel + coordinate geometries in `df` or conversion of georeferenced geometries + to pixel coordinates as needed. Required if `meters` is ``True`` and + `df` lacks a ``crs`` attribute. + geom_col : str, optional + The column containing geometries in `df`. Defaults to ``"geometry"``. + affine_obj : `list` or :class:`affine.Affine`, optional + Affine transformation to use to convert geoms in `df` from a geographic + crs to pixel space. Only provide this argument if `df` is a + :class:`geopandas.GeoDataFrame` with coordinates in a georeferenced + coordinate space. Ignored if `reference_im` is provided. + + Returns + ------- + buffered_df : :class:`pandas.DataFrame` + A :class:`pandas.DataFrame` in the original coordinate reference system + with objects buffered per `buffer`. + + See Also + -------- + road_mask : Function to create road network masks. + contact_mask : Function to create masks of contact points between polygons. + """ + if reference_im is not None: + reference_im = _check_rasterio_im_load(reference_im) + + if hasattr(df, 'crs'): + orig_crs = df.crs + else: + orig_crs = None # will represent pixel crs + + # Check if dataframe is in the appropriate units and reproject if not + if not meters: + if hasattr(df, 'crs') and reference_im is not None: + # if the df is georeferenced and a reference_im is provided, + # use reference_im to transform df to px coordinates + df_for_buffer = geojson_to_px_gdf(df.copy(), reference_im) + elif hasattr(df, 'crs') and reference_im is None: + df_for_buffer = affine_transform_gdf(df.copy(), + affine_obj=affine_obj) + else: # if it's already in px coordinates + df_for_buffer = df.copy() + + else: + # check if the df is in a metric crs + if hasattr(df, 'crs'): + if crs_is_metric(df): + df_for_buffer = df.copy() + else: + df_for_buffer = reproject(df.copy()) # defaults to UTM + else: + # assume df is in px coords - use reference_im to georegister + if reference_im is not None: + df_for_buffer = georegister_px_df(df.copy(), + im_path=reference_im) + else: + raise ValueError('If using `meters=True`, either `df` must be ' + 'a geopandas GeoDataFrame or `reference_im` ' + 'must be provided for georegistration.') + + df_for_buffer[geom_col] = df_for_buffer[geom_col].apply( + lambda x: x.buffer(buffer)) + + # return to original crs + if getattr(df_for_buffer, 'crs', None) != orig_crs: + if orig_crs is not None and \ + getattr(df_for_buffer, 'crs', None) is not None: + buffered_df = df_for_buffer.to_crs(orig_crs) + elif orig_crs is None: # but df_for_buffer has one: meters=True case + buffered_df = geojson_to_px_gdf(df_for_buffer, reference_im) + else: # orig_crs exists, but df_for_buffer doesn't have one + buffered_df = georegister_px_df(df_for_buffer, + im_path=reference_im, + affine_obj=affine_obj, + crs=orig_crs) + else: + buffered_df = df_for_buffer + + return buffered_df + + def mask_to_poly_geojson(mask_arr, reference_im=None, output_path=None, output_type='csv', min_area=40, bg_value=0, - do_transform=False, simplify=False, + do_transform=None, simplify=False, tolerance=0.5, **kwargs): """Get polygons from an image mask. @@ -468,113 +725,26 @@ def mask_to_poly_geojson(mask_arr, reference_im=None, output_path=None, return polygon_gdf -def road_mask(df, out_file=None, reference_im=None, geom_col='geometry', - do_transform=False, affine_obj=None, shape=(900, 900), buffer_in_m=2, - out_type='int', burn_value=255, burn_field=None): - """Convert a dataframe of geometries to a pixel mask. - Arguments - --------- - df : :class:`pandas.DataFrame` or :class:`geopandas.GeoDataFrame` - A :class:`pandas.DataFrame` or :class:`geopandas.GeoDataFrame` instance - with a column containing geometries (identified by `geom_col`). If the - geometries in `df` are not in pixel coordinates, then `affine` or - `reference_im` must be passed to provide the transformation to convert. - out_file : str, optional - Path to an image file to save the output to. Must be compatible with - :class:`rasterio.DatasetReader`. If provided, a `reference_im` must be - provided (for metadata purposes). - reference_im : :class:`rasterio.DatasetReader` or `str`, optional - An image to extract necessary coordinate information from: the - affine transformation matrix, the image extent, etc. If provided, - `affine_obj` and `shape` are ignored. - geom_col : str, optional - The column containing geometries in `df`. Defaults to ``"geometry"``. - do_transform : bool, optional - Should the values in `df` be transformed from geospatial coordinates - to pixel coordinates? Defaults to no (``False``). If ``True``, either - `reference_im` or `affine_obj` must be provided as a source for the - the required affine transformation matrix. - affine_obj : `list` or :class:`affine.Affine`, optional - Affine transformation to use to convert from geo coordinates to pixel - space. Only provide this argument if `df` is a - :class:`geopandas.GeoDataFrame` with coordinates in a georeferenced - coordinate space. Ignored if `reference_im` is provided or if - ``do_transform=False``. - shape : tuple, optional - An ``(x_size, y_size)`` tuple defining the pixel extent of the output - mask. Ignored if `reference_im` is provided. - buffer_in_m : 'float' or 'int' - The amount to buffer a roadway linestring on either side in meters. - For example a value == 2 will create a roadway 4 meters in total width - around the centerline. - out_type : 'float' or 'int' - burn_value : `int` or `float`, optional - The value to use for labeling objects in the mask. Defaults to 255 (the - max value for ``uint8`` arrays). The mask array will be set to the same - dtype as `burn_value`. Ignored if `burn_field` is provided. - burn_field : str, optional - Name of a column in `df` that provides values for `burn_value` for each - independent object. If provided, `burn_value` is ignored. - Returns - ------- - mask : :class:`numpy.array` - A pixel mask with 0s for non-object pixels and `burn_value` at object - pixels. `mask` dtype will coincide with `burn_value`. - """ - - # start with required checks and pre-population of values - if out_file and not reference_im: - raise ValueError( - 'If saving output to file, `reference_im` must be provided.') - df = _check_df_load(df) - df[geom_col] = df[geom_col].apply(_check_wkt_load) - - # Check if dataframe is in the appropriate units (meters, and reproject if not) - unit = vector_gdf_get_projection_unit(df) - if unit != "meter": - # Pick UTM zone - coords = [df[geom_col].bounds['minx'].min(), df[geom_col].bounds['miny'].min(), df[geom_col].bounds['maxx'].max(), df[geom_col].bounds['maxy'].max()] - crs = calculate_UTM_crs(coords) - # REPROJECT - orig_crs = df.crs - df = df.to_crs(crs) - df[geom_col] = df[geom_col].apply(lambda x: x.buffer(buffer_in_m)) - # Send back to old CRS - df = df.to_crs(orig_crs) +def crs_is_metric(gdf): + """Check if a GeoDataFrame's CRS is in metric units.""" + units = str(gdf_get_projection_unit(gdf)).strip().lower() + if units in ['"meter"', '"metre"', "'meter'", "'meter'", + 'meter', 'metre']: + return True else: - df[geom_col] = df[geom_col].apply(lambda x: x.buffer(buffer_in_m)) + return False - # load in geoms if wkt - if not do_transform: - affine_obj = Affine(1, 0, 0, 0, 1, 0) # identity transform - if reference_im: - reference_im = _check_rasterio_im_load(reference_im) - shape = reference_im.shape - if do_transform: - affine_obj = reference_im.transform - - # extract geometries and pair them with burn values - if burn_field: - if out_type == 'int': - feature_list = list(zip(df[geom_col], - df[burn_field].astype('uint8'))) - else: - feature_list = list(zip(df[geom_col], - df[burn_field].astype('uint8'))) - else: - feature_list = list(zip(df[geom_col], [burn_value] * len(df))) +def _check_do_transform(df, reference_im, affine_obj): + """Check whether or not a transformation should be performed.""" + try: + crs = getattr(df, 'crs') + except AttributeError: + return False # if it doesn't have a CRS attribute - output_arr = features.rasterize(shapes=feature_list, out_shape=shape, - transform=affine_obj) - - if out_file: - meta = reference_im.meta.copy() - meta.update(count=1) - if out_type == 'int': - meta.update(dtype='uint8') - with rasterio.open(out_file, 'w', **meta) as dst: - dst.write(output_arr, indexes=1) - - return output_arr + if not crs: + return False # return False for do_transform if crs is falsey + elif crs and (reference_im is not None or affine_obj is not None): + # if the input has a CRS and another obj was provided for xforming + return True diff --git a/solaris/vector/polygon.py b/solaris/vector/polygon.py index 85ed1ae6..01e0dca9 100644 --- a/solaris/vector/polygon.py +++ b/solaris/vector/polygon.py @@ -3,8 +3,9 @@ from affine import Affine import rasterio from rasterio.warp import transform_bounds +from rasterio.crs import CRS from ..utils.geo import list_to_affine, _reduce_geom_precision -from ..utils.core import _check_gdf_load +from ..utils.core import _check_gdf_load, _check_crs, _check_rasterio_im_load from ..raster.image import get_geo_transform from shapely.geometry import box, Polygon import pandas as pd @@ -129,6 +130,10 @@ def affine_transform_gdf(gdf, affine_obj, inverse=False, geom_col="geometry", if precision is not None: gdf['geometry'] = gdf['geometry'].apply( _reduce_geom_precision, precision=precision) + + # the CRS is no longer valid - remove it + gdf.crs = None + return gdf @@ -149,10 +154,10 @@ def georegister_px_df(df, im_path=None, affine_obj=None, crs=None, An affine transformation to apply to `geom` in the form of an ``[a, b, d, e, xoff, yoff]`` list or an :class:`affine.Affine` object. Required if not using `raster_src`. - crs : dict, optional - The coordinate reference system for the output GeoDataFrame. Required - if not providing a raster image to extract the information from. Format - should be ``{'init': 'epsgxxxx'}``, replacing xxxx with the EPSG code. + crs : int + The coordinate reference system for the output GeoDataFrame as an EPSG + code integer. Required if not providing a raster image to extract the + information from. geom_col : str, optional The column containing geometry in `df`. If not provided, defaults to ``"geometry"``. @@ -165,16 +170,17 @@ def georegister_px_df(df, im_path=None, affine_obj=None, crs=None, """ if im_path is not None: - affine_obj = rasterio.open(im_path).transform - crs = rasterio.open(im_path).crs + im = _check_rasterio_im_load(im_path) + affine_obj = im.transform + crs = im.crs else: if not affine_obj or not crs: - raise ValueError( - 'If an image path is not provided, ' + - 'affine_obj and crs must be.') + raise ValueError('If an image path is not provided, ' + 'affine_obj and crs must be.') + crs = _check_crs(crs) tmp_df = affine_transform_gdf(df, affine_obj, geom_col=geom_col, precision=precision) - result = gpd.GeoDataFrame(tmp_df, crs=crs) + result = gpd.GeoDataFrame(tmp_df, crs='epsg:' + str(crs)) if output_path is not None: if output_path.lower().endswith('json'): @@ -197,9 +203,7 @@ def geojson_to_px_gdf(geojson, im_path, geom_col='geometry', precision=None, column named ``'geometry'`` in this argument. im_path : str Path to a georeferenced image (ie a GeoTIFF) that geolocates to the - same geography as the `geojson`(s). If a directory, the bounds of each - GeoTIFF will be loaded in and all overlapping geometries will be - transformed. This function will also accept a + same geography as the `geojson`(s). This function will also accept a :class:`osgeo.gdal.Dataset` or :class:`rasterio.DatasetReader` with georeferencing information in this argument. geom_col : str, optional @@ -222,20 +226,14 @@ def geojson_to_px_gdf(geojson, im_path, geom_col='geometry', precision=None, """ # get the bbox and affine transforms for the image - if isinstance(im_path, str): - bbox = box(*rasterio.open(im_path).bounds) - affine_obj = rasterio.open(im_path).transform - im_crs = rasterio.open(im_path).crs - - else: - bbox = box(im_path.bounds) - affine_obj = im_path.transform - im_crs = im_path.crs - + im = _check_rasterio_im_load(im_path) + if isinstance(im_path, rasterio.DatasetReader): + im_path = im_path.name # make sure the geo vector data is loaded in as geodataframe(s) gdf = _check_gdf_load(geojson) + overlap_gdf = get_overlapping_subset(gdf, im) - overlap_gdf = get_overlapping_subset(gdf, bbox=bbox, bbox_crs=im_crs) + affine_obj = im.transform transformed_gdf = affine_transform_gdf(overlap_gdf, affine_obj=affine_obj, inverse=True, precision=precision, geom_col=geom_col) @@ -271,6 +269,10 @@ def get_overlapping_subset(gdf, im=None, bbox=None, bbox_crs=None): `bbox` is passed and `im` is not, a `bbox_crs` should be provided to ensure correct geolocation - if it isn't, it will be assumed to have the same crs as `gdf`. + bbox_crs : int, optional + The coordinate reference system that the bounding box is in as an EPSG + int. If not provided, it's assumed that the CRS is the same as `im` + (if provided) or `gdf` (if not). Returns ------- @@ -280,22 +282,27 @@ def get_overlapping_subset(gdf, im=None, bbox=None, bbox_crs=None): Coordinates are kept in the CRS of `gdf`. """ - if not im and not bbox: + if im is None and bbox is None: raise ValueError('Either `im` or `bbox` must be provided.') - if isinstance(gdf, str): - gdf = gpd.read_file(gdf) - if isinstance(im, str): - im = rasterio.open(im) + gdf = _check_gdf_load(gdf) sindex = gdf.sindex - # use transform_bounds in case the crs is different - no effect if not - if im: + if im is not None: + im = _check_rasterio_im_load(im) bbox = transform_bounds(im.crs, gdf.crs, *im.bounds) - else: - if isinstance(bbox, Polygon): - bbox = bbox.bounds - if not bbox_crs: + bbox_crs = im.crs + # use transform_bounds in case the crs is different - no effect if not + if isinstance(bbox, Polygon): + bbox = bbox.bounds + if bbox_crs is None: + try: bbox_crs = gdf.crs - bbox = transform_bounds(bbox_crs, gdf.crs, *bbox) + except AttributeError: + raise ValueError('If `im` and `bbox_crs` are not provided, `gdf`' + 'must provide a coordinate reference system.') + else: + bbox_crs = _check_crs(bbox_crs) + bbox_crs = CRS.from_epsg(bbox_crs) + bbox = transform_bounds(bbox_crs, gdf.crs, *bbox) try: intersectors = list(sindex.intersection(bbox)) except RTreeError: @@ -304,40 +311,49 @@ def get_overlapping_subset(gdf, im=None, bbox=None, bbox_crs=None): return gdf.iloc[intersectors, :] -def gdf_to_yolo(geodataframe, image, output_dir, column='single_id', im_size=(0, 0), min_overlap=0.66, remove_no_labels=1): - """Convert a geodataframe containing polygons to yolo/yolt readable format (.txt files). +def gdf_to_yolo(geodataframe, image, output_dir, column='single_id', + im_size=(0, 0), min_overlap=0.66, remove_no_labels=1): + """Convert a geodataframe containing polygons to yolo/yolt format. + Arguments --------- geodataframe : str - Path to a :class:`geopandas.GeoDataFrame` with a - column named ``'geometry'``. Can be created from a geojson with labels for unique objects. - Can be converted to this format with geodataframe=gpd.read_file("./xView_30.geojson") + Path to a :class:`geopandas.GeoDataFrame` with a column named + ``'geometry'``. Can be created from a geojson with labels for unique + objects. Can be converted to this format with + ``geodataframe=gpd.read_file("./xView_30.geojson")``. im_path : str - Path to a georeferenced image (ie a GeoTIFF or png created with GDAL) that geolocates to the - same geography as the `geojson`(s). If a directory, the bounds of each - GeoTIFF will be loaded in and all overlapping geometries will be - transformed. This function will also accept a - :class:`osgeo.gdal.Dataset` or :class:`rasterio.DatasetReader` with - georeferencing information in this argument. + Path to a georeferenced image (ie a GeoTIFF or png created with GDAL) + that geolocates to the same geography as the `geojson`(s). If a + directory, the bounds of each GeoTIFF will be loaded in and all + overlapping geometries will be transformed. This function will also + accept a :class:`osgeo.gdal.Dataset` or :class:`rasterio.DatasetReader` + with georeferencing information in this argument. output_dir : str - Path to an output directory where all of the yolo readable text files will be placed. - column : str - The column name that contians an unique integer id for each of object class. - im_size : tuple - A tuple specifying the x and y heighth of a an image. If specified as (0,0) this is automatically - extracted from the image. - min_overlap : float - A float value ranging from 0 to +1. This is a percantge. If a polygon does not overlap the image - by at least min_overlap, the polygon is discarded. i.e. 0.66 = 66%, at least 66% of the polygon must - intersect with the image or else it is discarded. Default value of 0.66. - remove_no_labels : int - An int value of 0 or 1. If 1, any image not containing any objects will be moved to a directory in the same root - path as your input image. If 0, no images will be moved. Default value of 1. + Path to an output directory where all of the yolo readable text files + will be placed. + column : str, optional + The column name that contians an unique integer id for each of object + class. + im_size : tuple, optional + A tuple specifying the x and y heighth of a an image. If specified as + ``(0,0)`` (the default,) then the size is determined automatically. + min_overlap : float, optional + A float value ranging from 0 to 1. This is a percantage. If a polygon + does not overlap the image by at least min_overlap, the polygon is + discarded. i.e. 0.66 = 66%. Default value of 0.66. + remove_no_labels : int, optional + An int value of 0 or 1. If 1, any image not containing any objects + will be moved to a directory in the same root path as your input image. + If 0, no images will be moved. Default value of 1. + Returns ------- - gdf : :class:`geopandas.GeoDataFrame`. The txt file will be written to the output_dir, however the the output gdf itself - can be returned from this function if required. + gdf : :class:`geopandas.GeoDataFrame`. + The txt file will be written to the output_dir, however the the output + gdf itself is returned. """ + if im_size == (0, 0): imsize_extract = rasterio.open(image).read() if len(imsize_extract.shape) == 3: diff --git a/tests/test_eval/baseeval_test.py b/tests/test_eval/evaluator_test.py similarity index 76% rename from tests/test_eval/baseeval_test.py rename to tests/test_eval/evaluator_test.py index bf0a2d24..fcb1ef39 100644 --- a/tests/test_eval/baseeval_test.py +++ b/tests/test_eval/evaluator_test.py @@ -1,15 +1,15 @@ import os -from solaris.eval.baseeval import EvalBase +from solaris.eval.base import Evaluator import solaris import geopandas as gpd import pandas as pd -class TestEvalBase(object): +class TestEvaluator(object): def test_init_from_file(self): - """Test instantiation of an EvalBase instance from a file.""" - base_instance = EvalBase(os.path.join(solaris.data.data_dir, - 'gt.geojson')) + """Test instantiation of an Evaluator instance from a file.""" + base_instance = Evaluator(os.path.join(solaris.data.data_dir, + 'gt.geojson')) gdf = solaris.data.gt_gdf() assert base_instance.ground_truth_sindex.bounds == gdf.sindex.bounds assert base_instance.proposal_GDF.equals(gpd.GeoDataFrame([])) @@ -17,18 +17,18 @@ def test_init_from_file(self): base_instance.ground_truth_GDF_Edit) def test_init_from_gdf(self): - """Test instantiation of an EvalBase from a pre-loaded GeoDataFrame.""" + """Test instantiation of an Evaluator from a pre-loaded GeoDataFrame.""" gdf = solaris.data.gt_gdf() - base_instance = EvalBase(gdf) + base_instance = Evaluator(gdf) assert base_instance.ground_truth_sindex.bounds == gdf.sindex.bounds assert base_instance.proposal_GDF.equals(gpd.GeoDataFrame([])) assert base_instance.ground_truth_GDF.equals( base_instance.ground_truth_GDF_Edit) def test_init_empty_geojson(self): - """Test instantiation of EvalBase with an empty geojson file.""" - base_instance = EvalBase(os.path.join(solaris.data.data_dir, - 'empty.geojson')) + """Test instantiation of Evaluator with an empty geojson file.""" + base_instance = Evaluator(os.path.join(solaris.data.data_dir, + 'empty.geojson')) expected_gdf = gpd.GeoDataFrame({'sindex': [], 'condition': [], 'geometry': []}) @@ -36,7 +36,7 @@ def test_init_empty_geojson(self): def test_score_proposals(self): """Test reading in a proposal GDF from a geojson and scoring it.""" - eb = EvalBase(os.path.join(solaris.data.data_dir, 'gt.geojson')) + eb = Evaluator(os.path.join(solaris.data.data_dir, 'gt.geojson')) eb.load_proposal(os.path.join(solaris.data.data_dir, 'pred.geojson')) pred_gdf = solaris.data.pred_gdf() assert eb.proposal_GDF.iloc[:, 0:3].sort_index().equals(pred_gdf) @@ -58,10 +58,11 @@ def test_iou_by_building(self): path_pred = os.path.join(data_folder, 'SN2_sample_preds.csv') path_ious = os.path.join(data_folder, 'SN2_sample_iou_by_building.csv') path_temp = './temp.pd' - eb = EvalBase(path_truth) + eb = Evaluator(path_truth) eb.load_proposal(path_pred, conf_field_list=['Confidence'], proposalCSV=True) - eb.eval_iou_spacenet_csv(miniou=0.5, imageIDField='ImageId', minArea=20) + eb.eval_iou_spacenet_csv(miniou=0.5, imageIDField='ImageId', + min_area=20) output = eb.get_iou_by_building() result_actual = pd.DataFrame(output) result_actual.sort_values(by=['ImageId', 'BuildingId'], inplace=True) @@ -69,7 +70,7 @@ def test_iou_by_building(self): result_expected = pd.read_csv(path_ious, index_col=0) result_expected.sort_values(by=['ImageId', 'BuildingId'], inplace=True) ious_expected = list(result_expected['iou_score']) - maxdifference = max([abs(x-y) for x,y in zip(ious_actual, - ious_expected)]) + maxdifference = max([abs(x-y) for x, y in zip(ious_actual, + ious_expected)]) epsilon = 1E-9 assert maxdifference < epsilon diff --git a/tests/test_eval/evalfunctions_test.py b/tests/test_eval/iou_test.py similarity index 96% rename from tests/test_eval/evalfunctions_test.py rename to tests/test_eval/iou_test.py index 9b244901..28e5abfe 100644 --- a/tests/test_eval/evalfunctions_test.py +++ b/tests/test_eval/iou_test.py @@ -1,4 +1,4 @@ -from solaris.eval.evalfunctions import calculate_iou, process_iou +from solaris.eval.iou import calculate_iou, process_iou from solaris import data from shapely.geometry import Polygon diff --git a/tests/test_eval/off_nadir_dataset_test.py b/tests/test_eval/off_nadir_dataset_test.py index 875c31ab..57054e9c 100644 --- a/tests/test_eval/off_nadir_dataset_test.py +++ b/tests/test_eval/off_nadir_dataset_test.py @@ -1,12 +1,12 @@ import os -from solaris.eval.challenges.off_nadir_dataset import eval_off_nadir import solaris +from solaris.eval.challenges import off_nadir_buildings import subprocess import pandas as pd class TestEvalOffNadir(object): - """Tests for the ``eval_off_nadir`` function.""" + """Tests for the ``off_nadir`` function.""" def test_scoring(self): """Test a round of scoring.""" @@ -15,7 +15,7 @@ def test_scoring(self): 'test_results.csv')) pred_results_full = pd.read_csv(os.path.join(solaris.data.data_dir, 'test_results_full.csv')) - results_df, results_df_full = eval_off_nadir( + results_df, results_df_full = off_nadir_buildings( os.path.join(solaris.data.data_dir, 'sample_preds.csv'), os.path.join(solaris.data.data_dir, 'sample_truth.csv') ) diff --git a/tests/test_eval/pixel_test.py b/tests/test_eval/pixel_test.py new file mode 100644 index 00000000..520ce814 --- /dev/null +++ b/tests/test_eval/pixel_test.py @@ -0,0 +1,108 @@ +"""Tests for ``solaris.eval.pixel_metrics`` functions.""" + +import numpy as np +from solaris.eval.pixel import iou, f1, relaxed_f1 + + +class TestIoU(object): + """Test ``sol.eval.pixel.iou()``.""" + + def test_iou_basic(self): + truth = np.array([[0, 0, 1], + [0, 1, 0], + [1, 1, 1]]) + prop = np.array([[0, 0, 0], + [1, 1, 0], + [1, 0, 1]]) + assert iou(truth, prop) == 0.5 + + def test_iou_pvals(self): + truth = np.array([[0, 0, 1], + [0, 1, 0], + [1, 1, 1]]) + prop = np.array([[0, 0.1, 0.4], + [0.8, 0.7, 0.5], + [1, 0, 1]]) + assert iou(truth, prop, prop_threshold=0.55) == 0.5 + + +class TestF1(object): + """Test ``sol.eval.pixel.f1()``.""" + + def test_f1_basic(self): + eps = 1e-7 + truth = np.array([[0, 0, 1], + [0, 1, 0], + [1, 1, 1]]) + prop = np.array([[0, 0, 0], + [1, 1, 0], + [1, 0, 1]]) + f1_score, precision, recall = f1(truth, prop) + assert (precision - 0.75) < eps + assert (recall - 0.6) < eps + assert (f1_score - 2./3) < eps + + def test_f1_pvals(self): + eps = 1e-7 + truth = np.array([[0, 0, 1], + [0, 1, 0], + [1, 1, 1]]) + prop = np.array([[0, 0.1, 0.4], + [0.8, 0.7, 0.5], + [1, 0, 1]]) + f1_score, precision, recall = f1(truth, prop, prop_threshold=0.55) + assert (precision - 0.75) < eps + assert (recall - 0.6) < eps + assert (f1_score - 2./3) < eps + + def test_f1_no_pos_preds(self): + truth = np.array([[0, 0, 1], + [0, 1, 0], + [1, 1, 1]]) + prop = np.zeros(shape=(3, 3)) + assert f1(truth, prop) == (0, 0, 0) + + def test_f1_no_pos_truth(self): + prop = np.array([[0, 0, 1], + [0, 1, 0], + [1, 1, 1]]) + truth = np.zeros(shape=(3, 3)) + assert f1(truth, prop) == (0, 0, 0) + + +class TestRelaxedF1(object): + """Test ``sol.eval.pixel.relaxed_f1()``.""" + + def test_relaxed_f1_basic(self): + eps = 1e-7 + truth_mask = np.array( + [[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [1., 1., 1., 1., 1., 1., 1., 1., 1., 1.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]] + ) + prop_mask = np.array( + [[0., 0., 0., 1., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.], + [1., 1., 0., 1., 1., 1., 1., 1., 1., 1.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.], + [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.]] + ) + + rel_f1, rel_prec, rel_rec = relaxed_f1(truth_mask, + prop_mask, + radius=3) + assert (rel_f1 - 0.8571428571428571) < eps + assert rel_prec - 0.75 < eps + assert rel_rec - 1.0 < eps diff --git a/tests/test_eval/spacenet_buildings2_dataset_test.py b/tests/test_eval/spacenet_buildings2_dataset_test.py index 62ee2391..9789302a 100644 --- a/tests/test_eval/spacenet_buildings2_dataset_test.py +++ b/tests/test_eval/spacenet_buildings2_dataset_test.py @@ -1,13 +1,12 @@ import os -from solaris.eval.challenges.spacenet_buildings2_dataset \ - import eval_spacenet_buildings2 +from solaris.eval.challenges import spacenet_buildings_2 import solaris import subprocess import pandas as pd class TestEvalSpaceNetBuildings2(object): - """Tests for the ``eval_spacenet_buildings2`` function.""" + """Tests for the ``spacenet_buildings_2`` function.""" def test_scoring(self): """Test a round of scoring.""" @@ -17,7 +16,7 @@ def test_scoring(self): pred_results_full = pd.read_csv( os.path.join(solaris.data.data_dir, 'SN2_test_results_full.csv')) - results_df, results_df_full = eval_spacenet_buildings2( + results_df, results_df_full = spacenet_buildings_2( os.path.join(solaris.data.data_dir, 'SN2_sample_preds.csv'), os.path.join(solaris.data.data_dir, 'SN2_sample_truth.csv') ) @@ -48,7 +47,7 @@ def test_cli(self): 'SN2_sample_truth.csv') subprocess.call(['spacenet_eval', '--proposal_csv='+proposal_csv, '--truth_csv='+truth_csv, - '--challenge=spaceNet-buildings2', + '--challenge=spacenet-buildings2', '--output_file=test_out']) test_results = pd.read_csv('test_out.csv') full_test_results = pd.read_csv('test_out_full.csv') diff --git a/tests/test_imports.py b/tests/test_imports.py index 9d6a0472..a942425e 100644 --- a/tests/test_imports.py +++ b/tests/test_imports.py @@ -5,11 +5,9 @@ def test_imports(self): from solaris.utils import core, geo, config, tile, cli from solaris import data from solaris.vector import polygon, graph, mask - from solaris.tile import main, vector_utils + from solaris.tile import raster_tile, vector_tile from solaris.raster import image from solaris.nets import callbacks, datagen, infer, model_io, losses from solaris.nets import train, transform, zoo - from solaris.eval import baseeval, evalfunctions - from solaris.eval.challenges import off_nadir_dataset - from solaris.eval.challenges import spacenet_buildings2_dataset + from solaris.eval import base, iou, challenges, pixel import solaris diff --git a/tests/test_nets/test_callbacks.py b/tests/test_nets/test_callbacks.py index 2a5b6c06..0c431b4f 100644 --- a/tests/test_nets/test_callbacks.py +++ b/tests/test_nets/test_callbacks.py @@ -1,5 +1,8 @@ from solaris.nets.callbacks import KerasTerminateOnMetricNaN, get_callbacks from solaris.nets.callbacks import get_lr_schedule +from solaris.nets.torch_callbacks import TorchEarlyStopping, \ + TorchModelCheckpoint, TorchTerminateOnNaN, TorchTerminateOnMetricNaN + import tensorflow as tf import torch import numpy as np @@ -72,47 +75,41 @@ def test_get_torch_callbacks(self): 'update_frequency': None }, 'terminate_on_nan': {}, - 'terminate_on_metric_nan': {}, - 'model_checkpoint': - {'filepath': 'sample_path.h5'}, - 'early_stopping': {}, - 'csv_logger': - {'filename': 'sample_path.csv'}, - 'reduce_lr_on_plateau': {} + 'terminate_on_metric_nan': { + 'stopping_metric': 'accuracy' + }, + 'model_checkpoint': { + 'filepath': 'sample_path.h5' + }, + 'early_stopping': {} } } } result = get_callbacks(framework, config) - assert len(result) == 7 + assert len(result) == 5 has_lr_sched = False has_term_nan = False has_term_met_nan = False has_mod_ckpt = False has_early_stopping = False - has_csv_logger = False - has_red_lr_plat = False + for callback in result: if callback == torch.optim.lr_scheduler.ExponentialLR: has_lr_sched = True - elif callback == 'terminate_on_nan': + elif isinstance(callback, TorchTerminateOnNaN): has_term_nan = True - elif callback == 'terminate_on_metric_nan': + elif isinstance(callback, TorchTerminateOnMetricNaN): has_term_met_nan = True - elif callback == 'model_checkpoint': + elif isinstance(callback, TorchModelCheckpoint): has_mod_ckpt = True - elif callback == 'early_stopping': + elif isinstance(callback, TorchEarlyStopping): has_early_stopping = True - elif callback == 'csv_logger': - has_csv_logger = True - elif callback == 'reduce_lr_on_plateau': - has_red_lr_plat = True + assert has_lr_sched assert has_term_nan assert has_term_met_nan assert has_mod_ckpt assert has_early_stopping - assert has_csv_logger - assert has_red_lr_plat class TestLRSchedulers(object): diff --git a/tests/test_nets/test_datagen.py b/tests/test_nets/test_datagen.py index 30a89bdb..868837a3 100644 --- a/tests/test_nets/test_datagen.py +++ b/tests/test_nets/test_datagen.py @@ -1,6 +1,7 @@ import os from solaris.nets.datagen import make_data_generator, InferenceTiler from solaris.data import data_dir +from solaris.utils.io import _check_channel_order import pandas as pd import numpy as np import skimage @@ -79,11 +80,11 @@ def test_torch_dataset(self): 'datagen_sample', 'sample_mask_1.tif')) expected_mask[expected_mask != 0] = 1 # this should be binary - - assert np.array_equal(sample['image'], - expected_im[np.newaxis, :, :, np.newaxis]) - assert np.array_equal(sample['label'], - expected_mask[np.newaxis, :, :, np.newaxis]) + print(sample['mask'].shape) + assert np.array_equal(sample['image'].numpy(), + expected_im[np.newaxis, :, :]) + assert np.array_equal(sample['mask'].numpy(), + expected_mask[np.newaxis, np.newaxis, :, :]) class TestInferenceTiler(object): diff --git a/tests/test_raster/test_image.py b/tests/test_raster/test_image.py index b62fe1a6..ade0ccb2 100644 --- a/tests/test_raster/test_image.py +++ b/tests/test_raster/test_image.py @@ -38,7 +38,7 @@ def test_stitch_InferenceTiler_output(self): restored_im = stitch_images(tiles, idx_refs=tile_inds, out_width=900, out_height=900) expected_result = sol.utils.io.imread( - os.path.join(data_dir, 'sample_geotiff.tif')) + os.path.join(data_dir, 'sample_geotiff.tif'), make_8bit=True) assert np.array_equal(restored_im, expected_result) diff --git a/tests/test_tile/test_tile.py b/tests/test_tile/test_tile.py new file mode 100644 index 00000000..9440b963 --- /dev/null +++ b/tests/test_tile/test_tile.py @@ -0,0 +1,52 @@ +import os +import skimage +import numpy as np +from solaris.tile.raster_tile import RasterTiler +from solaris.tile.vector_tile import VectorTiler +from solaris.data import data_dir +import geopandas as gpd +from shapely.ops import cascaded_union + + +class TestTilers(object): + def test_tiler(self): + raster_tiler = RasterTiler(os.path.join(data_dir, + 'rastertile_test_result'), + src_tile_size=(90, 90)) + raster_tiler.tile(src=os.path.join(data_dir, 'sample_geotiff.tif')) + raster_tiling_result_files = os.listdir(os.path.join( + data_dir, 'rastertile_test_result')) + assert len(raster_tiling_result_files) == len(os.listdir(os.path.join( + data_dir, 'rastertile_test_expected'))) + for f in raster_tiling_result_files: + result = skimage.io.imread(os.path.join(data_dir, + 'rastertile_test_result', + f)) + expected = skimage.io.imread( + os.path.join(data_dir, 'rastertile_test_expected', f)) + assert np.array_equal(result, expected) + os.remove(os.path.join(data_dir, 'rastertile_test_result', f)) + os.rmdir(os.path.join(data_dir, 'rastertile_test_result')) + vector_tiler = VectorTiler(os.path.join(data_dir, + 'vectortile_test_result')) + vector_tiler.tile(os.path.join(data_dir, 'geotiff_labels.geojson'), + raster_tiler.tile_bounds) + vector_tiling_result_files = os.listdir(os.path.join( + data_dir, 'vectortile_test_result')) + assert len(vector_tiling_result_files) == len(os.listdir(os.path.join( + data_dir, 'vectortile_test_expected'))) + for f in vector_tiling_result_files: + result = gpd.read_file(os.path.join(data_dir, + 'vectortile_test_result', + f)) + expected = gpd.read_file(os.path.join(data_dir, + 'vectortile_test_expected', + f)) + if len(result) == 0: + assert len(expected) == 0 + else: + result = cascaded_union(result.geometry) + expected = cascaded_union(expected.geometry) + assert result.intersection(expected).area/result.area > 0.99999 + os.remove(os.path.join(data_dir, 'vectortile_test_result', f)) + os.rmdir(os.path.join(data_dir, 'vectortile_test_result')) diff --git a/tests/test_utils/test_geo.py b/tests/test_utils/test_geo.py index 6dfb0f1f..f6704c16 100644 --- a/tests/test_utils/test_geo.py +++ b/tests/test_utils/test_geo.py @@ -4,9 +4,12 @@ import shapely from affine import Affine from shapely.wkt import loads +from shapely.ops import cascaded_union from solaris.data import data_dir -from solaris.utils.geo import list_to_affine, split_multi_geometries, \ - geometries_internal_intersection +from solaris.utils.geo import list_to_affine, split_multi_geometries +from solaris.utils.geo import geometries_internal_intersection +from solaris.utils.geo import reproject, reproject_geometry +import rasterio class TestCoordTransformer(object): @@ -79,3 +82,59 @@ def test_grouped_split_multipolygon(self): data_dir, 'split_multi_grouped_result.json')).drop(columns='id') assert expected.equals(output) + + +class TestReproject(object): + """Test reprojection functionality.""" + + def test_reproject_rasterio_dataset(self): + input_data = os.path.join(data_dir, 'sample_geotiff.tif') + output = reproject(input_data, target_crs=4326, + dest_path=os.path.join(data_dir, 'tmp.tiff')) + with rasterio.open(input_data) as input_rio: + input_bounds = input_rio.bounds + expected_bounds = rasterio.warp.transform_bounds(input_rio.crs, + 'EPSG:4326', + *input_bounds) + expected_bounds = tuple([round(i, 4) for i in tuple(expected_bounds)]) + output_bounds = tuple([round(i, 4) for i in tuple(output.bounds)]) + + assert expected_bounds == output_bounds + assert output.crs.to_epsg() == 4326 + + os.remove(os.path.join(data_dir, 'tmp.tiff')) + + def test_reproject_gdf(self): + input_data = os.path.join(data_dir, 'gt.geojson') + output = reproject(input_data, target_crs=4326, + dest_path=os.path.join(data_dir, 'tmp.json')) + expected_result = gpd.read_file(os.path.join(data_dir, + 'gt_epsg4326.json')) + out_geoms = cascaded_union(output.geometry) + exp_geoms = cascaded_union(expected_result.geometry) + + assert out_geoms.intersection(exp_geoms).area/out_geoms.area > 0.99999 + os.remove(os.path.join(data_dir, 'tmp.json')) + + def test_reproject_gdf_utm_default(self): + input_data = os.path.join(data_dir, 'gt_epsg4326.json') + output = reproject(input_data) + expected_result = gpd.read_file(os.path.join(data_dir, 'gt.geojson')) + out_geoms = cascaded_union(output.geometry) + exp_geoms = cascaded_union(expected_result.geometry) + + assert out_geoms.intersection(exp_geoms).area/out_geoms.area > 0.99999 + + +class TestReprojectGeometry(object): + """Test reprojection of single geometries.""" + + def test_reproject_from_wkt(self): + input_str = "POLYGON ((736687.5456353347 3722455.06780279, 736686.9301210654 3722464.96326352, 736691.6397869177 3722470.9059681, 736705.5443059544 3722472.614050498, 736706.8992101226 3722462.858909504, 736704.866059878 3722459.457111885, 736713.1443474176 3722452.103498172, 736710.0312805283 3722447.309985571, 736700.3886167214 3722454.263705271, 736698.4577440721 3722451.98534527, 736690.1272768064 3722451.291527834, 736689.4108667439 3722455.113813923, 736687.5456353347 3722455.06780279))" + result_str = "POLYGON ((-84.4487639 33.6156071, -84.44876790000001 33.6156964, -84.4487156 33.61574889999999, -84.44856540000001 33.6157612, -84.44855339999999 33.61567300000001, -84.44857620000001 33.6156428, -84.448489 33.6155747, -84.4485238 33.6155322, -84.4486258 33.615597, -84.4486472 33.61557689999999, -84.4487371 33.6155725, -84.4487438 33.6156071, -84.4487639 33.6156071))" + result_geom = loads(result_str) + reproj_geom = reproject_geometry(input_str, input_crs=32616, + target_crs=4326) + area_sim = result_geom.intersection(reproj_geom).area/result_geom.area + + assert area_sim > 0.99999 diff --git a/tests/test_vector/test_mask.py b/tests/test_vector/test_mask.py index fa4731b5..920a306d 100644 --- a/tests/test_vector/test_mask.py +++ b/tests/test_vector/test_mask.py @@ -60,6 +60,23 @@ def test_make_mask_w_file_and_transform(self): # clean up os.remove(os.path.join(data_dir, 'test_out.tif')) + def test_make_mask_infer_do_transform_true(self): + output_mask = footprint_mask( + os.path.join(data_dir, 'geotiff_labels.geojson'), + reference_im=os.path.join(data_dir, 'sample_geotiff.tif'), + out_file=os.path.join(data_dir, 'test_out.tif') + ) + truth_mask = skimage.io.imread( + os.path.join(data_dir, 'sample_fp_mask_from_geojson.tif') + ) + saved_output_mask = skimage.io.imread(os.path.join(data_dir, + 'test_out.tif')) + + assert np.array_equal(output_mask, truth_mask) + assert np.array_equal(saved_output_mask, truth_mask) + # clean up + os.remove(os.path.join(data_dir, 'test_out.tif')) + class TestBoundaryMask(object): """Tests for solaris.vector.mask.boundary_mask.""" @@ -229,7 +246,7 @@ def test_make_mask_w_file_and_transform(self): output_mask = road_mask( os.path.join(data_dir, 'sample_roads_for_masking.geojson'), reference_im=os.path.join(data_dir, 'road_mask_input.tif'), - do_transform=True, + width=4, meters=True, do_transform=True, out_file=os.path.join(data_dir, 'test_out.tif') ) truth_mask = skimage.io.imread(