Issues (3)

src/Polarcy.php (3 issues)

1
<?php
2
3
namespace Hotrush\Polarcy;
4
5
class Polarcy
6
{
7
    /**
8
     * @var PointsCollection
9
     */
10
    private $pointsCollection;
11
12
    /**
13
     * @var Point
14
     */
15
    private $averagePoint;
16
17
    /**
18
     * @var float
19
     */
20
    private $accuracy;
21
22
    /**
23
     * @var float|int
24
     */
25
    private $maxDistance;
26
27
    /**
28
     * @var float|int
29
     */
30
    private $minPerPoint;
31
32
    /**
33
     * Polarcy constructor.
34
     *
35
     * @param PointsCollection $pointsCollection
36
     * @param null $maxDistance
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $maxDistance is correct as it would always require null to be passed?
Loading history...
37
     * @param null $minPerPoint
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $minPerPoint is correct as it would always require null to be passed?
Loading history...
38
     */
39
    public function __construct(PointsCollection $pointsCollection, $maxDistance = null, $minPerPoint = null)
40
    {
41
        if ($pointsCollection->total() < 2) {
42
            throw new \InvalidArgumentException('PointsCollection must have at least two points');
43
        }
44
45
        $this->pointsCollection = $pointsCollection;
46
        $this->maxDistance = $maxDistance;
47
        $this->minPerPoint = $minPerPoint ?: $this->minPerPoint();
0 ignored issues
show
$minPerPoint is of type null, thus it always evaluated to false.
Loading history...
48
    }
49
50
    /**
51
     * @return Point
52
     */
53
    public function averagePoint()
54
    {
55
        if (!$this->averagePoint) {
56
            $this->calculate();
57
        }
58
59
        return $this->averagePoint;
60
    }
61
62
    /**
63
     * @return float
64
     */
65
    public function accuracy()
66
    {
67
        if (!$this->accuracy) {
68
            $this->calculate();
69
        }
70
71
        return $this->accuracy;
72
    }
73
74
    /**
75
     * Run the calculations
76
     *
77
     * @return void
78
     */
79
    public function calculate()
80
    {
81
        $this->calculateAveragePoint();
82
        $this->calculateAccuracy();
83
    }
84
85
    /**
86
     * @return float|int
87
     */
88
    private function minPerPoint()
89
    {
90
        return 100 / pow($this->pointsCollection->total(), 2);
91
    }
92
93
    /**
94
     * @return void
95
     */
96
    private function calculateAveragePoint()
97
    {
98
        $xSum = array_sum(array_map(function (Point $point) {
99
            return $point->x();
100
        }, $this->pointsCollection->all()));
101
        $ySum = array_sum(array_map(function (Point $point) {
102
            return $point->y();
103
        }, $this->pointsCollection->all()));
104
105
        $this->averagePoint = new Point(
106
            $xSum / $this->pointsCollection->total(),
107
            $ySum / $this->pointsCollection->total()
108
        );
109
    }
110
111
    /**
112
     * @return void
113
     */
114
    private function calculateAccuracy()
115
    {
116
        $maxPerPoint = 100 / $this->pointsCollection->total();
117
        $distances = [];
118
119
        foreach ($this->pointsCollection->all() as $point) {
120
            $distances[] = Helpers::distanceBetweenPoints($point, $this->averagePoint);
121
        }
122
123
        if (!$this->maxDistance) {
124
            rsort($distances, SORT_NUMERIC);
125
            $this->maxDistance = $distances[0];
126
        }
127
128
        if ($this->maxDistance > 0) {
129
            foreach ($distances as $distance) {
130
                $remoteness = ($this->maxDistance - $distance) / $this->maxDistance;
131
                $this->accuracy += $remoteness > 0 ? $remoteness * $maxPerPoint : $this->minPerPoint;
132
            }
133
        } else {
134
            $this->accuracy = 100;
135
        }
136
    }
137
}