Passed
Branch master (480142)
by Bobby
10:44
created

Calculator::resetPoints()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 1
rs 10
1
<?php
2
namespace Ballen\Distical;
3
4
use Ballen\Distical\Entities\LatLong;
5
use Ballen\Distical\Entities\Distance;
6
7
/**
8
 * Distical
9
 *
10
 * Distical is a simple distance calculator library for PHP 5.3+ which
11
 * amongst other things can calculate the distance between two or more lat/long
12
 * co-ordinates.
13
 *
14
 * @author Bobby Allen <[email protected]>
15
 * @license http://opensource.org/licenses/MIT
16
 * @link https://github.com/allebb/distical
17
 * @link http://bobbyallen.me
18
 *
19
 */
20
class Calculator
21
{
22
23
    /**
24
     * Stores the earth's mean radius, used by the calculate() method.
25
     */
26
    const MEAN_EARTH_RADIUS = 6372.797;
27
28
    /**
29
     * LatLon points to measure between.
30
     * @var Entities\LatLong[]
31
     */
32
    private $points;
33
34
    /**
35
     * The constructor
36
     * @param Entities\LatLong $pointA Optional initial point.
37
     * @param Entities\LatLong $pointB Optional final point.
38
     */
39 22
    public function __construct($pointA = null, $pointB = null)
40
    {
41 22
        if (($pointA instanceof LatLong) && ($pointB instanceof LatLong)) {
42 2
            $this->between($pointA, $pointB);
43
        }
44
    }
45
46
    /**
47
     * Adds a new lat/long co-ordinate to measure.
48
     * @param LatLong $point The LatLong co-ordinate object.
49
     * @param string $key Optional co-ordinate key (name).
50
     * @return \Ballen\Distical\Calculator
51
     */
52 22
    public function addPoint(LatLong $point, $key = null)
53
    {
54 22
        if (is_null($key)) {
55 19
            $this->points[] = $point;
56 19
            return $this;
57
        }
58 3
        $this->points[$key] = $point;
59 3
        return $this;
60
    }
61
62
    /**
63
     * Remove a lat/long co-ordinate from the points collection.
64
     * @param int|string $key The name or ID of the point key.
65
     * @throws \InvalidArgumentException
66
     * @return \Ballen\Distical\Calculator
67
     */
68 2
    public function removePoint($key = null)
69
    {
70 2
        if (isset($this->points[$key])) {
71 1
            unset($this->points[$key]);
72 1
            return $this;
73
        }
74 1
        throw new \InvalidArgumentException('The point key does not exist.');
75
    }
76
77
    /**
78
     * Resets the points collection.
79
     * @return \Ballen\Distical\Calculator
80
     */
81 3
    public function resetPoints()
82
    {
83 3
        $this->points = [];
84 3
        return $this;        
85
    }
86
87
    /**
88
     * Helper method to get distance between two points.
89
     * @param LatLong $pointA Point A (eg. Departure point)
90
     * @param LatLong $pointB Point B (eg. Arrival point)
91
     * @return \Ballen\Distical\Calculator
92
     * @throws \RuntimeException
93
     */
94 11
    public function between(LatLong $pointA, LatLong $pointB)
95
    {
96 11
        if (!empty($this->points)) {
97 1
            throw new \RuntimeException('The between() method can only be called when it is the first set or co-ordinates.');
98
        }
99 10
        $this->addPoint($pointA);
100 10
        $this->addPoint($pointB);
101 10
        return $this;
102
    }
103
104
    /**
105
     * Calculates the distance between two lat/lng posistions.
106
     * @param LatLong $pointA Point A (eg. Departure point)
107
     * @param LatLong $pointB Point B (eg. Arrival point)
108
     * @return double
109
     */
110 18
    private function distanceBetweenPoints(LatLong $pointA, LatLong $pointB)
111
    {
112 18
        $pi180 = M_PI / 180;
113 18
        $latA = $pointA->lat() * $pi180;
114 18
        $lngA = $pointA->lng() * $pi180;
115 18
        $latB = $pointB->lat() * $pi180;
116 18
        $lngB = $pointB->lng() * $pi180;
117 18
        $dlat = $latB - $latA;
118 18
        $dlng = $lngB - $lngA;
119 18
        $calcA = sin($dlat / 2) * sin($dlat / 2) + cos($latA) * cos($latB) * sin($dlng / 2) * sin($dlng / 2);
120 18
        $calcB = 2 * atan2(sqrt($calcA), sqrt(1 - $calcA));
121 18
        return self::MEAN_EARTH_RADIUS * $calcB;
122
    }
123
124
    /**
125
     * Calculates the distance between each of the points.
126
     * @param bool $shouldResetPoints Clears points collection after calculating distance if true
127
     * @return double Distance in kilometres.
128
     */
129 20
    private function calculate(bool $shouldResetPoints = false)
130
    {
131 20
        if (count($this->points) < 2) {
132 5
            throw new \RuntimeException('There must be two or more points (co-ordinates) before a calculation can be performed.');
133
        }
134 18
        $total = 0;
135 18
        foreach ($this->points as $point) {
136 18
            if (isset($previous)) {
137 18
                $total += $this->distanceBetweenPoints($previous, $point);
138
            }
139 18
            $previous = $point;
140
        }
141
142 18
        if ($shouldResetPoints === true) {
143 1
            $this->resetPoints();
144
        }
145
        
146 18
        return $total;
147
    }
148
149
    /**
150
     * Returns the total distance between the two lat/lng points.
151
     * @param bool $shouldResetPoints Resets points collection after calculating distance if true
152
     * @return Distance
153
     */
154 20
    public function get(bool $shouldResetPoints = false)
155
    {
156 20
        return new Distance($this->calculate($shouldResetPoints));
157
    }
158
}
159