Completed
Push — master ( eedcfb...3a03fe )
by Marcus
02:26
created

Coordinate::hasSameLocation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Location;
6
7
use InvalidArgumentException;
8
use Location\Distance\DistanceInterface;
9
use Location\Distance\Haversine;
10
use Location\Formatter\Coordinate\FormatterInterface;
11
12
/**
13
 * Coordinate Implementation
14
 *
15
 * @author Marcus Jaschen <[email protected]>
16
 */
17
class Coordinate implements GeometryInterface
18
{
19
    /**
20
     * @var float
21
     */
22
    protected $lat;
23
24
    /**
25
     * @var float
26
     */
27
    protected $lng;
28
29
    /**
30
     * @var Ellipsoid
31
     */
32
    protected $ellipsoid;
33
34
    /**
35
     * @param float $lat -90.0 .. +90.0
36
     * @param float $lng -180.0 .. +180.0
37
     * @param Ellipsoid $ellipsoid if omitted, WGS-84 is used
38
     *
39
     * @throws \InvalidArgumentException
40
     */
41
    public function __construct(float $lat, float $lng, Ellipsoid $ellipsoid = null)
42
    {
43
        if (! $this->isValidLatitude($lat)) {
44
            throw new InvalidArgumentException('Latitude value must be numeric -90.0 .. +90.0 (given: ' . $lat . ')');
45
        }
46
47
        if (! $this->isValidLongitude($lng)) {
48
            throw new InvalidArgumentException(
49
                'Longitude value must be numeric -180.0 .. +180.0 (given: ' . $lng . ')'
50
            );
51
        }
52
53
        $this->lat = $lat;
54
        $this->lng = $lng;
55
56
        if ($ellipsoid instanceof Ellipsoid) {
57
            $this->ellipsoid = $ellipsoid;
58
59
            return;
60
        }
61
62
        $this->ellipsoid = Ellipsoid::createDefault();
63
    }
64
65
    /**
66
     * @return float
67
     */
68
    public function getLat(): float
69
    {
70
        return $this->lat;
71
    }
72
73
    /**
74
     * @return float
75
     */
76
    public function getLng(): float
77
    {
78
        return $this->lng;
79
    }
80
81
    /**
82
     * Returns an array containing the point
83
     *
84
     * @return Coordinate[]
85
     */
86
    public function getPoints(): array
87
    {
88
        return [$this];
89
    }
90
91
    /**
92
     * @return Ellipsoid
93
     */
94
    public function getEllipsoid(): Ellipsoid
95
    {
96
        return $this->ellipsoid;
97
    }
98
99
    /**
100
     * Calculates the distance between the given coordinate
101
     * and this coordinate.
102
     *
103
     * @param Coordinate $coordinate
104
     * @param DistanceInterface $calculator instance of distance calculation class
105
     *
106
     * @return float
107
     */
108
    public function getDistance(Coordinate $coordinate, DistanceInterface $calculator): float
109
    {
110
        return $calculator->getDistance($this, $coordinate);
111
    }
112
113
    /**
114
     * Checks if two points describe the same location within an allowed distance.
115
     *
116
     * Uses the Haversine distance calculator for distance calculation as it's
117
     * precise enough for short-distance calculations.
118
     *
119
     * @param Coordinate $coordinate
120
     * @param float $allowedDistance the default value is one millimeter.
121
     *
122
     * @return bool
123
     *
124
     * @see Haversine
125
     */
126
    public function hasSameLocation(Coordinate $coordinate, float $allowedDistance = .001): bool
127
    {
128
        return $this->getDistance($coordinate, new Haversine()) <= $allowedDistance;
129
    }
130
131
    /**
132
     * @param FormatterInterface $formatter
133
     *
134
     * @return mixed
135
     */
136
    public function format(FormatterInterface $formatter)
137
    {
138
        return $formatter->format($this);
139
    }
140
141
    /**
142
     * Validates latitude
143
     *
144
     * @param float $latitude
145
     *
146
     * @return bool
147
     */
148
    protected function isValidLatitude(float $latitude): bool
149
    {
150
        return $this->isNumericInBounds($latitude, -90.0, 90.0);
151
    }
152
153
    /**
154
     * Validates longitude
155
     *
156
     * @param float $longitude
157
     *
158
     * @return bool
159
     */
160
    protected function isValidLongitude(float $longitude): bool
161
    {
162
        return $this->isNumericInBounds($longitude, -180.0, 180.0);
163
    }
164
165
    /**
166
     * Checks if the given value is (1) numeric, and (2) between lower
167
     * and upper bounds (including the bounds values).
168
     *
169
     * @param float $value
170
     * @param float $lower
171
     * @param float $upper
172
     *
173
     * @return bool
174
     */
175
    protected function isNumericInBounds(float $value, float $lower, float $upper): bool
176
    {
177
        return !($value < $lower || $value > $upper);
178
    }
179
}
180