#!/usr/bin/env python

""" Test suite to test polygon functionality. """

from builtins import str
from builtins import range
from future.utils import raise_
import unittest
import os
import numpy as num
from anuga.utilities.numerical_tools import ensure_numeric
from anuga.utilities.system_tools import get_anuga_pathname

from anuga.geometry.polygon import _poly_xy, separate_points_by_polygon, \
    populate_polygon, polygon_area, is_inside_polygon, \
    read_polygon, point_on_line, point_in_polygon, \
    plot_polygons, outside_polygon, is_outside_polygon, \
    intersection, is_complex, polygon_overlap, not_polygon_overlap,\
    line_intersect, not_line_intersect,\
    is_inside_triangle, interpolate_polyline, inside_polygon, \
    in_and_outside_polygon

from anuga.geometry.polygon_function import Polygon_function
from anuga.coordinate_transforms.geo_reference import Geo_reference
from anuga.geospatial_data.geospatial_data import Geospatial_data


def simple_function(x, y):
    return x+y


class Test_Polygon(unittest.TestCase):
    def setUp(self):
        pass

    def tearDown(self):
        pass

    def test_that_C_extension_compiles(self):
        FN = 'polygon_ext.c'
        try:
            import anuga.geometry.polygon_ext as polygon_ext
        except:
            from anuga.utilities.compile import compile

            try:
                compile(FN)
            except:
                raise_(Exception, 'Could not compile %s' % FN)
            else:
                import anuga.geometry.polygon_ext as polygon_ext

    # Polygon stuff
    def test_polygon_function_constants(self):
        p1 = [[0, 0], [10, 0], [10, 10], [0, 10]]
        p2 = [[0, 0], [10, 10], [15, 5], [20, 10], [25, 0], [30, 10], [40, -10]]

        f = Polygon_function([(p1, 1.0)])
        z = f([5, 5, 27, 35], [5, 9, 8, -5])    # Two first inside p1
        assert num.allclose(z, [1, 1, 0, 0])

        f = Polygon_function([(p2, 2.0)])
        z = f([5, 5, 27, 35], [5, 9, 8, -5])    # First and last inside p2
        assert num.allclose(z, [2, 0, 0, 2])

        # Combined
        f = Polygon_function([(p1, 1.0), (p2, 2.0)])
        z = f([5, 5, 27, 35], [5, 9, 8, -5])
        assert num.allclose(z, [2, 1, 0, 2])

    def test_polygon_function_csvfile(self):
        from os import sep, getenv

        # Get path where this test is run
        path = get_anuga_pathname()

        # Form absolute filename and read
        filename = os.path.join(
            path, 'utilities', 'tests', 'data', 'mainland_only.csv')
        p1 = read_polygon(filename)

        f = Polygon_function([(p1, 10.0)])
        z = f([430000, 480000], [490000, 7720000])  # 1st outside, 2nd inside

        assert num.allclose(z, [0, 10])

    def test_read_polygon_complex(self):
        from os import sep, getenv

        # Get path where anuga installed
        path = get_anuga_pathname()

        # Form absolute filename and read
        filename = os.path.join(
            path, 'utilities', 'tests', 'data', 'complex_polygon.csv')
        # Should cause an Exception
        try:
            p1 = read_polygon(filename)
        except Exception:
            pass
        else:
            raise Exception('Closed polygon should have raised exception')

    def test_read_polygon_not_complex(self):
        from os import sep, getenv
        # This polygon is not self intersecting, but was interpreted as self
        # intersecting by a previous version of the code

        # Get path where anuga installed
        path = get_anuga_pathname()

        # Form absolute filename and read
        filename = os.path.join(
            path, 'utilities', 'tests', 'data',  'non_complex_polygon.csv')
        # Should cause an Exception
        try:
            p1 = read_polygon(filename)
        except Exception:
            raise Exception(
                'Non-complex polygon should not have raised exception')

    def test_read_polygon_open_complex(self):
        from os import sep, getenv

        # Get path where this test is run
        path = get_anuga_pathname()

        # Form absolute filename and read
        filename = os.path.join(
            path, 'utilities', 'tests', 'data',  'complex_polygon.csv')
        # Should not cause an Exception
        p1 = read_polygon(filename, closed=False)

    def test_polygon_function_georef(self):
        """Check that georeferencing works"""

        from anuga.coordinate_transforms.geo_reference import Geo_reference

        geo = Geo_reference(56, 200, 1000)

        # Make points 'absolute'
        p1 = [[200, 1000], [210, 1000], [210, 1010], [200, 1010]]
        p2 = [[200, 1000], [210, 1010], [215, 1005], [220, 1010], [225, 1000],
              [230, 1010], [240, 990]]

        f = Polygon_function([(p1, 1.0)], geo_reference=geo)
        z = f([5, 5, 27, 35], [5, 9, 8, -5])  # Two first inside p1

        assert num.allclose(z, [1, 1, 0, 0])

        f = Polygon_function([(p2, 2.0)], geo_reference=geo)
        z = f([5, 5, 27, 35], [5, 9, 8, -5])  # First and last inside p2
        assert num.allclose(z, [2, 0, 0, 2])

        # Combined
        f = Polygon_function([(p1, 1.0), (p2, 2.0)], geo_reference=geo)
        z = f([5, 5, 27, 35], [5, 9, 8, -5])
        assert num.allclose(z, [2, 1, 0, 2])

        # Check that it would fail without geo
        f = Polygon_function([(p1, 1.0), (p2, 2.0)])
        z = f([5, 5, 27, 35], [5, 9, 8, -5])
        assert not num.allclose(z, [2, 1, 0, 2])

    def test_polygon_function_callable(self):
        """Check that values passed into Polygon_function can be callable."""

        p1 = [[0, 0], [10, 0], [10, 10], [0, 10]]
        p2 = [[0, 0], [10, 10], [15, 5], [20, 10], [25, 0], [30, 10], [40, -10]]

        f = Polygon_function([(p1, simple_function)])
        z = f([5, 5, 27, 35], [5, 9, 8, -5])  # Two first inside p1
        assert num.allclose(z, [10, 14, 0, 0])

        # Combined
        f = Polygon_function([(p1, simple_function), (p2, 2.0)])
        z = f([5, 5, 27, 35], [5, 9, 8, -5])
        assert num.allclose(z, [2, 14, 0, 2])

        # Combined w default
        f = Polygon_function([(p1, simple_function), (p2, 2.0)], default=3.14)
        z = f([5, 5, 27, 35], [5, 9, 8, -5])
        assert num.allclose(z, [2, 14, 3.14, 2])

        # Combined w default func
        f = Polygon_function([(p1, simple_function), (p2, 2.0)],
                             default=simple_function)
        z = f([5, 5, 27, 35], [5, 9, 8, -5])
        assert num.allclose(z, [2, 14, 35, 2])

    def test_point_on_line(self):
        # Endpoints first
        assert point_on_line([0, 0], [[0, 0], [1, 0]])
        assert point_on_line([1, 0], [[0, 0], [1, 0]])

        # Endpoints first - non-simple
        assert point_on_line([-0.1, 0.0], [[-0.1, 0.0], [1.5, 0.6]])
        assert point_on_line([1.5, 0.6], [[-0.1, 0.0], [1.5, 0.6]])

        # Then points on line
        assert point_on_line([0.5, 0], [[0, 0], [1, 0]])
        assert point_on_line([0, 0.5], [[0, 1], [0, 0]])
        assert point_on_line([1, 0.5], [[1, 1], [1, 0]])
        assert point_on_line([0.5, 0.5], [[0, 0], [1, 1]])

        # Then points not on line
        assert not point_on_line([0.5, 0], [[0, 1], [1, 1]])
        assert not point_on_line([0, 0.5], [[0, 0], [1, 1]])

        # From real example that failed
        assert not point_on_line([40, 50], [[40, 20], [40, 40]])

        # From real example that failed
        assert not point_on_line([40, 19], [[40, 20], [40, 40]])

        # Degenerate line
        assert point_on_line([40, 19], [[40, 19], [40, 19]])
        assert not point_on_line([40, 19], [[40, 40], [40, 40]])

    def test_is_inside_polygon_main(self):
        # Simplest case: Polygon is the unit square
        polygon = [[0, 0], [1, 0], [1, 1], [0, 1]]

        assert is_inside_polygon((0.5, 0.5), polygon)
        assert not is_inside_polygon((0.5, 1.5), polygon)
        assert not is_inside_polygon((0.5, -0.5), polygon)
        assert not is_inside_polygon((-0.5, 0.5), polygon)
        assert not is_inside_polygon((1.5, 0.5), polygon)

        # Try point on borders
        assert is_inside_polygon((1., 0.5), polygon, closed=True)
        assert is_inside_polygon((0.5, 1.), polygon, closed=True)
        assert is_inside_polygon((0., 0.5), polygon, closed=True)
        assert is_inside_polygon((0.5, 0.), polygon, closed=True)

        assert not is_inside_polygon((0.5, 1.), polygon, closed=False)
        assert not is_inside_polygon((0., 0.5), polygon, closed=False)
        assert not is_inside_polygon((0.5, 0.), polygon, closed=False)
        assert not is_inside_polygon((1., 0.5), polygon, closed=False)

    def test_inside_polygon_main_1(self):
        """test_is_inside_polygon

        Test fast version of of is_inside_polygon
        """

        # Simplest case: Polygon is the unit square
        polygon = [[0, 0], [1, 0], [1, 1], [0, 1]]

        assert is_inside_polygon((0.5, 0.5), polygon)
        assert not is_inside_polygon((0.5, 1.5), polygon)
        assert not is_inside_polygon((0.5, -0.5), polygon)
        assert not is_inside_polygon((-0.5, 0.5), polygon)
        assert not is_inside_polygon((1.5, 0.5), polygon)

        # Try point on borders
        assert is_inside_polygon((1., 0.5), polygon, closed=True)
        assert is_inside_polygon((0.5, 1), polygon, closed=True)
        assert is_inside_polygon((0., 0.5), polygon, closed=True)
        assert is_inside_polygon((0.5, 0.), polygon, closed=True)

        assert not is_inside_polygon((0.5, 1), polygon, closed=False)
        assert not is_inside_polygon((0., 0.5), polygon, closed=False)
        assert not is_inside_polygon((0.5, 0.), polygon, closed=False)
        assert not is_inside_polygon((1., 0.5), polygon, closed=False)

    def test_inside_polygon_main_2(self):
        # Simplest case: Polygon is the unit square
        polygon = [[0, 0], [1, 0], [1, 1], [0, 1]]

        # From real example (that failed)
        polygon = [[20, 20], [40, 20], [40, 40], [20, 40]]
        points = [[40, 50]]
        res = inside_polygon(points, polygon)
        assert len(res) == 0

        polygon = [[20., 20.], [40., 20.], [40., 40.], [20., 40.]]
        points = [[25, 25], [30, 20], [40, 50], [90, 20], [40, 90]]
        res = inside_polygon(points, polygon)
        assert len(res) == 2
        assert num.allclose(res, [0, 1])

        # More convoluted and non convex polygon
        polygon = [[0, 0], [1, 0], [0.5, -1], [2, -1], [2, 1], [0, 1]]
        assert is_inside_polygon((0.5, 0.5), polygon)
        assert is_inside_polygon((1, -0.5), polygon)
        assert is_inside_polygon((1.5, 0), polygon)

        assert not is_inside_polygon((0.5, 1.5), polygon)
        assert not is_inside_polygon((0.5, -0.5), polygon)

        assert is_inside_polygon((0.5, 0.5), polygon)
        assert is_inside_polygon((1, -0.5), polygon)
        assert is_inside_polygon((1.5, 0), polygon)

        assert not is_inside_polygon((0.5, 1.5), polygon)
        assert not is_inside_polygon((0.5, -0.5), polygon)

        # Very convoluted polygon
        polygon = [[0, 0], [10, 10], [15, 5], [
            20, 10], [25, 0], [30, 10], [40, -10]]
        assert is_inside_polygon((5, 5), polygon)
        assert is_inside_polygon((17, 7), polygon)
        assert is_inside_polygon((27, 2), polygon)
        assert is_inside_polygon((35, -5), polygon)
        assert not is_inside_polygon((15, 7), polygon)
        assert not is_inside_polygon((24, 3), polygon)
        assert not is_inside_polygon((25, -10), polygon)

        # Another combination (that failed)
        polygon = [[0, 0], [10, 0], [10, 10], [0, 10]]
        assert is_inside_polygon((5, 5), polygon)
        assert is_inside_polygon((7, 7), polygon)
        assert not is_inside_polygon((-17, 7), polygon)
        assert not is_inside_polygon((7, 17), polygon)
        assert not is_inside_polygon((17, 7), polygon)
        assert not is_inside_polygon((27, 8), polygon)
        assert not is_inside_polygon((35, -5), polygon)

        # Multiple polygons
        polygon = [[0, 0], [1, 0], [1, 1], [0, 1], [0, 0], [10, 10],
                   [11, 10], [11, 11], [10, 11], [10, 10]]
        assert is_inside_polygon((0.5, 0.5), polygon)
        assert is_inside_polygon((10.5, 10.5), polygon)

        # FIXME: Fails if point is 5.5, 5.5
        assert not is_inside_polygon((0, 5.5), polygon)

        # Polygon with a hole
        polygon = [[-1, -1], [2, -1], [2, 2], [-1, 2], [-1, -1],
                   [0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]

        assert is_inside_polygon((0, -0.5), polygon)
        assert not is_inside_polygon((0.5, 0.5), polygon)

    def test_duplicate_points_being_ok(self):
        # Simplest case: Polygon is the unit square
        polygon = [[0, 0], [1, 0], [1, 0], [1, 0], [1, 1], [0, 1], [0, 0]]

        assert is_inside_polygon((0.5, 0.5), polygon)
        assert not is_inside_polygon((0.5, 1.5), polygon)
        assert not is_inside_polygon((0.5, -0.5), polygon)
        assert not is_inside_polygon((-0.5, 0.5), polygon)
        assert not is_inside_polygon((1.5, 0.5), polygon)

        # Try point on borders
        assert is_inside_polygon((1., 0.5), polygon, closed=True)
        assert is_inside_polygon((0.5, 1), polygon, closed=True)
        assert is_inside_polygon((0., 0.5), polygon, closed=True)
        assert is_inside_polygon((0.5, 0.), polygon, closed=True)

        assert not is_inside_polygon((0.5, 1), polygon, closed=False)
        assert not is_inside_polygon((0., 0.5), polygon, closed=False)
        assert not is_inside_polygon((0.5, 0.), polygon, closed=False)
        assert not is_inside_polygon((1., 0.5), polygon, closed=False)

        # From real example
        polygon = [[20, 20], [40, 20], [40, 40], [20, 40]]
        points = [[40, 50]]
        res = inside_polygon(points, polygon)
        assert len(res) == 0

    def test_inside_polygon_vector_version(self):
        # Now try the vector formulation returning indices
        polygon = [[0, 0], [1, 0], [0.5, -1], [2, -1], [2, 1], [0, 1]]
        points = [[0.5, 0.5], [1, -0.5], [1.5, 0], [0.5, 1.5], [0.5, -0.5]]
        res = inside_polygon(points, polygon, verbose=False)
        assert num.allclose(res, [0, 1, 2])

    def test_outside_polygon(self):
        # unit square
        U = [[0, 0], [1, 0], [1, 1], [0, 1]]

        # evaluate to False as the point 0.5, 0.5 is inside the unit square
        assert not is_outside_polygon([0.5, 0.5], U)

        # evaluate to True as the point 1.5, 0.5 is outside the unit square
        assert is_outside_polygon([1.5, 0.5], U)

        indices = outside_polygon([[0.5, 0.5], [1, -0.5], [0.3, 0.2]], U)
        assert num.allclose(indices, [1])

        # One more test of vector formulation returning indices
        polygon = [[0, 0], [1, 0], [0.5, -1], [2, -1], [2, 1], [0, 1]]
        points = [[0.5, 0.5], [1, -0.5], [1.5, 0], [0.5, 1.5], [0.5, -0.5]]
        res = outside_polygon(points, polygon)
        assert num.allclose(res, [3, 4])

        polygon = [[0, 0], [1, 0], [0.5, -1], [2, -1], [2, 1], [0, 1]]
        points = [[0.5, 1.4], [0.5, 0.5], [1, -0.5], [1.5, 0],
                  [0.5, 1.5], [0.5, -0.5]]
        res = outside_polygon(points, polygon)

        assert num.allclose(res, [0, 4, 5])

    def test_outside_polygon2(self):
        # unit square
        U = [[0, 0], [1, 0], [1, 1], [0, 1]]

        # evaluate to False as the point 0.5, 1.0 is inside the unit square

        assert outside_polygon([0.5, 1.0], U, closed=True).size == 0

        # evaluate to True as the point 0.5, 1.0 is outside the unit square
        assert is_outside_polygon([0.5, 1.0], U, closed=False)

    def test_all_outside_polygon(self):
        """Test case where all points are outside poly"""

        # unit square
        U = [[0, 0], [1, 0], [1, 1], [0, 1]]

        points = [[2, 2], [1, 3], [-1, 1], [0, 2]]      # All outside

        indices, count = separate_points_by_polygon(points, U)
        assert count == 0                           # None inside
        assert num.allclose(indices, [3, 2, 1, 0])

        indices = outside_polygon(points, U, closed=True)
        assert num.allclose(indices, [0, 1, 2, 3])

        indices = inside_polygon(points, U, closed=True)
        assert num.allclose(indices, [])

    def test_all_inside_polygon(self):
        """Test case where all points are inside poly"""

        # unit square
        U = [[0, 0], [1, 0], [1, 1], [0, 1]]

        # All inside (or on edge)
        points = [[0.5, 0.5], [0.2, 0.3], [0, 0.5]]

        indices, count = separate_points_by_polygon(points, U)
        assert count == 3       # All inside
        assert num.allclose(indices, [0, 1, 2])

        indices = outside_polygon(points, U, closed=True)
        assert num.allclose(indices, [])

        indices = inside_polygon(points, U, closed=True)
        assert num.allclose(indices, [0, 1, 2])

    def test_separate_points_by_polygon(self):
        # unit square
        U = [[0, 0], [1, 0], [1, 1], [0, 1]]

        indices, count = separate_points_by_polygon([[0.5, 0.5],
                                                     [1, -0.5],
                                                     [0.3, 0.2]],
                                                    U)
        assert num.allclose(indices, [0, 2, 1])
        assert count == 2

        # One more test of vector formulation returning indices
        polygon = [[0, 0], [1, 0], [0.5, -1], [2, -1], [2, 1], [0, 1]]
        points = [[0.5, 0.5], [1, -0.5], [1.5, 0], [0.5, 1.5], [0.5, -0.5]]
        res, count = separate_points_by_polygon(points, polygon)

        assert num.allclose(res, [0, 1, 2, 4, 3])
        assert count == 3

        polygon = [[0, 0], [1, 0], [0.5, -1], [2, -1], [2, 1], [0, 1]]
        points = [[0.5, 1.4], [0.5, 0.5], [1, -0.5], [1.5, 0], [0.5, 1.5],
                  [0.5, -0.5]]
        res, count = separate_points_by_polygon(points, polygon)

        assert num.allclose(res, [1, 2, 3, 5, 4, 0])
        assert count == 3

    def test_polygon_overlap(self):
        # rectangular polygon, 2 triangles overlap
        polygon = [[3., 3.], [3., 4.], [5., 4.], [5., 3.]]
        triangles = [[7., 10.],  # does not overlap
                     [8., 12.],
                     [9., 10.],
                     [3., 2.],  # intersect
                     [4., 5.],
                     [5., 3.],
                     [3., 2.],  # intersect
                     [4., 5.],
                     [5., 2.],
                     [0., 1.],  # polygon inside triangle
                     [5., 50.],
                     [10., 1.],
                     [3.5, 3.25],  # triangle inside polygon
                     [4., 3.75],
                     [4.5, 3.25]]

        indices = polygon_overlap(triangles, polygon)
        assert num.allclose([1, 2, 3, 4], indices)

        # 5 sided polygon, 2 triangles overlap
        polygon = [[3., 3.], [3., 4.], [5., 4.], [5.5, 3.5], [5., 3.]]
        triangles = [[7., 10.],  # does not overlap
                     [8., 12.],
                     [9., 10.],
                     [3., 2.],  # intersect
                     [4., 5.],
                     [5., 3.],
                     [3., 2.],  # intersect
                     [4., 5.],
                     [5., 2.],
                     [0., 1.],  # polygon inside triangle
                     [5., 50.],
                     [10., 1.],
                     [3.5, 3.25],  # triangle inside polygon
                     [4., 3.75],
                     [4.5, 3.25]]

        indices = polygon_overlap(triangles, polygon)
        assert num.allclose([1, 2, 3, 4], indices)

    def test_not_polygon_overlap(self):
        # rectangular polygon, 2 triangles overlap
        polygon = [[3., 3.], [3., 4.], [5., 4.], [5., 3.]]
        triangles = [[7., 10.],  # does not overlap
                     [8., 12.],
                     [9., 10.],
                     [3., 2.],  # intersect
                     [4., 5.],
                     [5., 3.],
                     [3., 2.],  # intersect
                     [4., 5.],
                     [5., 2.],
                     [0., 1.],  # polygon inside triangle
                     [5., 50.],
                     [10., 1.],
                     [3.5, 3.25],  # triangle inside polygon
                     [4., 3.75],
                     [4.5, 3.25]]

        indices = not_polygon_overlap(triangles, polygon)
        assert num.allclose([0], indices)

        # 5 sided polygon, 2 triangles overlap
        polygon = [[3., 3.], [3., 4.], [5., 4.], [5.5, 3.5], [5., 3.]]
        triangles = [[7., 10.],  # does not overlap
                     [8., 12.],
                     [9., 10.],
                     [3., 2.],  # intersect
                     [4., 5.],
                     [5., 3.],
                     [3., 2.],  # intersect
                     [4., 5.],
                     [5., 2.],
                     [0., 1.],  # polygon inside triangle
                     [5., 50.],
                     [10., 1.],
                     [3.5, 3.25],  # triangle inside polygon
                     [4., 3.75],
                     [4.5, 3.25]]

        indices = not_polygon_overlap(triangles, polygon)
        assert num.allclose([0], indices)

    def test_line_intersect(self):
        # rectangular polygon, 2 triangles overlap
        line = [[3., 3.], [4., 3.]]
        triangles = [[7., 10.],  # does not overlap
                     [8., 12.],
                     [9., 10.],
                     [3., 2.],  # intersect
                     [4., 5.],
                     [5., 3.],
                     [3., 2.],  # intersect
                     [4., 5.],
                     [5., 2.],
                     [0., 1.],  # polygon inside triangle
                     [5., 50.],
                     [10., 1.],
                     [3.5, 3.25],  # no overlap
                     [4., 3.75],
                     [4.5, 3.25],
                     [2., 3.],  # overlap
                     [5., 3.],
                     [3., 1.]]

        indices = line_intersect(triangles, line)
        assert num.allclose([1, 2, 3, 5], indices)

    def test_not_line_intersect(self):
        # rectangular polygon, 2 triangles overlap
        line = [[3., 3.], [4., 3.]]
        triangles = [[7., 10.],  # does not overlap
                     [8., 12.],
                     [9., 10.],
                     [3., 2.],  # intersect
                     [4., 5.],
                     [5., 3.],
                     [3., 2.],  # intersect
                     [4., 5.],
                     [5., 2.],
                     [0., 1.],  # polygon inside triangle
                     [5., 50.],
                     [10., 1.],
                     [3.5, 3.25],  # no overlap
                     [4., 3.75],
                     [4.5, 3.25],
                     [2., 3.],  # overlap
                     [5., 3.],
                     [3., 1.]]

        indices = not_line_intersect(triangles, line)
        assert num.allclose([4, 0], indices)

    def test_is_inside_triangle(self):
        # Simplest case:
        triangle = [[0, 0], [1, 0], [0.5, 1]]

        assert is_inside_triangle((0.5, 0.5), triangle)
        assert is_inside_triangle((0.9, 0.1), triangle)
        assert not is_inside_triangle((0.5, 1.5), triangle)
        assert not is_inside_triangle((0.5, -0.5), triangle)
        assert not is_inside_triangle((-0.5, 0.5), triangle)
        assert not is_inside_triangle((1.5, 0.5), triangle)

        # Try point on borders
        assert is_inside_triangle((0.5, 0), triangle, closed=True)
        assert is_inside_triangle((1, 0), triangle, closed=True)

        assert not is_inside_triangle((0.5, 0), triangle, closed=False)
        assert not is_inside_triangle((1, 0), triangle, closed=False)

        # Try vertices
        for P in triangle:
            assert is_inside_triangle(P, triangle, closed=True)
            assert not is_inside_triangle(P, triangle, closed=False)

        # Slightly different
        triangle = [[0, 0.1], [1, -0.2], [0.5, 1]]
        assert is_inside_triangle((0.5, 0.5), triangle)
        assert is_inside_triangle((0.4, 0.1), triangle)
        assert not is_inside_triangle((1, 1), triangle)

        # Try vertices
        for P in triangle:
            assert is_inside_triangle(P, triangle, closed=True)
            assert not is_inside_triangle(P, triangle, closed=False)

    def test_populate_polygon(self):
        polygon = [[0, 0], [1, 0], [1, 1], [0, 1]]
        points = populate_polygon(polygon, 5)

        assert len(points) == 5
        for point in points:
            assert is_inside_polygon(point, polygon)

            assert is_inside_polygon(point, polygon)

        # Very convoluted polygon
        polygon = [[0, 0], [10, 10], [15, 5], [
            20, 10], [25, 0], [30, 10], [40, -10]]
        points = populate_polygon(polygon, 5)
        assert len(points) == 5
        for point in points:
            assert is_inside_polygon(point, polygon)
            assert is_inside_polygon(point, polygon)

    def test_populate_polygon_with_exclude(self):
        polygon = [[0, 0], [1, 0], [1, 1], [0, 1]]
        ex_poly = [[0, 0], [0.5, 0], [0.5, 0.5], [0, 0.5]]     # SW quarter
        points = populate_polygon(polygon, 5, exclude=[ex_poly])

        assert len(points) == 5
        for point in points:
            assert is_inside_polygon(point, polygon)
            assert not is_inside_polygon(point, ex_poly)

        # overlap
        polygon = [[0, 0], [1, 0], [1, 1], [0, 1]]
        ex_poly = [[-1, -1], [0.5, 0], [0.5, 0.5], [-1, 0.5]]
        points = populate_polygon(polygon, 5, exclude=[ex_poly])

        assert len(points) == 5
        for point in points:
            assert is_inside_polygon(point, polygon)
            assert not is_inside_polygon(point, ex_poly)

        # Multiple
        polygon = [[0, 0], [1, 0], [1, 1], [0, 1]]
        ex_poly1 = [[0, 0], [0.5, 0], [0.5, 0.5], [0, 0.5]]    # SW quarter
        ex_poly2 = [[0.5, 0.5], [0.5, 1], [1, 1], [1, 0.5]]    # NE quarter

        points = populate_polygon(polygon, 20, exclude=[ex_poly1, ex_poly2])

        assert len(points) == 20
        for point in points:
            assert is_inside_polygon(point, polygon)
            assert not is_inside_polygon(point, ex_poly1)
            assert not is_inside_polygon(point, ex_poly2)

        # Very convoluted polygon
        polygon = [[0, 0], [10, 10], [15, 5], [
            20, 10], [25, 0], [30, 10], [40, -10]]
        ex_poly = [[-1, -1], [5, 0], [5, 5], [-1, 5]]
        points = populate_polygon(polygon, 20, exclude=[ex_poly])

        assert len(points) == 20
        for point in points:
            assert is_inside_polygon(point, polygon)
            assert not is_inside_polygon(point, ex_poly), '%s' % str(point)

    def test_populate_polygon_with_exclude2(self):
        min_outer = 0
        max_outer = 1000
        polygon_outer = [[min_outer, min_outer], [max_outer, min_outer],
                         [max_outer, max_outer], [min_outer, max_outer]]

        delta = 10
        min_inner1 = min_outer + delta
        max_inner1 = max_outer - delta
        inner1_polygon = [[min_inner1, min_inner1], [max_inner1, min_inner1],
                          [max_inner1, max_inner1], [min_inner1, max_inner1]]

        density_inner2 = 1000
        min_inner2 = min_outer + 2*delta
        max_inner2 = max_outer - 2*delta
        inner2_polygon = [[min_inner2, min_inner2], [max_inner2, min_inner2],
                          [max_inner2, max_inner2], [min_inner2, max_inner2]]

        points = populate_polygon(polygon_outer, 20,
                                  exclude=[inner1_polygon, inner2_polygon])

        assert len(points) == 20
        for point in points:
            assert is_inside_polygon(point, polygon_outer)
            assert not is_inside_polygon(point, inner1_polygon)
            assert not is_inside_polygon(point, inner2_polygon)

        # Very convoluted polygon
        polygon = [[0, 0], [10, 10], [15, 5], [
            20, 10], [25, 0], [30, 10], [40, -10]]
        ex_poly = [[-1, -1], [5, 0], [5, 5], [-1, 5]]
        points = populate_polygon(polygon, 20, exclude=[ex_poly])

        assert len(points) == 20
        for point in points:
            assert is_inside_polygon(point, polygon)
            assert not is_inside_polygon(point, ex_poly), '%s' % str(point)

    def test_point_in_polygon(self):
        polygon = [[0, 0], [1, 0], [1, 1], [0, 1]]
        point = point_in_polygon(polygon)
        assert is_inside_polygon(point, polygon)

        # this may get into a vicious loop
        # polygon = [[1e32,1e54], [1,0], [1,1], [0,1]]

        polygon = [[1e15, 1e7], [1, 0], [1, 1], [0, 1]]
        point = point_in_polygon(polygon)
        assert is_inside_polygon(point, polygon)

        polygon = [[0, 0], [1, 0], [1, 1], [1e8, 1e8]]
        point = point_in_polygon(polygon)
        assert is_inside_polygon(point, polygon)

        polygon = [[1e32, 1e54], [-1e32, 1e54], [1e32, -1e54]]
        point = point_in_polygon(polygon)
        assert is_inside_polygon(point, polygon)

        polygon = [[1e18, 1e15], [1, 0], [0, 1]]
        point = point_in_polygon(polygon)
        assert is_inside_polygon(point, polygon)

    def test_in_and_outside_polygon_main(self):
        # Simplest case: Polygon is the unit square
        polygon = [[0, 0], [1, 0], [1, 1], [0, 1]]

        inside, outside = in_and_outside_polygon((0.5, 0.5), polygon)
        assert inside[0] == 0
        assert len(outside) == 0

        inside, outside = in_and_outside_polygon((1., 0.5), polygon,
                                                 closed=True)
        assert inside[0] == 0
        assert len(outside) == 0

        inside, outside = in_and_outside_polygon((1., 0.5), polygon,
                                                 closed=False)
        assert len(inside) == 0
        assert outside[0] == 0

        points = [(1., 0.25), (1., 0.75)]
        inside, outside = in_and_outside_polygon(points, polygon, closed=True)
        assert num.alltrue(inside == [0, 1])
        assert len(outside) == 0

        inside, outside = in_and_outside_polygon(points, polygon, closed=False)
        assert len(inside) == 0
        assert num.alltrue(outside == [0, 1])

        points = [(100., 0.25), (0.5, 0.5)]
        inside, outside = in_and_outside_polygon(points, polygon)
        assert num.alltrue(inside == [1])
        assert outside[0] == 0

        points = [(100., 0.25), (0.5, 0.5), (39, 20),
                  (0.6, 0.7), (56, 43), (67, 90)]
        inside, outside = in_and_outside_polygon(points, polygon)
        assert num.alltrue(inside == [1, 3])
        assert num.alltrue(outside == [0, 2, 4, 5])

    def test_intersection1(self):
        line0 = [[-1, 0], [1, 0]]
        line1 = [[0, -1], [0, 1]]

        status, value = intersection(line0, line1)
        assert status == 1
        assert num.allclose(value, [0.0, 0.0])

    def test_intersection2(self):
        line0 = [[0, 0], [24, 12]]
        line1 = [[0, 12], [24, 0]]

        status, value = intersection(line0, line1)
        assert status == 1
        assert num.allclose(value, [12.0, 6.0])

        # Swap direction of one line
        line1 = [[24, 0], [0, 12]]

        status, value = intersection(line0, line1)
        assert status == 1
        assert num.allclose(value, [12.0, 6.0])

        # Swap order of lines
        status, value = intersection(line1, line0)
        assert status == 1
        assert num.allclose(value, [12.0, 6.0])

    def test_intersection3(self):
        line0 = [[0, 0], [24, 12]]
        line1 = [[0, 17], [24, 0]]

        status, value = intersection(line0, line1)
        assert status == 1
        assert num.allclose(value, [14.068965517, 7.0344827586])

        # Swap direction of one line
        line1 = [[24, 0], [0, 17]]

        status, value = intersection(line0, line1)
        assert status == 1
        assert num.allclose(value, [14.068965517, 7.0344827586])

        # Swap order of lines
        status, value = intersection(line1, line0)
        assert status == 1
        assert num.allclose(value, [14.068965517, 7.0344827586])

    def test_intersection_endpoints(self):
        """test_intersection_endpoints(self):

        Test that coinciding endpoints are picked up
        """

        line0 = [[0, 0], [1, 1]]
        line1 = [[1, 1], [2, 1]]

        status, value = intersection(line0, line1)
        assert status == 1
        assert num.allclose(value, [1.0, 1.0])

        line0 = [[1, 1], [2, 0]]
        line1 = [[1, 1], [2, 1]]

        status, value = intersection(line0, line1)
        assert status == 1
        assert num.allclose(value, [1.0, 1.0])

    # This function is a helper function for
    # the test_intersection_bug_20081110_?() set of tests.
    # This function tests all parallel line cases for 4 collinear points.
    # This function should never be run directly by the unittest code.
    def helper_parallel_intersection_code(self, P1, P2, P3, P4):
        # lines in same direction, no overlap
        # 0:         ---->----
        # 1:                     --------->-----------
        line0 = [P1, P2]
        line1 = [P3, P4]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 3, 'Expected status 3, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(value is None, 'Expected value of None, got %s' %
                                       str(value))

        # lines in same direction, no overlap
        # 0:         ----<----
        # 1:                     ---------<-----------
        line0 = [P2, P1]
        line1 = [P4, P3]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 3, 'Expected status 3, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(value is None, 'Expected value of None, got %s' %
                                       str(value))

        # lines in opposite direction, no overlap
        # 0:         ----<----
        # 1:                     --------->-----------
        line0 = [P2, P1]
        line1 = [P3, P4]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 3, 'Expected status 3, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(value is None, 'Expected value of None, got %s' %
                                       str(value))

        # lines in opposite direction, no overlap
        # 0:         ---->----
        # 1:                     ---------<-----------
        line0 = [P1, P2]
        line1 = [P4, P3]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 3, 'Expected status 3, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(value is None, 'Expected value of None, got %s' %
                                       str(value))

        # ----------------------------------------------------------------------

        # line0 fully within line1, same direction
        # 0:         ---->----
        # 1:    --------->-----------
        # value should be line0:
        #            ---->----
        line0 = [P2, P3]
        line1 = [P1, P4]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line0))

        # line0 fully within line1, same direction
        # 0:         ----<----
        # 1:    ---------<-----------
        # value should be line0:
        #            ----<----
        line0 = [P3, P2]
        line1 = [P4, P1]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line0))

        # line0 fully within line1, opposite direction
        # 0:         ----<----
        # 1:    --------->-----------
        # value should be line0:
        #            ----<----
        line0 = [P3, P2]
        line1 = [P1, P4]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line0))

        # line0 fully within line1, opposite direction
        # 0:         ---->----
        # 1:    ---------<-----------
        # value should be line0:
        #            ---->----
        line0 = [P2, P3]
        line1 = [P4, P1]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line0))

        # ----------------------------------------------------------------------

        # line1 fully within line0, same direction
        # 0:    --------->-----------
        # 1:         ---->----
        # value should be line1:
        #            ---->----
        line0 = [P1, P4]
        line1 = [P2, P3]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line1))

        # line1 fully within line0, same direction
        # 0:    ---------<-----------
        # 1:         ----<----
        # value should be line1:
        #            ----<----
        line0 = [P4, P1]
        line1 = [P3, P2]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line1))

        # line1 fully within line0, opposite direction
        # 0:    ---------<-----------
        # 1:         ---->----
        # value should be line1:
        #            ---->----
        line0 = [P4, P1]
        line1 = [P2, P3]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line1))

        # line1 fully within line0, opposite direction
        # 0:    --------->-----------
        # 1:         ----<----
        # value should be line1:
        #            ----<----
        line0 = [P1, P4]
        line1 = [P3, P2]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line1))

        # ----------------------------------------------------------------------

        # line in same direction, partial overlap
        # 0:    ----->-----
        # 1:       ------->--------
        # value should be segment line1_start->line0_end:
        #          --->----
        line0 = [P1, P3]
        line1 = [P2, P4]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, [line1[0], line0[1]]))

        # line in same direction, partial overlap
        # 0:    -----<-----
        # 1:       -------<--------
        # value should be segment line0_start->line1_end:
        #          ---<----
        line0 = [P3, P1]
        line1 = [P4, P2]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, [line0[0], line1[1]]))

        # line in opposite direction, partial overlap
        # 0:    -----<-----
        # 1:       ------->--------
        # value should be segment line0_start->line1_start:
        #          ---<----
        line0 = [P3, P1]
        line1 = [P2, P4]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, [line0[0], line1[0]]))

        # line in opposite direction, partial overlap
        # 0:    ----->-----
        # 1:       -------<--------
        # value should be segment line1_end->line0_end:
        #          --->----
        line0 = [P1, P3]
        line1 = [P4, P2]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, [line1[1], line0[1]]))

        # ----------------------------------------------------------------------

        # line in same direction, partial overlap
        # 0:       ------>------
        # 1:    ------>------
        # value should be segment line0_start->line1_end:
        #          --->----
        line0 = [P2, P4]
        line1 = [P1, P3]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, [line0[0], line1[1]]))

        # line in same direction, partial overlap
        # 0:       ------<------
        # 1:    ------<------
        # value should be segment line1_start->line0_end:
        #          ----<-----
        line0 = [P4, P2]
        line1 = [P3, P1]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, [line1[0], line0[1]]))

        # line in opposite direction, partial overlap
        # 0:       ------<------
        # 1:    ----->------
        # value should be segment line1_end->line0_end:
        #          --->----
        line0 = [P4, P2]
        line1 = [P1, P3]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, [line1[1], line0[1]]))

        # line in opposite direction, partial overlap
        # 0:       ------>------
        # 1:    -----<------
        # value should be segment line0_start->line1_start:
        #          ---<----
        line0 = [P2, P4]
        line1 = [P3, P1]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, [line0[0], line1[0]]))

        # ----------------------------------------------------------------------

        # line in same direction, same left point, line1 longer
        # 0:    ----->------
        # 1:    ------->--------
        # value should be line0:
        #       ----->------
        line0 = [P1, P3]
        line1 = [P1, P4]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line0))

        # line in same direction, same left point, line1 longer
        # 0:    -----<------
        # 1:    -------<--------
        # value should be line0:
        #       -----<------
        line0 = [P3, P1]
        line1 = [P4, P1]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line0))

        # line in opposite direction, same left point, line1 longer
        # 0:    ----->------
        # 1:    -------<--------
        # value should be line0:
        #       ----->------
        line0 = [P1, P3]
        line1 = [P4, P1]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line0))

        # line in opposite direction, same start point, line1 longer
        # 0:    -----<------
        # 1:    ------->--------
        # value should be line0:
        #       -----<------
        line0 = [P3, P1]
        line1 = [P1, P4]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line0))

        # ----------------------------------------------------------------------

        # line in same direction, same left point, same right point
        # 0:    ------->--------
        # 1:    ------->--------
        # value should be line0 or line1:
        #       ------->--------
        line0 = [P1, P3]
        line1 = [P1, P3]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        line0 = num.array(line0)
        self.assertTrue(num.allclose(value, line0))

        # line in same direction, same left point, same right point
        # 0:    -------<--------
        # 1:    -------<--------
        # value should be line0 (or line1):
        #       -------<--------
        line0 = [P3, P1]
        line1 = [P3, P1]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line0))

        # line in opposite direction, same left point, same right point
        # 0:    ------->--------
        # 1:    -------<--------
        # value should be line0:
        #       ------->--------
        line0 = [P1, P3]
        line1 = [P3, P1]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line0))

        # line in opposite direction, same left point, same right point
        # 0:    -------<--------
        # 1:    ------->--------
        # value should be line0:
        #       -------<--------
        line0 = [P3, P1]
        line1 = [P1, P3]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line0))

        # ----------------------------------------------------------------------

        # line in same direction, same right point, line1 longer
        # 0:        ----->------
        # 1:    ------->--------
        # value should be line0:
        #           ----->------
        line0 = [P2, P4]
        line1 = [P1, P4]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line0))

        # line in same direction, same right point, line1 longer
        # 0:        -----<------
        # 1:    -------<--------
        # value should be line0:
        #           -----<------
        line0 = [P4, P2]
        line1 = [P4, P1]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line0))

        # line in opposite direction, same right point, line1 longer
        # 0:        ----->------
        # 1:    -------<--------
        # value should be line0:
        #           ----->------
        line0 = [P2, P4]
        line1 = [P4, P1]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line0))

        # line in opposite direction, same right point, line1 longer
        # 0:        -----<------
        # 1:    ------->--------
        # value should be line0:
        #           -----<------
        line0 = [P4, P2]
        line1 = [P1, P4]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line0))

        # ----------------------------------------------------------------------

        # line in same direction, same left point, line0 longer
        # 0:    ------->--------
        # 1:    ----->------
        # value should be line1:
        #       ----->------
        line0 = [P1, P4]
        line1 = [P1, P3]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line1))

        # line in same direction, same left point, line0 longer
        # 0:    -------<--------
        # 1:    -----<------
        # value should be line1:
        #       -----<------
        line0 = [P4, P1]
        line1 = [P3, P1]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line1))

        # line in opposite direction, same left point, line0 longer
        # 0:    ------->--------
        # 1:    -----<------
        # value should be line1:
        #       -----<------
        line0 = [P1, P4]
        line1 = [P3, P1]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line1))

        # line in opposite direction, same left point, line0 longer
        # 0:    -------<--------
        # 1:    ----->------
        # value should be line1:
        #       ----->------
        line0 = [P4, P1]
        line1 = [P1, P3]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line1))

        # ----------------------------------------------------------------------

        # line in same direction, same right point, line0 longer
        # 0:    ------->--------
        # 1:        ----->------
        # value should be line1:
        #           ----->------
        line0 = [P1, P4]
        line1 = [P2, P4]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line1))

        # line in same direction, same right point, line0 longer
        # 0:    -------<--------
        # 1:        -----<------
        # value should be line1:
        #           -----<------
        line0 = [P4, P1]
        line1 = [P4, P2]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line1))

        # line in opposite direction, same right point, line0 longer
        # 0:    ------->--------
        # 1:        -----<------
        # value should be line1:
        #           -----<------
        line0 = [P1, P4]
        line1 = [P4, P2]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line1))

        # line in opposite direction, same right point, line0 longer
        # 0:    -------<--------
        # 1:        ----->------
        # value should be line1:
        #           ----->------
        line0 = [P4, P1]
        line1 = [P2, P4]
        status, value = intersection(line0, line1)
        self.assertFalse(status != 2, 'Expected status 2, got status=%s, value=%s' %
                         (str(status), str(value)))
        self.assertTrue(num.allclose(value, line1))

    def test_intersection_bug_20081110_TR(self):
        """test_intersection_bug_20081110(self)

        Test all cases in top-right quadrant
        """

        # define 4 collinear points in top-right quadrant
        #    P1---P2---P3---P4
        P1 = [1.0, 1.0]
        P2 = [2.0, 2.0]
        P3 = [3.0, 3.0]
        P4 = [4.0, 4.0]

        self.helper_parallel_intersection_code(P1, P2, P3, P4)
        P1 = [1.0, 1.0+1.0e-9]
        self.helper_parallel_intersection_code(P1, P2, P3, P4)
        P1 = [1.0, 1.0]
        P2 = [2.0, 2.0+1.0e-9]
        self.helper_parallel_intersection_code(P1, P2, P3, P4)
        P2 = [2.0, 2.0]
        P3 = [3.0, 3.0+1.0e-9]
        self.helper_parallel_intersection_code(P1, P2, P3, P4)
        P3 = [3.0, 3.0]
        P4 = [4.0, 4.0+1.0e-9]
        self.helper_parallel_intersection_code(P1, P2, P3, P4)

    def test_intersection_bug_20081110_TL(self):
        """test_intersection_bug_20081110(self)

        Test all cases in top-left quadrant
        """

        # define 4 collinear points in top-left quadrant
        #    P1---P2---P3---P4
        P1 = [-1.0, 1.0]
        P2 = [-2.0, 2.0]
        P3 = [-3.0, 3.0]
        P4 = [-4.0, 4.0]

        self.helper_parallel_intersection_code(P1, P2, P3, P4)
        P1 = [-1.0, 1.0+1.0e-9]
        self.helper_parallel_intersection_code(P1, P2, P3, P4)
        P1 = [-1.0, 1.0]
        P2 = [-2.0, 2.0+1.0e-9]
        self.helper_parallel_intersection_code(P1, P2, P3, P4)
        P2 = [-2.0, 2.0]
        P3 = [-3.0, 3.0+1.0e-9]
        self.helper_parallel_intersection_code(P1, P2, P3, P4)
        P3 = [-3.0, 3.0]
        P4 = [-4.0, 4.0+1.0e-9]
        self.helper_parallel_intersection_code(P1, P2, P3, P4)

    def test_intersection_bug_20081110_BL(self):
        """test_intersection_bug_20081110(self)

        Test all cases in bottom-left quadrant
        """

        # define 4 collinear points in bottom-left quadrant
        #    P1---P2---P3---P4
        P1 = [-1.0, -1.0]
        P2 = [-2.0, -2.0]
        P3 = [-3.0, -3.0]
        P4 = [-4.0, -4.0]

        self.helper_parallel_intersection_code(P1, P2, P3, P4)
        P1 = [-1.0, -1.0+1.0e-9]
        self.helper_parallel_intersection_code(P1, P2, P3, P4)
        P1 = [-1.0, -1.0]
        P2 = [-2.0, -2.0+1.0e-9]
        self.helper_parallel_intersection_code(P1, P2, P3, P4)
        P2 = [-2.0, -2.0]
        P3 = [-3.0, -3.0+1.0e-9]
        self.helper_parallel_intersection_code(P1, P2, P3, P4)
        P3 = [-3.0, -3.0]
        P4 = [-4.0, -4.0+1.0e-9]
        self.helper_parallel_intersection_code(P1, P2, P3, P4)

    def test_intersection_bug_20081110_BR(self):
        """test_intersection_bug_20081110(self)

        Test all cases in bottom-right quadrant
        """

        # define 4 collinear points in bottom-right quadrant
        #    P1---P2---P3---P4
        P1 = [1.0, -1.0]
        P2 = [2.0, -2.0]
        P3 = [3.0, -3.0]
        P4 = [4.0, -4.0]

        self.helper_parallel_intersection_code(P1, P2, P3, P4)
        P1 = [1.0, -1.0+1.0e-9]
        self.helper_parallel_intersection_code(P1, P2, P3, P4)
        P1 = [1.0, -1.0]
        P2 = [2.0, -2.0+1.0e-9]
        self.helper_parallel_intersection_code(P1, P2, P3, P4)
        P2 = [2.0, -2.0]
        P3 = [3.0, -3.0+1.0e-9]
        self.helper_parallel_intersection_code(P1, P2, P3, P4)
        P3 = [3.0, -3.0]
        P4 = [4.0, -4.0+1.0e-9]
        self.helper_parallel_intersection_code(P1, P2, P3, P4)

    def test_intersection_bug_20081110_TR_TL(self):
        """test_intersection_bug_20081110(self)

        Test all cases in top-right & top-left quadrant
        """

        # define 4 collinear points, 1 in TL, 3 in TR
        #    P1-+-P2---P3---P4
        P1 = [-3.0, 1.0]
        P2 = [1.0, 5.0]
        P3 = [2.0, 6.0]
        P4 = [3.0, 7.0]
        self.helper_parallel_intersection_code(P1, P2, P3, P4)

        # define 4 collinear points, 2 in TL, 2 in TR
        #    P1---P2-+-P3---P4
        P1 = [-3.0, 1.0]
        P2 = [-2.0, 2.0]
        P3 = [2.0, 6.0]
        P4 = [3.0, 7.0]
        self.helper_parallel_intersection_code(P1, P2, P3, P4)

        # define 4 collinear points, 3 in TL, 1 in TR
        #    P1---P2---P3-+-P4
        P1 = [-3.0, 1.0]
        P2 = [-2.0, 2.0]
        P3 = [-1.0, 3.0]
        P4 = [3.0, 7.0]
        self.helper_parallel_intersection_code(P1, P2, P3, P4)

    def test_intersection_bug_20081110_TR_BL(self):
        """test_intersection_bug_20081110(self)

        Test all cases in top-right & bottom-left quadrant
        """

        # define 4 collinear points, 1 in BL, 3 in TR
        #    P1-+-P2---P3---P4
        P1 = [-4.0, -3.0]
        P2 = [1.0,  2.0]
        P3 = [2.0,  3.0]
        P4 = [3.0,  4.0]
        self.helper_parallel_intersection_code(P1, P2, P3, P4)

        # define 4 collinear points, 2 in TL, 2 in TR
        #    P1---P2-+-P3---P4
        P1 = [-4.0, -3.0]
        P2 = [-3.0, -2.0]
        P3 = [2.0,  3.0]
        P4 = [3.0,  4.0]
        self.helper_parallel_intersection_code(P1, P2, P3, P4)

        # define 4 collinear points, 3 in TL, 1 in TR
        #    P1---P2---P3-+-P4
        P1 = [-4.0, -3.0]
        P2 = [-3.0, -2.0]
        P3 = [-2.0, -1.0]
        P4 = [3.0,  4.0]
        self.helper_parallel_intersection_code(P1, P2, P3, P4)

    def test_intersection_bug_20081110_TR_BR(self):
        """test_intersection_bug_20081110(self)

        Test all cases in top-right & bottom-right quadrant
        """

        # define 4 collinear points, 1 in BR, 3 in TR
        #    P1-+-P2---P3---P4
        P1 = [1.0, -3.0]
        P2 = [5.0,  1.0]
        P3 = [6.0,  2.0]
        P4 = [7.0,  3.0]
        self.helper_parallel_intersection_code(P1, P2, P3, P4)

        # define 4 collinear points, 2 in BR, 2 in TR
        #    P1---P2-+-P3---P4
        P1 = [1.0, -3.0]
        P2 = [2.0, -2.0]
        P3 = [6.0,  2.0]
        P4 = [7.0,  3.0]
        self.helper_parallel_intersection_code(P1, P2, P3, P4)

        # define 4 collinear points, 3 in BR, 1 in TR
        #    P1---P2---P3-+-P4
        P1 = [1.0, -3.0]
        P2 = [2.0, -2.0]
        P3 = [3.0, -1.0]
        P4 = [7.0,  3.0]
        self.helper_parallel_intersection_code(P1, P2, P3, P4)

    def test_intersection_direction_invariance(self):
        """This runs through a number of examples and checks that
        direction of lines don't matter.
        """

        line0 = [[0, 0], [100, 100]]

        common_end_point = [20, 150]

        for i in range(100):
            x = 20 + i * 1.0/100

            line1 = [[x, 0], common_end_point]
            status, p1 = intersection(line0, line1)
            assert status == 1

            # Swap direction of line1
            line1 = [common_end_point, [x, 0]]
            status, p2 = intersection(line0, line1)
            assert status == 1

            msg = ('Orientation of line should not matter.\n'
                   'However, segment [%f,%f], [%f, %f]' %
                   (x, 0, common_end_point[0], common_end_point[1]))
            msg += ' gave %s, \nbut when reversed we got %s' % (p1, p2)
            assert num.allclose(p1, p2), msg

            # Swap order of lines
            status, p3 = intersection(line1, line0)
            assert status == 1
            msg = 'Order of lines gave different results'
            assert num.allclose(p1, p3), msg

    def test_no_intersection(self):
        """ Test 2 non-touching lines don't intersect. """
        line0 = [[-1, 1], [1, 1]]
        line1 = [[0, -1], [0, 0]]

        status, value = intersection(line0, line1)
        assert status == 0
        assert value is None

    def test_intersection_parallel(self):
        line0 = [[-1, 1], [1, 1]]
        line1 = [[-1, 0], [5, 0]]

        status, value = intersection(line0, line1)
        assert status == 4
        assert value is None

        line0 = [[0, 0], [10, 100]]
        line1 = [[-10, 5], [0, 105]]

        status, value = intersection(line0, line1)
        assert status == 4
        assert value is None

    def test_intersection_coincide(self):
        """Test what happens when two lines partly coincide"""

        # Overlap 1
        line0 = [[0, 0], [5, 0]]
        line1 = [[-3, 0], [3, 0]]

        status, value = intersection(line0, line1)
        assert status == 2
        assert num.allclose(value, [[0, 0], [3, 0]])

        # Overlap 2
        line0 = [[-10, 0], [5, 0]]
        line1 = [[-3, 0], [10, 0]]

        status, value = intersection(line0, line1)
        assert status == 2
        assert num.allclose(value, [[-3, 0], [5, 0]])

        # Inclusion 1
        line0 = [[0, 0], [5, 0]]
        line1 = [[2, 0], [3, 0]]

        status, value = intersection(line0, line1)
        assert status == 2
        assert num.allclose(value, line1)

        # Inclusion 2
        line0 = [[1, 0], [5, 0]]
        line1 = [[-10, 0], [15, 0]]

        status, value = intersection(line0, line1)
        assert status == 2
        assert num.allclose(value, line0)

        # Exclusion (no intersection)
        line0 = [[-10, 0], [1, 0]]
        line1 = [[3, 0], [15, 0]]

        status, value = intersection(line0, line1)
        assert status == 3
        assert value is None

        # Try examples with some slope (y=2*x+5)

        # Overlap
        line0 = [[0, 5], [7, 19]]
        line1 = [[1, 7], [10, 25]]
        status, value = intersection(line0, line1)
        assert status == 2
        assert num.allclose(value, [[1, 7], [7, 19]])

        status, value = intersection(line1, line0)
        assert status == 2
        assert num.allclose(value, [[1, 7], [7, 19]])

        # Swap direction
        line0 = [[7, 19], [0, 5]]
        line1 = [[1, 7], [10, 25]]
        status, value = intersection(line0, line1)
        assert status == 2
        assert num.allclose(value, [[7, 19], [1, 7]])

        line0 = [[0, 5], [7, 19]]
        line1 = [[10, 25], [1, 7]]
        status, value = intersection(line0, line1)
        assert status == 2
        assert num.allclose(value, [[1, 7], [7, 19]])

        # Inclusion
        line0 = [[1, 7], [7, 19]]
        line1 = [[0, 5], [10, 25]]
        status, value = intersection(line0, line1)
        assert status == 2
        assert num.allclose(value, [[1, 7], [7, 19]])

        line0 = [[0, 5], [10, 25]]
        line1 = [[1, 7], [7, 19]]
        status, value = intersection(line0, line1)
        assert status == 2
        assert num.allclose(value, [[1, 7], [7, 19]])

        line0 = [[0, 5], [10, 25]]
        line1 = [[7, 19], [1, 7]]
        status, value = intersection(line0, line1)
        assert status == 2
        assert num.allclose(value, [[7, 19], [1, 7]])

    def test_inside_polygon_main(self):
        # From real example (that failed)
        polygon = [[20, 20], [40, 20], [40, 40], [20, 40]]
        points = [[40, 50]]
        res = inside_polygon(points, polygon)
        assert len(res) == 0

        polygon = [[20, 20], [40, 20], [40, 40], [20, 40]]
        points = [[25, 25], [30, 20], [40, 50], [90, 20], [40, 90]]
        res = inside_polygon(points, polygon)
        assert len(res) == 2
        assert num.allclose(res, [0, 1])

    def test_polygon_area(self):
        """ Test getting the area of a polygon. """
        # Simplest case: Polygon is the unit square
        polygon = [[0, 0], [1, 0], [1, 1], [0, 1]]
        assert polygon_area(polygon) == 1

        # Simple case: Polygon is a rectangle
        polygon = [[0, 0], [1, 0], [1, 4], [0, 4]]
        assert polygon_area(polygon) == 4

        # Simple case: Polygon is a unit triangle
        polygon = [[0, 0], [1, 0], [0, 1]]
        assert polygon_area(polygon) == 0.5

        # Simple case: Polygon is a diamond
        polygon = [[0, 0], [1, 1], [2, 0], [1, -1]]
        assert polygon_area(polygon) == 2.0

        # Complex case where numerical errors might occur
        polygon = [[314037.58727982, 6224952.2960092],
                   [314038.58727982, 6224952.2960092],
                   [314038.58727982, 6224953.2960092],
                   [314037.58727982, 6224953.2960092]]
        assert polygon_area(polygon) == 1.0

    def test_poly_xy(self):
        # Simplest case: Polygon is the unit square
        polygon = [[0, 0], [1, 0], [1, 1], [0, 1]]
        x, y = _poly_xy(polygon)
        assert len(x) == len(polygon)+1
        assert len(y) == len(polygon)+1
        assert x[0] == 0
        assert x[1] == 1
        assert x[2] == 1
        assert x[3] == 0
        assert y[0] == 0
        assert y[1] == 0
        assert y[2] == 1
        assert y[3] == 1

    # Arbitrary polygon
        polygon = [[1, 5], [1, 1], [100, 10], [1, 10], [3, 6]]
        x, y = _poly_xy(polygon)
        assert len(x) == len(polygon)+1
        assert len(y) == len(polygon)+1
        assert x[0] == 1
        assert x[1] == 1
        assert x[2] == 100
        assert x[3] == 1
        assert x[4] == 3
        assert y[0] == 5
        assert y[1] == 1
        assert y[2] == 10
        assert y[3] == 10
        assert y[4] == 6

    def test_plot_polygons(self):
        import os

        # Simplest case: Polygon is the unit square
        polygon1 = [[0, 0], [1, 0], [1, 1], [0, 1]]
        polygon2 = [[1, 1], [2, 1], [3, 2], [2, 2]]
        plot_polygons([polygon1, polygon2], figname='test1')

        # Another case
        polygon3 = [[1, 5], [10, 1], [100, 10], [50, 9], [3, 6]]
        plot_polygons([polygon2, polygon3], figname='test2')

        # plt.close('all')

        for plotfile in ['test1.png', 'test2.png']:
            assert os.path.exists(plotfile)
            os.remove(plotfile)

    def test_inside_polygon_geospatial(self):
        """ Test geospatial coords inside poly. """
        # Simplest case: Polygon is the unit square
        polygon_absolute = [[0, 0], [1, 0], [1, 1], [0, 1]]
        poly_geo_ref = Geo_reference(57, 100, 100)
        polygon = poly_geo_ref.change_points_geo_ref(polygon_absolute)
        poly_spatial = Geospatial_data(polygon, geo_reference=poly_geo_ref)

        points_absolute = (0.5, 0.5)
        points_geo_ref = Geo_reference(57, 78, -56)
        points = points_geo_ref.change_points_geo_ref(points_absolute)
        points_spatial = Geospatial_data(points, geo_reference=points_geo_ref)

        assert is_inside_polygon(points_absolute, polygon_absolute)
        assert is_inside_polygon(ensure_numeric(points_absolute),
                                 ensure_numeric(polygon_absolute))
        assert is_inside_polygon(points_absolute, poly_spatial)
        assert is_inside_polygon(points_spatial, poly_spatial)
        assert is_inside_polygon(points_spatial, polygon_absolute)

        assert is_inside_polygon(points_absolute, polygon_absolute)

    def test_decimate_polygon(self):
        from anuga.geometry.polygon import decimate_polygon
        polygon = [[0, 0], [10, 10], [15, 5], [20, 10],
                   [25, 0], [30, 10], [40, -10], [35, -5]]

        dpoly = decimate_polygon(polygon, factor=2)

        assert len(dpoly)*2 == len(polygon)

    def test_interpolate_polyline(self):
        """test_interpolate_polyline(self):

        This test is added under the assumption that the function
        interpolate_polyline implemented by John Jakeman works.
        It has been exercised somewhat by tests of sts boundary,
        but never before separately.
        """

        f = num.array([58.06150614, 58.06150614, 58.06150614])
        vertex_coordinates = num.array([[0., 0., ],
                                        [4.04092634, 1106.11074699],
                                        [8.08836552, 2212.16910609]])
        gauge_neighbour_id = [1, 2, -1]
        point_coordinates = num.array([[2.21870766e+03, 1.09802864e+03],
                                       [1.62739645e+03, 2.20626983e+03],
                                       [5.20084967e+02, 2.21030386e+03],
                                       [6.06464546e+00, 1.65913993e+03],
                                       [1.61934862e+03, -5.88143836e+00],
                                       [5.11996623e+02, -1.85956061e+00],
                                       [2.02046270e+00, 5.53055373e+02]])

        z_ref = [0., 0., 0., 58.06150614, 0., 0., 58.06150614]

        z = interpolate_polyline(f, vertex_coordinates,
                                 gauge_neighbour_id, point_coordinates)
        assert num.allclose(z, z_ref)

        # Another f
        f = num.array([58.06150614, 158.06150614, 258.06150614])
        z_ref = [0., 0., 0., 208.06150645, 0., 0., 108.0615061]
        z = interpolate_polyline(f, vertex_coordinates,
                                 gauge_neighbour_id, point_coordinates)
        assert num.allclose(z, z_ref)

        # Other and simpler numbers
        f = num.array([1, 5, 13])
        vertex_coordinates = num.array([[0., 0., ],
                                        [4., 4.],
                                        [8., 8.]])
        point_coordinates = num.array([[0.1, 0.1],
                                       [3.5, 3.5],
                                       [4.0, 4.0],
                                       [5.2, 5.2],
                                       [7.0, 7.0],
                                       [8.3, 8.3]])
        gauge_neighbour_id = [1, 2, -1]

        z = interpolate_polyline(f, vertex_coordinates,
                                 gauge_neighbour_id, point_coordinates)
        z_ref = [1.1, 4.5, 5., 7.4, 11., 0.]
        assert num.allclose(z, z_ref)

        # Test exception thrown for one point
        f = num.array([5])
        vertex_coordinates = num.array([[4., 4.]])
        try:
            z = interpolate_polyline(f, vertex_coordinates,
                                     gauge_neighbour_id, point_coordinates)
        except Exception:
            pass
        else:
            raise Exception('One point should have raised exception')

        # More polyline nodes
        data = num.array([1, 5, 13, 12, 6, 29])
        polyline_nodes = num.array([[0., 0.],
                                    [4., 4.],
                                    [8., 8.],
                                    [10., 10.],
                                    [10., 5.],
                                    [10., 0.]])
        point_coordinates = num.array([[0.1, 0.1],
                                       [3.5, 3.5],
                                       [4.0, 4.0],
                                       [5.2, 5.2],
                                       [7.0, 7.0],
                                       [8.3, 8.3],
                                       [10., 10.],
                                       [10., 9.],
                                       [10., 7.1],
                                       [10., 4.3],
                                       [10., 1.0]])
        gauge_neighbour_id = [1, 2, 3, 4, 5, -1]
        z = interpolate_polyline(data, polyline_nodes,
                                 gauge_neighbour_id, point_coordinates)
        z_ref = [1.1, 4.5, 5., 7.4, 11., 12.85, 12., 10.8, 8.52, 9.22, 24.4]
        assert num.allclose(z, z_ref)

    def test_is_inside_triangle_more(self):
        """ Test if points inside triangles are detected correctly. """
        res = is_inside_triangle([0.5, 0.5], [[0.5,  0.],
                                              [0.5,  0.5],
                                              [0.,   0.]])
        assert res is True

        res = is_inside_triangle([0.59999999999999998, 0.29999999999999999],
                                 [[0.5,  0.], [0.5, 0.5], [0., 0.]])
        assert res is False

        res = is_inside_triangle([0.59999999999999998, 0.29999999999999999],
                                 [[1., 0.], [1., 0.5], [0.5, 0.]])
        assert res is False

        res = is_inside_triangle([0.59999999999999998, 0.29999999999999999],
                                 [[0.5, 0.5], [0.5, 0.], [1., 0.5]])
        assert res is True

        res = is_inside_triangle([0.10000000000000001, 0.20000000000000001],
                                 [[0.5, 0.], [0.5, 0.5], [0., 0.]])
        assert res is False

        res = is_inside_triangle([0.10000000000000001, 0.20000000000000001],
                                 [[0., 0.5], [0., 0.], [0.5, 0.5]])
        assert res is True

        res = is_inside_triangle([0.69999999999999996, 0.69999999999999996],
                                 [[0.5, 0.], [0.5, 0.5], [0., 0.]])
        assert res is False

        res = is_inside_triangle([0.59999999999999998, 0.29999999999999999],
                                 [[0.25, 0.5], [0.25, 0.25], [0.5, 0.5]])
        assert res is False

        res = is_inside_triangle([10, 3],
                                 [[0.1, 0.],
                                  [0.1, 0.08333333],
                                  [0., 0.]])
        assert res is False

    def test_is_polygon_complex(self):
        """ Test a concave and a complex poly with is_complex, to make
            sure it can detect self-intersection.
        """
        concave_poly = [[0, 0], [10, 0], [5, 5], [10, 10], [0, 10]]
        complex_poly = [[0, 0], [10, 0], [5, 5], [4, 15], [5, 7], [10, 10],
                        [0, 10]]

        assert not is_complex(concave_poly)
        assert is_complex(complex_poly)

    def test_is_polygon_complex2(self):
        """ Test a concave and a complex poly with is_complex, to make sure it
            can detect self-intersection. This test uses more complicated
            polygons.
        """
        concave_poly = [[0, 0], [10, 0], [11, 0], [5, 5], [7, 6], [10, 10],
                        [1, 5], [0, 10]]
        complex_poly = [[0, 0], [12, 12], [10, 0], [5, 5], [3, 18], [4, 15],
                        [5, 7], [10, 10], [0, 10], [16, 12]]

        assert not is_complex(concave_poly)
        assert is_complex(complex_poly)

################################################################################


if __name__ == "__main__":
    # _intersection_bug_20081110_TR_BL')
    suite = unittest.makeSuite(Test_Polygon, 'test')
    runner = unittest.TextTestRunner()
    runner.run(suite)
