"""
Name:      decimation_test
Purpose:   v.in.lidar decimation test

Author:    Vaclav Petras
Copyright: (C) 2015 by Vaclav Petras and the GRASS Development Team
Licence:   This program is free software under the GNU General Public
           License (>=v2). Read the file COPYING that comes with GRASS
           for details.
"""

import os
from grass.gunittest.case import TestCase
from grass.gunittest.main import test


POINTS = """\
17.46938776,18.67346939,143,1,1,2
20.93877551,17.44897959,125,1,1,2
18.89795918,14.18367347,130,1,1,3
15.91836735,10.67346939,126,1,1,3
21.26530612,11.04081633,128,1,2,3
22.24489796,13.89795918,123,2,2,3
23.79591837,17.12244898,151,1,2,3
17.2244898,16.34693878,124,2,2,4
17.14285714,14.10204082,134,1,3,4
19.87755102,11.81632653,146,2,3,4
18.48979592,11.48979592,140.6,2,3,4
21.26530612,15.73469388,147,3,3,5
21.18367347,19.32653061,138,1,3,5
23.91836735,18.83673469,144,2,3,5
23.51020408,13.65306122,143,3,3,5
23.55102041,11.32653061,123,1,4,5
18.41009273,14.51618034,140.4,2,4,5
22.13996161,17.2278263,147,3,4,5
21.41013052,11.05432488,132,4,4,5
"""


class FilterTest(TestCase):
    """Test case for filter and selection options

    This tests expects v.random and v.out.lidar to work properly.
    """

    # Setup variables to be used for outputs
    vector_points = 'vinlidar_filters_original'
    imported_points = 'vinlidar_filters_imported'
    las_file = 'vinlidar_filters_points.las'
    npoints = 300

    @classmethod
    def setUpClass(cls):
        """Ensures expected computational region and generated data"""
        cls.use_temp_region()
        cls.runModule('g.region', n=20, s=10, e=25, w=15, res=1)
        cls.runModule('v.in.ascii', input='-', stdin_=POINTS,
                      flags='z', z=3, cat=0, separator='comma',
                      output=cls.vector_points,
                      columns="x double precision, y double precision,"
                              " z double precision, return_n integer,"
                              " n_returns integer, class_n integer")
        cls.runModule('v.out.lidar',
                      input=cls.vector_points, layer=1,
                      output=cls.las_file,
                      return_column='return_n',
                      n_returns_column='n_returns',
                      class_column='class_n')

    @classmethod
    def tearDownClass(cls):
        """Remove the temporary region and generated data"""
        cls.runModule('g.remove', flags='f', type='vector',
            name=cls.vector_points)
        if os.path.isfile(cls.las_file):
            os.remove(cls.las_file)
        cls.del_temp_region()

    def tearDown(self):
        """Remove the outputs created by the import

        This is executed after each test run.
        """
        self.runModule('g.remove', flags='f', type='vector',
            name=self.imported_points)

    def test_no_filter(self):
        """Test to see if the standard outputs are created

        This shows if the inpute data are as expected.
        """
        self.assertModule('v.in.lidar', input=self.las_file,
            output=self.imported_points, flags='bt')
        self.assertVectorExists(self.imported_points)
        self.assertVectorFitsTopoInfo(
            vector=self.imported_points,
            reference=dict(points=19))

    def return_filter(self, name, npoints):
        """Mid return filter test"""
        self.assertModule('v.in.lidar', input=self.las_file,
            output=self.imported_points, flags='bt',
            return_filter=name)
        self.assertVectorExists(self.imported_points)
        self.assertVectorFitsTopoInfo(
            vector=self.imported_points,
            reference=dict(points=npoints))

    def test_first_return_filter(self):
        """First return filter test"""
        self.return_filter('first', 9)

    def test_mid_return_filter(self):
        """Mid return filter test"""
        self.return_filter('mid', 5)

    def test_last_return_filter(self):
        """Last return filter test"""
        self.return_filter('last', 5)

    def class_filter(self, class_n, npoints):
        """Actual code for testing class filter"""
        self.assertModule('v.in.lidar', input=self.las_file,
            output=self.imported_points, flags='bt',
            class_filter=class_n)
        self.assertVectorExists(self.imported_points)
        self.assertVectorFitsTopoInfo(
            vector=self.imported_points,
            reference=dict(points=npoints))

    def test_class_2_filter(self):
        """Test to filter classes"""
        self.class_filter(2, 2)

    def test_class_3_filter(self):
        """Test to filter classes"""
        self.class_filter(3, 5)

    def test_class_4_filter(self):
        """Test to filter classes"""
        self.class_filter(4, 4)

    def test_class_5_filter(self):
        """Test to filter classes"""
        self.class_filter(5, 8)

    def return_and_class_filter(self, return_name, class_n, npoints):
        """Return and class filter combined test code"""
        self.assertModule('v.in.lidar', input=self.las_file,
            output=self.imported_points, flags='bt',
            return_filter=return_name, class_filter=class_n)
        self.assertVectorExists(self.imported_points)
        self.assertVectorFitsTopoInfo(
            vector=self.imported_points,
            reference=dict(points=npoints))

    def test_first_return_and_class_filter(self):
        """Combined test for return and class"""
        self.return_and_class_filter('first', 2, 2)

    def test_last_return_and_class_filter(self):
        """Combined test for return and class"""
        self.return_and_class_filter('last', 5, 3)

    def zrange_filter(self, zrange, npoints):
        """Actual code for zrange option test"""
        self.assertModule('v.in.lidar', input=self.las_file,
            output=self.imported_points, flags='bt',
            zrange=zrange)
        self.assertVectorExists(self.imported_points)
        self.assertVectorFitsTopoInfo(
            vector=self.imported_points,
            reference=dict(points=npoints))

    def test_zrange_filter(self):
        """Test zrange option"""
        self.zrange_filter((130.1, 139.9), 3)

    def test_non_int_zrange_filter(self):
        """Test zrange option with float number

        One test point has z right under and one other right above the min.
        """
        self.zrange_filter((140.5, 900), 8)

    def test_zrange_and_class_filter(self):
        """zrange and class_filter option combined test"""
        self.assertModule('v.in.lidar', input=self.las_file,
            output=self.imported_points, flags='bt',
            zrange=(141, 900), class_filter=5)
        self.assertVectorExists(self.imported_points)
        self.assertVectorFitsTopoInfo(
            vector=self.imported_points,
            reference=dict(points=4))

    def test_zrange_and_return_filter(self):
        """zrange and class_filter option combined test"""
        self.assertModule('v.in.lidar', input=self.las_file,
            output=self.imported_points, flags='bt',
            zrange=(141, 900), return_filter='last')
        self.assertVectorExists(self.imported_points)
        self.assertVectorFitsTopoInfo(
            vector=self.imported_points,
            reference=dict(points=2))


if __name__ == '__main__':
    test()
