1#!/usr/bin/python3
2#
3# Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
4# Copyright (C) 2019, Sam Thursfield <sam@afuera.me.uk>
5#
6# This program is free software; you can redistribute it and/or
7# modify it under the terms of the GNU General Public License
8# as published by the Free Software Foundation; either version 2
9# of the License, or (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19# 02110-1301, USA.
20#
21"""
22Test the distance-calculation functions in Sparql. Only requires the Store
23"""
24import unittest as ut
25from storetest import CommonTrackerStoreTest as CommonTrackerStoreTest
26
27POINT_COORDS = [
28    (0, 0), (1, 1), (2, 2), (3, 3), (4, 4)
29]
30
31
32class TestDistanceFunctions (CommonTrackerStoreTest):
33    """
34    Insert some points and get the distance between them.
35    """
36
37    def setUp(self):
38        self.counter = 0
39        for lat, log in POINT_COORDS:
40            insert = """
41            INSERT {
42            <%s> a mlo:GeoPoint ;
43                mlo:longitude %d ;
44                mlo:latitude %d .
45            }
46            """ % ("point://test/point/" + str(self.counter), log, lat)
47            self.tracker.update(insert)
48            self.counter += 1
49
50    def tearDown(self):
51        for i in range(0, self.counter):
52            delete = """
53            DELETE {
54            <%s> a rdfs:Resource.
55            }
56            """ % ("point://test/point/" + str (i))
57            self.tracker.update(delete)
58
59    def get_distance_between_points(self, sum_func, id1, id2):
60
61        assert 0 <= id1 <= len(POINT_COORDS)
62        assert 0 <= id2 <= len(POINT_COORDS)
63        assert sum_func == "cartesian" or sum_func == "haversine"
64
65        query_1_to_2 = """
66        SELECT xsd:integer(tracker:%s-distance(?lat1,?lat2,?lon1,?lon2))
67        WHERE {
68          <point://test/point/%d> a mlo:GeoPoint ;
69             mlo:latitude ?lat1 ;
70             mlo:longitude ?lon1 .
71
72          <point://test/point/%d> a mlo:GeoPoint ;
73             mlo:latitude ?lat2 ;
74             mlo:longitude ?lon2 .
75        }
76        """ % (sum_func, id1, id2)
77        result = self.tracker.query(query_1_to_2)
78        return int(result[0][0])
79
80    def test_distance_cartesian_symmetry(self):
81        """
82        setUp: Insert 5 points in the pre-defined coordinates
83        1. TEST: Check cartesian distance from point A to B, and from B to A
84                 (should be the same :P)
85        tearDown: Remove the test points inserted before
86        """
87        a_to_b = self.get_distance_between_points("cartesian", 1, 2)
88        assert a_to_b == 204601
89
90        b_to_a = self.get_distance_between_points("cartesian", 2, 1)
91        assert b_to_a == 204601
92
93        assert a_to_b == b_to_a
94
95    def test_distance_haversine_symmetry(self):
96        """
97        setUp: Insert 5 points in the pre-defined coordinates
98        1. TEST: Check cartesian distance from point A to B, and from B to A
99                 (should be the same :P)
100        tearDown: Remove the test points inserted before
101        """
102        a_to_b = self.get_distance_between_points("haversine", 1, 2)
103        assert a_to_b == 157225
104
105        b_to_a = self.get_distance_between_points("haversine", 2, 1)
106        assert b_to_a == 157225
107
108        assert a_to_b == b_to_a
109
110    def test_distance_cartesian_proportion(self):
111        d_1_to_2 = self.get_distance_between_points("cartesian", 1, 2)
112        d_2_to_3 = self.get_distance_between_points("cartesian", 2, 3)
113        d_3_to_4 = self.get_distance_between_points("cartesian", 3, 4)
114        assert d_1_to_2 > d_2_to_3 > d_3_to_4
115
116    def test_distance_haversine_proportion(self):
117        d_1_to_2 = self.get_distance_between_points("haversine", 1, 2)
118        d_2_to_3 = self.get_distance_between_points("haversine", 2, 3)
119        d_3_to_4 = self.get_distance_between_points("haversine", 3, 4)
120        assert d_1_to_2 > d_2_to_3 > d_3_to_4
121
122    def test_distance_different(self):
123        d_2_to_3h = self.get_distance_between_points("haversine", 2, 3)
124        d_2_to_3c = self.get_distance_between_points("cartesian", 2, 3)
125        assert d_2_to_3h < d_2_to_3c
126
127
128if __name__ == '__main__':
129    ut.main()
130