diff --git a/.gitignore b/.gitignore index 1cc548ae..abf2ccfe 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,8 @@ x86/ /build /buildx /docs/_build +/docs/build + # Vagrant .vagrant/ @@ -48,3 +50,6 @@ x86/ .settings/ /.cache /buildx + +.idea/ +cmake-build-debug/ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..d75250f2 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,32 @@ +os: linux + +language: cpp + +services: + - docker + +install: skip + +script: + - echo "skipping tests" + - echo ${DOCKER_PASSWORD} | docker login -u ${DOCKER_USERNAME} --password-stdin + - docker pull tudelft3d/val3dity:latest || true + - docker build . --cache-from tudelft3d/val3dity:latest -t tudelft3d/val3dity:latest + + +deploy: + - provider: script + script: docker push tudelft3d/val3dity:latest + cleanup: false + on: + branch: master + - provider: script + script: docker tag tudelft3d/val3dity:latest tudelft3d/val3dity:dev ; docker push tudelft3d/val3dity:dev + cleanup: false + on: + branch: develop + - provider: script + script: docker tag tudelft3d/val3dity:latest tudelft3d/val3dity:${TRAVIS_TAG} ; docker push tudelft3d/val3dity:${TRAVIS_TAG} + cleanup: false + on: + tags: true diff --git a/CMakeLists.txt b/CMakeLists.txt index d986caaa..634a3858 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,11 +3,10 @@ project( val3dity ) cmake_minimum_required (VERSION 3.2) -add_definitions(-std=c++11) +add_definitions(-std=c++14) -set(CMAKE_CXX_FLAGS "-O2") - -set(CMAKE_BUILD_TYPE "Release") +set( CMAKE_BUILD_TYPE "Release") +set( CMAKE_CXX_FLAGS "-O2" ) set( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true ) @@ -20,7 +19,7 @@ if (MSVC) endif(MSVC) # CGAL -find_package( CGAL QUIET COMPONENTS core ) +find_package( CGAL QUIET COMPONENTS ) if ( CGAL_FOUND ) include( ${CGAL_USE_FILE} ) message(STATUS "CGAL found") @@ -35,6 +34,7 @@ endif() find_package(Eigen3) include( ${EIGEN3_USE_FILE} ) + # Boost find_package( Boost REQUIRED COMPONENTS filesystem) diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..c5712570 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,115 @@ +FROM alpine:3.10 +LABEL maintainer.email="b.dukai@tudelft.nl" maintainer.name="Balázs Dukai" + +ENV CXX="g++ -std=c++98" + +# Install geos +RUN apk --update add --virtual .geos-deps \ + which \ + make \ + gcc \ + g++ \ + file \ + git \ + autoconf \ + automake \ + libtool && \ + cd /tmp && \ + git clone https://git.osgeo.org/gitea/geos/geos.git geos && \ + cd geos && \ + git checkout 3.7.1 && \ + ./autogen.sh && \ + ./configure && \ + make && \ + make check && \ + make install && \ + cd / && \ + apk del .geos-deps && \ + rm -rf /tmp/* && \ + rm -rf /user/local/man + +# install CGAL +RUN apk --update add --virtual .cgal-deps \ + which \ + make \ + cmake \ + gcc \ + g++ \ + eigen-dev \ + boost-dev \ + gmp-dev \ + mpfr-dev \ + zlib-dev && \ + cd /tmp && \ + wget https://github.com/CGAL/cgal/releases/download/releases%2FCGAL-5.0.2/CGAL-5.0.2.tar.xz && \ + tar xf CGAL-5.0.2.tar.xz && \ + rm -f CGAL-5.0.2.tar.xz && \ + cd CGAL-5.0.2 && \ + mkdir build && \ + cd build && \ + cmake -DCMAKE_BUILD_TYPE=Release -DWITH_Eigen3=ON .. && \ + make && \ + make install && \ + cd / && \ + apk del .cgal-deps && \ + rm -rf /tmp/* && \ + rm -rf /user/local/man + +# install val3dity +COPY . /tmp +RUN apk --update add --virtual .val3dity-deps \ + make \ + cmake \ + gcc \ + g++ \ + boost-dev \ + eigen-dev \ + gmp-dev \ + mpfr-dev \ + python3 \ + pytest \ + py-yaml \ + py-lxml \ + py3-setuptools && \ + cd /tmp && \ + mkdir build && \ + cd build && \ + cmake .. && \ + make && \ + make install && \ + cd .. &&\ + ls -ls . && \ + val3dity data/cityjson/cube.json && \ + python3 -m pytest --runfull && \ + apk del .val3dity-deps && \ + cd / && \ + apk --update add \ + make \ + gcc \ + g++ \ + boost \ + eigen \ + gmp \ + bash \ + mpfr3 && \ + rm -rf /tmp/* && \ + rm -rf /user/local/man + +RUN mkdir /data && \ + chown 1001 /data && \ + chgrp 0 /data && \ + chmod g=u /data && \ + chgrp 0 /etc/passwd && \ + chmod g=u /etc/passwd + +RUN val3dity --version + +COPY --chown=1001:0 uid_entrypoint.sh /usr/local/bin/ + +USER 1001 + +WORKDIR /data + +ENTRYPOINT ["/usr/local/bin/uid_entrypoint.sh"] + +CMD ["--version"] diff --git a/README.md b/README.md index d770f543..14b94aa8 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,52 @@ + +[![GitHub license](https://img.shields.io/github/license/tudelft3d/val3dity)](https://github.com/tudelft3d/val3dity/blob/master/LICENSE) [![webapp](https://img.shields.io/badge/webapp-geovalidation.bk.tudelft.nl%2Fval3dity%2F-fea93b)](http://geovalidation.bk.tudelft.nl/val3dity/) [![DOI](https://img.shields.io/badge/DOI-10.1186%2Fs40965--018--0043--x-blueviolet)](http://dx.doi.org/10.1186/s40965-018-0043-x) + # val3dity val3dity---pronounced 'val-three-dity'---allows us to validate 3D primitives according to the international standard ISO19107. Think of it as [PostGIS ST_IsValid](http://postgis.net/docs/ST_IsValid.html), but for 3D primitives (PostGIS only validates 2D primitives). -In short, it verifies whether a 3D primitive respects the definition as given in [ISO19107](http://www.iso.org/iso/catalogue_detail.htm?csnumber=26012) and GML/CityGML. -All the 3D primitives of GML are supported: +In short, it verifies whether a 3D primitive respects the definition as given in [ISO 19107](http://www.iso.org/iso/catalogue_detail.htm?csnumber=26012) and [GML](https://en.wikipedia.org/wiki/Geography_Markup_Language). + +The validation of the following 3D primitives is fully supported: - - `` - - `` - - `` - - `` - - `` + - ``MultiSurface`` + - ``CompositeSurface`` + - ``Solid`` + - ``MultiSolid`` + - ``CompositeSolid`` Unlike many other validation tools in 3D GIS, inner rings in polygons/surfaces are supported and so are cavities in solids (also called voids or inner shells). -However, as is the case for CityGML, only planar and linear primitives are allowed: no curves or spheres or other parametrically-modelled primitives are supported. There is no plan to support these geometries, because val3dity is developed with 3D city models in focus. +However, as is the case for many formats used in practice, only planar and linear primitives are allowed: no curves or spheres or other parametrically-modelled primitives are supported. +There is no plan to support these geometries. val3dity accepts as input: - - [CityGML](https://www.citygml.org) - - [CityJSON](http://www.cityjson.org) - [GML files](https://en.wikipedia.org/wiki/Geography_Markup_Language) of any flavour + - [CityJSON](http://www.cityjson.org) + - [CityGML (v1 & v2 only; v3 will not be supported)](https://www.citygml.org) + - [IndoorGML](http://indoorgml.net/) - [OBJ](https://en.wikipedia.org/wiki/Wavefront_.obj_file) - [OFF](https://en.wikipedia.org/wiki/OFF_(file_format)) +For the City/JSON/GML and IndoorGML formats, extra validations (specific to the format) are performed, eg the overlap between different parts of a building, or the validation of the navigation graph in IndoorGML. + + +## Usage + +val3dity is a command-line interface (CLI) programme only, with [several options](https://val3dity.readthedocs.io/en/latest/usage/#options-for-the-validation). + +[![val3dity cli demo](./misc/cli.svg)](https://asciinema.org/a/329835) +_No demo visible here? View it on [asciinema](https://asciinema.org/a/329835)._ + + +## Web application + +If you don't want to go through the troubles of compiling and/or installing val3dity, we suggest you use the [web application](http://geovalidation.bk.tudelft.nl/val3dity). +Simply upload your file to our server and get a validation report back. +We delete the file as soon as it has been validated (promised!). +However, a file is limited to 50MB. + ## Installation of the command-line tool @@ -33,24 +57,26 @@ This is relatively easy under Mac and Linux. To compile val3dity yourself, you first need to install the following free libraries: - 1. [CGAL v4.10+](http://www.cgal.org) (:/data tudelft3d/val3dity: + +Where `` is docker image tag from [https://hub.docker.com/r/tudelft3d/val3dity/tags](https://hub.docker.com/r/tudelft3d/val3dity/tags) + +If you are using the `--report` option, then **keep in mind that `` need to be writable by any user, otherwise your output won't be saved.** + ## Documentation and help -Read the full documentation at [http://geovalidation.bk.tudelft.nl/val3dity/docs/](http://geovalidation.bk.tudelft.nl/val3dity/docs/). +Read the full documentation at [https://val3dity.rtfd.io](https://val3dity.rtfd.io). The primary channel to communicate with the developers is the Issues section. If you have a question or came across a bug, please submit an issue there. However we ask you check first whether your problem has already been solved by someone else. + + +## If you use val3dity in a scientific context, please cite these articles: + +Ledoux, Hugo (2019). val3dity: validation of 3D GIS primitives according to the international standards. *Open Geospatial Data, Software and Standards*, 3(1), 2018, pp.1 [[DOI](http://dx.doi.org/10.1186/s40965-018-0043-x)] + +Ledoux, Hugo (2013). On the validation of solids represented with the international standards for geographic information. Computer-Aided Civil and Infrastructure Engineering, 28(9):693-706. [[PDF](https://3d.bk.tudelft.nl/hledoux/pdfs/13_cacaie.pdf)] [[DOI](http://dx.doi.org/10.1111/mice.12043)] + +## Error codes + +![](./docs/_static/errorcodes.png) diff --git a/VAGRANT.md b/VAGRANT.md deleted file mode 100644 index 2f0e9652..00000000 --- a/VAGRANT.md +++ /dev/null @@ -1,63 +0,0 @@ -# Installing Vagrant - -To download Vagrant for supported platforms, see [here](https://www.vagrantup.com/downloads.html) - -# Installing VirtualBox - -If choosing VirtualBox as your virtualization provider, see [here](https://www.virtualbox.org/wiki/Downloads). - -You may have to enable hardware virtualization extensions in your BIOS before using it. - -If running on Ubuntu, you may have to install a newer version of VirtualBox than what is available in the public repositories in order for it to work correctly with Vagrant. - -# Setting up val3dity with Vagrant & VirtualBox -Once Vagrant has been installed, you can start an environment by checking out the val3dity code, then changing to the directory which contains the Vagrantfile by typing: - - # Windows users will need to uncomment the line ending configuration option. - git clone git@github.com:tudelft3d/val3dity.git val3dity #--config core.autocrlf=input - cd val3dity - vagrant up - -# Other Virtualization Providers - -If you would like to use Parallels instead of VirtualBox, please run the following command: -``` -vagrant up --provider=parallels -``` -Please note that this requires the Parallels Vagrant plugin, which can be installed: -``` -vagrant plugin install vagrant-parallels -``` - -Similarly, if you would like to use VMware Workstation instead of VirtualBox, please run the following command: -``` -vagrant up --provider vmware_workstation -``` -Please note that this requires the VMware Vagrant plugin, which can be installed: -``` -vagrant plugin install vagrant-vmware-workstation -``` - -# Vagrant Provisioning - -The initialization of the vagrant vm (`vagrant up`) will take about an hour at first. - -You should be able to log into the running VM by typing: - - vagrant ssh - -Within this login shell, you can build the code, run the server or the tests. For example, to run the tests: - -``` - vagrant ssh - cd ~/val3dity_build - cmake ../val3dity - make - make install -``` - -The val3dity root folder (on host) is mapped to the `/home/vagrant/val3dity` folder on the guest. Note that # Vagrant shared folders (e.g. `/home/vagrant/val3dity`) incur a heavy performance penalty within the virtual machine when there is heavy I/O, so they should only be used for source files. Any compilation step, database files, and so on should be done outside the shared folder filesystem inside the guest filesystem itself. - -**Acknowledgement** - -The majority of this description was taken from the [Hootenanny project](https://github.com/ngageoint/hootenanny/blob/master/VAGRANT.md) diff --git a/VagrantProvisionUbuntu1604.sh b/VagrantProvisionUbuntu1604.sh deleted file mode 100644 index ec522137..00000000 --- a/VagrantProvisionUbuntu1604.sh +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env bash - -# Set environment variables -BUILDS="/opt" -HOME="/home/vagrant" -echo HOME: $HOME - -apt-get update >/dev/null 2>&1 - -# CMake, C/++ compiler --------------------------------------------------------- -echo "Installing C/++ compiler and CMake..." -apt-get install -y cmake gcc g++ >/dev/null 2>&1 - -# pip -------------------------------------------------------------------------- -echo "Installing pip..." -apt-get install -y python3-pip >/dev/null 2>&1 - -# CGAL requirements ------------------------------------------------------------ -echo "Installing Eigen3, Boost, GMP, MPFR..." -# get Boost -apt-get install -y libboost-all-dev >/dev/null 2>&1 -# get Eigen3 -apt-get install -y libeigen3-dev >/dev/null 2>&1 -# get GMP and MPFR -apt-get install -y libgmp10 libgmp-dev libmpfr-dev libmpfr4 >/dev/null 2>&1 - -apt-get update >/dev/null 2>&1 -apt-get upgrade -y >/dev/null 2>&1 - -# GEOS ------------------------------------------------------------------------- -echo "Downloading and building GEOS 3.6.2 ..." -cd $BUILDS -wget https://git.osgeo.org/gitea/geos/geos/archive/3.6.2.tar.gz >/dev/null 2>&1 -tar xf 3.6.2.tar.gz -rm 3.6.2.tar.gz -mkdir $BUILDS/geos/build && cd $BUILDS/geos/build -cmake .. -make -geos_test=$(make check | grep -o '100% tests passed') -if [ "$geos_test" = "100% tests passed" ]; then - make install - ldconfig - make clean -else - echo "GEOS test failed, exiting provisioning" - exit 1 -fi - - -# CGAL ------------------------------------------------------------------------- -# For some reason CGAL cannot link Eigen3 to boost_thread when the release tarball -# is used for compilation. However, everything works when CGAL is compiled from -# the source tarball (copy of GitHub master branch). Only the md5sum is not -# correct in the provided md5sum.txt -echo "Downloading and building CGAL 4.10.2 ..." -cd $BUILDS -wget https://github.com/CGAL/cgal/archive/releases/CGAL-4.10.2.tar.gz >/dev/null 2>&1 -#wget https://github.com/CGAL/cgal/releases/download/releases%2FCGAL-4.10.2/md5sum.txt >/dev/null 2>&1 - -#CGAL_TAR=$(md5sum CGAL-4.10.2.tar.xz) -#CGAL_MD5=$(cat md5sum.txt | grep CGAL-4.10.2.tar.xz) -#if [ "$CGAL_TAR" = "$CGAL_MD5" ]; then -# tar -xf CGAL-4.10.2.tar.xz -# rm CGAL-4.10.2.tar.xz -# rm md5sum.txt -#else -# echo "md5sum of CGAL not matching, exiting provisioning" -# exit 1 -#fi - -tar -xf CGAL-4.10.2.tar.gz -rm CGAL-4.10.2.tar.gz - -mkdir $BUILDS/cgal-releases-CGAL-4.10.2/build && cd $BUILDS/cgal-releases-CGAL-4.10.2/build -cmake .. -DWITH_Eigen3=ON -make -make install -make clean -ldconfig - - -# pytest ----------------------------------------------------------------------- -echo "Installing pytest..." -pip3 install pytest - -# val3dity --------------------------------------------------------------------- -echo "Building val3dity..." -# Vagrant shared folders incur a heavy performance penalty within the -# virtual machine when there is heavy I/O, so they should only be used for -# source files. Any compilation step, database files, and so on should be done -# outside the shared folder filesystem inside the guest filesystem itself. -mkdir $HOME/val3dity_build && cd $HOME/val3dity_build -cmake $HOME/val3dity -make -make install -cd $HOME - -val3dity --version - -chown vagrant:vagrant $HOME/val3dity_build - -echo "See VAGRANT.md for additional configuration instructions and then run 'vagrant ssh' to log into the val3dity virtual machine." diff --git a/Vagrantfile b/Vagrantfile deleted file mode 100644 index c710107f..00000000 --- a/Vagrantfile +++ /dev/null @@ -1,74 +0,0 @@ -# -*- mode: ruby -*- -# vi: set ft=ruby : - -# All Vagrant configuration is done below. The "2" in Vagrant.configure -# configures the configuration version (we support older styles for -# backwards compatibility). Please don't change it unless you know what -# you're doing. -Vagrant.configure("2") do |config| - # The most common configuration options are documented and commented below. - # For a complete reference, please see the online documentation at - # https://docs.vagrantup.com. - - # Every Vagrant development environment requires a box. You can search for - # boxes at https://vagrantcloud.com/search. -# config.vm.box = "debian/stretch64" - config.vm.box = "ubuntu/xenial64" - - # Disable automatic box update checking. If you disable this, then - # boxes will only be checked for updates when the user runs - # `vagrant box outdated`. This is not recommended. - # config.vm.box_check_update = false - - # VirtualBox Guest Additions plugin - config.vbguest.auto_update = true - - # Create a forwarded port mapping which allows access to a specific port - # within the machine from a port on the host machine. In the example below, - # accessing "localhost:8080" will access port 80 on the guest machine. - # NOTE: This will enable public access to the opened port - # config.vm.network "forwarded_port", guest: 80, host: 8080 - - # Create a forwarded port mapping which allows access to a specific port - # within the machine from a port on the host machine and only allow access - # via 127.0.0.1 to disable public access - # config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1" - - # Create a private network, which allows host-only access to the machine - # using a specific IP. - # config.vm.network "private_network", ip: "192.168.33.10" - - # Create a public network, which generally matched to bridged network. - # Bridged networks make the machine appear as another physical device on - # your network. - # config.vm.network "public_network" - - # Share an additional folder to the guest VM. The first argument is - # the path on the host to the actual folder. The second argument is - # the path on the guest to mount the folder. And the optional third - # argument is a set of non-required options. - config.vm.synced_folder ".", "/vagrant", disabled: true - config.vm.synced_folder ".", "/home/vagrant/val3dity" - - # Provider-specific configuration so you can fine-tune various - # backing providers for Vagrant. These expose provider-specific options. - # Example for VirtualBox: - - config.vm.provider "virtualbox" do |vb| - # Display the VirtualBox GUI when booting the machine - # vb.gui = true - - # Customize the amount of memory and CPUs on the VM: - vb.memory = 2048 - vb.cpus = 2 - end - - # View the documentation for the provider you are using for more - # information on available options. - - # Enable provisioning with a shell script. Additional provisioners such as - # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the - # documentation for more information about their specific syntax and use. -# config.vm.provision "shell", path: "VagrantProvisionDebian9.sh" - config.vm.provision "shell", path: "VagrantProvisionUbuntu1604.sh" -end diff --git a/changelog.md b/changelog.md index f1eefe40..7809a151 100644 --- a/changelog.md +++ b/changelog.md @@ -2,7 +2,22 @@ # Changelog ## [Unreleased] -- validation of IndoorGML features +- validation of topological relationships between features, eg ensuring that buildings in a city do not overlap +- support for all GML3 primitives (for IndoorGML): the so-called "Compact Geometries" (http://schemas.opengis.net/gml/3.3/geometryCompact.xsd) +- phasing out CityGML support + +## [2.2.0] - 2020-05-14 +### Added +- support for IndoorGML datasets, not just the geometries but specific validation tests for the primal-dual consistency were added +- support for GeometryTemplates in CityJSON (https://www.cityjson.org/specs/#geometry-templates) + +### Changed +- completely revamped the JSON error report, it's better and clearer +- no more HTML output for the report (output of report is now with option `--report`, be careful), but you can just drag your JSON report to the HTML page (in folder `/report_browser/index.html`) or at http://geovalidation.bk.tudelft.nl/val3dity/browser/ +- improved the error codes a bit: new errors for IndoorGML (7xx) + for handy faulty inputs (error 904) +- code must now be compiled against CGAL5, easier to install and compile for everyone +- CityGML support is deprecated. This is the last version that will support CityGML, I will remove it completely from v2.3+. Most errors/issues reported have to do with a faulty parser of CityGML, and not with val3dity itself. I cannot justify spending 100s of hours fixing the parser, I tried hard but this not possible anymore. If someone wants to do it then great I am willing to help them, but I will not invest time on this anymore. Just upgrade your files to CityJSON (https://www.cityjson.org/help/users/conversion/), I'll always maintain a full CityJSON parser! + ## [2.1.1] - 2019-04-30 ### Changed diff --git a/docs/_static/errorcodes.graffle b/docs/_static/errorcodes.graffle index 24875308..c34e1964 100644 Binary files a/docs/_static/errorcodes.graffle and b/docs/_static/errorcodes.graffle differ diff --git a/docs/_static/errorcodes.png b/docs/_static/errorcodes.png new file mode 100644 index 00000000..43ee2f1f Binary files /dev/null and b/docs/_static/errorcodes.png differ diff --git a/docs/_static/errorcodes.svg b/docs/_static/errorcodes.svg deleted file mode 100644 index 424733b6..00000000 --- a/docs/_static/errorcodes.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/docs/_static/report1.png b/docs/_static/report1.png index 3b744f0e..94618648 100644 Binary files a/docs/_static/report1.png and b/docs/_static/report1.png differ diff --git a/docs/_static/report2.png b/docs/_static/report2.png index b981ff92..2169e302 100644 Binary files a/docs/_static/report2.png and b/docs/_static/report2.png differ diff --git a/docs/conf.py b/docs/conf.py index 35c46119..dcc874bf 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -47,7 +47,7 @@ # General information about the project. project = u'val3dity' -copyright = u'2011-2018, Hugo Ledoux' +copyright = u'2011-2020, Hugo Ledoux' author = u'Hugo Ledoux' # The version info for the project you're documenting, acts as replacement for @@ -55,9 +55,9 @@ # built documents. # # The short X.Y version. -version = u'2.1' +version = u'2.2.0' # The full version, including alpha/beta/rc tags. -release = u'2.1' +release = u'2.2.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/errors.rst b/docs/errors.rst index 9001744d..86623497 100644 --- a/docs/errors.rst +++ b/docs/errors.rst @@ -4,7 +4,7 @@ Errors ====== -.. image:: _static/errorcodes.svg +.. image:: _static/errorcodes.png :width: 100% .. contents:: :local: @@ -169,6 +169,14 @@ When the polygon is used as a bounding surface of a shell, then the rings have t .. image:: _static/208.png :width: 25% + +.. _error_300: + +300 -- NOT_VALID_2_MANIFOLD +--------------------------- +The shell is not valid, but the exact error (errors 3xx) is not known. This error happens when the construction of the shell failed for unknown reason. Hopefully you don't get that error. + + .. _error_301: 301 -- TOO_FEW_POLYGONS @@ -194,6 +202,9 @@ Each shell must be simple, ie it must be a 2-manifold. Two cases are possible: 1. An edge of a shell is non-manifold when there are more than 2 incident polygons to it. 2. A vertex is non-manifold when its incident polygons do not form one 'umbrella' +Notice that this error might be returned for shells having their surfaces that are not consistently oriented (while they are 2-manifold). +Imagine you have a cube with 6 surfaces, if some surfaces have their normal pointing outwards, and some inwards, then this error might be returned (or :ref:`error_307`, depending on the configuration). + .. image:: _static/303.png .. image:: _static/304.png @@ -313,6 +324,34 @@ Another option is to use the option ``--geom_is_sem_surfaces`` which gathers all We only offer this option as a convenience (and because we are nice people), and we strongly encourage you to define a geometry. +.. _error_701: + +701 -- CELLS_OVERLAP +-------------------- +Two IndoorGML cells overlap with each other. Similar to :ref:`error_601`: but for IndoorGML. +The overlap allowed can be controlled with the validation option :ref:`option_overlap_tol`:. + + +.. _error_702: + +702 -- DUAL_VERTEX_OUTSIDE_CELL +------------------------------- +The dual vertex of an IndoorGML cell is located outside the cell. + + +.. _error_703: + +703 -- PRIMAL_DUAL_XLINKS_ERROR +------------------------------- +The XLinks in an IndoorGML file are wrong, the primal and the dual are not correctly linked. The schema validation (.xsd) of IndoorGML does not validate this. + +.. _error_704: + +704 -- PRIMAL_DUAL_ADJACENCIES_INCONSISTENT +------------------------------------------- +The adjacency of IndoorGML cells in the primal and the dual are no consistent. Basically, if two 3-cells are adjacent in the primal, are they also in the dual (do they have a dual edge)? The :ref:`option_overlap_tol`: influences this. + + .. _error_901: 901 -- INVALID_INPUT_FILE @@ -331,6 +370,13 @@ The input file contains empty primitives, eg an OBJ where to surfaces are define ----------------------------- The parameters used for the validation are not valid. + +.. _error_904: + +904 -- FORMAT_NOT_SUPPORTED +--------------------------- +It can be that certain versions of a supported format are not supported, eg v3.0 of CityGML is not. + .. _error_999: 999 -- UNKNOWN_ERROR diff --git a/docs/faq.rst b/docs/faq.rst index 489d368d..45ab7998 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -22,30 +22,23 @@ You can read more about the `details `_: .. image:: _static/report1.png :width: 49% .. image:: _static/report2.png :width: 49% - -:ref:`report_json` - -Outputs a single JSON file with the information shown in the HTML page. -Use it to produce your own report or extract statistics. - -The top level objects are called "features", and there are at this moment 2 kinds of features: CityObjects (for CityGML objects, like Building or Bridge) and GenericObject (when the features are not known, eg for an OBJ file). -Each feature contains a list of 3D primitives. -The errors are reported as children of either a 3D primitive (errors 1xx to 5xx) or a feature (errors 6xx). -The errors 9xx are reported in the ``"errors_dataset"``. -All the features and primitives contain an entry ``"validity"`` state whether it is valid or not. +There you get an overview of the statistics per features and primitives, and each feature has its primitives and errors as children. @@ -120,6 +113,5 @@ The IDs for the shells and surfaces in the report, are they 0-based or 1-based? Where can I get files containing ``gml:Solids`` or ``gml:CompositeSolid``? -------------------------------------------------------------------------- In the folder ``/data/`` of the `GitHub repository of val3dity `_ there are many examples of files containing different primitives, and in different formats. -Also, on the `official CityGML website `_ there are a few files with 3D buildings, and all the `known publicly available 3D cities are listed too `_. diff --git a/docs/index.rst b/docs/index.rst index 5134393f..6e99e8ca 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -18,19 +18,25 @@ The validation of the following 3D primitives is fully supported: - ``MultiSolid`` - ``CompositeSolid`` -Unlike many other validation tools in 3D GIS, inner rings in polygons/surfaces are supported, and so are cavities in solids (also called voids or inner shells). -However, only planar and linear primitives are allowed: no curves or spheres or other parametrically-modelled primitives are supported (and there is no plan to do so!). +Unlike many other validation tools in 3D GIS, inner rings in polygons/surfaces are supported and so are cavities in solids (also called voids or inner shells). +However, as is the case for many formats used in practice, only planar and linear primitives are allowed: no curves or spheres or other parametrically-modelled primitives are supported. +There is no plan to support these geometries. val3dity accepts as input: - - `CityJSON `_ - - `CityGML `_ - `GML file `_ of any flavour + - `CityJSON `_ + - `CityGML (v1 & v2 only, v3 will not be supported) `_ + - `IndoorGML `_ - `OBJ `_ - `OFF `_ +For the City/JSON/GML and IndoorGML formats, extra validations (specific to the format) are performed, eg the overlap between different parts of a building, or the validation of the navigation graph in IndoorGML. + .. note:: - Most of the details are available in this scientific article, if you use val3dity for scientific purposes please cite this article: + If you use val3dity in a scientific context, please cite these articles: + + Ledoux, Hugo (2019). val3dity: validation of 3D GIS primitives according to the international standards. *Open Geospatial Data, Software and Standards*, 3(1), 2018, pp.1 `[DOI] `_ Ledoux, Hugo (2013). On the validation of solids represented with the international standards for geographic information. *Computer-Aided Civil and Infrastructure Engineering*, 28(9):693-706. `[PDF] `_ `[DOI] `_ diff --git a/docs/install.rst b/docs/install.rst index 36511450..d21acb19 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -3,63 +3,6 @@ Installation ============ -macOS & Linux -------------- - -We provide the source code that you need to compile with CMake. - - -To compile val3dity yourself, you first need to install the following free libraries: - - 1. `CGAL v4.10+ `_ (`_ - 3. `GEOS `_ - 4. `CMake `_ - -Under macOS, it's super easy, we suggest using `Homebrew `_: - -.. code-block:: bash - - $ brew install cgal --with-eigen - $ brew install cmake - $ brew install geos - -Under Linux (at least Ubuntu), CGAL has to be compiled because apt-get doesn't give you a version with Eigen. -Thus, in a nutshell, - - 1. download `latest CGAL code `_ - 2. install Eigen library: ``$ sudo apt-get install libeigen3-dev`` - 3. compile CGAL by first activating the Eigen option in the CMake (``$ cmake . -DWITH_Eigen3=ON``), or use `cmake-gui `_ and activate it (option is called ``WITH_Eigen3``) - 4. ``export CGAL_DIR=/path/to/CGAL-4.1x`` which will tell your shell to use that version of CGAL (thus more version of CGAL can be installed on the same computer; see that `handy manual `_) - -To compile and run val3dity (from the val3dity folder): - -.. code-block:: bash - - $ mkdir build - $ cd build - $ cmake .. - $ make - $ ./val3dity ../data/cityjson/cube.json - -The summary of the validation is reported, and you should see that `cube.json` contains one valid primitive. - -To verify that everything went fine during the compilation, run the unit tests (from the root folder of val3dity; must use Python3): - -.. code-block:: bash - - $ python3 -m pytest --runfull - -You shouldn't get any errors - - -Finally, to see all the options possible: - -.. code-block:: bash - - $ ./val3dity --help - - Windows ------- @@ -73,3 +16,14 @@ If you don't want to go through the troubles of compiling and/or installing val3 Simply upload your file to our server and get a validation report back. We delete the file as soon as it has been validated. However, a file is limited to 50MB. + + + +Compile (for macOS & Linux use) +------------------------------- + +The details to compile and use val3dity are on `GitHub `_. + + + + diff --git a/docs/usage.rst b/docs/usage.rst index 45755d1f..e108bdce 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -24,21 +24,14 @@ To validate all the 3D primitives in a CityJSON file and see a summary output: $ val3dity my3dcity.json -To validate all the 3D primitives in a CityGML file and get a HTML report, provide the path to the folder (eg ``/report_dir``) that will contain the report ``report.html``: +To validate all the 3D primitives in a CityGML file and get a JSON report ``myreport.json``: .. code-block:: bash - $ val3dity -r /report_dir input.gml + $ val3dity input.gml --report ./myreport.json -To validate all the 3D primitives in a CityGML file and get a `JSON `_ report: - -.. code-block:: bash - - $ val3dity --report_json report.json input.gml - - -To validate each 3D primitive in ``input.json``, and use a tolerance for testing the planarity of the surface of 20cm: +To validate each 3D primitive in ``input.json``, and use a tolerance for testing the planarity of the surface of 20cm (0.2): .. code-block:: bash @@ -63,18 +56,20 @@ Accepted input val3dity accepts as input: - - `CityJSON `_ - - `CityGML `_ - `GML file `_ of any flavour + - `CityJSON `_ + - `CityGML (v1 & v2 only, v3 will not be supported) `_ + - `IndoorGML `_ - `OBJ `_ - `OFF `_ -For **CityGML/CityJSON** files, all the City Objects (eg ``Building`` or ``Bridge``) are processed and their 3D primitives are validated. +For **CityJSON/CityGML** files, all the City Objects (eg ``Building`` or ``Bridge``) are processed and their 3D primitives are validated. The 3D primitives are bundled under their City Objects in the report. - If your CityGML/CityJSON contains ``Buildings`` with one or more ``BuildingParts``, val3dity will perform an extra validation: it will ensure that the 3D primitives do not overlap (technically that the interior of each ``BuildingPart`` does not intersect with the interior of any other part of the ``Building``). If there is one or more intersections, then :ref:`error_601` will be reported. +For **IndoorGML** files, all the cells (in the primal subdivisions, the rooms) are validated individually, and then some extra validation tests are run on the dual navigation network. All errors 7xx are related specifically to IndoorGML. + For **GML** files, the file is simply scanned for the 3D primitives and validates these according to the rules in ISO19107, all the rest is ignored. For **OBJ** and **OFF** files, each primitive will be validated according to the ISO19107 rules. One must specify how the primitives should be validated (``MultiSurface``, ``CompositeSurface``, or ``Solid``). @@ -126,14 +121,6 @@ Options for the validation ************************** | The geometry of a CityGML object is formed by its semantic surfaces instead of the geometry (which is missing in the file). Only to be used if :ref:`error_609` occurs. If no semantic surfaces are present, then 609 can still occur even the use of this option. ----- - - -``--onlyinvalid`` -***************** -| Only the invalid primitives are reported in the validation report. - - ---- .. _option_overlap_tol: @@ -143,7 +130,7 @@ Options for the validation | Tolerance for testing the overlap between primitives in ``CompositeSolids`` and ``BuildingParts`` | default = -1 (disabled) -The maximum allowed distance for overlaps. Helps to validate the topological relationship between ``Solids`` forming a ``CompositeSolid`` or the ``BuildingParts`` of a building. +The maximum allowed distance for overlaps. Helps to validate the topological relationship between ``Solids`` forming a ``CompositeSolid``, the ``BuildingParts`` of a building in CityJSON, or the cells in IndoorGML. The tolerance ``--overlap_tol 0.05`` means that each of the solids is given a 0.05unit *fuzzy* boundary (thus 5cm if meters are the unit of the input), and thus this is considered when validating. ``0.0unit`` means that the original boundaries are used. Using an overlap tolerance significantly reduces the speed of the validator, because rather complex geometric operations are performed. @@ -190,17 +177,15 @@ Helps to detect small folds in a surface. ``--planarity_n_tol`` refers to the no ``-r, --report`` **************** -| Outputs the validation report to the file given. The report is an navigable HTML that can be viewed in a web browser. For this option, the path to the report directory (eg ``/report_dir``) is required. This directory will contain ``report.html`` together with the required files to render it. Open it in a web browser. - +| Outputs the validation report to the file given. The report is in JSON file format, and can be used to produce nice reports automatically or to extract statistics. Use `val3dity report browser `_ with your report. ---- -.. _report_json: - -``--report_json`` -***************** -| Outputs the validation report to the file given. The report is in JSON file format, and can be used to produce nice reports automatically (our HTML reports does exactly this) or to extract statistics. +.. _listerrors: +``--listerrors`` +**************** +| Outputs a list of the val3dity errors. ---- diff --git a/misc/cli.svg b/misc/cli.svg new file mode 100644 index 00000000..cbc21152 --- /dev/null +++ b/misc/cli.svg @@ -0,0 +1 @@ +hledoux@geovalidation:~/mydata$hledoux@geovalidation:~/mydata$vhledoux@geovalidation:~/mydata$vahledoux@geovalidation:~/mydata$valhledoux@geovalidation:~/mydata$val3hledoux@geovalidation:~/mydata$val3dityERROR:Requiredargumentmissing:inputfileUSAGE:val3dity[OPTION]myinput.jsonTry`val3dity--help`formoreoptionshledoux@geovalidation:~/mydata$val3dity-hledoux@geovalidation:~/mydata$val3dity--hledoux@geovalidation:~/mydata$val3dity--helphledoux@geovalidation:~/mydata$val3dity--help|hledoux@geovalidation:~/mydata$val3dity--help|less=====val3dity=====OPTIONS-r,--reportoutputreportinJSONformat-l,--licenseseethesoftwarelicense--listerrorslistallthepossibleerrors--unittestsunittestsoutput--ignore204ignoreerror204--geom_is_sem_surfacesgeometryofaCityGMLobjectisformedbyitssemanticsurfaces-p,--primitivewhichgeometricprimitivetovalidate<Solid|CompositeSurface|MultiSurface>--verboseverboseoutput--overlap_toltolerancefortestingoverlapCompositeSolidsandBuildingParts(default=0.0)--snap_toltoleranceforsnappingverticesinGML(default=0.001)--planarity_n_toltoleranceforplanaritybasedonnormalsdeviation(default=20.0degree)--planarity_d2p_toltoleranceforplanaritydistance_to_plane(default=0.01)--,--ignore_rest:Ignorestherestofthelabeledargumentsfollowingthisflag.--versionDisplaysversioninformationandexits.-h,--helpDisplaysusageinformationandexits.--inputfile(required)inputfileineitherGML,CityJSON,OBJ,orOFF==SOMEEXAMPLES==val3dityCityJSON_input.jsonValidateseach3DprimitiveinCityJSON_input.jsonandoutputsasummarypercityobjectval3dityinput.json-r/home/elvis/temp/myreport.jsonValidateseach3Dprimitiveininput.json(CityJSONfile)andoutputsadetailedJSONreport'/home/elvis/temp/myreport.json'val3dityinput.gml--overlap_tol0.05Validateseach3Dprimitiveininput.gml,atoleranceof0.05unitisusedfortheCompositeSolidsandBuildingPartsval3dityinput.json--verboseAlldetailsofthevalidationareprintedoutval3dityinput.objValidatesthegeometriesininput.objasiftheywereaSolid(default)val3dityinput.off-pMultiSurfaceValidatesthegeometriesininput.offasaMultiSurfaceval3dityinput.gml--snap_tol0.1Theverticesininput.gmlcloserthan0.1unitaresnappedtogether(END)hledoux@geovalidation:~/mydata$lsLoD3_Railway.jsontorus.jsonhledoux@geovalidation:~/mydata$val3ditythledoux@geovalidation:~/mydata$val3ditytorus.json---val3dityCopyright(c)2011-2020,3Dgeoinformationresearchgroup,TUDelftThisprogramcomeswithABSOLUTELYNOWARRANTY.Thisisfreesoftware,andyouarewelcometoredistributeitundercertainconditions;fordetailsrunval3ditywiththe'--license'option.CityJSONinputfile#CityObjectsfound:1Translatingallcoordinatesby(-0,-0)Primitive(s)validated:All(CityGML/CityJSON/IndoorGMLhavealltheir3Dprimitivesvalidated)Parametersusedforvalidation:snap_tol0.001planarity_d2p_tol0.01planarity_n_tol20overlap_tolnoneValidationof1feature(s):[==================================================]100%+++++++++++++++++++SUMMARY+++++++++++++++++++Inputfiletype:CityJSON+++++Total#ofFeatures:1#valid:1(100.0%)#invalid:0(0.0%)Types:GenericCityObjectTotal#ofprimitives:1Solid+++++++++++++++++++++++++++++++++++++++++++++++==>Thevalidationreportwasn'tsaved,useoption'--report'.hledoux@geovalidation:~/mydata$val3ditytorus.json--rehledoux@geovalidation:~/mydata$val3ditytorus.json--reporthledoux@geovalidation:~/mydata$val3ditytorus.json--report~/temp/r.jsonValidationreportsavedto"/home/hledoux/temp/r.json"Browseitscontent:==>http://geovalidation.bk.tudelft.nl/val3dity/browser/hledoux@geovalidation:~/mydata$val3dityLoD3_Railway.json#CityObjectsfound:121Translatingallcoordinatesby(-0.56,-0.64)Validationof121feature(s):Total#ofFeatures:121#valid:89(73.6%)#invalid:32(26.4%)BridgeBridgeConstructionElementBridgeInstallationBuildingBuildingInstallationCityFurnitureCityObjectGroupRailwaySolitaryVegetationObjectTINReliefTunnelTunnelInstallationWaterBodyTotal#ofprimitives:176#valid:145(82.4%)#invalid:31(17.6%)CompositeSurfaceMultiSurfaceGeometryTemplateErrorspresent:102--CONSECUTIVE_POINTS_SAME24primitive(s)104--RING_SELF_INTERSECTION6primitive(s)201--INTERSECTION_RINGS1primitive(s)204--NON_PLANAR_POLYGON_NORMALS_DEVIATION5primitive(s)609--CITYOBJECT_HAS_NO_GEOMETRY1feature(s)hledoux@geovalidation:~/mydata$val3dity--listerrors101--TOO_FEW_POINTS102--CONSECUTIVE_POINTS_SAME103--RING_NOT_CLOSED104--RING_SELF_INTERSECTION201--INTERSECTION_RINGS202--DUPLICATED_RINGS203--NON_PLANAR_POLYGON_DISTANCE_PLANE204--NON_PLANAR_POLYGON_NORMALS_DEVIATION205--POLYGON_INTERIOR_DISCONNECTED206--INNER_RING_OUTSIDE207--INNER_RINGS_NESTED208--ORIENTATION_RINGS_SAME300--NOT_VALID_2_MANIFOLD301--TOO_FEW_POLYGONS302--SHELL_NOT_CLOSED303--NON_MANIFOLD_CASE305--MULTIPLE_CONNECTED_COMPONENTS306--SHELL_SELF_INTERSECTION307--POLYGON_WRONG_ORIENTATION401--INTERSECTION_SHELLS402--DUPLICATED_SHELLS403--INNER_SHELL_OUTSIDE404--SOLID_INTERIOR_DISCONNECTED405--WRONG_ORIENTATION_SHELL501--INTERSECTION_SOLIDS502--DUPLICATED_SOLIDS503--DISCONNECTED_SOLIDS601--BUILDINGPARTS_OVERLAP609--CITYOBJECT_HAS_NO_GEOMETRY701--CELLS_OVERLAP702--DUAL_VERTEX_OUTSIDE_CELL703--PRIMAL_DUAL_XLINKS_ERROR704--PRIMAL_DUAL_ADJACENCIES_INCONSISTENT901--INVALID_INPUT_FILE902--EMPTY_PRIMITIVE903--WRONG_INPUT_PARAMETERS904--FORMAT_NOT_SUPPORTED999--UNKNOWN_ERRORExplanationsandexamplesofeacherrorathttps://val3dity.readthedocs.io/en/2.2.0-beta.2/errors/hledoux@geovalidation:~/mydata$val3dity--hhledoux@geovalidation:~/mydata$val3dity--hehledoux@geovalidation:~/mydata$val3dity--helhledoux@geovalidation:~/mydata$val3dity--help|lhledoux@geovalidation:~/mydata$val3dity--help|lehledoux@geovalidation:~/mydata$val3dity--help|leshledoux@geovalidation:~/mydata$lhledoux@geovalidation:~/mydata$val3ditytrhledoux@geovalidation:~/mydata$val3ditytohledoux@geovalidation:~/mydata$val3ditytorhledoux@geovalidation:~/mydata$val3ditytoruhledoux@geovalidation:~/mydata$val3ditytorushledoux@geovalidation:~/mydata$val3ditytorus.hledoux@geovalidation:~/mydata$val3ditytorus.jhledoux@geovalidation:~/mydata$val3ditytorus.jshledoux@geovalidation:~/mydata$val3ditytorus.jsohledoux@geovalidation:~/mydata$val3ditytorus.json-hledoux@geovalidation:~/mydata$val3ditytorus.json--hledoux@geovalidation:~/mydata$val3ditytorus.json--rhledoux@geovalidation:~/mydata$val3ditytorus.json--reohledoux@geovalidation:~/mydata$val3ditytorus.json--rephledoux@geovalidation:~/mydata$val3ditytorus.json--repohledoux@geovalidation:~/mydata$val3ditytorus.json--reporhledoux@geovalidation:~/mydata$val3ditytorus.json--report~hledoux@geovalidation:~/mydata$val3ditytorus.json--report~/hledoux@geovalidation:~/mydata$val3ditytorus.json--report~/thledoux@geovalidation:~/mydata$val3ditytorus.json--report~/tehledoux@geovalidation:~/mydata$val3ditytorus.json--report~/temhledoux@geovalidation:~/mydata$val3ditytorus.json--report~/temphledoux@geovalidation:~/mydata$val3ditytorus.json--report~/temp/hledoux@geovalidation:~/mydata$val3ditytorus.json--report~/temp/rhledoux@geovalidation:~/mydata$val3ditytorus.json--report~/temp/r.hledoux@geovalidation:~/mydata$val3ditytorus.json--report~/temp/r.jhledoux@geovalidation:~/mydata$val3ditytorus.json--report~/temp/r.jshledoux@geovalidation:~/mydata$val3ditytorus.json--report~/temp/r.jsohledoux@geovalidation:~/mydata$val3dityLhledoux@geovalidation:~/mydata$val3dityLo[====>]8%[========>]16%[============>]24%[================>]33%[====================>]41%[========================>]49%[============================>]57%[=================================>]66%[=====================================>]74%[=========================================>]82%[=============================================>]90%[=================================================>]99%hledoux@geovalidation:~/mydata$val3dity--lhledoux@geovalidation:~/mydata$val3dity--lihledoux@geovalidation:~/mydata$val3dity--lishledoux@geovalidation:~/mydata$val3dity--listhledoux@geovalidation:~/mydata$val3dity--listehledoux@geovalidation:~/mydata$val3dity--listerhledoux@geovalidation:~/mydata$val3dity--listerrhledoux@geovalidation:~/mydata$val3dity--listerrohledoux@geovalidation:~/mydata$val3dity--listerrorhledoux@geovalidation:~/mydata$exit \ No newline at end of file diff --git a/report_browser/css/bootstrap.min.css b/report_browser/css/bootstrap.min.css new file mode 100644 index 00000000..92e3fe87 --- /dev/null +++ b/report_browser/css/bootstrap.min.css @@ -0,0 +1,7 @@ +/*! + * Bootstrap v4.3.1 (https://getbootstrap.com/) + * Copyright 2011-2019 The Bootstrap Authors + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-break:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding-top:.375rem;padding-bottom:.375rem;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:center right calc(.375em + .1875rem);background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-control.is-valid~.valid-feedback,.form-control.is-valid~.valid-tooltip,.was-validated .form-control:valid~.valid-feedback,.was-validated .form-control:valid~.valid-tooltip{display:block}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc((1em + .75rem) * 3 / 4 + 1.75rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-select.is-valid~.valid-feedback,.custom-select.is-valid~.valid-tooltip,.was-validated .custom-select:valid~.valid-feedback,.was-validated .custom-select:valid~.valid-tooltip{display:block}.form-control-file.is-valid~.valid-feedback,.form-control-file.is-valid~.valid-tooltip,.was-validated .form-control-file:valid~.valid-feedback,.was-validated .form-control-file:valid~.valid-tooltip{display:block}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid~.valid-feedback,.custom-control-input.is-valid~.valid-tooltip,.was-validated .custom-control-input:valid~.valid-feedback,.was-validated .custom-control-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid~.valid-feedback,.custom-file-input.is-valid~.valid-tooltip,.was-validated .custom-file-input:valid~.valid-feedback,.was-validated .custom-file-input:valid~.valid-tooltip{display:block}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E");background-repeat:no-repeat;background-position:center right calc(.375em + .1875rem);background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-control.is-invalid~.invalid-feedback,.form-control.is-invalid~.invalid-tooltip,.was-validated .form-control:invalid~.invalid-feedback,.was-validated .form-control:invalid~.invalid-tooltip{display:block}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc((1em + .75rem) * 3 / 4 + 1.75rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-select.is-invalid~.invalid-feedback,.custom-select.is-invalid~.invalid-tooltip,.was-validated .custom-select:invalid~.invalid-feedback,.was-validated .custom-select:invalid~.invalid-tooltip{display:block}.form-control-file.is-invalid~.invalid-feedback,.form-control-file.is-invalid~.invalid-tooltip,.was-validated .form-control-file:invalid~.invalid-feedback,.was-validated .form-control-file:invalid~.invalid-tooltip{display:block}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid~.invalid-feedback,.custom-control-input.is-invalid~.invalid-tooltip,.was-validated .custom-control-input:invalid~.invalid-feedback,.was-validated .custom-control-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid~.invalid-feedback,.custom-file-input.is-invalid~.invalid-tooltip,.was-validated .custom-file-input:invalid~.invalid-feedback,.was-validated .custom-file-input:invalid~.invalid-tooltip{display:block}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline;box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:no-repeat 50%/50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:calc(1rem + .4rem);padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar>.container,.navbar>.container-fluid{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:calc(.25rem - 1px)}.card-img-top{width:100%;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img-bottom{width:100%;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{display:-ms-flexbox;display:flex;-ms-flex:1 0 0%;flex:1 0 0%;-ms-flex-direction:column;flex-direction:column;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion>.card{overflow:hidden}.accordion>.card:not(:first-of-type) .card-header:first-child{border-radius:0}.accordion>.card:not(:first-of-type):not(:last-of-type){border-bottom:0;border-radius:0}.accordion>.card:first-of-type{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:last-of-type{border-top-left-radius:0;border-top-right-radius:0}.accordion>.card .card-header{margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:2;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:1;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-sm .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-md .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-lg .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-xl .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}.list-group-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush .list-group-item:last-child{margin-bottom:-1px}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{margin-bottom:0;border-bottom:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:.3rem;border-top-right-radius:.3rem}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:1rem;border-top:1px solid #dee2e6;border-bottom-right-radius:.3rem;border-bottom-left-radius:.3rem}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:0s .6s opacity}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%/100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;overflow-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/report_browser/css/main.css b/report_browser/css/main.css new file mode 100644 index 00000000..416b4e18 --- /dev/null +++ b/report_browser/css/main.css @@ -0,0 +1,10 @@ +.version-id { + font-weight: normal; + color: #666; +} +.version-message { + font-weight: bold +} +.version-info { + color: slategray +} \ No newline at end of file diff --git a/report_browser/img/val3dity2.png b/report_browser/img/val3dity2.png new file mode 100644 index 00000000..9896a275 Binary files /dev/null and b/report_browser/img/val3dity2.png differ diff --git a/report_browser/img/welldone.png b/report_browser/img/welldone.png new file mode 100644 index 00000000..06982613 Binary files /dev/null and b/report_browser/img/welldone.png differ diff --git a/report_browser/index.html b/report_browser/index.html new file mode 100644 index 00000000..798bf6a1 --- /dev/null +++ b/report_browser/index.html @@ -0,0 +1,286 @@ + + + + + + + + + + + + + val3dity report browser + + +
+
+
+
+
+ +
+ +
+
+ Input +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
input file{{ report.input_file }}
input file typeCityGML (watchout: CityGML support is deprecated, future version will not support it, upgrade to CityJSON 🚀){{ report.input_file_type }}
val3dity version{{ report.val3dity_version }}
validation time{{ report.time }}
{{ name }}{{ value }}
+
+
+ +
+
+ Overview +
+
+ +
+ +
+
+ +
+
    +
  • + Primitives +
    + +
    {{ getOverviewPercentage(report.primitives_overview) }}%
    +
    {{ (100 - getOverviewPercentage(report.primitives_overview)) }}%
    +
    +
  • +
  • + Features +
    + +
    {{ getOverviewPercentage(report.features_overview) + '%' }}
    +
    {{ (100 - getOverviewPercentage(report.features_overview)) + '%' }}
    +
    +
  • +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + +
totalvalidinvalid
{{ f.type }}{{ f.total }}{{ f.valid }}{{ f.total - f.valid }}
+
+ +
+
+ + + + + + + + + + + + + + + + + +
totalvalidinvalid
{{ f.type }}{{ f.total }}{{ f.valid }}{{ f.total - f.valid }}
+
+ +
+
+ + + + + + + +
{{ e }}{{ errors[e].name }}
+
+ +
+
+ + + + + + + + + + + + + + + +
errordescriptioninformation
{{ e.code }}{{ e.description }}{{ e.info }}
+
+ +
+
+
+
+
+ All features with details +
+
+
+
+
+ Search +
+ +
+
+
+ Show: +
+ + +
+
+ + +
+
+ + +
+ Error types: +
+ + +
+
+
+
    + +
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ +

report browser

+
+
+ + +
+
+
+
+
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/report_browser/js/bc945c94ff.js b/report_browser/js/bc945c94ff.js new file mode 100644 index 00000000..fe83fdc1 --- /dev/null +++ b/report_browser/js/bc945c94ff.js @@ -0,0 +1,2 @@ +window.FontAwesomeKitConfig = {"asyncLoading":{"enabled":true},"autoA11y":{"enabled":true},"baseUrl":"https://kit-free.fontawesome.com","detectConflictsUntil":null,"license":"free","method":"css","minify":{"enabled":true},"v4FontFaceShim":{"enabled":true},"v4shim":{"enabled":true},"version":"latest"}; +!function(){function r(e){var t,n=[],i=document,o=i.documentElement.doScroll,r="DOMContentLoaded",a=(o?/^loaded|^c/:/^loaded|^i|^c/).test(i.readyState);a||i.addEventListener(r,t=function(){for(i.removeEventListener(r,t),a=1;t=n.shift();)t()}),a?setTimeout(e,0):n.push(e)}!function(){if(!(void 0===window.Element||"classList"in document.documentElement)){var e,t,n,i=Array.prototype,o=i.push,r=i.splice,a=i.join;d.prototype={add:function(e){this.contains(e)||(o.call(this,e),this.el.className=this.toString())},contains:function(e){return-1!=this.el.className.indexOf(e)},item:function(e){return this[e]||null},remove:function(e){if(this.contains(e)){for(var t=0;tthis._items.length-1||t<0))if(this._isSliding)g(this._element).one(Q.SLID,function(){return e.to(t)});else{if(n===t)return this.pause(),void this.cycle();var i=ndocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},t._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},t._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=t.left+t.right
',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:Ee},je="show",He="out",Re={HIDE:"hide"+De,HIDDEN:"hidden"+De,SHOW:"show"+De,SHOWN:"shown"+De,INSERTED:"inserted"+De,CLICK:"click"+De,FOCUSIN:"focusin"+De,FOCUSOUT:"focusout"+De,MOUSEENTER:"mouseenter"+De,MOUSELEAVE:"mouseleave"+De},xe="fade",Fe="show",Ue=".tooltip-inner",We=".arrow",qe="hover",Me="focus",Ke="click",Qe="manual",Be=function(){function i(t,e){if("undefined"==typeof u)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var t=i.prototype;return t.enable=function(){this._isEnabled=!0},t.disable=function(){this._isEnabled=!1},t.toggleEnabled=function(){this._isEnabled=!this._isEnabled},t.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=g(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(g(this.getTipElement()).hasClass(Fe))return void this._leave(null,this);this._enter(null,this)}},t.dispose=function(){clearTimeout(this._timeout),g.removeData(this.element,this.constructor.DATA_KEY),g(this.element).off(this.constructor.EVENT_KEY),g(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&g(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,(this._activeTrigger=null)!==this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},t.show=function(){var e=this;if("none"===g(this.element).css("display"))throw new Error("Please use show on visible elements");var t=g.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){g(this.element).trigger(t);var n=_.findShadowRoot(this.element),i=g.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(t.isDefaultPrevented()||!i)return;var o=this.getTipElement(),r=_.getUID(this.constructor.NAME);o.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&g(o).addClass(xe);var s="function"==typeof this.config.placement?this.config.placement.call(this,o,this.element):this.config.placement,a=this._getAttachment(s);this.addAttachmentClass(a);var l=this._getContainer();g(o).data(this.constructor.DATA_KEY,this),g.contains(this.element.ownerDocument.documentElement,this.tip)||g(o).appendTo(l),g(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new u(this.element,o,{placement:a,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:We},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}}),g(o).addClass(Fe),"ontouchstart"in document.documentElement&&g(document.body).children().on("mouseover",null,g.noop);var c=function(){e.config.animation&&e._fixTransition();var t=e._hoverState;e._hoverState=null,g(e.element).trigger(e.constructor.Event.SHOWN),t===He&&e._leave(null,e)};if(g(this.tip).hasClass(xe)){var h=_.getTransitionDurationFromElement(this.tip);g(this.tip).one(_.TRANSITION_END,c).emulateTransitionEnd(h)}else c()}},t.hide=function(t){var e=this,n=this.getTipElement(),i=g.Event(this.constructor.Event.HIDE),o=function(){e._hoverState!==je&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),g(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(g(this.element).trigger(i),!i.isDefaultPrevented()){if(g(n).removeClass(Fe),"ontouchstart"in document.documentElement&&g(document.body).children().off("mouseover",null,g.noop),this._activeTrigger[Ke]=!1,this._activeTrigger[Me]=!1,this._activeTrigger[qe]=!1,g(this.tip).hasClass(xe)){var r=_.getTransitionDurationFromElement(n);g(n).one(_.TRANSITION_END,o).emulateTransitionEnd(r)}else o();this._hoverState=""}},t.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},t.isWithContent=function(){return Boolean(this.getTitle())},t.addAttachmentClass=function(t){g(this.getTipElement()).addClass(Ae+"-"+t)},t.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},t.setContent=function(){var t=this.getTipElement();this.setElementContent(g(t.querySelectorAll(Ue)),this.getTitle()),g(t).removeClass(xe+" "+Fe)},t.setElementContent=function(t,e){"object"!=typeof e||!e.nodeType&&!e.jquery?this.config.html?(this.config.sanitize&&(e=Se(e,this.config.whiteList,this.config.sanitizeFn)),t.html(e)):t.text(e):this.config.html?g(e).parent().is(t)||t.empty().append(e):t.text(g(e).text())},t.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},t._getOffset=function(){var e=this,t={};return"function"==typeof this.config.offset?t.fn=function(t){return t.offsets=l({},t.offsets,e.config.offset(t.offsets,e.element)||{}),t}:t.offset=this.config.offset,t},t._getContainer=function(){return!1===this.config.container?document.body:_.isElement(this.config.container)?g(this.config.container):g(document).find(this.config.container)},t._getAttachment=function(t){return Pe[t.toUpperCase()]},t._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(t){if("click"===t)g(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(t){return i.toggle(t)});else if(t!==Qe){var e=t===qe?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=t===qe?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;g(i.element).on(e,i.config.selector,function(t){return i._enter(t)}).on(n,i.config.selector,function(t){return i._leave(t)})}}),g(this.element).closest(".modal").on("hide.bs.modal",function(){i.element&&i.hide()}),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},t._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},t._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?Me:qe]=!0),g(e.getTipElement()).hasClass(Fe)||e._hoverState===je?e._hoverState=je:(clearTimeout(e._timeout),e._hoverState=je,e.config.delay&&e.config.delay.show?e._timeout=setTimeout(function(){e._hoverState===je&&e.show()},e.config.delay.show):e.show())},t._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?Me:qe]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=He,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout(function(){e._hoverState===He&&e.hide()},e.config.delay.hide):e.hide())},t._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},t._getConfig=function(t){var e=g(this.element).data();return Object.keys(e).forEach(function(t){-1!==Oe.indexOf(t)&&delete e[t]}),"number"==typeof(t=l({},this.constructor.Default,e,"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),_.typeCheckConfig(be,t,this.constructor.DefaultType),t.sanitize&&(t.template=Se(t.template,t.whiteList,t.sanitizeFn)),t},t._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},t._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(Ne);null!==e&&e.length&&t.removeClass(e.join(""))},t._handlePopperPlacementChange=function(t){var e=t.instance;this.tip=e.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},t._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(g(t).removeClass(xe),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},i._jQueryInterface=function(n){return this.each(function(){var t=g(this).data(Ie),e="object"==typeof n&&n;if((t||!/dispose|hide/.test(n))&&(t||(t=new i(this,e),g(this).data(Ie,t)),"string"==typeof n)){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.3.1"}},{key:"Default",get:function(){return Le}},{key:"NAME",get:function(){return be}},{key:"DATA_KEY",get:function(){return Ie}},{key:"Event",get:function(){return Re}},{key:"EVENT_KEY",get:function(){return De}},{key:"DefaultType",get:function(){return ke}}]),i}();g.fn[be]=Be._jQueryInterface,g.fn[be].Constructor=Be,g.fn[be].noConflict=function(){return g.fn[be]=we,Be._jQueryInterface};var Ve="popover",Ye="bs.popover",ze="."+Ye,Xe=g.fn[Ve],$e="bs-popover",Ge=new RegExp("(^|\\s)"+$e+"\\S+","g"),Je=l({},Be.Default,{placement:"right",trigger:"click",content:"",template:''}),Ze=l({},Be.DefaultType,{content:"(string|element|function)"}),tn="fade",en="show",nn=".popover-header",on=".popover-body",rn={HIDE:"hide"+ze,HIDDEN:"hidden"+ze,SHOW:"show"+ze,SHOWN:"shown"+ze,INSERTED:"inserted"+ze,CLICK:"click"+ze,FOCUSIN:"focusin"+ze,FOCUSOUT:"focusout"+ze,MOUSEENTER:"mouseenter"+ze,MOUSELEAVE:"mouseleave"+ze},sn=function(t){var e,n;function i(){return t.apply(this,arguments)||this}n=t,(e=i).prototype=Object.create(n.prototype),(e.prototype.constructor=e).__proto__=n;var o=i.prototype;return o.isWithContent=function(){return this.getTitle()||this._getContent()},o.addAttachmentClass=function(t){g(this.getTipElement()).addClass($e+"-"+t)},o.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},o.setContent=function(){var t=g(this.getTipElement());this.setElementContent(t.find(nn),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(on),e),t.removeClass(tn+" "+en)},o._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},o._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(Ge);null!==e&&0=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t + + {{ primitive.type }} + {{ primitive.validity ? 'valid' : 'invalid' }} + {{ primitive.id }} +
    +
  • Error {{ e.code }} {{ e.description }} | id={{ e.id }} | info={{ e.info }}
  • + +
+ + `, + data: function() { + return { + isOpen: false + } + }, + methods: { + toggle() { + this.isOpen = !this.isOpen; + } + } +}) + +Vue.component('feature-item', { + props: ['feature'], + template: ` +
  • + + {{ feature.type }} + {{ feature.validity ? 'valid' : 'invalid' }} + {{ feature.id }} +
      +
    • Error {{ e.code }} {{ e.description }} | id={{ e.id }} | info={{ e.info }}
    • + +
    +
  • + `, + data: function() { + return { + isOpen: false + } + }, + methods: { + toggle() { + this.isOpen = !this.isOpen; + } + } +}) + +var app = new Vue({ + el: '#app', + data: function() { + var fre = this.getUrlVars()["f"]; + if (fre != null) { + console.log("URL params for the report to load."); + this.load_report_from_url(fre); + } + return { + file_loaded: false, + report: {}, + search_term: null, + feature_filter: "all", + error_filters: [], + } + }, + methods: { + getUrlVars() { + var vars = {}; + var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) { + vars[key] = value; + }); + return vars; + }, + load_report_from_url(fre) { + console.log("fetching the report from a URL"); + var theurl = fre; + var self = this; + $.getJSON(theurl, + function(data) { + // console.log(data); + self.set_report_data(data); + }); + console.log("fetched."); + }, + reset() { + this.report = {}; + this.filter_value = false; + this.file_loaded = false; + this.error_filters = []; + }, + getAlertClass(percentage) { + if( percentage > 80) { + return ['alert-success']; + } + else if (percentage > 50) { + return ['alert-warning']; + } + else { + return ['alert-danger']; + } + }, + getOverviewPercentage(report_list) { + var total = report_list.reduce((acc, item) => acc + item.total, 0); + var valid = report_list.reduce((acc, item) => acc + item.valid, 0); + if (total == 0){ + return 0; + } + return Math.floor((valid / total) * 100);; + }, + selectedFile() { + console.log("Selected a val3dity report JSON file..."); + console.log(this.$refs.reportFile.files[0]); + + let file = this.$refs.reportFile.files[0]; + if (!file || file.type != "application/json") + { + console.log("This is not a val3dity report JSON file. Abort."); + alert("This is not a val3dity report file (must be JSON & v2.2+)."); + return; + } + + let reader = new FileReader(); + reader.readAsText(file, "UTF-8"); + reader.onload = evt => { + jre = JSON.parse(evt.target.result); + if (jre.type == "val3dity_report") { + this.set_report_data(jre); + } + else { + console.log("This is not a val3dity report JSON file. Abort."); + alert("This is not a val3dity report file (must be JSON & v2.2+)."); + return; + } + } + }, + set_report_data(data) { + this.report = data; + this.file_loaded = true; + this.error_filters = this.report.all_errors; + } + }, + computed: { + filteredFeatures: function () { + if (this.feature_filter == "valid") { + var new_features = $.extend(true, [], this.report.features); + + return new_features.filter(function f(p) { + if (p.validity == true) { + return true; + } + + if (p.primitives) { + return (p.primitives = p.primitives.filter(f)).length + } + }); + } + else if (this.feature_filter == "invalid") { + var new_features = $.extend(true, [], this.report.features); + + let error_filters = this.error_filters; + + return new_features.filter(function f(p) { + var show = false; + + if (p.errors && p.errors.some(e => error_filters.includes(e.code))) { + show = true; + } + + if (p.primitives) { + show = show | (p.primitives = p.primitives.filter(f)).length; + } + + return show; + }); + } + else if (this.feature_filter == "all") { + return this.report.features; + } + } + } +}) + +Vue.config.devtools = true + + diff --git a/report_browser/js/jquery.min.js b/report_browser/js/jquery.min.js new file mode 100644 index 00000000..a1c07fd8 --- /dev/null +++ b/report_browser/js/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.4.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],E=C.document,r=Object.getPrototypeOf,s=t.slice,g=t.concat,u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.4.1",k=function(e,t){return new k.fn.init(e,t)},p=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function d(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp($),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+$),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ne=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(m.childNodes),m.childNodes),t[m.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&((e?e.ownerDocument||e:m)!==C&&T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!A[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&U.test(t)){(s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=k),o=(l=h(t)).length;while(o--)l[o]="#"+s+" "+xe(l[o]);c=l.join(","),f=ee.test(t)&&ye(e.parentNode)||e}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){A(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[k]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:m;return r!==C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),m!==C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=k,!C.getElementsByName||!C.getElementsByName(k).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+k+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+k+"+*").length||v.push(".#.+[+~]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",$)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e===C||e.ownerDocument===m&&y(m,e)?-1:t===C||t.ownerDocument===m&&y(m,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===C?-1:t===C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]===m?-1:s[r]===m?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==C&&T(e),d.matchesSelector&&E&&!A[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){A(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=p[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&p(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?k.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?k.grep(e,function(e){return e===n!==r}):"string"!=typeof n?k.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(k.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:L.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof k?t[0]:t,k.merge(this,k.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),D.test(r[1])&&k.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(k):k.makeArray(e,this)}).prototype=k.fn,q=k(E);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}k.fn.extend({has:function(e){var t=k(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?k.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;nx",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ee=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function Se(){return!1}function Ne(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Se;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return k().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=k.guid++)),e.each(function(){k.event.add(this,t,i,r,n)})}function De(e,i,o){o?(Q.set(e,i,!1),k.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Q.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(k.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Q.set(this,i,r),t=o(this,i),this[i](),r!==(n=Q.get(this,i))||t?Q.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Q.set(this,i,{value:k.event.trigger(k.extend(r[0],k.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,i)&&k.event.add(e,i,ke)}k.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&k.find.matchesSelector(ie,i),n.guid||(n.guid=k.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(R)||[""]).length;while(l--)d=g=(s=Ee.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=k.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=k.event.special[d]||{},c=k.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&k.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),k.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(R)||[""]).length;while(l--)if(d=g=(s=Ee.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=k.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||k.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)k.event.remove(e,d+t[l],n,r,!0);k.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=k.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=k.event.special[s.type]||{};for(u[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/\s*$/g;function Oe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&k(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Re(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Me(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Q.hasData(e)&&(o=Q.access(e),a=Q.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=oe(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||k.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Vt,Gt=[],Yt=/(=)\?(?=&|$)|\?\?/;k.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Gt.pop()||k.expando+"_"+kt++;return this[e]=!0,e}}),k.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Yt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Yt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Yt,"$1"+r):!1!==e.jsonp&&(e.url+=(St.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||k.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?k(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Gt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Vt=E.implementation.createHTMLDocument("").body).innerHTML="
    ",2===Vt.childNodes.length),k.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=D.exec(e))?[t.createElement(i[1])]:(i=we([e],t,o),o&&o.length&&k(o).remove(),k.merge([],i.childNodes)));var r,i,o},k.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(k.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},k.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){k.fn[t]=function(e){return this.on(t,e)}}),k.expr.pseudos.animated=function(t){return k.grep(k.timers,function(e){return t===e.elem}).length},k.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=k.css(e,"position"),c=k(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=k.css(e,"top"),u=k.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,k.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},k.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){k.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===k.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===k.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=k(e).offset()).top+=k.css(e,"borderTopWidth",!0),i.left+=k.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-k.css(r,"marginTop",!0),left:t.left-i.left-k.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===k.css(e,"position"))e=e.offsetParent;return e||ie})}}),k.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;k.fn[t]=function(e){return _(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),k.each(["top","left"],function(e,n){k.cssHooks[n]=ze(y.pixelPosition,function(e,t){if(t)return t=_e(e,n),$e.test(t)?k(e).position()[n]+"px":t})}),k.each({Height:"height",Width:"width"},function(a,s){k.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){k.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return _(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?k.css(e,t,i):k.style(e,t,n,i)},s,n?e:void 0,n)}})}),k.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(e,n){k.fn[n]=function(e,t){return 0=0&&Math.floor(t)===t&&isFinite(e)}function u(e){return n(e)&&"function"==typeof e.then&&"function"==typeof e.catch}function l(e){return null==e?"":Array.isArray(e)||s(e)&&e.toString===a?JSON.stringify(e,null,2):String(e)}function f(e){var t=parseFloat(e);return isNaN(t)?e:t}function p(e,t){for(var n=Object.create(null),r=e.split(","),i=0;i-1)return e.splice(n,1)}}var m=Object.prototype.hasOwnProperty;function y(e,t){return m.call(e,t)}function g(e){var t=Object.create(null);return function(n){return t[n]||(t[n]=e(n))}}var _=/-(\w)/g,b=g(function(e){return e.replace(_,function(e,t){return t?t.toUpperCase():""})}),$=g(function(e){return e.charAt(0).toUpperCase()+e.slice(1)}),w=/\B([A-Z])/g,C=g(function(e){return e.replace(w,"-$1").toLowerCase()});var x=Function.prototype.bind?function(e,t){return e.bind(t)}:function(e,t){function n(n){var r=arguments.length;return r?r>1?e.apply(t,arguments):e.call(t,n):e.call(t)}return n._length=e.length,n};function k(e,t){t=t||0;for(var n=e.length-t,r=new Array(n);n--;)r[n]=e[n+t];return r}function A(e,t){for(var n in t)e[n]=t[n];return e}function O(e){for(var t={},n=0;n0,Z=J&&J.indexOf("edge/")>0,G=(J&&J.indexOf("android"),J&&/iphone|ipad|ipod|ios/.test(J)||"ios"===K),X=(J&&/chrome\/\d+/.test(J),J&&/phantomjs/.test(J),J&&J.match(/firefox\/(\d+)/)),Y={}.watch,Q=!1;if(z)try{var ee={};Object.defineProperty(ee,"passive",{get:function(){Q=!0}}),window.addEventListener("test-passive",null,ee)}catch(e){}var te=function(){return void 0===B&&(B=!z&&!V&&"undefined"!=typeof global&&(global.process&&"server"===global.process.env.VUE_ENV)),B},ne=z&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__;function re(e){return"function"==typeof e&&/native code/.test(e.toString())}var ie,oe="undefined"!=typeof Symbol&&re(Symbol)&&"undefined"!=typeof Reflect&&re(Reflect.ownKeys);ie="undefined"!=typeof Set&&re(Set)?Set:function(){function e(){this.set=Object.create(null)}return e.prototype.has=function(e){return!0===this.set[e]},e.prototype.add=function(e){this.set[e]=!0},e.prototype.clear=function(){this.set=Object.create(null)},e}();var ae=S,se=0,ce=function(){this.id=se++,this.subs=[]};ce.prototype.addSub=function(e){this.subs.push(e)},ce.prototype.removeSub=function(e){h(this.subs,e)},ce.prototype.depend=function(){ce.target&&ce.target.addDep(this)},ce.prototype.notify=function(){for(var e=this.subs.slice(),t=0,n=e.length;t-1)if(o&&!y(i,"default"))a=!1;else if(""===a||a===C(e)){var c=Pe(String,i.type);(c<0||s0&&(st((u=e(u,(a||"")+"_"+c))[0])&&st(f)&&(s[l]=he(f.text+u[0].text),u.shift()),s.push.apply(s,u)):i(u)?st(f)?s[l]=he(f.text+u):""!==u&&s.push(he(u)):st(u)&&st(f)?s[l]=he(f.text+u.text):(r(o._isVList)&&n(u.tag)&&t(u.key)&&n(a)&&(u.key="__vlist"+a+"_"+c+"__"),s.push(u)));return s}(e):void 0}function st(e){return n(e)&&n(e.text)&&!1===e.isComment}function ct(e,t){if(e){for(var n=Object.create(null),r=oe?Reflect.ownKeys(e):Object.keys(e),i=0;i0,a=t?!!t.$stable:!o,s=t&&t.$key;if(t){if(t._normalized)return t._normalized;if(a&&r&&r!==e&&s===r.$key&&!o&&!r.$hasNormal)return r;for(var c in i={},t)t[c]&&"$"!==c[0]&&(i[c]=pt(n,c,t[c]))}else i={};for(var u in n)u in i||(i[u]=dt(n,u));return t&&Object.isExtensible(t)&&(t._normalized=i),R(i,"$stable",a),R(i,"$key",s),R(i,"$hasNormal",o),i}function pt(e,t,n){var r=function(){var e=arguments.length?n.apply(null,arguments):n({});return(e=e&&"object"==typeof e&&!Array.isArray(e)?[e]:at(e))&&(0===e.length||1===e.length&&e[0].isComment)?void 0:e};return n.proxy&&Object.defineProperty(e,t,{get:r,enumerable:!0,configurable:!0}),r}function dt(e,t){return function(){return e[t]}}function vt(e,t){var r,i,a,s,c;if(Array.isArray(e)||"string"==typeof e)for(r=new Array(e.length),i=0,a=e.length;idocument.createEvent("Event").timeStamp&&(sn=function(){return cn.now()})}function un(){var e,t;for(an=sn(),rn=!0,Qt.sort(function(e,t){return e.id-t.id}),on=0;onon&&Qt[n].id>e.id;)n--;Qt.splice(n+1,0,e)}else Qt.push(e);nn||(nn=!0,Ye(un))}}(this)},fn.prototype.run=function(){if(this.active){var e=this.get();if(e!==this.value||o(e)||this.deep){var t=this.value;if(this.value=e,this.user)try{this.cb.call(this.vm,e,t)}catch(e){Re(e,this.vm,'callback for watcher "'+this.expression+'"')}else this.cb.call(this.vm,e,t)}}},fn.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},fn.prototype.depend=function(){for(var e=this.deps.length;e--;)this.deps[e].depend()},fn.prototype.teardown=function(){if(this.active){this.vm._isBeingDestroyed||h(this.vm._watchers,this);for(var e=this.deps.length;e--;)this.deps[e].removeSub(this);this.active=!1}};var pn={enumerable:!0,configurable:!0,get:S,set:S};function dn(e,t,n){pn.get=function(){return this[t][n]},pn.set=function(e){this[t][n]=e},Object.defineProperty(e,n,pn)}function vn(e){e._watchers=[];var t=e.$options;t.props&&function(e,t){var n=e.$options.propsData||{},r=e._props={},i=e.$options._propKeys=[];e.$parent&&$e(!1);var o=function(o){i.push(o);var a=Me(o,t,n,e);xe(r,o,a),o in e||dn(e,"_props",o)};for(var a in t)o(a);$e(!0)}(e,t.props),t.methods&&function(e,t){e.$options.props;for(var n in t)e[n]="function"!=typeof t[n]?S:x(t[n],e)}(e,t.methods),t.data?function(e){var t=e.$options.data;s(t=e._data="function"==typeof t?function(e,t){le();try{return e.call(t,t)}catch(e){return Re(e,t,"data()"),{}}finally{fe()}}(t,e):t||{})||(t={});var n=Object.keys(t),r=e.$options.props,i=(e.$options.methods,n.length);for(;i--;){var o=n[i];r&&y(r,o)||(a=void 0,36!==(a=(o+"").charCodeAt(0))&&95!==a&&dn(e,"_data",o))}var a;Ce(t,!0)}(e):Ce(e._data={},!0),t.computed&&function(e,t){var n=e._computedWatchers=Object.create(null),r=te();for(var i in t){var o=t[i],a="function"==typeof o?o:o.get;r||(n[i]=new fn(e,a||S,S,hn)),i in e||mn(e,i,o)}}(e,t.computed),t.watch&&t.watch!==Y&&function(e,t){for(var n in t){var r=t[n];if(Array.isArray(r))for(var i=0;i-1:"string"==typeof e?e.split(",").indexOf(t)>-1:(n=e,"[object RegExp]"===a.call(n)&&e.test(t));var n}function An(e,t){var n=e.cache,r=e.keys,i=e._vnode;for(var o in n){var a=n[o];if(a){var s=xn(a.componentOptions);s&&!t(s)&&On(n,o,r,i)}}}function On(e,t,n,r){var i=e[t];!i||r&&i.tag===r.tag||i.componentInstance.$destroy(),e[t]=null,h(n,t)}!function(t){t.prototype._init=function(t){var n=this;n._uid=bn++,n._isVue=!0,t&&t._isComponent?function(e,t){var n=e.$options=Object.create(e.constructor.options),r=t._parentVnode;n.parent=t.parent,n._parentVnode=r;var i=r.componentOptions;n.propsData=i.propsData,n._parentListeners=i.listeners,n._renderChildren=i.children,n._componentTag=i.tag,t.render&&(n.render=t.render,n.staticRenderFns=t.staticRenderFns)}(n,t):n.$options=De($n(n.constructor),t||{},n),n._renderProxy=n,n._self=n,function(e){var t=e.$options,n=t.parent;if(n&&!t.abstract){for(;n.$options.abstract&&n.$parent;)n=n.$parent;n.$children.push(e)}e.$parent=n,e.$root=n?n.$root:e,e.$children=[],e.$refs={},e._watcher=null,e._inactive=null,e._directInactive=!1,e._isMounted=!1,e._isDestroyed=!1,e._isBeingDestroyed=!1}(n),function(e){e._events=Object.create(null),e._hasHookEvent=!1;var t=e.$options._parentListeners;t&&qt(e,t)}(n),function(t){t._vnode=null,t._staticTrees=null;var n=t.$options,r=t.$vnode=n._parentVnode,i=r&&r.context;t.$slots=ut(n._renderChildren,i),t.$scopedSlots=e,t._c=function(e,n,r,i){return Pt(t,e,n,r,i,!1)},t.$createElement=function(e,n,r,i){return Pt(t,e,n,r,i,!0)};var o=r&&r.data;xe(t,"$attrs",o&&o.attrs||e,null,!0),xe(t,"$listeners",n._parentListeners||e,null,!0)}(n),Yt(n,"beforeCreate"),function(e){var t=ct(e.$options.inject,e);t&&($e(!1),Object.keys(t).forEach(function(n){xe(e,n,t[n])}),$e(!0))}(n),vn(n),function(e){var t=e.$options.provide;t&&(e._provided="function"==typeof t?t.call(e):t)}(n),Yt(n,"created"),n.$options.el&&n.$mount(n.$options.el)}}(wn),function(e){var t={get:function(){return this._data}},n={get:function(){return this._props}};Object.defineProperty(e.prototype,"$data",t),Object.defineProperty(e.prototype,"$props",n),e.prototype.$set=ke,e.prototype.$delete=Ae,e.prototype.$watch=function(e,t,n){if(s(t))return _n(this,e,t,n);(n=n||{}).user=!0;var r=new fn(this,e,t,n);if(n.immediate)try{t.call(this,r.value)}catch(e){Re(e,this,'callback for immediate watcher "'+r.expression+'"')}return function(){r.teardown()}}}(wn),function(e){var t=/^hook:/;e.prototype.$on=function(e,n){var r=this;if(Array.isArray(e))for(var i=0,o=e.length;i1?k(t):t;for(var n=k(arguments,1),r='event handler for "'+e+'"',i=0,o=t.length;iparseInt(this.max)&&On(a,s[0],s,this._vnode)),t.data.keepAlive=!0}return t||e&&e[0]}}};!function(e){var t={get:function(){return F}};Object.defineProperty(e,"config",t),e.util={warn:ae,extend:A,mergeOptions:De,defineReactive:xe},e.set=ke,e.delete=Ae,e.nextTick=Ye,e.observable=function(e){return Ce(e),e},e.options=Object.create(null),M.forEach(function(t){e.options[t+"s"]=Object.create(null)}),e.options._base=e,A(e.options.components,Tn),function(e){e.use=function(e){var t=this._installedPlugins||(this._installedPlugins=[]);if(t.indexOf(e)>-1)return this;var n=k(arguments,1);return n.unshift(this),"function"==typeof e.install?e.install.apply(e,n):"function"==typeof e&&e.apply(null,n),t.push(e),this}}(e),function(e){e.mixin=function(e){return this.options=De(this.options,e),this}}(e),Cn(e),function(e){M.forEach(function(t){e[t]=function(e,n){return n?("component"===t&&s(n)&&(n.name=n.name||e,n=this.options._base.extend(n)),"directive"===t&&"function"==typeof n&&(n={bind:n,update:n}),this.options[t+"s"][e]=n,n):this.options[t+"s"][e]}})}(e)}(wn),Object.defineProperty(wn.prototype,"$isServer",{get:te}),Object.defineProperty(wn.prototype,"$ssrContext",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Object.defineProperty(wn,"FunctionalRenderContext",{value:Tt}),wn.version="2.6.11";var En=p("style,class"),Nn=p("input,textarea,option,select,progress"),jn=function(e,t,n){return"value"===n&&Nn(e)&&"button"!==t||"selected"===n&&"option"===e||"checked"===n&&"input"===e||"muted"===n&&"video"===e},Dn=p("contenteditable,draggable,spellcheck"),Ln=p("events,caret,typing,plaintext-only"),Mn=function(e,t){return Hn(t)||"false"===t?"false":"contenteditable"===e&&Ln(t)?t:"true"},In=p("allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,translate,truespeed,typemustmatch,visible"),Fn="http://www.w3.org/1999/xlink",Pn=function(e){return":"===e.charAt(5)&&"xlink"===e.slice(0,5)},Rn=function(e){return Pn(e)?e.slice(6,e.length):""},Hn=function(e){return null==e||!1===e};function Bn(e){for(var t=e.data,r=e,i=e;n(i.componentInstance);)(i=i.componentInstance._vnode)&&i.data&&(t=Un(i.data,t));for(;n(r=r.parent);)r&&r.data&&(t=Un(t,r.data));return function(e,t){if(n(e)||n(t))return zn(e,Vn(t));return""}(t.staticClass,t.class)}function Un(e,t){return{staticClass:zn(e.staticClass,t.staticClass),class:n(e.class)?[e.class,t.class]:t.class}}function zn(e,t){return e?t?e+" "+t:e:t||""}function Vn(e){return Array.isArray(e)?function(e){for(var t,r="",i=0,o=e.length;i-1?hr(e,t,n):In(t)?Hn(n)?e.removeAttribute(t):(n="allowfullscreen"===t&&"EMBED"===e.tagName?"true":t,e.setAttribute(t,n)):Dn(t)?e.setAttribute(t,Mn(t,n)):Pn(t)?Hn(n)?e.removeAttributeNS(Fn,Rn(t)):e.setAttributeNS(Fn,t,n):hr(e,t,n)}function hr(e,t,n){if(Hn(n))e.removeAttribute(t);else{if(q&&!W&&"TEXTAREA"===e.tagName&&"placeholder"===t&&""!==n&&!e.__ieph){var r=function(t){t.stopImmediatePropagation(),e.removeEventListener("input",r)};e.addEventListener("input",r),e.__ieph=!0}e.setAttribute(t,n)}}var mr={create:dr,update:dr};function yr(e,r){var i=r.elm,o=r.data,a=e.data;if(!(t(o.staticClass)&&t(o.class)&&(t(a)||t(a.staticClass)&&t(a.class)))){var s=Bn(r),c=i._transitionClasses;n(c)&&(s=zn(s,Vn(c))),s!==i._prevClass&&(i.setAttribute("class",s),i._prevClass=s)}}var gr,_r,br,$r,wr,Cr,xr={create:yr,update:yr},kr=/[\w).+\-_$\]]/;function Ar(e){var t,n,r,i,o,a=!1,s=!1,c=!1,u=!1,l=0,f=0,p=0,d=0;for(r=0;r=0&&" "===(h=e.charAt(v));v--);h&&kr.test(h)||(u=!0)}}else void 0===i?(d=r+1,i=e.slice(0,r).trim()):m();function m(){(o||(o=[])).push(e.slice(d,r).trim()),d=r+1}if(void 0===i?i=e.slice(0,r).trim():0!==d&&m(),o)for(r=0;r-1?{exp:e.slice(0,$r),key:'"'+e.slice($r+1)+'"'}:{exp:e,key:null};_r=e,$r=wr=Cr=0;for(;!zr();)Vr(br=Ur())?Jr(br):91===br&&Kr(br);return{exp:e.slice(0,wr),key:e.slice(wr+1,Cr)}}(e);return null===n.key?e+"="+t:"$set("+n.exp+", "+n.key+", "+t+")"}function Ur(){return _r.charCodeAt(++$r)}function zr(){return $r>=gr}function Vr(e){return 34===e||39===e}function Kr(e){var t=1;for(wr=$r;!zr();)if(Vr(e=Ur()))Jr(e);else if(91===e&&t++,93===e&&t--,0===t){Cr=$r;break}}function Jr(e){for(var t=e;!zr()&&(e=Ur())!==t;);}var qr,Wr="__r",Zr="__c";function Gr(e,t,n){var r=qr;return function i(){null!==t.apply(null,arguments)&&Qr(e,i,n,r)}}var Xr=Ve&&!(X&&Number(X[1])<=53);function Yr(e,t,n,r){if(Xr){var i=an,o=t;t=o._wrapper=function(e){if(e.target===e.currentTarget||e.timeStamp>=i||e.timeStamp<=0||e.target.ownerDocument!==document)return o.apply(this,arguments)}}qr.addEventListener(e,t,Q?{capture:n,passive:r}:n)}function Qr(e,t,n,r){(r||qr).removeEventListener(e,t._wrapper||t,n)}function ei(e,r){if(!t(e.data.on)||!t(r.data.on)){var i=r.data.on||{},o=e.data.on||{};qr=r.elm,function(e){if(n(e[Wr])){var t=q?"change":"input";e[t]=[].concat(e[Wr],e[t]||[]),delete e[Wr]}n(e[Zr])&&(e.change=[].concat(e[Zr],e.change||[]),delete e[Zr])}(i),rt(i,o,Yr,Qr,Gr,r.context),qr=void 0}}var ti,ni={create:ei,update:ei};function ri(e,r){if(!t(e.data.domProps)||!t(r.data.domProps)){var i,o,a=r.elm,s=e.data.domProps||{},c=r.data.domProps||{};for(i in n(c.__ob__)&&(c=r.data.domProps=A({},c)),s)i in c||(a[i]="");for(i in c){if(o=c[i],"textContent"===i||"innerHTML"===i){if(r.children&&(r.children.length=0),o===s[i])continue;1===a.childNodes.length&&a.removeChild(a.childNodes[0])}if("value"===i&&"PROGRESS"!==a.tagName){a._value=o;var u=t(o)?"":String(o);ii(a,u)&&(a.value=u)}else if("innerHTML"===i&&qn(a.tagName)&&t(a.innerHTML)){(ti=ti||document.createElement("div")).innerHTML=""+o+"";for(var l=ti.firstChild;a.firstChild;)a.removeChild(a.firstChild);for(;l.firstChild;)a.appendChild(l.firstChild)}else if(o!==s[i])try{a[i]=o}catch(e){}}}}function ii(e,t){return!e.composing&&("OPTION"===e.tagName||function(e,t){var n=!0;try{n=document.activeElement!==e}catch(e){}return n&&e.value!==t}(e,t)||function(e,t){var r=e.value,i=e._vModifiers;if(n(i)){if(i.number)return f(r)!==f(t);if(i.trim)return r.trim()!==t.trim()}return r!==t}(e,t))}var oi={create:ri,update:ri},ai=g(function(e){var t={},n=/:(.+)/;return e.split(/;(?![^(]*\))/g).forEach(function(e){if(e){var r=e.split(n);r.length>1&&(t[r[0].trim()]=r[1].trim())}}),t});function si(e){var t=ci(e.style);return e.staticStyle?A(e.staticStyle,t):t}function ci(e){return Array.isArray(e)?O(e):"string"==typeof e?ai(e):e}var ui,li=/^--/,fi=/\s*!important$/,pi=function(e,t,n){if(li.test(t))e.style.setProperty(t,n);else if(fi.test(n))e.style.setProperty(C(t),n.replace(fi,""),"important");else{var r=vi(t);if(Array.isArray(n))for(var i=0,o=n.length;i-1?t.split(yi).forEach(function(t){return e.classList.add(t)}):e.classList.add(t);else{var n=" "+(e.getAttribute("class")||"")+" ";n.indexOf(" "+t+" ")<0&&e.setAttribute("class",(n+t).trim())}}function _i(e,t){if(t&&(t=t.trim()))if(e.classList)t.indexOf(" ")>-1?t.split(yi).forEach(function(t){return e.classList.remove(t)}):e.classList.remove(t),e.classList.length||e.removeAttribute("class");else{for(var n=" "+(e.getAttribute("class")||"")+" ",r=" "+t+" ";n.indexOf(r)>=0;)n=n.replace(r," ");(n=n.trim())?e.setAttribute("class",n):e.removeAttribute("class")}}function bi(e){if(e){if("object"==typeof e){var t={};return!1!==e.css&&A(t,$i(e.name||"v")),A(t,e),t}return"string"==typeof e?$i(e):void 0}}var $i=g(function(e){return{enterClass:e+"-enter",enterToClass:e+"-enter-to",enterActiveClass:e+"-enter-active",leaveClass:e+"-leave",leaveToClass:e+"-leave-to",leaveActiveClass:e+"-leave-active"}}),wi=z&&!W,Ci="transition",xi="animation",ki="transition",Ai="transitionend",Oi="animation",Si="animationend";wi&&(void 0===window.ontransitionend&&void 0!==window.onwebkittransitionend&&(ki="WebkitTransition",Ai="webkitTransitionEnd"),void 0===window.onanimationend&&void 0!==window.onwebkitanimationend&&(Oi="WebkitAnimation",Si="webkitAnimationEnd"));var Ti=z?window.requestAnimationFrame?window.requestAnimationFrame.bind(window):setTimeout:function(e){return e()};function Ei(e){Ti(function(){Ti(e)})}function Ni(e,t){var n=e._transitionClasses||(e._transitionClasses=[]);n.indexOf(t)<0&&(n.push(t),gi(e,t))}function ji(e,t){e._transitionClasses&&h(e._transitionClasses,t),_i(e,t)}function Di(e,t,n){var r=Mi(e,t),i=r.type,o=r.timeout,a=r.propCount;if(!i)return n();var s=i===Ci?Ai:Si,c=0,u=function(){e.removeEventListener(s,l),n()},l=function(t){t.target===e&&++c>=a&&u()};setTimeout(function(){c0&&(n=Ci,l=a,f=o.length):t===xi?u>0&&(n=xi,l=u,f=c.length):f=(n=(l=Math.max(a,u))>0?a>u?Ci:xi:null)?n===Ci?o.length:c.length:0,{type:n,timeout:l,propCount:f,hasTransform:n===Ci&&Li.test(r[ki+"Property"])}}function Ii(e,t){for(;e.length1}function Ui(e,t){!0!==t.data.show&&Pi(t)}var zi=function(e){var o,a,s={},c=e.modules,u=e.nodeOps;for(o=0;ov?_(e,t(i[y+1])?null:i[y+1].elm,i,d,y,o):d>y&&$(r,p,v)}(p,h,y,o,l):n(y)?(n(e.text)&&u.setTextContent(p,""),_(p,null,y,0,y.length-1,o)):n(h)?$(h,0,h.length-1):n(e.text)&&u.setTextContent(p,""):e.text!==i.text&&u.setTextContent(p,i.text),n(v)&&n(d=v.hook)&&n(d=d.postpatch)&&d(e,i)}}}function k(e,t,i){if(r(i)&&n(e.parent))e.parent.data.pendingInsert=t;else for(var o=0;o-1,a.selected!==o&&(a.selected=o);else if(N(Wi(a),r))return void(e.selectedIndex!==s&&(e.selectedIndex=s));i||(e.selectedIndex=-1)}}function qi(e,t){return t.every(function(t){return!N(t,e)})}function Wi(e){return"_value"in e?e._value:e.value}function Zi(e){e.target.composing=!0}function Gi(e){e.target.composing&&(e.target.composing=!1,Xi(e.target,"input"))}function Xi(e,t){var n=document.createEvent("HTMLEvents");n.initEvent(t,!0,!0),e.dispatchEvent(n)}function Yi(e){return!e.componentInstance||e.data&&e.data.transition?e:Yi(e.componentInstance._vnode)}var Qi={model:Vi,show:{bind:function(e,t,n){var r=t.value,i=(n=Yi(n)).data&&n.data.transition,o=e.__vOriginalDisplay="none"===e.style.display?"":e.style.display;r&&i?(n.data.show=!0,Pi(n,function(){e.style.display=o})):e.style.display=r?o:"none"},update:function(e,t,n){var r=t.value;!r!=!t.oldValue&&((n=Yi(n)).data&&n.data.transition?(n.data.show=!0,r?Pi(n,function(){e.style.display=e.__vOriginalDisplay}):Ri(n,function(){e.style.display="none"})):e.style.display=r?e.__vOriginalDisplay:"none")},unbind:function(e,t,n,r,i){i||(e.style.display=e.__vOriginalDisplay)}}},eo={name:String,appear:Boolean,css:Boolean,mode:String,type:String,enterClass:String,leaveClass:String,enterToClass:String,leaveToClass:String,enterActiveClass:String,leaveActiveClass:String,appearClass:String,appearActiveClass:String,appearToClass:String,duration:[Number,String,Object]};function to(e){var t=e&&e.componentOptions;return t&&t.Ctor.options.abstract?to(zt(t.children)):e}function no(e){var t={},n=e.$options;for(var r in n.propsData)t[r]=e[r];var i=n._parentListeners;for(var o in i)t[b(o)]=i[o];return t}function ro(e,t){if(/\d-keep-alive$/.test(t.tag))return e("keep-alive",{props:t.componentOptions.propsData})}var io=function(e){return e.tag||Ut(e)},oo=function(e){return"show"===e.name},ao={name:"transition",props:eo,abstract:!0,render:function(e){var t=this,n=this.$slots.default;if(n&&(n=n.filter(io)).length){var r=this.mode,o=n[0];if(function(e){for(;e=e.parent;)if(e.data.transition)return!0}(this.$vnode))return o;var a=to(o);if(!a)return o;if(this._leaving)return ro(e,o);var s="__transition-"+this._uid+"-";a.key=null==a.key?a.isComment?s+"comment":s+a.tag:i(a.key)?0===String(a.key).indexOf(s)?a.key:s+a.key:a.key;var c=(a.data||(a.data={})).transition=no(this),u=this._vnode,l=to(u);if(a.data.directives&&a.data.directives.some(oo)&&(a.data.show=!0),l&&l.data&&!function(e,t){return t.key===e.key&&t.tag===e.tag}(a,l)&&!Ut(l)&&(!l.componentInstance||!l.componentInstance._vnode.isComment)){var f=l.data.transition=A({},c);if("out-in"===r)return this._leaving=!0,it(f,"afterLeave",function(){t._leaving=!1,t.$forceUpdate()}),ro(e,o);if("in-out"===r){if(Ut(a))return u;var p,d=function(){p()};it(c,"afterEnter",d),it(c,"enterCancelled",d),it(f,"delayLeave",function(e){p=e})}}return o}}},so=A({tag:String,moveClass:String},eo);function co(e){e.elm._moveCb&&e.elm._moveCb(),e.elm._enterCb&&e.elm._enterCb()}function uo(e){e.data.newPos=e.elm.getBoundingClientRect()}function lo(e){var t=e.data.pos,n=e.data.newPos,r=t.left-n.left,i=t.top-n.top;if(r||i){e.data.moved=!0;var o=e.elm.style;o.transform=o.WebkitTransform="translate("+r+"px,"+i+"px)",o.transitionDuration="0s"}}delete so.mode;var fo={Transition:ao,TransitionGroup:{props:so,beforeMount:function(){var e=this,t=this._update;this._update=function(n,r){var i=Zt(e);e.__patch__(e._vnode,e.kept,!1,!0),e._vnode=e.kept,i(),t.call(e,n,r)}},render:function(e){for(var t=this.tag||this.$vnode.data.tag||"span",n=Object.create(null),r=this.prevChildren=this.children,i=this.$slots.default||[],o=this.children=[],a=no(this),s=0;s-1?Gn[e]=t.constructor===window.HTMLUnknownElement||t.constructor===window.HTMLElement:Gn[e]=/HTMLUnknownElement/.test(t.toString())},A(wn.options.directives,Qi),A(wn.options.components,fo),wn.prototype.__patch__=z?zi:S,wn.prototype.$mount=function(e,t){return function(e,t,n){var r;return e.$el=t,e.$options.render||(e.$options.render=ve),Yt(e,"beforeMount"),r=function(){e._update(e._render(),n)},new fn(e,r,S,{before:function(){e._isMounted&&!e._isDestroyed&&Yt(e,"beforeUpdate")}},!0),n=!1,null==e.$vnode&&(e._isMounted=!0,Yt(e,"mounted")),e}(this,e=e&&z?Yn(e):void 0,t)},z&&setTimeout(function(){F.devtools&&ne&&ne.emit("init",wn)},0);var po=/\{\{((?:.|\r?\n)+?)\}\}/g,vo=/[-.*+?^${}()|[\]\/\\]/g,ho=g(function(e){var t=e[0].replace(vo,"\\$&"),n=e[1].replace(vo,"\\$&");return new RegExp(t+"((?:.|\\n)+?)"+n,"g")});var mo={staticKeys:["staticClass"],transformNode:function(e,t){t.warn;var n=Fr(e,"class");n&&(e.staticClass=JSON.stringify(n));var r=Ir(e,"class",!1);r&&(e.classBinding=r)},genData:function(e){var t="";return e.staticClass&&(t+="staticClass:"+e.staticClass+","),e.classBinding&&(t+="class:"+e.classBinding+","),t}};var yo,go={staticKeys:["staticStyle"],transformNode:function(e,t){t.warn;var n=Fr(e,"style");n&&(e.staticStyle=JSON.stringify(ai(n)));var r=Ir(e,"style",!1);r&&(e.styleBinding=r)},genData:function(e){var t="";return e.staticStyle&&(t+="staticStyle:"+e.staticStyle+","),e.styleBinding&&(t+="style:("+e.styleBinding+"),"),t}},_o=function(e){return(yo=yo||document.createElement("div")).innerHTML=e,yo.textContent},bo=p("area,base,br,col,embed,frame,hr,img,input,isindex,keygen,link,meta,param,source,track,wbr"),$o=p("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source"),wo=p("address,article,aside,base,blockquote,body,caption,col,colgroup,dd,details,dialog,div,dl,dt,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,legend,li,menuitem,meta,optgroup,option,param,rp,rt,source,style,summary,tbody,td,tfoot,th,thead,title,tr,track"),Co=/^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,xo=/^\s*((?:v-[\w-]+:|@|:|#)\[[^=]+\][^\s"'<>\/=]*)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,ko="[a-zA-Z_][\\-\\.0-9_a-zA-Z"+P.source+"]*",Ao="((?:"+ko+"\\:)?"+ko+")",Oo=new RegExp("^<"+Ao),So=/^\s*(\/?)>/,To=new RegExp("^<\\/"+Ao+"[^>]*>"),Eo=/^]+>/i,No=/^",""":'"',"&":"&"," ":"\n"," ":"\t","'":"'"},Io=/&(?:lt|gt|quot|amp|#39);/g,Fo=/&(?:lt|gt|quot|amp|#39|#10|#9);/g,Po=p("pre,textarea",!0),Ro=function(e,t){return e&&Po(e)&&"\n"===t[0]};function Ho(e,t){var n=t?Fo:Io;return e.replace(n,function(e){return Mo[e]})}var Bo,Uo,zo,Vo,Ko,Jo,qo,Wo,Zo=/^@|^v-on:/,Go=/^v-|^@|^:|^#/,Xo=/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/,Yo=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,Qo=/^\(|\)$/g,ea=/^\[.*\]$/,ta=/:(.*)$/,na=/^:|^\.|^v-bind:/,ra=/\.[^.\]]+(?=[^\]]*$)/g,ia=/^v-slot(:|$)|^#/,oa=/[\r\n]/,aa=/\s+/g,sa=g(_o),ca="_empty_";function ua(e,t,n){return{type:1,tag:e,attrsList:t,attrsMap:ma(t),rawAttrsMap:{},parent:n,children:[]}}function la(e,t){Bo=t.warn||Sr,Jo=t.isPreTag||T,qo=t.mustUseProp||T,Wo=t.getTagNamespace||T;t.isReservedTag;zo=Tr(t.modules,"transformNode"),Vo=Tr(t.modules,"preTransformNode"),Ko=Tr(t.modules,"postTransformNode"),Uo=t.delimiters;var n,r,i=[],o=!1!==t.preserveWhitespace,a=t.whitespace,s=!1,c=!1;function u(e){if(l(e),s||e.processed||(e=fa(e,t)),i.length||e===n||n.if&&(e.elseif||e.else)&&da(n,{exp:e.elseif,block:e}),r&&!e.forbidden)if(e.elseif||e.else)a=e,(u=function(e){var t=e.length;for(;t--;){if(1===e[t].type)return e[t];e.pop()}}(r.children))&&u.if&&da(u,{exp:a.elseif,block:a});else{if(e.slotScope){var o=e.slotTarget||'"default"';(r.scopedSlots||(r.scopedSlots={}))[o]=e}r.children.push(e),e.parent=r}var a,u;e.children=e.children.filter(function(e){return!e.slotScope}),l(e),e.pre&&(s=!1),Jo(e.tag)&&(c=!1);for(var f=0;f]*>)","i")),p=e.replace(f,function(e,n,r){return u=r.length,Do(l)||"noscript"===l||(n=n.replace(//g,"$1").replace(//g,"$1")),Ro(l,n)&&(n=n.slice(1)),t.chars&&t.chars(n),""});c+=e.length-p.length,e=p,A(l,c-u,c)}else{var d=e.indexOf("<");if(0===d){if(No.test(e)){var v=e.indexOf("--\x3e");if(v>=0){t.shouldKeepComment&&t.comment(e.substring(4,v),c,c+v+3),C(v+3);continue}}if(jo.test(e)){var h=e.indexOf("]>");if(h>=0){C(h+2);continue}}var m=e.match(Eo);if(m){C(m[0].length);continue}var y=e.match(To);if(y){var g=c;C(y[0].length),A(y[1],g,c);continue}var _=x();if(_){k(_),Ro(_.tagName,e)&&C(1);continue}}var b=void 0,$=void 0,w=void 0;if(d>=0){for($=e.slice(d);!(To.test($)||Oo.test($)||No.test($)||jo.test($)||(w=$.indexOf("<",1))<0);)d+=w,$=e.slice(d);b=e.substring(0,d)}d<0&&(b=e),b&&C(b.length),t.chars&&b&&t.chars(b,c-b.length,c)}if(e===n){t.chars&&t.chars(e);break}}function C(t){c+=t,e=e.substring(t)}function x(){var t=e.match(Oo);if(t){var n,r,i={tagName:t[1],attrs:[],start:c};for(C(t[0].length);!(n=e.match(So))&&(r=e.match(xo)||e.match(Co));)r.start=c,C(r[0].length),r.end=c,i.attrs.push(r);if(n)return i.unarySlash=n[1],C(n[0].length),i.end=c,i}}function k(e){var n=e.tagName,c=e.unarySlash;o&&("p"===r&&wo(n)&&A(r),s(n)&&r===n&&A(n));for(var u=a(n)||!!c,l=e.attrs.length,f=new Array(l),p=0;p=0&&i[a].lowerCasedTag!==s;a--);else a=0;if(a>=0){for(var u=i.length-1;u>=a;u--)t.end&&t.end(i[u].tag,n,o);i.length=a,r=a&&i[a-1].tag}else"br"===s?t.start&&t.start(e,[],!0,n,o):"p"===s&&(t.start&&t.start(e,[],!1,n,o),t.end&&t.end(e,n,o))}A()}(e,{warn:Bo,expectHTML:t.expectHTML,isUnaryTag:t.isUnaryTag,canBeLeftOpenTag:t.canBeLeftOpenTag,shouldDecodeNewlines:t.shouldDecodeNewlines,shouldDecodeNewlinesForHref:t.shouldDecodeNewlinesForHref,shouldKeepComment:t.comments,outputSourceRange:t.outputSourceRange,start:function(e,o,a,l,f){var p=r&&r.ns||Wo(e);q&&"svg"===p&&(o=function(e){for(var t=[],n=0;nc&&(s.push(o=e.slice(c,i)),a.push(JSON.stringify(o)));var u=Ar(r[1].trim());a.push("_s("+u+")"),s.push({"@binding":u}),c=i+r[0].length}return c-1"+("true"===o?":("+t+")":":_q("+t+","+o+")")),Mr(e,"change","var $$a="+t+",$$el=$event.target,$$c=$$el.checked?("+o+"):("+a+");if(Array.isArray($$a)){var $$v="+(r?"_n("+i+")":i)+",$$i=_i($$a,$$v);if($$el.checked){$$i<0&&("+Br(t,"$$a.concat([$$v])")+")}else{$$i>-1&&("+Br(t,"$$a.slice(0,$$i).concat($$a.slice($$i+1))")+")}}else{"+Br(t,"$$c")+"}",null,!0)}(e,r,i);else if("input"===o&&"radio"===a)!function(e,t,n){var r=n&&n.number,i=Ir(e,"value")||"null";Er(e,"checked","_q("+t+","+(i=r?"_n("+i+")":i)+")"),Mr(e,"change",Br(t,i),null,!0)}(e,r,i);else if("input"===o||"textarea"===o)!function(e,t,n){var r=e.attrsMap.type,i=n||{},o=i.lazy,a=i.number,s=i.trim,c=!o&&"range"!==r,u=o?"change":"range"===r?Wr:"input",l="$event.target.value";s&&(l="$event.target.value.trim()"),a&&(l="_n("+l+")");var f=Br(t,l);c&&(f="if($event.target.composing)return;"+f),Er(e,"value","("+t+")"),Mr(e,u,f,null,!0),(s||a)&&Mr(e,"blur","$forceUpdate()")}(e,r,i);else if(!F.isReservedTag(o))return Hr(e,r,i),!1;return!0},text:function(e,t){t.value&&Er(e,"textContent","_s("+t.value+")",t)},html:function(e,t){t.value&&Er(e,"innerHTML","_s("+t.value+")",t)}},isPreTag:function(e){return"pre"===e},isUnaryTag:bo,mustUseProp:jn,canBeLeftOpenTag:$o,isReservedTag:Wn,getTagNamespace:Zn,staticKeys:function(e){return e.reduce(function(e,t){return e.concat(t.staticKeys||[])},[]).join(",")}(ba)},xa=g(function(e){return p("type,tag,attrsList,attrsMap,plain,parent,children,attrs,start,end,rawAttrsMap"+(e?","+e:""))});function ka(e,t){e&&($a=xa(t.staticKeys||""),wa=t.isReservedTag||T,function e(t){t.static=function(e){if(2===e.type)return!1;if(3===e.type)return!0;return!(!e.pre&&(e.hasBindings||e.if||e.for||d(e.tag)||!wa(e.tag)||function(e){for(;e.parent;){if("template"!==(e=e.parent).tag)return!1;if(e.for)return!0}return!1}(e)||!Object.keys(e).every($a)))}(t);if(1===t.type){if(!wa(t.tag)&&"slot"!==t.tag&&null==t.attrsMap["inline-template"])return;for(var n=0,r=t.children.length;n|^function(?:\s+[\w$]+)?\s*\(/,Oa=/\([^)]*?\);*$/,Sa=/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/,Ta={esc:27,tab:9,enter:13,space:32,up:38,left:37,right:39,down:40,delete:[8,46]},Ea={esc:["Esc","Escape"],tab:"Tab",enter:"Enter",space:[" ","Spacebar"],up:["Up","ArrowUp"],left:["Left","ArrowLeft"],right:["Right","ArrowRight"],down:["Down","ArrowDown"],delete:["Backspace","Delete","Del"]},Na=function(e){return"if("+e+")return null;"},ja={stop:"$event.stopPropagation();",prevent:"$event.preventDefault();",self:Na("$event.target !== $event.currentTarget"),ctrl:Na("!$event.ctrlKey"),shift:Na("!$event.shiftKey"),alt:Na("!$event.altKey"),meta:Na("!$event.metaKey"),left:Na("'button' in $event && $event.button !== 0"),middle:Na("'button' in $event && $event.button !== 1"),right:Na("'button' in $event && $event.button !== 2")};function Da(e,t){var n=t?"nativeOn:":"on:",r="",i="";for(var o in e){var a=La(e[o]);e[o]&&e[o].dynamic?i+=o+","+a+",":r+='"'+o+'":'+a+","}return r="{"+r.slice(0,-1)+"}",i?n+"_d("+r+",["+i.slice(0,-1)+"])":n+r}function La(e){if(!e)return"function(){}";if(Array.isArray(e))return"["+e.map(function(e){return La(e)}).join(",")+"]";var t=Sa.test(e.value),n=Aa.test(e.value),r=Sa.test(e.value.replace(Oa,""));if(e.modifiers){var i="",o="",a=[];for(var s in e.modifiers)if(ja[s])o+=ja[s],Ta[s]&&a.push(s);else if("exact"===s){var c=e.modifiers;o+=Na(["ctrl","shift","alt","meta"].filter(function(e){return!c[e]}).map(function(e){return"$event."+e+"Key"}).join("||"))}else a.push(s);return a.length&&(i+=function(e){return"if(!$event.type.indexOf('key')&&"+e.map(Ma).join("&&")+")return null;"}(a)),o&&(i+=o),"function($event){"+i+(t?"return "+e.value+"($event)":n?"return ("+e.value+")($event)":r?"return "+e.value:e.value)+"}"}return t||n?e.value:"function($event){"+(r?"return "+e.value:e.value)+"}"}function Ma(e){var t=parseInt(e,10);if(t)return"$event.keyCode!=="+t;var n=Ta[e],r=Ea[e];return"_k($event.keyCode,"+JSON.stringify(e)+","+JSON.stringify(n)+",$event.key,"+JSON.stringify(r)+")"}var Ia={on:function(e,t){e.wrapListeners=function(e){return"_g("+e+","+t.value+")"}},bind:function(e,t){e.wrapData=function(n){return"_b("+n+",'"+e.tag+"',"+t.value+","+(t.modifiers&&t.modifiers.prop?"true":"false")+(t.modifiers&&t.modifiers.sync?",true":"")+")"}},cloak:S},Fa=function(e){this.options=e,this.warn=e.warn||Sr,this.transforms=Tr(e.modules,"transformCode"),this.dataGenFns=Tr(e.modules,"genData"),this.directives=A(A({},Ia),e.directives);var t=e.isReservedTag||T;this.maybeComponent=function(e){return!!e.component||!t(e.tag)},this.onceId=0,this.staticRenderFns=[],this.pre=!1};function Pa(e,t){var n=new Fa(t);return{render:"with(this){return "+(e?Ra(e,n):'_c("div")')+"}",staticRenderFns:n.staticRenderFns}}function Ra(e,t){if(e.parent&&(e.pre=e.pre||e.parent.pre),e.staticRoot&&!e.staticProcessed)return Ha(e,t);if(e.once&&!e.onceProcessed)return Ba(e,t);if(e.for&&!e.forProcessed)return za(e,t);if(e.if&&!e.ifProcessed)return Ua(e,t);if("template"!==e.tag||e.slotTarget||t.pre){if("slot"===e.tag)return function(e,t){var n=e.slotName||'"default"',r=qa(e,t),i="_t("+n+(r?","+r:""),o=e.attrs||e.dynamicAttrs?Ga((e.attrs||[]).concat(e.dynamicAttrs||[]).map(function(e){return{name:b(e.name),value:e.value,dynamic:e.dynamic}})):null,a=e.attrsMap["v-bind"];!o&&!a||r||(i+=",null");o&&(i+=","+o);a&&(i+=(o?"":",null")+","+a);return i+")"}(e,t);var n;if(e.component)n=function(e,t,n){var r=t.inlineTemplate?null:qa(t,n,!0);return"_c("+e+","+Va(t,n)+(r?","+r:"")+")"}(e.component,e,t);else{var r;(!e.plain||e.pre&&t.maybeComponent(e))&&(r=Va(e,t));var i=e.inlineTemplate?null:qa(e,t,!0);n="_c('"+e.tag+"'"+(r?","+r:"")+(i?","+i:"")+")"}for(var o=0;o>>0}(a):"")+")"}(e,e.scopedSlots,t)+","),e.model&&(n+="model:{value:"+e.model.value+",callback:"+e.model.callback+",expression:"+e.model.expression+"},"),e.inlineTemplate){var o=function(e,t){var n=e.children[0];if(n&&1===n.type){var r=Pa(n,t.options);return"inlineTemplate:{render:function(){"+r.render+"},staticRenderFns:["+r.staticRenderFns.map(function(e){return"function(){"+e+"}"}).join(",")+"]}"}}(e,t);o&&(n+=o+",")}return n=n.replace(/,$/,"")+"}",e.dynamicAttrs&&(n="_b("+n+',"'+e.tag+'",'+Ga(e.dynamicAttrs)+")"),e.wrapData&&(n=e.wrapData(n)),e.wrapListeners&&(n=e.wrapListeners(n)),n}function Ka(e){return 1===e.type&&("slot"===e.tag||e.children.some(Ka))}function Ja(e,t){var n=e.attrsMap["slot-scope"];if(e.if&&!e.ifProcessed&&!n)return Ua(e,t,Ja,"null");if(e.for&&!e.forProcessed)return za(e,t,Ja);var r=e.slotScope===ca?"":String(e.slotScope),i="function("+r+"){return "+("template"===e.tag?e.if&&n?"("+e.if+")?"+(qa(e,t)||"undefined")+":undefined":qa(e,t)||"undefined":Ra(e,t))+"}",o=r?"":",proxy:true";return"{key:"+(e.slotTarget||'"default"')+",fn:"+i+o+"}"}function qa(e,t,n,r,i){var o=e.children;if(o.length){var a=o[0];if(1===o.length&&a.for&&"template"!==a.tag&&"slot"!==a.tag){var s=n?t.maybeComponent(a)?",1":",0":"";return""+(r||Ra)(a,t)+s}var c=n?function(e,t){for(var n=0,r=0;r':'
    ',ts.innerHTML.indexOf(" ")>0}var os=!!z&&is(!1),as=!!z&&is(!0),ss=g(function(e){var t=Yn(e);return t&&t.innerHTML}),cs=wn.prototype.$mount;return wn.prototype.$mount=function(e,t){if((e=e&&Yn(e))===document.body||e===document.documentElement)return this;var n=this.$options;if(!n.render){var r=n.template;if(r)if("string"==typeof r)"#"===r.charAt(0)&&(r=ss(r));else{if(!r.nodeType)return this;r=r.innerHTML}else e&&(r=function(e){if(e.outerHTML)return e.outerHTML;var t=document.createElement("div");return t.appendChild(e.cloneNode(!0)),t.innerHTML}(e));if(r){var i=rs(r,{outputSourceRange:!1,shouldDecodeNewlines:os,shouldDecodeNewlinesForHref:as,delimiters:n.delimiters,comments:n.comments},this),o=i.render,a=i.staticRenderFns;n.render=o,n.staticRenderFns=a}}return cs.call(this,e,t)},wn.compile=rs,wn}); \ No newline at end of file diff --git a/report_browser/js/vue2-filters.min.js b/report_browser/js/vue2-filters.min.js new file mode 100644 index 00000000..c21e37e9 --- /dev/null +++ b/report_browser/js/vue2-filters.min.js @@ -0,0 +1 @@ +!function(t,e){if("object"==typeof exports&&"object"==typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var r=e();for(var n in r)("object"==typeof exports?exports:t)[n]=r[n]}}("undefined"!=typeof self?self:this,function(){return n={},i.m=r=[function(t,e,r){"use strict";r.r(e);var i={};r.r(i),r.d(i,"capitalize",function(){return p}),r.d(i,"uppercase",function(){return y}),r.d(i,"lowercase",function(){return d}),r.d(i,"placeholder",function(){return b}),r.d(i,"truncate",function(){return v});var o={};function n(t){return function(t){if(Array.isArray(t)){for(var e=0,r=new Array(t.length);e=i.length-1?u(t,e,r):u(t,e,r)||n(t,e,r+1)}),t.slice().sort(n)}function w(t,e){var r=A.apply(this,arguments);return r.splice(1),r}var j=function(t,e,r,n){var i,o,u,a,c=this&&this.currency?this.currency:{};e=h.exist(e)?e:c.symbol,r=h.exist(r)?r:c.decimalDigits,n=n||c;var l=/(\d{3})(?=\d)/g;if(t=parseFloat(t),!isFinite(t)||!t&&0!==t)return"";e=void 0!==e?e:"$",r=void 0!==r?r:2,i=null!=n.thousandsSeparator?n.thousandsSeparator:",",o=null==n.symbolOnLeft||n.symbolOnLeft,u=null!=n.spaceBetweenAmountAndSymbol&&n.spaceBetweenAmountAndSymbol,a=null!=n.showPlusSign&&n.showPlusSign;var s=function(t,e){return(+(Math.round(+(t+"e"+e))+"e"+-e)).toFixed(e)}(Math.abs(t),r);s=n.decimalSeparator?s.replace(".",n.decimalSeparator):s;var f=r?s.slice(0,-1-r):s,p=f.length%3,y=0=Math.pow(1024,4)?"".concat((t/Math.pow(1024,4)).toFixed(e)," TB"):t>=Math.pow(1024,3)?"".concat((t/Math.pow(1024,3)).toFixed(e)," GB"):t>=Math.pow(1024,2)?"".concat((t/Math.pow(1024,2)).toFixed(e)," MB"):1024<=t?"".concat((t/1024).toFixed(e)," kB"):"".concat(t,1===t?" byte":" bytes")};var O=function(t,e,r){var n=this&&this.pluralize?this.pluralize:{},i="";return!0===(null!=(r=r||n).includeNumber&&r.includeNumber)&&(i+=t+" "),!t&&0!==t||!e||(Array.isArray(e)?i+=e[t-1]||e[e.length-1]:i+=e+(1===t?"":"s")),i};var M=function(t,e){var r=this&&this.ordinal?this.ordinal:{},n="";!0===(null!=(e=e||r).includeNumber&&e.includeNumber)&&(n+=t);var i=t%10,o=t%100;return n+=1==i&&11!=o?"st":2==i&&12!=o?"nd":3==i&&13!=o?"rd":"th"};function $(t,e){return(+(Math.round(+(t+"e"+e))+"e"+-e)).toFixed(e)}Math.sign=function(t){return 0===(t=+t)||isNaN(t)?t:0=n[r].value);r--);return(t=(t/n[r].value).toFixed(e.decimals).replace(/\.0+$|(\.[0-9]*[1-9])0+$/,"$1"))+e.unit.replace("a",n[r].symbol)}(o.float,i);return i.sign+c}var l=0===i.decimals?$(o.float,0):o.int;switch(i.base){case"":l="";break;case"0,0":l=function(t,e){var r=/(\d+)(\d{3})/,n=t.toString().split("."),i=n[0],o=1 lsErrors; - if (do_primitives_overlap(_lsPrimitives, 601, lsErrors, tol_overlap) == true) + if (do_primitives_interior_overlap(_lsPrimitives, 601, lsErrors, tol_overlap) == true) { bValid = false; std::clog << "Error: overlapping building parts" << std::endl; diff --git a/src/CompositeSolid.cpp b/src/CompositeSolid.cpp index f69a3053..8f614fa0 100644 --- a/src/CompositeSolid.cpp +++ b/src/CompositeSolid.cpp @@ -1,7 +1,7 @@ /* val3dity - Copyright (c) 2011-2017, 3D geoinformation research group, TU Delft + Copyright (c) 2011-2020, 3D geoinformation research group, TU Delft This file is part of val3dity. @@ -219,7 +219,7 @@ json CompositeSolid::get_report_json() else j["id"] = "none"; j["numbersolids"] = this->number_of_solids(); - j["errors"]; + j["errors"] = json::array(); for (auto& err : _errors) { for (auto& e : _errors[std::get<0>(err)]) @@ -227,7 +227,7 @@ json CompositeSolid::get_report_json() json jj; jj["type"] = "Error"; jj["code"] = std::get<0>(err); - jj["description"] = errorcode2description(std::get<0>(err)); + jj["description"] = ALL_ERRORS[std::get<0>(err)]; jj["id"] = std::get<0>(e); jj["info"] = std::get<1>(e); j["errors"].push_back(jj); diff --git a/src/CompositeSurface.cpp b/src/CompositeSurface.cpp index 29c295fb..8d752bd4 100644 --- a/src/CompositeSurface.cpp +++ b/src/CompositeSurface.cpp @@ -1,7 +1,7 @@ /* val3dity - Copyright (c) 2011-2017, 3D geoinformation research group, TU Delft + Copyright (c) 2011-2020, 3D geoinformation research group, TU Delft This file is part of val3dity. @@ -110,7 +110,7 @@ json CompositeSurface::get_report_json() json jj; jj["type"] = "Error"; jj["code"] = std::get<0>(err); - jj["description"] = errorcode2description(std::get<0>(err)); + jj["description"] = ALL_ERRORS[std::get<0>(err)]; jj["id"] = std::get<0>(e); jj["info"] = std::get<1>(e); j["errors"].push_back(jj); diff --git a/src/Feature.cpp b/src/Feature.cpp index fa47b4f6..8057ed5e 100644 --- a/src/Feature.cpp +++ b/src/Feature.cpp @@ -1,7 +1,7 @@ /* val3dity - Copyright (c) 2011-2017, 3D geoinformation research group, TU Delft + Copyright (c) 2011-2020, 3D geoinformation research group, TU Delft This file is part of val3dity. @@ -60,7 +60,7 @@ json Feature::get_report_json() j["id"] = _id; j["type"] = _type; j["validity"] = this->is_valid(); - j["errors_feature"]; + j["errors"] = json::array(); for (auto& err : _errors) { for (auto& e : _errors[std::get<0>(err)]) @@ -68,10 +68,10 @@ json Feature::get_report_json() json jj; jj["type"] = "Error"; jj["code"] = std::get<0>(err); - jj["description"] = errorcode2description(std::get<0>(err)); + jj["description"] = ALL_ERRORS[std::get<0>(err)]; jj["id"] = std::get<0>(e); jj["info"] = std::get<1>(e); - j["errors_feature"].push_back(jj); + j["errors"].push_back(jj); } } j["primitives"]; @@ -86,6 +86,11 @@ void Feature::add_primitive(Primitive* p) _lsPrimitives.push_back(p); } +int Feature::number_of_primitives() +{ + return _lsPrimitives.size(); +} + const std::vector& Feature::get_primitives() { return _lsPrimitives; @@ -103,6 +108,9 @@ bool Feature::validate_generic(double tol_planarity_d2p, double tol_planarity_no this->add_error(609, "Feature has no geometry defined.", ""); bValid = false; } + if (_lsPrimitives.size() > 500) { + std::cout << "Validating " << _lsPrimitives.size() << " geometric primitives, this could be slow." << std::endl << std::flush; + } for (auto& p : _lsPrimitives) { std::clog << "======== Validating Primitive ========" << std::endl; @@ -113,7 +121,8 @@ bool Feature::validate_generic(double tol_planarity_d2p, double tol_planarity_no case 2: std::clog << "MultiSolid" << std::endl; break; case 3: std::clog << "CompositeSurface" << std::endl; break; case 4: std::clog << "MultiSurface" << std::endl; break; - case 5: std::clog << "ALL" << std::endl; break; + case 5: std::clog << "GeometryTemplate" << std::endl; break; + case 9: std::clog << "ALL" << std::endl; break; } std::clog << "id: " << p->get_id() << std::endl; std::clog << "--" << std::endl; @@ -135,7 +144,7 @@ void Feature::add_error(int code, std::string whichgeoms, std::string info) _is_valid = 0; std::tuple a(whichgeoms, info); _errors[code].push_back(a); - std::clog << "\tERROR " << code << ": " << errorcode2description(code); + std::clog << "\tERROR " << code << ": " << ALL_ERRORS[code]; if (whichgeoms.empty() == false) std::clog << " (id: " << whichgeoms << ")"; std::clog << std::endl; diff --git a/src/Feature.h b/src/Feature.h index 89069460..a39ef16d 100644 --- a/src/Feature.h +++ b/src/Feature.h @@ -1,7 +1,7 @@ /* val3dity - Copyright (c) 2011-2017, 3D geoinformation research group, TU Delft + Copyright (c) 2011-2019, 3D geoinformation research group, TU Delft This file is part of val3dity. @@ -58,6 +58,8 @@ class Feature const std::vector& get_primitives(); + int number_of_primitives(); + void add_error(int code, std::string info, std::string whichgeoms); json get_report_json(); std::set get_unique_error_codes(); diff --git a/src/GeometryTemplate.cpp b/src/GeometryTemplate.cpp new file mode 100644 index 00000000..34a81722 --- /dev/null +++ b/src/GeometryTemplate.cpp @@ -0,0 +1,141 @@ +/* + val3dity + + Copyright (c) 2011-2020, 3D geoinformation research group, TU Delft + + This file is part of val3dity. + + val3dity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + val3dity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with val3dity. If not, see . + + For any information or further details about the use of val3dity, contact + Hugo Ledoux + + Faculty of Architecture & the Built Environment + Delft University of Technology + Julianalaan 134, Delft 2628BL, the Netherlands +*/ + +#include "GeometryTemplate.h" +#include "input.h" + +namespace val3dity +{ + +GeometryTemplate::GeometryTemplate(std::string id) { + _id = id; + _is_valid = -1; +} + +GeometryTemplate::~GeometryTemplate() { +} + +Primitive3D GeometryTemplate::get_type() +{ + return GEOMETRYTEMPLATE; +} + +bool GeometryTemplate::validate(double tol_planarity_d2p, double tol_planarity_normals, double tol_overlap) +{ + bool isValid = true; + for (auto& p : _lsPrimitives) + { + if (p->validate(tol_planarity_d2p, tol_planarity_normals) == false) + isValid = false; + } + _is_valid = isValid; + return isValid; +} + + +int GeometryTemplate::is_valid() +{ + if ( (_is_valid == 1) && (this->is_empty() == false) && (_errors.empty() == true) ) + return 1; + else + return _is_valid; +} + + +void GeometryTemplate::get_min_bbox(double& x, double& y) +{ + x = 0.0; + y = 0.0; +} + + +void GeometryTemplate::translate_vertices() +{} + +bool GeometryTemplate::is_empty() +{ + return _lsPrimitives.empty(); +} + + +json GeometryTemplate::get_report_json() +{ + json j; + bool isValid = true; + j["type"] = "GeometryTemplate"; + if (this->get_id() != "") + j["id"] = this->_id; + else + j["id"] = "none"; + // j["numbersolids"] = this->number_of_solids(); + j["errors"] = json::array(); + for (auto& err : _errors) + { + for (auto& e : _errors[std::get<0>(err)]) + { + json jj; + jj["type"] = "Error"; + jj["code"] = std::get<0>(err); + jj["description"] = ALL_ERRORS[std::get<0>(err)]; + jj["id"] = std::get<0>(e); + jj["info"] = std::get<1>(e); + j["errors"].push_back(jj); + isValid = false; + } + } + for (auto& p : _lsPrimitives) + { + j["primitives"].push_back(p->get_report_json()); + if (p->is_valid() == false) + isValid = false; + } + j["validity"] = isValid; + return j; +} + + + +bool GeometryTemplate::add_primitive(Primitive* s) { + _lsPrimitives.push_back(s); + return true; +} + +std::set GeometryTemplate::get_unique_error_codes() { + std::set errs = Primitive::get_unique_error_codes(); + for (auto& p : _lsPrimitives) { + std::set tmp = p->get_unique_error_codes(); + errs.insert(tmp.begin(), tmp.end()); + } + return errs; +} + +// int GeometryTemplate::number_of_solids() { +// return _lsSolids.size(); +// } + +} // namespace val3dity diff --git a/src/GeometryTemplate.h b/src/GeometryTemplate.h new file mode 100644 index 00000000..9909ad6c --- /dev/null +++ b/src/GeometryTemplate.h @@ -0,0 +1,65 @@ +/* + val3dity + + Copyright (c) 2011-2020, 3D geoinformation research group, TU Delft + + This file is part of val3dity. + + val3dity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + val3dity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with val3dity. If not, see . + + For any information or further details about the use of val3dity, contact + Hugo Ledoux + + Faculty of Architecture & the Built Environment + Delft University of Technology + Julianalaan 134, Delft 2628BL, the Netherlands +*/ + +#ifndef GeometryTemplate_h +#define GeometryTemplate_h + +#include "Primitive.h" +#include "Solid.h" + +#include +#include + +namespace val3dity +{ + +class GeometryTemplate : public Primitive +{ +public: + GeometryTemplate(std::string id = ""); + ~GeometryTemplate(); + + bool validate(double tol_planarity_d2p, double tol_planarity_normals, double tol_overlap = -1); + int is_valid(); + bool is_empty(); + json get_report_json(); + Primitive3D get_type(); + std::set get_unique_error_codes(); + + void get_min_bbox(double& x, double& y); + void translate_vertices(); + + bool add_primitive(Primitive* s); + +protected: + std::vector _lsPrimitives; +}; + +} // namespace val3dity + +#endif /* GeometryTemplate_h */ diff --git a/src/IndoorGraph.cpp b/src/IndoorGraph.cpp new file mode 100755 index 00000000..20c83c19 --- /dev/null +++ b/src/IndoorGraph.cpp @@ -0,0 +1,113 @@ +/* + val3dity + + Copyright (c) 2011-2020, 3D geoinformation research group, TU Delft + + This file is part of val3dity. + + val3dity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + val3dity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with val3dity. If not, see . + + For any information or further details about the use of val3dity, contact + Hugo Ledoux + + Faculty of Architecture & the Built Environment + Delft University of Technology + Julianalaan 134, Delft 2628BL, the Netherlands +*/ + + +#include "IndoorGraph.h" +#include "definitions.h" +#include "input.h" + +namespace val3dity +{ + +IndoorGraph::IndoorGraph(std::string theid) +{ + if (theid == "") + _id = "MISSING_ID"; + else + _id = theid; + _is_valid = -1; +} + + +IndoorGraph::~IndoorGraph() +{} + + +void IndoorGraph::add_vertex(std::string theid, double x, double y, double z, std::string dual, std::vector vadj) +{ + // std::cout << "ADDING VERTEX" << std::endl; + Point3 v(x, y, z); + _vertices[theid] = std::make_tuple(v, dual, vadj); +} + + +bool IndoorGraph::validate() +{ + std::cout << "VALIDATE IndoorGraph" << std::endl; + return true; + // if (_is_valid != -1) + // return _is_valid; + // bool bValid = Feature::validate_generic(tol_planarity_d2p, tol_planarity_normals, tol_overlap); + // _is_valid = bValid; + // return bValid; +} + + +bool IndoorGraph::is_valid() +{ + if (_is_valid == 1) + return true; + else + return false; +} + + +std::string IndoorGraph::get_type() +{ + return "IndoorGraph"; +} + + +bool IndoorGraph::has_vertex(std::string id) +{ + auto it = _vertices.find(id); + if (it == _vertices.end()) + return false; + else + return true; +} + + +std::vector IndoorGraph::get_vertices_ids() +{ + std::vector all; + for (auto& v: _vertices) + { + all.push_back(v.first); + } + return all; +} + + +std::tuple>& +IndoorGraph::get_vertex(std::string id) { + return _vertices[id]; +} + + +} // namespace val3dity diff --git a/src/IndoorGraph.h b/src/IndoorGraph.h new file mode 100644 index 00000000..8a7376e9 --- /dev/null +++ b/src/IndoorGraph.h @@ -0,0 +1,73 @@ +/* + val3dity + + Copyright (c) 2011-2020, 3D geoinformation research group, TU Delft + + This file is part of val3dity. + + val3dity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + val3dity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with val3dity. If not, see . + + For any information or further details about the use of val3dity, contact + Hugo Ledoux + + Faculty of Architecture & the Built Environment + Delft University of Technology + Julianalaan 134, Delft 2628BL, the Netherlands +*/ + +#ifndef __val3dity__IndoorGraph__ +#define __val3dity__IndoorGraph__ + +#include "Feature.h" +#include "definitions.h" + + +namespace val3dity +{ + +class IndoorGraph +{ +public: + IndoorGraph(std::string theid = ""); + ~IndoorGraph(); + + void add_vertex(std::string theid, + double x, + double y, + double z, + std::string dual, + std::vector vadj); + + bool validate(); + bool is_valid(); + std::string get_type(); + + bool has_vertex(std::string); + std::tuple>& + get_vertex(std::string id); + std::vector + get_vertices_ids(); + +protected: + std::string _id; + int _is_valid; + + // _vertices[theid] = std::make_tuple(v, dual, vadj); + std::map>> _vertices; + +}; + +} // namespace val3dity + +#endif /* defined(__val3dity__IndoorGraph__) */ diff --git a/src/IndoorModel.cpp b/src/IndoorModel.cpp new file mode 100755 index 00000000..e3ec8a58 --- /dev/null +++ b/src/IndoorModel.cpp @@ -0,0 +1,283 @@ +/* + val3dity + + Copyright (c) 2011-2020, 3D geoinformation research group, TU Delft + + This file is part of val3dity. + + val3dity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + val3dity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with val3dity. If not, see . + + For any information or further details about the use of val3dity, contact + Hugo Ledoux + + Faculty of Architecture & the Built Environment + Delft University of Technology + Julianalaan 134, Delft 2628BL, the Netherlands +*/ + + +#include "IndoorModel.h" +#include "definitions.h" +#include "input.h" +#include "Solid.h" +#include "validate_prim_toporel.h" + +namespace val3dity +{ + +IndoorModel::IndoorModel(std::string theid) +{ + _id = theid; + _type = "IndoorModel"; + _is_valid = -1; +} + + +IndoorModel::~IndoorModel() +{} + + +bool IndoorModel::validate(double tol_planarity_d2p, double tol_planarity_normals, double tol_overlap) +{ + // + // 1. each Cell is valid Solid + // 2. do Cells overlap each others? https://doc.cgal.org/latest/Polygon_mesh_processing/group__PMP__predicates__grp.html#ga1ff63ec6e762d45ea5775bf7b49f9270 + // 3. is dual vertex of each Cell locate inside the Cell + // 4. are graphs valid: c1->v4 && v4->c1 (faulty xlinks?) + // 5. adj in dual == adj in primal + // + + std::clog << "--- Validation of IndoorModel ---" << std::endl; + if (_is_valid != -1) + return _is_valid; + + bool bValid = true; +//-- 1. 4xx - ISO19107 check for Solid validity +//-- validate each IndoorCell geometry (Solids) + bValid = Feature::validate_generic(tol_planarity_d2p, tol_planarity_normals, tol_overlap); + +//-- 2. 701 - CELLS_OVERLAP +//-- overlapping test + std::clog << "--- Overlapping tests between Cells ---" << std::endl; + std::vector> lsCells; + for (auto& el : _cells) + lsCells.push_back(std::make_tuple(el.first, (Solid*)_lsPrimitives[std::get<0>(el.second)])); + std::vector lsErrors; + if (are_cells_interior_disconnected_with_aabb(lsCells, 701, lsErrors, tol_overlap) == false) + { + bValid = false; + std::clog << "Error: Cells have overlapping interior" << std::endl; + for (auto& e : lsErrors) + this->add_error(e.errorcode, e.info1, e.info2); + } + +//-- 3. 702 - DUAL_VERTEX_OUTSIDE_CELL +//-- is dual vertex of each cell located inside its Cell? + std::clog << "======== Validating Dual Vertex (Point-in-Solid tests) ========" << std::endl; + for (auto& el : _cells) + { + std::clog << "Cell (" << std::get<2>(el.second) << ") id=" << el.first; + // TODO : what to do with many graphs here? + //-- check if there's a dual, something there's not (and it's valid) + if (std::get<1>(el.second) == "") + { + std::clog << "\tNo dual vertex" << std::endl; + } + else + { + //-- does the dual graph contain that vertex ID? + if (this->get_number_graphs() == 0) { + std::stringstream msg; + msg << "CellSpace id=" << el.first; + this->add_error(702, msg.str(), "The dual graph is not in the file."); + continue; + } + + int gno = this->get_graph_containing_vertex(std::get<1>(el.second)); + if (gno == -1) + { + // std::stringstream msg; + // msg << "Cell (" << std::get<2>(el.second) << ") id=" << el.first << " dual doesn't exist"; + // this->add_error(704, msg.str(), ""); + std::clog << " ==> dual does not exist" << std::endl; + } + else + { + Point3 p = std::get<0>(_graphs[gno]->get_vertex(std::get<1>(el.second))); + Solid* s = (Solid*)_lsPrimitives[std::get<0>(el.second)]; + int inside = s->is_point_in_solid(p); + if (inside == -1) + { + std::stringstream msg; + msg << "CellSpace id=" << el.first; + this->add_error(702, msg.str(), ""); + } + else + std::clog << " --> ok" << std::endl; + } + } + } + +//-- 4. 703 - PRIMAL_DUAL_XLINKS_ERROR +//-- are primal-dual graphs valid +//-- this validates the XLinks basically, which is not done by XSD + std::clog << "======== Validating Primal-Dual links ========" << std::endl; + for (auto& el : _cells) + { + std::string pdid = std::get<1>(el.second); + //-- no dual to the cell + if (pdid == "") + { + //-- this is allowed, a cell does not need to have a dual vertex + std::clog << "Cell id=" << el.first << "has no dual vertex" << std::endl; + continue; + } + if (this->get_number_graphs() == 0) { + std::stringstream msg; + msg << "CellSpace id=" << el.first; + this->add_error(703, msg.str(), "The dual graph is not in the file."); + continue; + } + int gno = this->get_graph_containing_vertex(pdid); + if (gno == -1) { + std::stringstream msg; + msg << "Cell id=" << el.first << " dual vertex doesn't exist"; + this->add_error(703, msg.str(), ""); + } + else + { + //-- check 'reciprocity' of the XLinks between primal and dual + if (std::get<1>(_graphs[gno]->get_vertex(pdid)) != el.first) + { + std::stringstream msg; + msg << "Cell id=" << el.first << " and its dual vertex id=" << pdid << " are not reciprocally linked"; + this->add_error(703, msg.str(), ""); + } + } + } + //-- test if the neighbours of the dual vertices actually exist + //-- an IndoorGML dual node (a "State") has a 'connects', these should exists + for (auto& g: _graphs) { + auto allids = g->get_vertices_ids(); + for (auto &vid : allids) { + auto vadjs = g->get_vertex(vid); + for (auto &vadj: std::get<2>(vadjs)) { + if (vadj == "") { + std::stringstream msg; + msg << "Dual vertex id=" << vid << " reference a non-existing edge/Transition"; + this->add_error(703, msg.str(), ""); + } + else if (g->has_vertex(vadj) == false) { + std::stringstream msg; + msg << "Dual vertex id=" << vadj << " does not exist (referenced from id=" << vid; + this->add_error(703, msg.str(), ""); + } + } + } + } + + +//-- 5. 704 - PRIMAL_DUAL_ADJACENCIES_INCONSISTENT +//-- if 2 cells are adjacent in the primal, are they also in the dual? + std::clog << "======== Validating Primal-Dual links ========" << std::endl; + for (auto& el : _cells) + { + std::string pdid = std::get<1>(el.second); + if (this->get_number_graphs() == 0) { + std::stringstream msg; + msg << "Cell id=" << el.first << " dual vertex doesn't exist"; + this->add_error(704, msg.str(), ""); + continue; + } + for (auto& g: _graphs) { + for (auto &vadj: std::get<2>(g->get_vertex(pdid))) { + std::string cadjid = std::get<1>(g->get_vertex(vadj)); + if (el.first > cadjid) + continue; + std::clog << "Cells id=" << el.first << " id=" << cadjid; + int re = are_primitives_adjacent(_lsPrimitives[std::get<0>(el.second)], + _lsPrimitives[std::get<0>(_cells[cadjid])], + tol_overlap); + if (re == 0) { + std::stringstream msg; + msg << "Cells id=" << el.first << " & id=" << cadjid; + this->add_error(704, msg.str(), ""); + std::clog << " NOT valid" << std::endl; + } else if (re == -1) { + std::clog << " are not valid, skipped adjacency test" << std::endl; + } else { + std::clog << " --> ok" << std::endl; + } + } + } + } + +//-- bye-bye + _is_valid = bValid; + return bValid; +} + + +void IndoorModel::add_cell(std::string id, Primitive* p, std::string dual, std::string CellSpaceType) +{ + this->add_primitive(p); + int pos = (_lsPrimitives.size() - 1); + _cells[id] = std::make_tuple(pos, dual, CellSpaceType); +} + + +void IndoorModel::add_graph(IndoorGraph* g) +{ + _graphs.push_back(g); +} + + +bool IndoorModel::is_valid() { + if (_is_valid == 1) + return true; + else + return false; +} + + +std::string IndoorModel::get_type() { + return _type; +} + + +int IndoorModel::get_number_graphs() { + return _graphs.size(); +} + +bool IndoorModel::graph_has_dual_vertex(std::string theid) { + for (auto& g : _graphs) + if (g->has_vertex(theid) == true) + return true; + return false; +} + + +int IndoorModel::get_graph_containing_vertex(std::string theid) { + int i = 0; + for (auto& g : _graphs) { + if (g->has_vertex(theid) == true) { + return i; + } + i++; + } + return -1; +} + + +} // namespace val3dity diff --git a/src/IndoorModel.h b/src/IndoorModel.h new file mode 100644 index 00000000..42c6df6b --- /dev/null +++ b/src/IndoorModel.h @@ -0,0 +1,66 @@ +/* + val3dity + + Copyright (c) 2011-2020, 3D geoinformation research group, TU Delft + + This file is part of val3dity. + + val3dity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + val3dity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with val3dity. If not, see . + + For any information or further details about the use of val3dity, contact + Hugo Ledoux + + Faculty of Architecture & the Built Environment + Delft University of Technology + Julianalaan 134, Delft 2628BL, the Netherlands +*/ + +#ifndef __val3dity__IndoorModel__ +#define __val3dity__IndoorModel__ + +#include "Feature.h" +#include "definitions.h" +#include "IndoorGraph.h" + + +namespace val3dity +{ + +class IndoorModel : public Feature +{ +public: + IndoorModel(std::string theid); + ~IndoorModel(); + + bool validate(double tol_planarity_d2p, double tol_planarity_normals, double tol_overlap = -1); + bool is_valid(); + std::string get_type(); + + void add_cell(std::string id, Primitive* p, std::string dual, std::string CellSpaceType); + void add_graph(IndoorGraph* g); + + int get_number_graphs(); + bool graph_has_dual_vertex(std::string theid); + int get_graph_containing_vertex(std::string theid); + +protected: + //-- // + std::map> _cells; + std::vector _graphs; + +}; + +} // namespace val3dity + +#endif /* defined(__val3dity__IndoorModel__) */ diff --git a/src/MultiSolid.cpp b/src/MultiSolid.cpp index a02ff2c1..3eb7c63d 100644 --- a/src/MultiSolid.cpp +++ b/src/MultiSolid.cpp @@ -107,7 +107,7 @@ json MultiSolid::get_report_json() else j["id"] = "none"; j["numbersolids"] = this->number_of_solids(); - j["errors"]; + j["errors"] = json::array(); for (auto& err : _errors) { for (auto& e : _errors[std::get<0>(err)]) @@ -115,7 +115,7 @@ json MultiSolid::get_report_json() json jj; jj["type"] = "Error"; jj["code"] = std::get<0>(err); - jj["description"] = errorcode2description(std::get<0>(err)); + jj["description"] = ALL_ERRORS[std::get<0>(err)]; jj["id"] = std::get<0>(e); jj["info"] = std::get<1>(e); j["errors"].push_back(jj); diff --git a/src/MultiSurface.cpp b/src/MultiSurface.cpp index c5d17ef2..0a779dc2 100644 --- a/src/MultiSurface.cpp +++ b/src/MultiSurface.cpp @@ -116,7 +116,7 @@ json MultiSurface::get_report_json() json jj; jj["type"] = "Error"; jj["code"] = std::get<0>(err); - jj["description"] = errorcode2description(std::get<0>(err)); + jj["description"] = ALL_ERRORS[std::get<0>(err)]; jj["id"] = std::get<0>(e); jj["info"] = std::get<1>(e); j["errors"].push_back(jj); diff --git a/src/Primitive.cpp b/src/Primitive.cpp index 398f602d..a6ee9bf4 100644 --- a/src/Primitive.cpp +++ b/src/Primitive.cpp @@ -65,7 +65,7 @@ void Primitive::add_error(int code, std::string whichgeoms, std::string info) _is_valid = 0; std::tuple a(whichgeoms, info); _errors[code].push_back(a); - std::clog << "\tERROR " << code << ": " << errorcode2description(code); + std::clog << "\tERROR " << code << ": " << ALL_ERRORS[code]; if (whichgeoms.empty() == false) std::clog << " (id: " << whichgeoms << ")"; std::clog << std::endl; diff --git a/src/Solid.cpp b/src/Solid.cpp index da2c5099..f1791b82 100644 --- a/src/Solid.cpp +++ b/src/Solid.cpp @@ -137,6 +137,12 @@ bool Solid::validate(double tol_planarity_d2p, double tol_planarity_normals, dou { if (this->is_valid() == 0) { + std::string s; + for (auto& e : this->get_unique_error_codes()) { + s += std::to_string(e); + s += "; "; + } + std::clog << "Errors: " << s << std::endl; return false; } bool isValid = true; @@ -187,7 +193,7 @@ json Solid::get_report_json() j["numbershells"] = (this->num_ishells() + 1); j["numberfaces"] = this->num_faces(); j["numbervertices"] = this->num_vertices(); - j["errors"]; + j["errors"] = json::array(); for (auto& err : _errors) { for (auto& e : _errors[std::get<0>(err)]) @@ -195,7 +201,7 @@ json Solid::get_report_json() json jj; jj["type"] = "Error"; jj["code"] = std::get<0>(err); - jj["description"] = errorcode2description(std::get<0>(err)); + jj["description"] = ALL_ERRORS[std::get<0>(err)]; jj["id"] = std::get<0>(e); jj["info"] = std::get<1>(e); j["errors"].push_back(jj); @@ -237,6 +243,31 @@ Nef_polyhedron* Solid::get_nef_polyhedron() } +/* + -2 = not valid polyhedron + -1 = outside + 0 = directly on the boundary of polyhedron + 1 = inside + */ +int Solid::is_point_in_solid(Point3& p) +{ + int re = -2; + if (this->is_valid() == 1) + { + auto& s = _shells[0]; + re = s->side_of_triangle_surface(p); + for (int i = 1; i <= this->num_ishells(); i++) + { + s = _shells[i]; + int re2 = s->side_of_triangle_surface(p); + if ( (re2 == 0) || (re2 == 1) ) + re = -1; + } + } + return re; +} + + std::set Solid::get_unique_error_codes() { std::set errs = Primitive::get_unique_error_codes(); for (auto& sh : _shells) { @@ -408,4 +439,12 @@ bool Solid::validate_solid_with_nef() return isValid; } + +CGAL::Bbox_3 Solid::get_bbox() +{ + CgalPolyhedron* poly = this->get_oshell()->get_cgal_polyhedron(); + return CGAL::Polygon_mesh_processing::bbox(*poly); +} + + } // namespace val3dity diff --git a/src/Solid.h b/src/Solid.h index aa177e59..d7e23c2b 100644 --- a/src/Solid.h +++ b/src/Solid.h @@ -33,6 +33,8 @@ #include "definitions.h" #include "Surface.h" +#include + #include #include @@ -65,6 +67,10 @@ class Solid : public Primitive bool is_empty(); Primitive3D get_type(); std::set get_unique_error_codes(); + + int is_point_in_solid(Point3& p); + + CGAL::Bbox_3 get_bbox(); const std::vector& get_shells(); diff --git a/src/Surface.cpp b/src/Surface.cpp index c32477a7..03050998 100644 --- a/src/Surface.cpp +++ b/src/Surface.cpp @@ -32,6 +32,7 @@ #include "input.h" #include "validate_shell.h" #include +#include #include #include @@ -84,7 +85,7 @@ void Surface::add_error(int code, std::string faceid, std::string info) { std::tuple a(faceid, info); _errors[code].push_back(a); - std::clog << "\tERROR " << code << ": " << errorcode2description(code); + std::clog << "\tERROR " << code << ": " << ALL_ERRORS[code]; if (faceid.empty() == false) std::clog << " (face " << faceid << ")"; std::clog << std::endl; @@ -113,7 +114,7 @@ json Surface::get_report_json() json jj; jj["type"] = "Error"; jj["code"] = std::get<0>(err); - jj["description"] = errorcode2description(std::get<0>(err)); + jj["description"] = ALL_ERRORS[std::get<0>(err)]; jj["id"] = std::get<0>(e); jj["info"] = std::get<1>(e); j.push_back(jj); @@ -914,4 +915,23 @@ bool Surface::has_face_rings_toofewpoints(const std::vector< std::vector >& return bErrors; } +int Surface::side_of_triangle_surface(Point3& p) + /* + -2 = not valid polyhedron + -1 = outside + 0 = directly on the boundary of polyhedron + 1 = inside + */ +{ + int re = -2; + if ( (_polyhedron != NULL) && (CGAL::is_triangle_mesh(*_polyhedron) == true) ) + { + CGAL::Side_of_triangle_mesh inside(*_polyhedron); + Point3 p_translated(p.x() - Surface::_shiftx, p.y() - Surface::_shifty, p.z()); + re = inside(p_translated); + } + return re; +} + + } // namespace val3dity diff --git a/src/Surface.h b/src/Surface.h index e8e9d536..a623aa1f 100644 --- a/src/Surface.h +++ b/src/Surface.h @@ -74,6 +74,8 @@ class Surface std::string get_poly_representation(); std::string get_off_representation(); + int side_of_triangle_surface(Point3& p); + bool were_vertices_merged_during_parsing(); int get_number_parsed_vertices(); diff --git a/src/definitions.h b/src/definitions.h index a044888e..5b2d160c 100644 --- a/src/definitions.h +++ b/src/definitions.h @@ -95,12 +95,13 @@ typedef long long int64; typedef enum { - SOLID = 0, - COMPOSITESOLID = 1, - MULTISOLID = 2, - COMPOSITESURFACE = 3, - MULTISURFACE = 4, - ALL = 5, + SOLID = 0, + COMPOSITESOLID = 1, + MULTISOLID = 2, + COMPOSITESURFACE = 3, + MULTISURFACE = 4, + GEOMETRYTEMPLATE = 5, + ALL = 9, } Primitive3D; typedef enum @@ -120,6 +121,56 @@ struct Error { std::string info2; }; +static std::map ALL_ERRORS = { + //-- RING + {101, "TOO_FEW_POINTS"}, + {102, "CONSECUTIVE_POINTS_SAME"}, + {103, "RING_NOT_CLOSED"}, + {104, "RING_SELF_INTERSECTION"}, + //-- POLYGON + {201, "INTERSECTION_RINGS"}, + {202, "DUPLICATED_RINGS"}, + {203, "NON_PLANAR_POLYGON_DISTANCE_PLANE"}, + {204, "NON_PLANAR_POLYGON_NORMALS_DEVIATION"}, + {205, "POLYGON_INTERIOR_DISCONNECTED"}, + {206, "INNER_RING_OUTSIDE"}, + {207, "INNER_RINGS_NESTED"}, + {208, "ORIENTATION_RINGS_SAME"}, + //-- SHELL + {300, "NOT_VALID_2_MANIFOLD"}, + {301, "TOO_FEW_POLYGONS"}, + {302, "SHELL_NOT_CLOSED"}, + {303, "NON_MANIFOLD_CASE"}, + {305, "MULTIPLE_CONNECTED_COMPONENTS"}, + {306, "SHELL_SELF_INTERSECTION"}, + {307, "POLYGON_WRONG_ORIENTATION"}, + // {309, "VERTICES_NOT_USED"}, + //-- SOLID & MULTISOLID + {401, "INTERSECTION_SHELLS"}, + {402, "DUPLICATED_SHELLS"}, + {403, "INNER_SHELL_OUTSIDE"}, + {404, "SOLID_INTERIOR_DISCONNECTED"}, + {405, "WRONG_ORIENTATION_SHELL"}, + //-- COMPOSITESOLID + {501, "INTERSECTION_SOLIDS"}, + {502, "DUPLICATED_SOLIDS"}, + {503, "DISCONNECTED_SOLIDS"}, + //-- CityGML objects + {601, "BUILDINGPARTS_OVERLAP"}, + {609, "CITYOBJECT_HAS_NO_GEOMETRY"}, + //-- IndoorGML objects + {701, "CELLS_OVERLAP"}, + {702, "DUAL_VERTEX_OUTSIDE_CELL"}, + {703, "PRIMAL_DUAL_XLINKS_ERROR"}, + {704, "PRIMAL_DUAL_ADJACENCIES_INCONSISTENT"}, + //-- OTHERS + {901, "INVALID_INPUT_FILE"}, + {902, "EMPTY_PRIMITIVE"}, + {903, "WRONG_INPUT_PARAMETERS"}, + {904, "FORMAT_NOT_SUPPORTED"}, + {999, "UNKNOWN_ERROR"} +}; + } // namespace val3dity #endif diff --git a/src/input.cpp b/src/input.cpp index 8e1ae193..1816fdc8 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -1,7 +1,7 @@ /* val3dity - Copyright (c) 2011-2019, 3D geoinformation research group, TU Delft + Copyright (c) 2011-2020, 3D geoinformation research group, TU Delft This file is part of val3dity. @@ -31,12 +31,15 @@ #include "Feature.h" #include "CityObject.h" #include "GenericObject.h" +#include "IndoorModel.h" +#include "IndoorGraph.h" #include "Surface.h" #include "MultiSurface.h" #include "CompositeSurface.h" #include "Solid.h" #include "CompositeSolid.h" #include "MultiSolid.h" +#include "GeometryTemplate.h" using namespace std; @@ -91,7 +94,7 @@ std::string IOErrors::get_report_text() { for (auto i : err.second) { - ss << err.first << " -- " << errorcode2description(err.first) << std::endl; + ss << err.first << " -- " << ALL_ERRORS[err.first] << std::endl; ss << "\tInfo: " << i << std::endl; } } @@ -109,7 +112,7 @@ json IOErrors::get_report_json() json jj; jj["type"] = "Error"; jj["code"] = err.first; - jj["description"] = errorcode2description(err.first); + jj["description"] = ALL_ERRORS[err.first]; jj["info"] = i; j.push_back(jj); } @@ -118,55 +121,17 @@ json IOErrors::get_report_json() } -std::string errorcode2description(int code) { - switch(code) - { - case 0: return string("STATUS_OK"); break; - //-- RING - case 101: return string("TOO_FEW_POINTS"); break; - case 102: return string("CONSECUTIVE_POINTS_SAME"); break; - case 103: return string("RING_NOT_CLOSED"); break; - case 104: return string("RING_SELF_INTERSECTION"); break; - //-- POLYGON - case 201: return string("INTERSECTION_RINGS"); break; - case 202: return string("DUPLICATED_RINGS"); break; - case 203: return string("NON_PLANAR_POLYGON_DISTANCE_PLANE"); break; - case 204: return string("NON_PLANAR_POLYGON_NORMALS_DEVIATION"); break; - case 205: return string("POLYGON_INTERIOR_DISCONNECTED"); break; - case 206: return string("INNER_RING_OUTSIDE"); break; - case 207: return string("INNER_RINGS_NESTED"); break; - case 208: return string("ORIENTATION_RINGS_SAME"); break; - //-- SHELL - case 300: return string("NOT_VALID_2_MANIFOLD"); break; - case 301: return string("TOO_FEW_POLYGONS"); break; - case 302: return string("SHELL_NOT_CLOSED"); break; - case 303: return string("NON_MANIFOLD_CASE"); break; - case 305: return string("MULTIPLE_CONNECTED_COMPONENTS"); break; - case 306: return string("SHELL_SELF_INTERSECTION"); break; - case 307: return string("POLYGON_WRONG_ORIENTATION"); break; - // case 309: return string("VERTICES_NOT_USED"); break; - //-- SOLID & MULTISOLID - case 401: return string("INTERSECTION_SHELLS"); break; - case 402: return string("DUPLICATED_SHELLS"); break; - case 403: return string("INNER_SHELL_OUTSIDE"); break; - case 404: return string("SOLID_INTERIOR_DISCONNECTED"); break; - case 405: return string("WRONG_ORIENTATION_SHELL"); break; - //-- COMPOSITESOLID - case 501: return string("INTERSECTION_SOLIDS"); break; - case 502: return string("DUPLICATED_SOLIDS"); break; - case 503: return string("DISCONNECTED_SOLIDS"); break; - //-- CityGML objects - case 601: return string("BUILDINGPARTS_OVERLAP"); break; - case 609: return string("CITYOBJECT_HAS_NO_GEOMETRY"); break; - //-- OTHERS - case 901: return string("INVALID_INPUT_FILE"); break; - case 902: return string("EMPTY_PRIMITIVE"); break; - case 903: return string("WRONG_INPUT_PARAMETERS"); break; - case 999: return string("UNKNOWN_ERROR"); break; - default: return string("UNKNOWN_ERROR"); break; - } +std::string IOErrors::get_input_file_type() { + return _inputfiletype; +} + + +void IOErrors::set_input_file_type(std::string s) { + _inputfiletype = s; } + + //-- ignore XML namespace std::string localise(std::string s) { @@ -443,42 +408,42 @@ void print_information(std::string &ifile) std::map ns; pugi::xml_node ncm = doc.first_child(); std::string vcitygml; - get_namespaces(ncm, ns, vcitygml); + get_namespaces(ncm, vcitygml); if (vcitygml.empty() == true) { std::cout << "File does not have the CityGML namespace. Abort." << std::endl; return; } std::cout << "++++++++++++++++++++ GENERAL +++++++++++++++++++++" << std::endl; std::cout << "CityGML version: " << vcitygml << std::endl; - report_primitives(doc, ns); - report_building(doc, ns); + report_primitives(doc); + report_building(doc); } -void report_building(pugi::xml_document& doc, std::map& ns) { +void report_building(pugi::xml_document& doc) { std::cout << "++++++++++++++++++++ BUILDINGS +++++++++++++++++++" << std::endl; - std::string s = "//" + ns["building"] + "Building"; + std::string s = "//" + NS["building"] + "Building"; int nobuildings = doc.select_nodes(s.c_str()).size(); print_info_aligned("Building", nobuildings); - s = "//" + ns["building"] + "Building" + "/" + ns["building"] + "consistsOfBuildingPart" + "[1]"; + s = "//" + NS["building"] + "Building" + "/" + NS["building"] + "consistsOfBuildingPart" + "[1]"; int nobwbp = doc.select_nodes(s.c_str()).size(); print_info_aligned("without BuildingPart", (nobuildings - nobwbp), true); print_info_aligned("having BuildingPart", nobwbp, true); - s = "//" + ns["building"] + "Building" + "[@" + ns["gml"] + "id]"; + s = "//" + NS["building"] + "Building" + "[@" + NS["gml"] + "id]"; print_info_aligned("with gml:id", doc.select_nodes(s.c_str()).size(), true); - s = "//" + ns["building"] + "BuildingPart"; + s = "//" + NS["building"] + "BuildingPart"; int nobuildingparts = doc.select_nodes(s.c_str()).size(); print_info_aligned("BuildingPart", nobuildingparts); - s = "//" + ns["building"] + "BuildingPart" + "[@" + ns["gml"] + "id]"; + s = "//" + NS["building"] + "BuildingPart" + "[@" + NS["gml"] + "id]"; print_info_aligned("with gml:id", doc.select_nodes(s.c_str()).size(), true); for (int lod = 1; lod <= 3; lod++) { std::cout << "LOD" << lod << std::endl; int totals = 0; int totalms = 0; int totalsem = 0; - report_building_each_lod(doc, ns, lod, totals, totalms, totalsem); + report_building_each_lod(doc, lod, totals, totalms, totalsem); print_info_aligned("Building stored in gml:Solid", totals, true); print_info_aligned("Building stored in gml:MultiSurface", totalms, true); print_info_aligned("Building with semantics for surfaces", totalsem, true); @@ -494,15 +459,15 @@ void print_info_aligned(std::string o, size_t number, bool tab) { std::cout << std::setw(10) << std::right << number << std::endl; } -void report_building_each_lod(pugi::xml_document& doc, std::map& ns, int lod, int& total_solid, int& total_ms, int& total_sem) { +void report_building_each_lod(pugi::xml_document& doc, int lod, int& total_solid, int& total_ms, int& total_sem) { total_solid = 0; total_ms = 0; total_sem = 0; std::string slod = "lod" + std::to_string(lod); - std::string s = "//" + ns["building"] + "Building"; + std::string s = "//" + NS["building"] + "Building"; pugi::xpath_node_set nb = doc.select_nodes(s.c_str()); for (auto& b : nb) { - std::string s1 = ".//" + ns["building"] + slod + "Solid"; + std::string s1 = ".//" + NS["building"] + slod + "Solid"; pugi::xpath_node_set tmp = b.node().select_nodes(s1.c_str()); if (tmp.empty() == false) { for (auto& nbp : tmp) { @@ -510,7 +475,7 @@ void report_building_each_lod(pugi::xml_document& doc, std::map& ns } -void report_primitives(pugi::xml_document& doc, std::map& ns) { + +void report_primitives(pugi::xml_document& doc) { std::cout << "+++++++++++++++++++ PRIMITIVES +++++++++++++++++++" << std::endl; - std::string s = "//" + ns["gml"] + "Solid"; + std::string s = "//" + NS["gml"] + "Solid"; print_info_aligned("gml:Solid", doc.select_nodes(s.c_str()).size()); - s = "//" + ns["gml"] + "MultiSolid"; + s = "//" + NS["gml"] + "MultiSolid"; print_info_aligned("gml:MultiSolid", doc.select_nodes(s.c_str()).size()); - s = "//" + ns["gml"] + "CompositeSolid"; + s = "//" + NS["gml"] + "CompositeSolid"; print_info_aligned("gml:CompositeSolid", doc.select_nodes(s.c_str()).size()); - s = "//" + ns["gml"] + "MultiSurface"; + s = "//" + NS["gml"] + "MultiSurface"; print_info_aligned("gml:MultiSurface", doc.select_nodes(s.c_str()).size()); - s = "//" + ns["gml"] + "CompositeSurface"; + s = "//" + NS["gml"] + "CompositeSurface"; print_info_aligned("gml:CompositeSurface", doc.select_nodes(s.c_str()).size()); - s = "//" + ns["gml"] + "Polygon"; + s = "//" + NS["gml"] + "Polygon"; print_info_aligned("gml:Polygon", doc.select_nodes(s.c_str()).size()); std::cout << std::endl; @@ -630,6 +596,117 @@ void process_json_surface(std::vector< std::vector >& pgn, json& j, Surface } +void process_json_geometries_of_co(json& jco, CityObject* co, std::vector& lsGTs, json& j, double tol_snap) +{ + int idgeom = co->number_of_primitives(); + for (auto& g : jco["geometry"]) { + std::string theid = co->get_id() + "(" + std::to_string(idgeom) + ")"; + if (g["type"] == "Solid") + { + Solid* s = new Solid(theid); + bool oshell = true; + int c = 0; + for (auto& shell : g["boundaries"]) + { + Surface* sh = new Surface(c, tol_snap); + c++; + for (auto& polygon : shell) { + std::vector< std::vector > pa = polygon; + process_json_surface(pa, j, sh); + } + if (oshell == true) + { + oshell = false; + s->set_oshell(sh); + } + else + s->add_ishell(sh); + } + co->add_primitive(s); + } + else if ( (g["type"] == "MultiSurface") || (g["type"] == "CompositeSurface") ) + { + Surface* sh = new Surface(-1, tol_snap); + for (auto& p : g["boundaries"]) + { + std::vector< std::vector > pa = p; + process_json_surface(pa, j, sh); + } + if (g["type"] == "MultiSurface") + { + MultiSurface* ms = new MultiSurface(theid); + ms->set_surface(sh); + co->add_primitive(ms); + } + else + { + CompositeSurface* cs = new CompositeSurface(theid); + cs->set_surface(sh); + co->add_primitive(cs); + } + } + else if (g["type"] == "MultiSolid") + { + MultiSolid* ms = new MultiSolid(theid); + for (auto& solid : g["boundaries"]) + { + Solid* s = new Solid(); + bool oshell = true; + for (auto& shell : solid) + { + Surface* sh = new Surface(-1, tol_snap); + for (auto& polygon : shell) { + std::vector< std::vector > pa = polygon; + process_json_surface(pa, j, sh); + } + if (oshell == true) + { + oshell = false; + s->set_oshell(sh); + } + else + s->add_ishell(sh); + } + ms->add_solid(s); + } + co->add_primitive(ms); + } + else if (g["type"] == "CompositeSolid") + { + CompositeSolid* cs = new CompositeSolid(theid); + for (auto& solid : g["boundaries"]) + { + Solid* s = new Solid(); + bool oshell = true; + for (auto& shell : solid) + { + Surface* sh = new Surface(-1, tol_snap); + for (auto& polygon : shell) { + std::vector< std::vector > pa = polygon; + process_json_surface(pa, j, sh); + } + if (oshell == true) + { + oshell = false; + s->set_oshell(sh); + } + else + s->add_ishell(sh); + } + cs->add_solid(s); + } + co->add_primitive(cs); + } + else if (g["type"] == "GeometryInstance") + { + int gti = g["template"]; + GeometryTemplate* g2 = lsGTs[gti]; + co->add_primitive(g2); + } + idgeom++; + } +} + void read_file_cityjson(std::string &ifile, std::vector& lsFeatures, IOErrors& errs, double tol_snap) { std::ifstream input(ifile); @@ -637,125 +714,281 @@ void read_file_cityjson(std::string &ifile, std::vector& lsFeatures, I try { input >> j; - // TODO: other validation for CityJSON and just not JSON stuff? } catch (nlohmann::detail::parse_error e) { errs.add_error(901, "Input file not a valid JSON file."); return; } + // TODO: other validation for CityJSON or just let it crash? + if (j["type"] != "CityJSON") { + errs.add_error(901, "Input file not a CityJSON file."); + return; + } std::cout << "CityJSON input file" << std::endl; std::cout << "# City Objects found: " << j["CityObjects"].size() << std::endl; //-- compute (_minx, _miny) compute_min_xy(j); + //-- read and store the GeometryTemplates + std::vector lsGTs; + if (j.count("geometry-templates") == 1) + { + process_cityjson_geometrytemplates(j["geometry-templates"], lsGTs, tol_snap); + } + //-- process each CO for (json::iterator it = j["CityObjects"].begin(); it != j["CityObjects"].end(); ++it) { + //-- BuildingParts geometries are put with those of a Building + if (it.value()["type"] == "BuildingPart") + continue; CityObject* co = new CityObject(it.key(), it.value()["type"]); - int idgeom = 0; - for (auto& g : it.value()["geometry"]) { - std::string theid = it.key() + "(" + std::to_string(idgeom) + ")"; - if (g["type"] == "Solid") + process_json_geometries_of_co(it.value(), co, lsGTs, j, tol_snap); + //-- if Building has Parts, put them here in _lsPrimitives + if ( (it.value()["type"] == "Building") && (it.value().count("children") != 0) ) + { + for (std::string bpid : it.value()["children"]) { - Solid* s = new Solid(theid); - bool oshell = true; - int c = 0; - for (auto& shell : g["boundaries"]) - { - Surface* sh = new Surface(c, tol_snap); - c++; - for (auto& polygon : shell) { - std::vector< std::vector > pa = polygon; - process_json_surface(pa, j, sh); - } - if (oshell == true) - { - oshell = false; - s->set_oshell(sh); - } - else - s->add_ishell(sh); - } - co->add_primitive(s); + process_json_geometries_of_co(j["CityObjects"][bpid], co, lsGTs, j, tol_snap); } - else if ( (g["type"] == "MultiSurface") || (g["type"] == "CompositeSurface") ) + } + lsFeatures.push_back(co); + } +} + + +void process_cityjson_geometrytemplates(json& j, std::vector& lsGTs, double tol_snap) +{ + int count = 0; + for (auto& jt : j["templates"]) + { + GeometryTemplate* gt = new GeometryTemplate(std::to_string(count)); + if (jt["type"] == "Solid") + { + Solid* s = new Solid("0"); + bool oshell = true; + int c = 0; + for (auto& shell : jt["boundaries"]) { - Surface* sh = new Surface(-1, tol_snap); - for (auto& p : g["boundaries"]) - { - std::vector< std::vector > pa = p; - process_json_surface(pa, j, sh); + Surface* sh = new Surface(c, tol_snap); + c++; + for (auto& polygon : shell) { + std::vector< std::vector > pa = polygon; + process_json_surface_geometrytemplate(pa, j, sh); } - if (g["type"] == "MultiSurface") + if (oshell == true) { - MultiSurface* ms = new MultiSurface(theid); - ms->set_surface(sh); - co->add_primitive(ms); + oshell = false; + s->set_oshell(sh); } else + s->add_ishell(sh); + } + gt->add_primitive(s); + } + else if ( (jt["type"] == "MultiSurface") || (jt["type"] == "CompositeSurface") ) + { + Surface* sh = new Surface(-1, tol_snap); + for (auto& p : jt["boundaries"]) + { + std::vector< std::vector > pa = p; + process_json_surface_geometrytemplate(pa, j, sh); + } + if (jt["type"] == "MultiSurface") + { + MultiSurface* ms = new MultiSurface("0"); + ms->set_surface(sh); + gt->add_primitive(ms); + } + else + { + CompositeSurface* cs = new CompositeSurface("0"); + cs->set_surface(sh); + gt->add_primitive(cs); + } + } + lsGTs.push_back(gt); + count++; + } +} + + +void process_json_surface_geometrytemplate(std::vector< std::vector >& pgn, json& j, Surface* sh) +{ + std::vector< std::vector > pgnids; + for (auto& r : pgn) + { + std::vector newr; + for (auto& i : r) + { + double x; + double y; + double z; + x = double(j["vertices-templates"][i][0]); + y = double(j["vertices-templates"][i][1]); + z = double(j["vertices-templates"][i][2]); + Point3 p3(x, y, z); + newr.push_back(sh->add_point(p3)); + } + pgnids.push_back(newr); + } + sh->add_face(pgnids); +} + + +void process_gml_file_indoorgml(pugi::xml_document& doc, std::vector& lsFeatures, std::map& dallpoly, IOErrors& errs, double tol_snap) +{ + //-- 0. read the header of the file and find its gml:name, if any + std::string nameim = ""; + if (doc.first_child().attribute("gml:id") != 0) + nameim = doc.first_child().attribute("gml:id").value(); + else + nameim = "MyIndoorModel"; + IndoorModel* im = new IndoorModel(nameim); + lsFeatures.push_back(im); + + //-- 1. read each cellSpaceMember in the file (the primal objects) + //-- these can have different names, depending on the Extensions/ADEs used + std::string s = ".//" + NS["indoorgml"] + "cellSpaceMember"; + pugi::xpath_node_set nn = doc.select_nodes(s.c_str()); + int pcounter = 0; + for (pugi::xpath_node_set::const_iterator it = nn.begin(); it != nn.end(); ++it) + { + pugi::xml_node cs = it->node().first_child(); + std::string theid = ""; + std::string duality = ""; + //-- get the ID + if (cs.attribute("gml:id") != 0) { + // std::cout << "\t" << it->node().attribute("gml:id").value() << std::endl; + theid = cs.attribute("gml:id").value(); + } + else + theid = ("MISSING_ID_" + std::to_string(pcounter)); + //-- get the duality pointer (max one, sweet) + s = NS["indoorgml"] + "duality"; + for (pugi::xml_node child : cs.children(s.c_str())) + { + if (child.attribute("xlink:href") != 0) { + std::string s = child.attribute("xlink:href").value(); + if (s[0] == '#') + s = s.substr(1); + duality = s; + } + } + // IndoorCell* cell = new IndoorCell(theid, duality); + //-- get the geometry, either Solid or Surface + s = NS["indoorgml"] + "cellSpaceGeometry"; + Solid* sol; + for (pugi::xml_node child : cs.children(s.c_str())) + { + s = NS["indoorgml"] + "Geometry3D"; + for (pugi::xml_node child2 : child.children(s.c_str())) + { + s = NS["gml"] + "Solid"; + for (pugi::xml_node child3 : child2.children(s.c_str())) { - CompositeSurface* cs = new CompositeSurface(theid); - cs->set_surface(sh); - co->add_primitive(cs); + // std::cout << "Solid: " << child3.attribute("gml:id").value() << std::endl; + sol = process_gml_solid(child3, dallpoly, tol_snap, errs); + if (sol->get_id() == "") + sol->set_id("MISSING_ID"); + // cell->add_primitive(sol); } } - else if (g["type"] == "MultiSolid") + } + pcounter++; + im->add_cell(theid, sol, duality, cs.name()); + } + + //-- 2. read the dual graphs (yes there can be more than one) + s = ".//" + NS["indoorgml"] + "SpaceLayer"; + nn = doc.select_nodes(s.c_str()); + for (pugi::xpath_node_set::const_iterator it = nn.begin(); it != nn.end(); ++it) + { + std::string idg; + if (it->node().attribute("gml:id") != 0) { + idg = it->node().attribute("gml:id").value(); + } + else + idg = ""; + // IndoorGraph ig(idg); + IndoorGraph* ig = new IndoorGraph(idg); + //-- fetch all the edges + std::map> edges; + s = ".//" + NS["indoorgml"] + "Transition"; + pugi::xpath_node_set ntr = it->node().select_nodes(s.c_str()); + for (pugi::xpath_node_set::const_iterator it = ntr.begin(); it != ntr.end(); ++it) + { + std::string theid = it->node().attribute("gml:id").value(); + s = NS["indoorgml"] + "connects"; + std::vector connects; + for (pugi::xml_node child : it->node().children(s.c_str())) { - MultiSolid* ms = new MultiSolid(theid); - for (auto& solid : g["boundaries"]) - { - Solid* s = new Solid(); - bool oshell = true; - for (auto& shell : solid) - { - Surface* sh = new Surface(-1, tol_snap); - for (auto& polygon : shell) { - std::vector< std::vector > pa = polygon; - process_json_surface(pa, j, sh); - } - if (oshell == true) - { - oshell = false; - s->set_oshell(sh); - } - else - s->add_ishell(sh); - } - ms->add_solid(s); + if (child.attribute("xlink:href") != 0) { + std::string s = child.attribute("xlink:href").value(); + if (s[0] == '#') + s = s.substr(1); + connects.push_back(s); } - co->add_primitive(ms); } - else if (g["type"] == "CompositeSolid") + edges[theid] = std::make_tuple(connects[0], connects[1]); + } + //-- fetch all the nodes + s = ".//" + NS["indoorgml"] + "State"; + pugi::xpath_node_set nstate = it->node().select_nodes(s.c_str()); +// pugi::xpath_node_set nstate = doc.select_nodes(s.c_str()); + for (pugi::xpath_node_set::const_iterator it = nstate.begin(); it != nstate.end(); ++it) + { + // std::cout << "---\n" << it->node().attribute("gml:id").value() << std::endl; + std::string vid = it->node().attribute("gml:id").value(); + s = NS["indoorgml"] + "duality"; + std::string vdual; + pugi::xml_node child = it->node().child(s.c_str()); + if (child.attribute("xlink:href") != 0) { + vdual = child.attribute("xlink:href").value(); + if (vdual[0] == '#') + vdual = vdual.substr(1); + // std::cout << "dual node: " << vdual << std::endl; + } + s = NS["indoorgml"] + "connects"; + std::vector vadj; + for (pugi::xml_node child : it->node().children(s.c_str())) { - CompositeSolid* cs = new CompositeSolid(theid); - for (auto& solid : g["boundaries"]) - { - Solid* s = new Solid(); - bool oshell = true; - for (auto& shell : solid) - { - Surface* sh = new Surface(-1, tol_snap); - for (auto& polygon : shell) { - std::vector< std::vector > pa = polygon; - process_json_surface(pa, j, sh); - } - if (oshell == true) - { - oshell = false; - s->set_oshell(sh); - } - else - s->add_ishell(sh); - } - cs->add_solid(s); + if (child.attribute("xlink:href") != 0) { + std::string s = child.attribute("xlink:href").value(); + if (s[0] == '#') + s = s.substr(1); + if (std::get<1>(edges[s]) != vid) + vadj.push_back(std::get<1>(edges[s])); } - co->add_primitive(cs); - } - idgeom++; + } + s = ".//" + NS["gml"] + "pos"; + pugi::xpath_node n = it->node().select_node(s.c_str()); + // std::cout << n.node().child_value() << std::endl; + + std::string buf; + std::stringstream ss(n.node().child_value()); + std::vector tokens; + while (ss >> buf) + tokens.push_back(buf); + // long double x = std::stold(tokens[0]); + // long double y = std::stold(tokens[1]); + // long double z = std::stold(tokens[2]); + // TODO: use long double? + ig->add_vertex(vid, + std::stold(tokens[0]), + std::stold(tokens[1]), + std::stold(tokens[2]), + vdual, + vadj); } - lsFeatures.push_back(co); + im->add_graph(ig); } + if (im->get_number_graphs() == 0) + std::cout << "\t!!! No dual graph was found in the input file" << std::endl; } + + + void process_gml_file_primitives(pugi::xml_document& doc, std::vector& lsFeatures, std::map& dallpoly, IOErrors& errs, double tol_snap) { primitives_walker walker; @@ -942,6 +1175,13 @@ void read_file_gml(std::string &ifile, std::vector& lsFeatures, IOErro pugi::xml_node ncm = doc.first_child(); std::string vcitygml; get_namespaces(ncm, vcitygml); //-- results in global variable NS in this unit + + //-- CityGML v3 is not supported: warning to users + if (vcitygml == "v3.0") { + errs.add_error(904, "CityGML v3.0 files are not supported, use CityJSON (all versions fully supported) or downgrade to v2.0."); + return; + } + if (NS.count("gml") == 0) { errs.add_error(901, "Input file does not have the GML namespace."); @@ -955,8 +1195,14 @@ void read_file_gml(std::string &ifile, std::vector& lsFeatures, IOErro if ( (NS.count("citygml") != 0) && (ncm.name() == (NS["citygml"] + "CityModel")) ) { std::cout << "CityGML input file" << std::endl; + errs.set_input_file_type("CityGML"); process_gml_file_city_objects(doc, lsFeatures, dallpoly, errs, tol_snap, geom_is_sem_surfaces); } + else if ( (NS.count("indoorgml") != 0) && (ncm.name() == (NS["indoorgml"] + "IndoorFeatures")) ) { + std::cout << "IndoorGML input file" << std::endl; + errs.set_input_file_type("IndoorGML"); + process_gml_file_indoorgml(doc, lsFeatures, dallpoly, errs, tol_snap); + } else { std::cout << "GML input file (ie not CityGML)" << std::endl; @@ -985,9 +1231,17 @@ void get_namespaces(pugi::xml_node& root, std::string& vcitygml) { sns = "citygml"; vcitygml = "v2.0"; } + else if (value.find("http://www.opengis.net/citygml/3") != std::string::npos) { + sns = "citygml"; + vcitygml = "v3.0"; + } else if ( (value.find("http://www.opengis.net/gml") != std::string::npos) && (value.find("http://www.opengis.net/gmlcov") == std::string::npos) ) { - sns = "gml"; + sns = "gml"; + } + else if ( (value.find("http://www.opengis.net/indoorgml/1") != std::string::npos) && + (value.find("core") != std::string::npos) ) { + sns = "indoorgml"; } else if (value.find("http://www.opengis.net/citygml/building") != std::string::npos) sns = "building"; diff --git a/src/input.h b/src/input.h index f74ab23d..66c7bca1 100644 --- a/src/input.h +++ b/src/input.h @@ -52,10 +52,12 @@ namespace val3dity class Solid; class CompositeSolid; class MultiSolid; + class GeometryTemplate; class IOErrors { std::map > _errors; + std::string _inputfiletype; public: bool has_errors(); void add_error(int code, std::string info); @@ -63,6 +65,8 @@ class IOErrors { json get_report_json(); std::set get_unique_error_codes(); bool has_specific_error(int i); + std::string get_input_file_type(); + void set_input_file_type(std::string s); }; @@ -159,13 +163,11 @@ void get_namespaces(pugi::xml_node& root, std::string& vcitygml); void read_file_cityjson(std::string &ifile, std::vector& lsFeatures, IOErrors& errs, double tol_snap); void print_information(std::string &ifile); -void report_primitives(pugi::xml_document& doc, std::map& ns); -void report_building(pugi::xml_document& doc, std::map& ns); -void report_building_each_lod(pugi::xml_document& doc, std::map& ns, int lod, int& total_solid, int& total_ms, int& total_sem); -void get_namespaces(pugi::xml_node& root, std::map& ns, std::string& vcitygml); +void report_primitives(pugi::xml_document& doc); +void report_building(pugi::xml_document& doc); +void report_building_each_lod(pugi::xml_document& doc, int lod, int& total_solid, int& total_ms, int& total_sem); void print_info_aligned(std::string o, size_t number, bool tab = false); -std::string errorcode2description(int code); void read_file_obj(std::vector& lsFeatures, std::string &ifile, Primitive3D prim3d, IOErrors& errs, double tol_snap); Surface* read_file_poly(std::string &ifile, int shellid, IOErrors& errs); Surface* read_file_off(std::string &ifile, int shellid, IOErrors& errs); @@ -178,10 +180,15 @@ Solid* process_gml_solid(const pugi::xml_node& nsolid, std::map& dallpoly, double tol_snap, IOErrors& errs); CompositeSolid* process_gml_compositesolid(const pugi::xml_node& nms, std::map& dallpoly, double tol_snap, IOErrors& errs); + +void process_json_geometries_of_co(json& jco, CityObject* co, std::vector& lsGTs, json& j, double tol_snap); void process_json_surface(std::vector< std::vector >& pgn, nlohmann::json& j, Surface* s); +void process_cityjson_geometrytemplates(json& jgt, std::vector& lsGTs, double tol_snap); +void process_json_surface_geometrytemplate(std::vector< std::vector >& pgn, json& j, Surface* sh); void build_dico_xlinks(pugi::xml_document& doc, std::map& dallpoly, IOErrors& errs); void process_gml_file_city_objects(pugi::xml_document& doc, std::vector& lsFeatures, std::map& dallpoly, IOErrors& errs, double tol_snap, bool geom_is_sem_surfaces); void process_gml_file_primitives(pugi::xml_document& doc, std::vector& lsFeatures, std::map& dallpoly, IOErrors& errs, double tol_snap); +void process_gml_file_indoorgml(pugi::xml_document& doc, std::vector& lsFeatures, std::map& dallpoly, IOErrors& errs, double tol_snap); void printProgressBar(int percent); std::string localise(std::string s); diff --git a/src/main.cpp b/src/main.cpp index 56bdff5b..90977e29 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,7 @@ /* val3dity - Copyright (c) 2011-2019, 3D geoinformation research group, TU Delft + Copyright (c) 2011-2020, 3D geoinformation research group, TU Delft This file is part of val3dity. @@ -21,13 +21,12 @@ For any information or further details about the use of val3dity, contact Hugo Ledoux - Faculty of Architecture & the Built Environment + Faculty of the Built Environment & Architecture Delft University of Technology Julianalaan 134, Delft 2628BL, the Netherlands */ #include "input.h" -#include "reportoutput.h" #include "Primitive.h" #include "Surface.h" #include "MultiSurface.h" @@ -36,7 +35,6 @@ #include "Feature.h" #include "CityObject.h" #include "GenericObject.h" - #include "validate_prim_toporel.h" #include @@ -48,15 +46,20 @@ using namespace std; using namespace val3dity; using json = nlohmann::json; -std::string VAL3DITY_VERSION = "2.1.1"; +std::string VAL3DITY_VERSION = "2.2.0"; std::string print_summary_validation(std::vector& lsFeatures, IOErrors& ioerrs); std::string unit_test(std::vector& lsFeatures, IOErrors& ioerrs); -void get_report_json(json& jr, std::string ifile, std::vector& lsFeatures, double snap_tol, double overlap_tol, double planarity_d2p_tol, double planarity_n_tol, IOErrors ioerrs, bool onlyinvalid); -void print_license(); -void write_report_html(json& jr, std::string report); void write_report_json(json& jr, std::string report); +void get_report_json(json& jr, + std::string ifile, + std::vector& lsFeatures, + double snap_tol, + double overlap_tol, + double planarity_d2p_tol, + double planarity_n_tol, + IOErrors ioerrs); class MyOutput : public TCLAP::StdOutput @@ -79,26 +82,27 @@ class MyOutput : public TCLAP::StdOutput } std::cout << "==SOME EXAMPLES==" << std::endl; - std::cout << "\tval3dity CityGMLinput.gml" << std::endl; - std::cout << "\t\tValidates each 3D primitive in CityGMLinput.gml" << std::endl; - std::cout << "\t\tand outputs a summary per city object" << std::endl; + std::cout << "\tval3dity input.json" << std::endl; + std::cout << "\t\tValidate each City Object and each 3D primitive in input.json (CityJSON file)" << std::endl; + std::cout << "\t\tand print a summary" << std::endl; - std::cout << "\tval3dity input.json -r /home/elvis/temp/r" << std::endl; - std::cout << "\t\tValidates each 3D primitive in input.json (CityJSON file)" << std::endl; - std::cout << "\t\tand outputs a detailed report '/home/elvis/temp/r/report.html'" << std::endl; + std::cout << "\tval3dity input.json --report /home/elvis/temp/myreport.json" << std::endl; + std::cout << "\t\tValidate each 3D primitive in input.json (CityJSON file)" << std::endl; + std::cout << "\t\tand output a detailed JSON report '/home/elvis/temp/myreport.json';" << std::endl; + std::cout << "\t\tbrowse that report at http://geovalidation.bk.tudelft.nl/val3dity/browser/" << std::endl; std::cout << "\tval3dity input.gml --overlap_tol 0.05" << std::endl; - std::cout << "\t\tValidates each 3D primitive in input.gml," << std::endl; - std::cout << "\t\ta tolerance of 0.05 unit is used for the CompositeSolids and BuildingParts" << std::endl; + std::cout << "\t\tValidate each 3D primitive in input.gml (a GML file)," << std::endl; + std::cout << "\t\ta tolerance of 0.05 unit is used for the 3D adjacency between Solids." << std::endl; std::cout << "\tval3dity input.json --verbose" << std::endl; std::cout << "\t\tAll details of the validation are printed out" << std::endl; std::cout << "\tval3dity input.obj" << std::endl; - std::cout << "\t\tValidates the geometries in input.obj as if they were a Solid (default)" << std::endl; + std::cout << "\t\tValidate the geometries in input.obj as if they were an ISO19107 Solid (default)" << std::endl; std::cout << "\tval3dity input.off -p MultiSurface" << std::endl; - std::cout << "\t\tValidates the geometries in input.off as a MultiSurface" << std::endl; + std::cout << "\t\tValidate the geometries in input.off as an ISO19107 MultiSurface" << std::endl; std::cout << "\tval3dity input.gml --snap_tol 0.1" << std::endl; std::cout << "\t\tThe vertices in input.gml closer than 0.1unit are snapped together" << std::endl; @@ -117,6 +121,56 @@ class MyOutput : public TCLAP::StdOutput }; +class LicensePrint : public TCLAP::Visitor +{ + public: + + LicensePrint() : Visitor() {}; + void visit() + { + std::string thelicense = + "\nval3dity\n\n" + "Copyright (C) 2011-2020 3D geoinformation research group, TU Delft\n\n" + "val3dity is free software: you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation, either version 3 of the License, or\n" + "(at your option) any later version.\n\n" + "val3dity is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n\n" + "A copy of the GNU General Public License is available at\n" + " or\n" + "\n" + "Faculty of the Built Environment & Architecture\n" + "Delft University of Technology\n" + "Julianalaan 134, Delft 2628BL, the Netherlands\n"; + + std::cout << thelicense << std::endl; + exit(0); + }; +}; + + +class ListErrorsPrint : public TCLAP::Visitor +{ + public: + + ListErrorsPrint() : Visitor() {}; + void visit() + { + for (auto& e: ALL_ERRORS) { + std::cout << e.first << " -- " << e.second << std::endl; + } + std::cout << std::endl; + std::string url = "https://val3dity.readthedocs.io/en/" + VAL3DITY_VERSION + "/errors/"; + std::cout << "Explanations and examples of each error at " << url << std::endl; + exit(0); + }; +}; int main(int argc, char* const argv[]) { @@ -149,16 +203,10 @@ int main(int argc, char* const argv[]) "string"); TCLAP::ValueArg report("r", "report", - "output report in HTML format", - false, - "", - "string"); - TCLAP::ValueArg report_json("", - "report_json", "output report in JSON format", false, "", - "string"); + "string"); TCLAP::ValueArg primitives("p", "primitive", "which geometric primitive to validate ", @@ -169,18 +217,20 @@ int main(int argc, char* const argv[]) "verbose", "verbose output", false); - TCLAP::SwitchArg license("", + TCLAP::SwitchArg license("l", "license", "see the software license", - false); + false, + new LicensePrint() ); + TCLAP::SwitchArg listerrors("", + "listerrors", + "list all the possible errors", + false, + new ListErrorsPrint() ); TCLAP::SwitchArg unittests("", "unittests", "unit tests output", false); - TCLAP::SwitchArg onlyinvalid("", - "onlyinvalid", - "only invalid primitives are reported", - false); TCLAP::SwitchArg ignore204("", "ignore204", "ignore error 204", @@ -229,31 +279,40 @@ int main(int argc, char* const argv[]) cmd.add(geom_is_sem_surfaces); cmd.add(ignore204); cmd.add(unittests); - cmd.add(onlyinvalid); cmd.add(output_off); - // cmd.add(inputfile); - // cmd.add(license); - cmd.xorAdd( inputfile, license ); + cmd.add(inputfile); + cmd.add(listerrors); + cmd.add(license); cmd.add(ishellfiles); cmd.add(report); - cmd.add(report_json); cmd.parse( argc, argv ); - //-- vector with Features: CityObject, GenericObject, or IndoorObject (or others in the future) + //-- vector with Features: CityObject, GenericObject, + //-- or IndoorModel (or others in the future) std::vector lsFeatures; InputTypes inputtype = OTHER; std::string extension = inputfile.getValue().substr(inputfile.getValue().find_last_of(".") + 1); - if ( (extension == "gml") || (extension == "GML") || (extension == "xml") || (extension == "XML") ) + if ( (extension == "gml") || (extension == "GML") || (extension == "xml") || (extension == "XML") ) { inputtype = GML; - else if ( (extension == "poly") || (extension == "POLY") ) + ioerrs.set_input_file_type("GML"); + } + else if ( (extension == "poly") || (extension == "POLY") ) { inputtype = POLY; - else if ( (extension == "json") || (extension == "JSON") ) + ioerrs.set_input_file_type("POLY"); + } + else if ( (extension == "json") || (extension == "JSON") ) { inputtype = JSON; - else if ( (extension == "obj") || (extension == "OBJ") ) + ioerrs.set_input_file_type("CityJSON"); + } + else if ( (extension == "obj") || (extension == "OBJ") ) { inputtype = OBJ; - else if ( (extension == "off") || (extension == "OFF") ) + ioerrs.set_input_file_type("OBJ"); + } + else if ( (extension == "off") || (extension == "OFF") ) { inputtype = OFF; + ioerrs.set_input_file_type("OFF"); + } //-- if verbose == false then log to a file if (verbose.getValue() == false) @@ -262,16 +321,6 @@ int main(int argc, char* const argv[]) mylog.open("val3dity.log"); std::clog.rdbuf(mylog.rdbuf()); } - if (license.getValue() == true) - { - print_license(); - if (verbose.getValue() == false) - { - clog.rdbuf(savedBufferCLOG); - mylog.close(); - } - return(0); - } //-- no negative snap_tol value if (snap_tol.getValue() < 0) @@ -282,8 +331,9 @@ int main(int argc, char* const argv[]) if (inputtype == OTHER) { std::stringstream ss; - ss << "Input file (" << inputfile.getValue() << ") not supported."; - ioerrs.add_error(901, ss.str()); + ss << "Format of file " << inputfile.getValue() << " not supported (based on its extension)."; + ioerrs.add_error(904, ss.str()); + ioerrs.set_input_file_type("UNKNOWN"); } Primitive3D prim3d; @@ -303,7 +353,7 @@ int main(int argc, char* const argv[]) } std::string licensewarning = - "---\nval3dity Copyright (c) 2011-2019, 3D geoinformation research group, TU Delft \n" + "---\nval3dity Copyright (c) 2011-2020, 3D geoinformation research group, TU Delft \n" "This program comes with ABSOLUTELY NO WARRANTY.\n" "This is free software, and you are welcome to redistribute it\n" "under certain conditions; for details run val3dity with the '--license' option.\n---"; @@ -448,7 +498,7 @@ int main(int argc, char* const argv[]) std::cout << "CompositeSurface" << std::endl; else { std::cout << "All" << std::endl; - std::cout << "(CityGML/CityJSON have all their 3D primitives validated)" << std::endl; + std::cout << "(CityGML/CityJSON/IndoorGML have all their 3D primitives validated)" << std::endl; } //-- report on parameters used if (ignore204.getValue() == true) @@ -464,12 +514,14 @@ int main(int argc, char* const argv[]) std::cout << " overlap_tol" << setw(19) << "none" << std::endl; else std::cout << " overlap_tol" << setw(19) << overlap_tol.getValue() << std::endl; + std::cout << std::endl; } //-- now the validation starts if ( (lsFeatures.empty() == false) && (ioerrs.has_errors() == false) ) { int i = 1; + std::cout << "Validation of " << lsFeatures.size() << " feature(s):" << std::endl; for (auto& f : lsFeatures) { if ( (i % 10 == 0) && (verbose.getValue() == false) ) @@ -547,8 +599,8 @@ int main(int argc, char* const argv[]) std::cout << std::endl; } - //-- output report - if ( (report.getValue() != "") || (report_json.getValue() != "") ) + //-- output report in JSON + if (report.getValue() != "") { //-- save the json report in memory first json jr; @@ -559,17 +611,12 @@ int main(int argc, char* const argv[]) overlap_tol.getValue(), planarity_d2p_tol.getValue(), planarity_n_tol_updated, - ioerrs, - onlyinvalid.getValue()); - // HTML report - if (report.getValue() != "") - write_report_html(jr, report.getValue()); - // JSON report - if (report_json.getValue() != "") - write_report_json(jr, report_json.getValue()); + ioerrs); + if (report.getValue() != "") + write_report_json(jr, report.getValue()); } else - std::cout << "-->The validation report wasn't saved, use option '--report' (or '--report_json')." << std::endl; + std::cout << "==> The validation report wasn't saved, use option '--report'." << std::endl; //-- unittests if (unittests.getValue() == true) @@ -593,59 +640,22 @@ int main(int argc, char* const argv[]) void write_report_json(json& jr, std::string report) { boost::filesystem::path outpath(report); - if (boost::filesystem::exists(outpath.parent_path()) == false) - std::cout << "Error: file " << outpath << " impossible to create, wrong path." << std::endl; - else { - if (boost::filesystem::extension(outpath) != ".json") - outpath += ".json"; - std::ofstream o(outpath.string()); - o << jr.dump(2) << std::endl; - std::cout << "Full validation report (in JSON format) saved to " << outpath << std::endl; - } - -} - - - -void write_report_html(json& jr, std::string report) -{ - boost::filesystem::path outpath(report); - if (boost::filesystem::exists(outpath.parent_path()) == false) - std::cout << "Error: file " << outpath << " impossible to create, wrong path." << std::endl; - else { - if (boost::filesystem::exists(outpath) == false) - boost::filesystem::create_directory(outpath); - boost::filesystem::path outfile = outpath / "report.html"; - std::ofstream o(outfile.string()); - o << report_indexhtml << std::endl; - std::cout << "Full validation report (in HTML format) saved to " << outfile << std::endl; - o.close(); - - outfile = outpath / "report.js"; - o.open(outfile.string()); - o << "var report =" << jr << std::endl; - o.close(); - - outfile = outpath / "tree.html"; - o.open(outfile.string()); - o << report_tree << std::endl; - o.close(); - - outfile = outpath / "treeview.js"; - o.open(outfile.string()); - o << report_treeviewjs << std::endl; - o.close(); - - outfile = outpath / "val3dityconfig.js"; - o.open(outfile.string()); - o << report_val3dityconfigjs << std::endl; - o.close(); - - outfile = outpath / "index.css"; - o.open(outfile.string()); - o << report_indexcss << std::endl; - o.close(); + if (boost::filesystem::exists(outpath.parent_path()) == false) { + boost::filesystem::path fullpath(boost::filesystem::current_path() / report); + if (boost::filesystem::exists(fullpath.parent_path()) == false) { + std::cout << "Error: file " << outpath << " impossible to create, wrong path." << std::endl; + return; + } + else + outpath = fullpath; } + if (boost::filesystem::extension(outpath) != ".json") + outpath += ".json"; + std::ofstream o(outpath.string()); + o << jr.dump(2) << std::endl; + std::cout << "Validation report saved to " << boost::filesystem::canonical(outpath) << std::endl; + std::cout << "Browse its content:" << std::endl; + std::cout << "==>http://geovalidation.bk.tudelft.nl/val3dity/browser/\n" << std::endl; } @@ -689,6 +699,15 @@ std::string print_summary_validation(std::vector& lsFeatures, IOErrors for (auto& p : o->get_primitives()) noprim++; ss << "+++++++++++++++++++ SUMMARY +++++++++++++++++++" << std::endl; + ss << "Input file type:" << std::endl; + std::string ft = ioerrs.get_input_file_type(); + if (ft == "CityGML") { + ft += "\n [watchout: CityGML support is deprecated]"; + ft += "\n [future version will not support it]"; + ft += "\n [upgrade to CityJSON]"; + } + ss << " " << ft << std::endl; + ss << "+++++" << std::endl; int fInvalid = 0; for (auto& f : lsFeatures) { @@ -701,12 +720,12 @@ std::string print_summary_validation(std::vector& lsFeatures, IOErrors percentage = 0; else percentage = 100 * (fInvalid / float(lsFeatures.size())); - ss << "# valid: " << setw(22) << lsFeatures.size() - fInvalid; + ss << " # valid: " << setw(20) << lsFeatures.size() - fInvalid; if (lsFeatures.size() == 0) ss << " (" << 0 << "%)" << std::endl; else ss << std::fixed << setprecision(1) << " (" << 100 - percentage << "%)" << std::endl; - ss << "# invalid: " << setw(20) << fInvalid; + ss << " # invalid: " << setw(18) << fInvalid; ss << std::fixed << setprecision(1) << " (" << percentage << "%)" << std::endl; std::set thetypes; for (auto& f : lsFeatures) @@ -728,12 +747,12 @@ std::string print_summary_validation(std::vector& lsFeatures, IOErrors percentage = 0; else percentage = 100 * ((noprim - bValid) / float(noprim)); - ss << "# valid: " << setw(22) << bValid; + ss << " # valid: " << setw(20) << bValid; if (noprim == 0) ss << " (" << 0 << "%)" << std::endl; else ss << std::fixed << setprecision(1) << " (" << 100 - percentage << "%)" << std::endl; - ss << "# invalid: " << setw(20) << (noprim - bValid); + ss << " # invalid: " << setw(18) << (noprim - bValid); ss << std::fixed << setprecision(1) << " (" << percentage << "%)" << std::endl; std::set theprimitives; for (auto& f : lsFeatures) @@ -752,31 +771,52 @@ std::string print_summary_validation(std::vector& lsFeatures, IOErrors case 2: ss << "MultiSolid" << std::endl; break; case 3: ss << "CompositeSurface" << std::endl; break; case 4: ss << "MultiSurface" << std::endl; break; - case 5: ss << "ALL" << std::endl; break; + case 5: ss << "GeometryTemplate" << std::endl; break; + case 9: ss << "ALL" << std::endl; break; } } } //-- overview of errors - std::map errors; - for (auto& f : lsFeatures) + std::map errors_f; //-- features + for (auto& f : lsFeatures) for (auto& code : f->get_unique_error_codes()) - errors[code] = 0; - for (auto& f : lsFeatures) + if (code > 600) + errors_f[code] = 0; + for (auto& f : lsFeatures) for (auto& code : f->get_unique_error_codes()) - errors[code] += 1; - - if ( (errors.size() > 0) || (ioerrs.has_errors() == true) ) + if (code > 600) + errors_f[code] += 1; + std::map errors_p; //-- primitives + for (auto& f : lsFeatures) + for (auto& p : f->get_primitives()) + for (auto& code : p->get_unique_error_codes()) + errors_p[code] = 0; + for (auto& f : lsFeatures) + for (auto& p : f->get_primitives()) + for (auto& code : p->get_unique_error_codes()) + errors_p[code] += 1; + if ( (errors_p.size() > 0) || (errors_p.size() > 0) || (ioerrs.has_errors() == true) ) { ss << "+++++" << std::endl; ss << "Errors present:" << std::endl; - for (auto e : errors) + //-- primitives + for (auto e : errors_p) { - ss << " " << e.first << " -- " << errorcode2description(e.first) << std::endl; - ss << setw(11) << "(" << e.second << " primitives)" << std::endl; + ss << " " << e.first << " -- " << ALL_ERRORS[e.first] << std::endl; + ss << setw(11) << e.second; + ss << " primitive(s)"; + ss << std::endl; + } + for (auto e : errors_f) + { + ss << " " << e.first << " -- " << ALL_ERRORS[e.first] << std::endl; + ss << setw(11) << e.second; + ss << " feature(s)"; + ss << std::endl; } for (auto& e : ioerrs.get_unique_error_codes()) { - ss << " " << e << " -- " << errorcode2description(e) << std::endl; + ss << " " << e << " -- " << ALL_ERRORS[e] << std::endl; } } @@ -784,32 +824,6 @@ std::string print_summary_validation(std::vector& lsFeatures, IOErrors return ss.str(); } - -void print_license() { - std::string thelicense = - "\nval3dity\n\n" - "Copyright (C) 2011-2018 3D geoinformation research group, TU Delft\n\n" - "val3dity is free software: you can redistribute it and/or modify\n" - "it under the terms of the GNU General Public License as published by\n" - "the Free Software Foundation, either version 3 of the License, or\n" - "(at your option) any later version.\n\n" - "val3dity is distributed in the hope that it will be useful,\n" - "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" - "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" - "GNU General Public License for more details.\n\n" - "A copy of the GNU General Public License is available at\n" - " or\n" - "\n" - "Faculty of Architecture & the Built Environment\n" - "Delft University of Technology\n" - "Julianalaan 134, Delft 2628BL, the Netherlands\n"; - std::cout << thelicense << std::endl; -} - - void get_report_json(json& jr, std::string ifile, std::vector& lsFeatures, @@ -817,12 +831,12 @@ void get_report_json(json& jr, double overlap_tol, double planarity_d2p_tol, double planarity_n_tol, - IOErrors ioerrs, - bool onlyinvalid) + IOErrors ioerrs) { - jr["type"] = "val3dity report"; + jr["type"] = "val3dity_report"; jr["val3dity_version"] = VAL3DITY_VERSION; jr["input_file"] = ifile; + jr["input_file_type"] = ioerrs.get_input_file_type(); //-- time std::time_t rawtime; struct tm * timeinfo; @@ -832,78 +846,99 @@ void get_report_json(json& jr, std::strftime(buffer, 80, "%c %Z", timeinfo); jr["time"] = buffer; //-- user-defined param - jr["snap_tol"] = snap_tol; - jr["overlap_tol"] = overlap_tol; - jr["planarity_d2p_tol"] = planarity_d2p_tol; - jr["planarity_n_tol"] = planarity_n_tol; - + jr["parameters"]; + jr["parameters"]["snap_tol"] = snap_tol; + jr["parameters"]["overlap_tol"] = overlap_tol; + jr["parameters"]["planarity_d2p_tol"] = planarity_d2p_tol; + jr["parameters"]["planarity_n_tol"] = planarity_n_tol; + + //-- primitives overview + std::map > prim_o; //-- std::set theprimitives; - int noprim = 0; for (auto& f : lsFeatures) - { for (auto& p : f->get_primitives()) - { theprimitives.insert(p->get_type()); - noprim++; + for (auto& each : theprimitives) + prim_o[each] = std::make_tuple(0, 0); + for (auto& f : lsFeatures) { + for (auto& p : f->get_primitives()) { + std::get<0>(prim_o[p->get_type()]) += 1; + if (p->is_valid() == true) { + std::get<1>(prim_o[p->get_type()]) += 1; + } } } - jr["overview_primitives"]; - for (auto& each : theprimitives) - { - switch(each) + jr["primitives_overview"] = json::array(); + for (auto& each : prim_o) { + json j; + switch(each.first) { - case 0: jr["overview_primitives"].push_back("Solid"); break; - case 1: jr["overview_primitives"].push_back("CompositeSolid"); break; - case 2: jr["overview_primitives"].push_back("MultiSolid"); break; - case 3: jr["overview_primitives"].push_back("CompositeSurface"); break; - case 4: jr["overview_primitives"].push_back("MultiSurface"); break; - case 5: jr["overview_primitives"].push_back("ALL"); break; + case 0: j["type"] = "Solid"; break; + case 1: j["type"] = "CompositeSolid"; break; + case 2: j["type"] = "MultiSolid"; break; + case 3: j["type"] = "CompositeSurface"; break; + case 4: j["type"] = "MultiSurface"; break; + case 5: j["type"] = "GeometryTemplate"; break; + case 9: j["type"] = "ALL"; break; } + j["total"] = std::get<0>(each.second); + j["valid"] = std::get<1>(each.second); + jr["primitives_overview"].push_back(j); } - jr["total_primitives"] = noprim; - int bValid = 0; - for (auto& f : lsFeatures) - for (auto& p : f->get_primitives()) - if (p->is_valid() == true) - bValid++; - jr["valid_primitives"] = bValid; - jr["invalid_primitives"] = noprim - bValid; - //-- features + + //-- features overview + std::map > feat_o; //-- std::set thefeatures; - jr["total_features"] = lsFeatures.size(); - bValid = 0; for (auto& f : lsFeatures) - { thefeatures.insert(f->get_type()); - if (f->is_valid() == true) - bValid++; - } - jr["overview_features"]; for (auto& each : thefeatures) - jr["overview_features"].push_back(each); - jr["valid_features"] = bValid; - jr["invalid_features"] = lsFeatures.size() - bValid; + feat_o[each] = std::make_tuple(0, 0); + for (auto& f : lsFeatures) { + std::get<0>(feat_o[f->get_type()]) += 1; + if (f->is_valid() == true) { + std::get<1>(feat_o[f->get_type()]) += 1; + } + } + jr["features_overview"] = json::array(); + for (auto& each : feat_o) { + json j; + j["type"] = each.first; + j["total"] = std::get<0>(each.second); + j["valid"] = std::get<1>(each.second); + jr["features_overview"].push_back(j); + } + + //-- each of the features with their primitives listed + jr["features"] = json::array(); + for (auto& f : lsFeatures) + jr["features"].push_back(f->get_report_json()); + + //-- dataset errors (9xx) + jr["dataset_errors"] = json::array(); + if (ioerrs.has_errors() == true) + jr["dataset_errors"] = ioerrs.get_report_json(); + //-- overview of errors - // "overview-errors": [101, 203], - // "overview-errors-primitives": [5, 1], - std::map errors; - for (auto& f : lsFeatures) - for (auto& code : f->get_unique_error_codes()) - errors[code] = 0; - for (auto& f : lsFeatures) + std::set unique_errors; + for (auto& f : lsFeatures) for (auto& code : f->get_unique_error_codes()) - errors[code] += 1; - jr["overview_errors"]; - for (auto e : errors) - jr["overview_errors"].push_back(e.first); + unique_errors.insert(code); + jr["all_errors"] = json::array(); + for (auto& e : unique_errors) + jr["all_errors"].push_back(e); for (auto& e : ioerrs.get_unique_error_codes()) - jr["overview_errors"].push_back(e); - jr["errors_dataset"]; - jr["features"]; + jr["all_errors"].push_back(e); + + bool bValid = true; + for (auto& f : lsFeatures) { + if (f->is_valid() == false) { + bValid = false; + break; + } + } if (ioerrs.has_errors() == true) - jr["errors_dataset"] = ioerrs.get_report_json(); - for (auto& f : lsFeatures) - jr["features"].push_back(f->get_report_json()); + bValid = false; + jr["validity"] = bValid; } diff --git a/src/reportoutput.h b/src/reportoutput.h deleted file mode 100644 index a8e109f5..00000000 --- a/src/reportoutput.h +++ /dev/null @@ -1,1248 +0,0 @@ - -std::string report_indexhtml = R"( - - - - - - val3dity report - - - - - - - - - - - - - - - - - - -
    Input file:
    val3dity version:
    Validation time:
    - -

    Parameters used for validation

    - - - - - - - - - - - - - - - - - -
    - - planarity_d2p_tol: - -
    - - planarity_n_tol: - -
    - - snap_tol: - -
    - - overlap_tol: - -
    - -

    Validation summary

    - - - - - - -)"; - - -std::string report_tree = R"( - - - - - - - - - - -
    - - - - - - - - - - - - -)"; - -std::string report_indexcss = R"( -/* Page-wide styles */ -html, body, h1, h2, h3, p, table, td, th { - margin: 0; - padding: 0; - border: 0; - outline: 0; - font-weight: inherit; - font-style: inherit; - font-size: 100%; - font-family: inherit; - vertical-align: baseline; - } - -/* Set baseline grid to 16 pt. */ -body { - font-family: georgia, serif; - font-size: 1em; - } - -html>body { - font-size: 16px; - } - -/* Set base font size to 12/16 */ -p { - font-size: .75em; /* 12/16 */ - line-height: 1.33333333em; /* 16/12 */ - } - -table { - border-collapse: collapse; - table-layout: fixed; - } - -td { - vertical-align: top; -} - -#header { - background: #f8f8f8; - width: 100%; - border-bottom: 1px solid #eee; - } - -#header .content { - padding: 1em 3em; - } - -h1 { - font-size: 1.5em; - display: inline-block; -} - -h2 { - font-size: 1.25em; - display: inline-block; -} - -h3 { - padding: 1em 0 0 3em; - font-size: 1em; - font-weight: bold; -} - -table { - border-collapse: collapse; - width: 80%; - margin-left: 5em; - position: relative; -} - -td { - padding: 8px; - text-align: left; - border-bottom: 1px solid #ddd; -} - -th { - text-align: left; - font-style: italic; -} - -/*table#summary tr:hover { - padding-top: .5em; - background-color: #f5f5f5; -}*/ - -table#summary_features, table#summary_primitives { - margin-top: .5em; - margin-bottom: 1.5em; - padding-top: .5em; -} - -table#errors { - margin-top: 20px; - margin-bottom: 2em; -} - -#errors th { - padding-bottom: 10px; -} - -a.clickonme { - text-decoration: none; - background-color: #CCFF99; -} - -a { - text-decoration: none; - color: inherit; - } -a:hover { - text-decoration: underline; - color: inherit; - } - -.linear-ring-level{ - background-color: #FED2D2 -} -.polygon-level{ - background-color: #76D36B -} -.shell-level{ - background-color: #F2AF60 -} -.solid-level{ - background-color: #1BD2FF -} -.solid-interactions-level{ - background-color: #FDA4FF -} -.citygml-building{ - background-color: #D2A471 -} -.others{ - background-color: #1CE0E3 -} - - -/* Treeview */ -div#tree { - margin-top: 1em; - margin-left: 5em; -} - -button { - font-family: sans-serif; - font-size: .8em; - cursor: pointer; - border: 1px solid; - border-color: #999 #999 #999 #999; - padding: .2em .35em; -} - -button#expandAll { - margin-left: 6em; - margin-top: 1em; -} - -.filter_link { - margin: .75em .1em; -} - -button#invalid { - background: #FF8C75; -} - -.tree-leaf { - position: relative; - padding-top: 5px; -} -.tree-leaf .tree-child-leaves, .tree-error-code { - display: block; - margin-left: 3em; -} -.tree-leaf .hidden { - display: none; -} -.tree-leaf .tree-expando { - /*background: #ddd;*/ - border-radius: 10px; - cursor: pointer; - float: left; - height: 10px; - line-height: 10px; - position: relative; - text-align: center; - top: 4px; - width: 10px; -} -.tree-leaf .hidden { - visibility: hidden; -} -.tree-leaf .tree-expando:hover { - /*background: #aaa;*/ -} -.tree-leaf .tree-leaf-text, .tree-error-code { - display: table-cell; - cursor: pointer; - float: left; - margin-left: 10px; -} - -.tree-leaf .tree-leaf-id { - display: table-cell; - cursor: pointer; - float: left; - padding-left: 60px; - padding-right: 20px; - font-style: italic; -} - -.tree-leaf .tree-leaf-nrfaces, .tree-leaf-nrvertices, .tree-leaf-nrshells, .tree-leaf-nrsolids, .tree-error-info { - display: table-cell; - float: left; - padding-left: 20px; -} - -/*.tree-leaf .tree-leaf-text:hover { - color: blue; -}*/ -.tree-leaf .tree-leaf-content:before, .tree-leaf .tree-leaf-content:after { - content: " "; - display: table; -} -.tree-leaf .tree-leaf-content:after { - clear: both; -} - -)"; - - - -std::string report_treeviewjs = R"( -// Adapted from js-treeview, see: https://github.com/justinchmura/js-treeview - -(function (define) { - 'use strict'; - - (function (root, factory) { - if (typeof define === 'function' && define.amd) { - define(factory); - } else if (typeof exports === 'object') { - module.exports = factory(); - } else { - root.TreeView = factory(); - } - }(window, function () { - return (function () { - - /** List of events supported by the tree view */ - var events = [ - 'expand', - 'expandAll', - 'collapse', - 'collapseAll', - 'select' - ]; - - /** - * A forEach that will work with a NodeList and generic Arrays - * @param {array|NodeList} arr The array to iterate over - * @param {function} callback Function that executes for each element. First parameter is element, second is index - * @param {object} The context to execute callback with - */ - function forEach(arr, callback, scope) { - var i, len = arr.length; - for (i = 0; i < len; i += 1) { - callback.call(scope, arr[i], i); - } - } - - /** - * Emit an event from the tree view - * @param {string} name The name of the event to emit - */ - function emit(instance, name) { - var args = [].slice.call(arguments, 2); - if (events.indexOf(name) > -1) { - if (instance.handlers[name] && instance.handlers[name] instanceof Array) { - forEach(instance.handlers[name], function (handle) { - window.setTimeout(function () { - handle.callback.apply(handle.context, args); - }, 0); - }); - } - } else { - throw new Error(name + ' event cannot be found on TreeView.'); - } - } - - /** - * Renders the tree view in the DOM - */ - function render(self) { - var container = document.getElementById(self.node); - var leaves = [], click; - var renderLeaf = function (item) { - var leaf = document.createElement('div'); - var content = document.createElement('div'); - var text = document.createElement('div'); - var expando = document.createElement('div'); - var id = document.createElement('div'); - var nrfaces = document.createElement('div'); - var nrvertices = document.createElement('div'); - var nrsolids = document.createElement('div'); - var nrshells = document.createElement('div'); - var colour_invalid = "#FF8C75"; - var errcode = document.createElement('div'); - var errinfo = document.createElement('div'); - - leaf.setAttribute('class', 'tree-leaf'); - content.setAttribute('class', 'tree-leaf-content'); - content.setAttribute('data-item', JSON.stringify(item)); - - if(item.code) { - errcode.setAttribute('class', 'tree-error-code'); - errcode.textContent = item.code + " " + "\u2014" + " " + item.description; - id.setAttribute('class', 'tree-leaf-id'); - id.textContent = "ID:" + "\u00A0" + item.id; - errinfo.setAttribute('class', 'tree-error-info'); - errinfo.textContent = "Info:" + "\u00A0" + item.info; - content.appendChild(errcode); - content.appendChild(id); - content.appendChild(errinfo); - // set attribute so that errors stay in the filtered tree when 'Invalid' - leaf.setAttribute('validity', 'false'); - } else { - text.setAttribute('class', 'tree-leaf-text'); - text.textContent = item.type; - id.setAttribute('class', 'tree-leaf-id'); - id.textContent = "ID:" + "\u00A0" + item.id; - expando.setAttribute('class', 'tree-expando ' + (item.expanded ? 'expanded' : '')); - expando.textContent = item.expanded ? '–' : '+'; - - if(!item.validity) { - // set colour to red if invalid - text.style.background = colour_invalid; - id.style.background = colour_invalid; - leaf.setAttribute('validity', 'false'); - } else { - leaf.setAttribute('validity', 'true'); - } - - content.appendChild(expando); - content.appendChild(text); - content.appendChild(id); - - if(item.numbersolids != undefined) { - nrsolids.setAttribute('class', 'tree-leaf-nrsolids'); - nrsolids.textContent = "Solids:" + "\u00A0" + item.numbersolids; - content.appendChild(nrsolids); - if(!item.validity){ - nrsolids.style.background = colour_invalid; - } - } - //-- Solid - if(item.numbershells != undefined) { - nrshells.setAttribute('class', 'tree-leaf-nrshells'); - nrshells.textContent = "Shells:" + "\u00A0" + item.numbershells; - nrfaces.setAttribute('class', 'tree-leaf-nrfaces'); - nrfaces.textContent = "Faces:" + "\u00A0" + item.numberfaces; - nrvertices.setAttribute('class', 'tree-leaf-nrvertices'); - nrvertices.textContent = "Vertices:" + "\u00A0" + item.numbervertices; - if(!item.validity) { - nrshells.style.background = colour_invalid; - nrvertices.style.background = colour_invalid; - nrfaces.style.background = colour_invalid; - } - content.appendChild(nrshells); - content.appendChild(nrfaces); - content.appendChild(nrvertices); - } - //-- MultiSurface - else if(item.numbervertices != undefined) { - nrshells.setAttribute('class', 'tree-leaf-nrshells'); - nrfaces.setAttribute('class', 'tree-leaf-nrfaces'); - nrfaces.textContent = "Faces:" + "\u00A0" + item.numberfaces; - nrvertices.setAttribute('class', 'tree-leaf-nrvertices'); - nrvertices.textContent = "Vertices:" + "\u00A0" + item.numbervertices; - if(!item.validity) { - nrvertices.style.background = colour_invalid; - nrfaces.style.background = colour_invalid; - } - content.appendChild(nrfaces); - content.appendChild(nrvertices); - } - else { - // nothing - } - } - - leaf.appendChild(content); - - if ((item.primitives && item.primitives.length > 0) || - (item.errors && item.errors.length > 0) || - (item.errors_feature && item.errors_feature) - ) { - var children = document.createElement('div'); - children.setAttribute('class', 'tree-child-leaves'); - if (item.errors_feature) { - forEach(item.errors_feature, function (child) { - var childLeaf = renderLeaf(child); - children.appendChild(childLeaf); - }); - if (!item.expanded) { - children.classList.add('hidden'); - } - leaf.appendChild(children); - } - if (item.errors) { - forEach(item.errors, function (child) { - var childLeaf = renderLeaf(child); - children.appendChild(childLeaf); - }); - if (!item.expanded) { - children.classList.add('hidden'); - } - leaf.appendChild(children); - } - if (item.primitives) { - forEach(item.primitives, function (child) { - var childLeaf = renderLeaf(child); - children.appendChild(childLeaf); - }); - if (!item.expanded) { - children.classList.add('hidden'); - } - leaf.appendChild(children); - } - } else { - expando.classList.add('hidden'); - } - - return leaf; - }; - - forEach(self.data, function (item) { - leaves.push(renderLeaf.call(self, item)); - }); - container.innerHTML = leaves.map(function (leaf) { - return leaf.outerHTML; - }).join(''); - - click = function (e) { - var parent = (e.target || e.currentTarget).parentNode; - var data = JSON.parse(parent.getAttribute('data-item')); - var leaves = parent.parentNode.querySelector('.tree-child-leaves'); - if (leaves) { - if (leaves.classList.contains('hidden')) { - self.expand(parent, leaves); - } else { - self.collapse(parent, leaves); - } - } else { - emit(self, 'select', { - target: e, - data: data - }); - } - }; - - forEach(container.querySelectorAll('.tree-leaf-text'), function (node) { - node.onclick = click; - }); - forEach(container.querySelectorAll('.tree-expando'), function (node) { - node.onclick = click; - }); - } - - /** - * @constructor - * @property {object} handlers The attached event handlers - * @property {object} data The JSON object that represents the tree structure - * @property {DOMElement} node The DOM element to render the tree in - */ - function TreeView(data, node) { - this.handlers = {}; - this.node = node; - this.data = data; - render(this); - } - - /** - * Expands a leaflet by the expando or the leaf text - * @param {DOMElement} node The parent node that contains the leaves - * @param {DOMElement} leaves The leaves wrapper element - */ - TreeView.prototype.expand = function (node, leaves, skipEmit) { - var expando = node.querySelector('.tree-expando'); - expando.textContent = '-'; - leaves.classList.remove('hidden'); - if (skipEmit) { return; } - emit(this, 'expand', { - target: node, - leaves: leaves - }); - }; - - TreeView.prototype.expandAll = function () { - var self = this; - var nodes = document.getElementById(self.node).querySelectorAll('.tree-expando'); - forEach(nodes, function (node) { - var parent = node.parentNode; - var leaves = parent.parentNode.querySelector('.tree-child-leaves'); - if (parent && leaves && parent.hasAttribute('data-item')) { - self.expand(parent, leaves, true); - } - }); - emit(this, 'expandAll', {}); - }; - - /** - * Collapses a leaflet by the expando or the leaf text - * @param {DOMElement} node The parent node that contains the leaves - * @param {DOMElement} leaves The leaves wrapper element - */ - TreeView.prototype.collapse = function (node, leaves, skipEmit) { - var expando = node.querySelector('.tree-expando'); - expando.textContent = '+'; - leaves.classList.add('hidden'); - if (skipEmit) { return; } - emit(this, 'collapse', { - target: node, - leaves: leaves - }); - }; - - /** - */ - TreeView.prototype.collapseAll = function () { - var self = this; - var nodes = document.getElementById(self.node).querySelectorAll('.tree-expando'); - forEach(nodes, function (node) { - var parent = node.parentNode; - var leaves = parent.parentNode.querySelector('.tree-child-leaves'); - if (parent && leaves && parent.hasAttribute('data-item')) { - self.collapse(parent, leaves, true); - } - }); - emit(this, 'collapseAll', {}); - }; - - /** - * Attach an event handler to the tree view - * @param {string} name Name of the event to attach - * @param {function} callback The callback to execute on the event - * @param {object} scope The context to call the callback with - */ - TreeView.prototype.on = function (name, callback, scope) { - if (events.indexOf(name) > -1) { - if (!this.handlers[name]) { - this.handlers[name] = []; - } - this.handlers[name].push({ - callback: callback, - context: scope - }); - } else { - throw new Error(name + ' is not supported by TreeView.'); - } - }; - - /** - * Deattach an event handler from the tree view - * @param {string} name Name of the event to deattach - * @param {function} callback The function to deattach - */ - TreeView.prototype.off = function (name, callback) { - var index, found = false; - if (this.handlers[name] instanceof Array) { - this.handlers[name].forEach(function (handle, i) { - index = i; - if (handle.callback === callback && !found) { - found = true; - } - }); - if (found) { - this.handlers[name].splice(index, 1); - } - } - }; - - return TreeView; - }()); - })); -}(window.define)); -)"; - -std::string report_val3dityconfigjs = R"( - -var errors = { - "101": { - "name": "101 – TOO_FEW_POINTS", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#too-few-points" - } - }, - "102": { - "name": "102 – CONSECUTIVE_POINTS_SAME", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#consecutive-points-same" - } - }, - "103": { - "name": "103 – NOT_CLOSED", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#not-closed" - } - }, - "104": { - "name": "104 – SELF_INTERSECTION", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#self-intersection" - } - }, - "201": { - "name": "201 – INTERSECTION_RINGS", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#intersection-rings" - } - }, - "202": { - "name": "202 – DUPLICATED_RINGS", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#duplicated-rings" - } - }, - "203": { - "name": "203 – NON_PLANAR_POLYGON_DISTANCE_PLANE", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#non-planar-polygon-distance-plane" - } - }, - "204": { - "name": "204 – NON_PLANAR_POLYGON_NORMALS_DEVIATION", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#non-planar-polygon-normals-deviation" - } - }, - "205": { - "name": "205 – INTERIOR_DISCONNECTED", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#interior-disconnected" - } - }, - "206": { - "name": "206 – INNER_RING_OUTSIDE", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#inner-ring-outside" - } - }, - "207": { - "name": "207 – INNER_RINGS_NESTED", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#inner-rings-nested" - } - }, - "208": { - "name": "208 – ORIENTATION_RINGS_SAME", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#orientation-rings-same" - } - }, - "301": { - "name": "301 – TOO_FEW_POLYGONS", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#too-few-polygons" - } - }, - "302": { - "name": "302 – NOT_CLOSED", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#error-302" - } - }, - "303": { - "name": "303 – NON_MANIFOLD_CASE", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#non-manifold-case" - } - }, - "305": { - "name": "305 – MULTIPLE_CONNECTED_COMPONENTS", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#multiple-connected-components" - } - }, - "306": { - "name": "306 – SELF_INTERSECTION", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#error-306" - } - }, - "307": { - "name": "307 – POLYGON_WRONG_ORIENTATION", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#polygon-wrong-orientation" - } - }, - "401": { - "name": "401 – INTERSECTION_SHELLS", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#intersection-shells" - } - }, - "402": { - "name": "402 – DUPLICATED_SHELLS", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#duplicated-shells" - } - }, - "403": { - "name": "403 – INNER_SHELL_OUTSIDE", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#inner-shell-outside" - } - }, - "404": { - "name": "404 – INTERIOR_DISCONNECTED", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#error-404" - } - }, - "405": { - "name": "405 – WRONG_ORIENTATION_SHELL", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#wrong-orientation-shell" - } - }, - "501": { - "name": "501 – INTERSECTION_SOLIDS", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#intersection-solids" - } - }, - "502": { - "name": "502 – DUPLICATED_SOLIDS", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#duplicated-solids" - } - }, - "503": { - "name": "503 – DISCONNECTED_SOLIDS", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#disconnected-solids" - } - }, - "601": { - "name": "601 – BUILDINGPARTS_OVERLAP", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#buildingparts-overlap" - } - }, - "609": { - "name": "609 – CITYOBJECT_HAS_NO_GEOMETRY", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#cityobject-has-no-geometry" - } - }, - "901": { - "name": "901 – INVALID_INPUT_FILE", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#invalid-input-file" - } - }, - "902": { - "name": "902 – EMPTY_PRIMITIVE", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#empty-primitive" - } - }, - "903": { - "name": "903 – WRONG_INPUT_PARAMETERS", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#wrong-input-parameters" - } - }, - "999": { - "name": "999 – UNKNOWN_ERROR", - "link": { - "href": "http://geovalidation.bk.tudelft.nl/val3dity/docs/errors/#unknown-error" - } - } -} - - -function parse_valid_amounts(report) { - // Get the amount of in/valids per feature and primitive type and - // get the amount of objects per error - var feature_list = report.overview_features; - var primitive_list = report.overview_primitives; - var error_list = report.overview_errors; - var feat_dict = {}; - var prim_dict = {}; - var err_dict = {}; - - if (feature_list != null) { - for (var i=0; i'); -error_overview(err_dict); -)"; diff --git a/src/validate_prim_toporel.cpp b/src/validate_prim_toporel.cpp index 7c0bea08..618ce96c 100644 --- a/src/validate_prim_toporel.cpp +++ b/src/validate_prim_toporel.cpp @@ -29,19 +29,125 @@ #include "validate_prim_toporel.h" #include "Primitive.h" #include "geomtools.h" +#include "input.h" #include "Solid.h" #include "CompositeSolid.h" #include #include +#include + namespace val3dity { +typedef std::vector Nefs; +typedef Nefs::iterator Iterator; +typedef CGAL::Box_intersection_d::Box_with_handle_d AABB; + + +struct Report_intersections { + Nefs* nefs; + std::vector* lsCellIDs; + std::vector* lsErrors; + int ecode; + int* thecount; + + Report_intersections(Nefs& nefs, std::vector& lsCell, std::vector& le, int code, int& count) + : nefs(&nefs), lsCellIDs(&lsCell), lsErrors(&le), ecode(code), thecount(&count) + {} + + // callback functor that reports when 2 AABBs intersect + void operator()(const AABB& a, const AABB& b) + { + int id1 = (a.handle() - nefs->begin()); + int id2 = (b.handle() - nefs->begin()); + (*thecount)++; + if (*thecount % 25 == 0) { + int r = rand() % 3; + if (r == 0) + std::cout << "\b|" << std::flush; + else if (r == 1) + std::cout << "\b/" << std::flush; + else if (r == 2) + std::cout << "\b-" << std::flush; + else + std::cout << "\b\\" << std::flush; + } + //-- we check here if the 2 (usually eroded) Nefs are intersecting, + //-- if yes then an error is added to the list lsErrors + Nef_polyhedron* n1 = nefs->at(id1); + Nef_polyhedron* n2 = nefs->at(id2); + Nef_polyhedron emptynef(Nef_polyhedron::EMPTY); + if (n1->interior() * n2->interior() != emptynef) + { + Error e; + std::stringstream msg; + msg << lsCellIDs->at(id1) << " and " << lsCellIDs->at(id2); + e.errorcode = ecode; + e.info1 = msg.str(); + e.info2 = ""; + lsErrors->push_back(e); + } + } +}; + + + +bool are_cells_interior_disconnected_with_aabb(std::vector>& lsCells, + int errorcode_to_assign, + std::vector& lsErrors, + double tol_overlap) +{ + std::clog << "--- Constructing Nef Polyhedra ---" << std::endl; + std::vector lsNefs; + std::vector> subsetCells; + for (auto& c : lsCells) + { + Solid* ts = std::get<1>(c); + // TODO: only valid Solids are processed: what's the best way here? + if (ts->is_valid() != 1) + continue; + Nef_polyhedron* tmpnef = ts->get_nef_polyhedron(); + if (tol_overlap > 0) + { + lsNefs.push_back(erode_nef_polyhedron(tmpnef, tol_overlap)); + // delete tmpnef; + } + else + lsNefs.push_back(tmpnef); + subsetCells.push_back(c); + } + std::clog << "--- Constructing AABB tree ---" << std::endl; + std::vector aabbs; + int counter = 0; + for ( Iterator i = lsNefs.begin(); i != lsNefs.end(); ++i) + { + Solid* ts = std::get<1>(subsetCells[counter]); + aabbs.push_back( AABB( ts->get_bbox(), i) ); + counter++; + } + std::vector lsCellIDs; + for (auto each : subsetCells) + lsCellIDs.push_back(std::get<0>(each)); + std::clog << "--- Testing intersections between Nefs ---" << std::endl; + int n = lsErrors.size(); + int count = 0; + if (lsNefs.size() > 500) { + std::cout << "Testing intersections between " << lsNefs.size() << " cells, this could be slow." << std::endl << std::flush; + } + CGAL::box_self_intersection_d( aabbs.begin(), aabbs.end(), Report_intersections(lsNefs, lsCellIDs, lsErrors, errorcode_to_assign, count)); + std::clog << "Total AABB tests: " << count << std::endl; + if (lsErrors.size() > n) + return false; + else + return true; +} + -bool do_primitives_overlap(std::vector& lsPrimitives, - int errorcode_to_assign, - std::vector& lsErrors, - double tol_overlap) +bool do_primitives_interior_overlap(std::vector& lsPrimitives, + int errorcode_to_assign, + std::vector& lsErrors, + double tol_overlap) { bool isValid = true; //-- 1. create Nef for all primitives and erode if necessary @@ -58,7 +164,7 @@ bool do_primitives_overlap(std::vector& lsPrimitives, if (tol_overlap > 0) { lsNefs.push_back(erode_nef_polyhedron(tmpnef, tol_overlap)); - delete tmpnef; + // delete tmpnef; } else { @@ -94,4 +200,59 @@ bool do_primitives_overlap(std::vector& lsPrimitives, return !isValid; } + +//-- adjacent +//-- == +//-- eroded.interior() not overlapping +//-- + +//-- dilated.interior() overlapping +//------------- +// -1: input primitives not valid +// 0: not adjancent +// 1: adjacent +int are_primitives_adjacent(Primitive* p1, + Primitive* p2, + double tol_overlap) +{ + //-- only process valid primitives + if ( (p1->is_valid() != 1) || (p2->is_valid() != 1) ) + return -1; + + Nef_polyhedron emptynef(Nef_polyhedron::EMPTY); + Nef_polyhedron* n1; + Nef_polyhedron* n2; + if (p1->get_type() == SOLID) + n1 = dynamic_cast(p1)->get_nef_polyhedron(); + else if (p1->get_type() == COMPOSITESOLID) + n1 = dynamic_cast(p1)->get_nef_polyhedron(); + if (p2->get_type() == SOLID) + n2 = dynamic_cast(p2)->get_nef_polyhedron(); + else if (p2->get_type() == COMPOSITESOLID) + n2 = dynamic_cast(p2)->get_nef_polyhedron(); + + if (tol_overlap < 0.0) + { + if (n1->boundary() * n2->boundary() != emptynef) + return 1; + else + return 0; + } + else + { + //-- 1. erode the nefs + Nef_polyhedron* ne1 = erode_nef_polyhedron(n1, tol_overlap); + Nef_polyhedron* ne2 = erode_nef_polyhedron(n2, tol_overlap); + if (ne1->interior() * ne2->interior() != emptynef) + return 0; + //-- 2. dilate the Nefs + Nef_polyhedron* nd1 = dilate_nef_polyhedron(n1, tol_overlap); + Nef_polyhedron* nd2 = dilate_nef_polyhedron(n2, tol_overlap); + if (nd1->interior() * nd2->interior() == emptynef) + return 0; + return 1; + } +} + + + } // namespace val3dity diff --git a/src/validate_prim_toporel.h b/src/validate_prim_toporel.h index 975562c4..b6aab6f3 100644 --- a/src/validate_prim_toporel.h +++ b/src/validate_prim_toporel.h @@ -30,6 +30,7 @@ #define Validate_prim_toporel_h #include "definitions.h" +#include "Solid.h" namespace val3dity { @@ -37,7 +38,18 @@ namespace val3dity class Primitive; -bool do_primitives_overlap(std::vector& lsPrimitives, int errorcode_to_assign, std::vector& lsErrors, double tol_overlap); +bool do_primitives_interior_overlap(std::vector& lsPrimitives, + int errorcode_to_assign, + std::vector& lsErrors, + double tol_overlap); + +bool are_cells_interior_disconnected_with_aabb(std::vector>& lsCells, + int errorcode_to_assign, + std::vector& lsErrors, + double tol_overlap); + +int are_primitives_adjacent(Primitive* p1, Primitive* p2, double tol_overlap); + } // namespace val3dity diff --git a/tests/README.md b/tests/README.md index 018d4b57..04a88812 100644 --- a/tests/README.md +++ b/tests/README.md @@ -4,6 +4,11 @@ This README is solely concerned with the integration tests for val3dity. These t Due to the current setup of file referencing in the tests, `pytest` need to be run from the `/val3dity` root directory, thus `somepath/val3dity $ pytest` +`PyYAML` is required for running the metadata tests. +``` +$ pip install pyyaml +``` + Run basic set of tests: ``` $ pytest diff --git a/tests/test_metadata.py b/tests/test_metadata.py index b0d41e50..cc55e90c 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -11,7 +11,7 @@ def test_metadata(): file = os.path.abspath(os.path.join(root, data_dir, "test_metadata.yml")) with open(file, "r") as stream: - ds = yaml.load(stream) + ds = yaml.load(stream, Loader=yaml.Loader) cases = ds.keys() for case in cases: diff --git a/uid_entrypoint.sh b/uid_entrypoint.sh new file mode 100755 index 00000000..23c39743 --- /dev/null +++ b/uid_entrypoint.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +set -e + +# Create passwd entry for arbitrary user ID +if [[ -z "$(awk -F ':' "\$3 == $(id -u)" /etc/passwd)" ]]; then + echo "Adding arbitrary user" + echo "${USER_NAME:-default}:x:$(id -u):0:${USER_NAME:-default} user:${HOME}:/sbin/nologin" >> /etc/passwd + echo "$(awk -F ':' "\$3 == $(id -u)" /etc/passwd)" +fi + +echo "Running as $(id -un):$(id -gn)" + +if [[ $@ == /bin* ]] +then + exec "$@" +else + val3dity $@ +fi