DMS::format()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 42
rs 9.248
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Location\Formatter\Coordinate;
6
7
use InvalidArgumentException;
8
use Location\Coordinate;
9
10
/**
11
 * Coordinate Formatter "DMS"
12
 *
13
 * @author Marcus Jaschen <[email protected]>
14
 */
15
class DMS implements FormatterInterface
16
{
17
    public const UNITS_UTF8  = 'UTF-8';
18
    public const UNITS_ASCII = 'ASCII';
19
20
    /**
21
     * @var string Separator string between latitude and longitude
22
     */
23
    protected $separator;
24
25
    /**
26
     * Use cardinal letters for N/S and W/E instead of minus sign
27
     *
28
     * @var bool
29
     */
30
    protected $useCardinalLetters;
31
32
    /**
33
     * @var string
34
     *
35
     * @psalm-suppress PropertyNotSetInConstructor
36
     */
37
    protected $unitType;
38
39
    /**
40
     * @var array
41
     */
42
    protected $units = [
43
        'UTF-8' => [
44
            'deg' => '°',
45
            'min' => '′',
46
            'sec' => '″',
47
        ],
48
        'ASCII' => [
49
            'deg' => '°',
50
            'min' => '\'',
51
            'sec' => '"',
52
        ],
53
    ];
54
55
    /**
56
     * @param string $separator
57
     *
58
     * @throws InvalidArgumentException
59
     */
60
    public function __construct(string $separator = ' ')
61
    {
62
        $this->separator          = $separator;
63
        $this->useCardinalLetters = false;
64
        $this->setUnits(static::UNITS_UTF8);
65
    }
66
67
    /**
68
     * Sets the separator between latitude and longitude values
69
     *
70
     * @param string $separator
71
     *
72
     * @return DMS
73
     */
74
    public function setSeparator(string $separator): DMS
75
    {
76
        $this->separator = $separator;
77
78
        return $this;
79
    }
80
81
    /**
82
     * @param bool $value
83
     *
84
     * @return DMS
85
     */
86
    public function useCardinalLetters(bool $value): DMS
87
    {
88
        $this->useCardinalLetters = $value;
89
90
        return $this;
91
    }
92
93
    /**
94
     * @param string $type
95
     *
96
     * @return DMS
97
     * @throws InvalidArgumentException
98
     */
99
    public function setUnits(string $type): DMS
100
    {
101
        if (! array_key_exists($type, $this->units)) {
102
            throw new InvalidArgumentException('Invalid unit type');
103
        }
104
105
        $this->unitType = $type;
106
107
        return $this;
108
    }
109
110
    /**
111
     * @return string
112
     */
113
    public function getUnitType(): string
114
    {
115
        return $this->unitType;
116
    }
117
118
    /**
119
     * @param Coordinate $coordinate
120
     *
121
     * @return string
122
     */
123
    public function format(Coordinate $coordinate): string
124
    {
125
        $lat = $coordinate->getLat();
126
        $lng = $coordinate->getLng();
127
128
        $latValue   = abs($lat);
129
        $latDegrees = (int)$latValue;
130
131
        $latMinutesDecimal = $latValue - $latDegrees;
132
        $latMinutes        = (int)(60 * $latMinutesDecimal);
133
134
        $latSeconds = 60 * (60 * $latMinutesDecimal - $latMinutes);
135
136
        $lngValue   = abs($lng);
137
        $lngDegrees = (int)$lngValue;
138
139
        $lngMinutesDecimal = $lngValue - $lngDegrees;
140
        $lngMinutes        = (int)(60 * $lngMinutesDecimal);
141
142
        $lngSeconds = 60 * (60 * $lngMinutesDecimal - $lngMinutes);
143
144
        return sprintf(
145
            '%s%02d%s %02d%s %02d%s%s%s%s%03d%s %02d%s %02d%s%s',
146
            $this->getLatPrefix($lat),
147
            abs($latDegrees),
148
            $this->units[$this->unitType]['deg'],
149
            $latMinutes,
150
            $this->units[$this->unitType]['min'],
151
            round($latSeconds, 0),
152
            $this->units[$this->unitType]['sec'],
153
            $this->getLatSuffix($lat),
154
            $this->separator,
155
            $this->getLngPrefix($lng),
156
            abs($lngDegrees),
157
            $this->units[$this->unitType]['deg'],
158
            $lngMinutes,
159
            $this->units[$this->unitType]['min'],
160
            round($lngSeconds, 0),
161
            $this->units[$this->unitType]['sec'],
162
            $this->getLngSuffix($lng)
163
        );
164
    }
165
166
    /**
167
     * @param float $lat
168
     *
169
     * @return string
170
     */
171
    protected function getLatPrefix(float $lat): string
172
    {
173
        if ($this->useCardinalLetters || $lat >= 0) {
174
            return '';
175
        }
176
177
        return '-';
178
    }
179
180
    /**
181
     * @param float $lng
182
     *
183
     * @return string
184
     */
185
    protected function getLngPrefix(float $lng): string
186
    {
187
        if ($this->useCardinalLetters || $lng >= 0) {
188
            return '';
189
        }
190
191
        return '-';
192
    }
193
194
    /**
195
     * @param float $lat
196
     *
197
     * @return string
198
     */
199
    protected function getLatSuffix(float $lat): string
200
    {
201
        if (! $this->useCardinalLetters) {
202
            return '';
203
        }
204
205
        if ($lat >= 0) {
206
            return ' N';
207
        }
208
209
        return ' S';
210
    }
211
212
    /**
213
     * @param float $lng
214
     *
215
     * @return string
216
     */
217
    protected function getLngSuffix(float $lng): string
218
    {
219
        if (! $this->useCardinalLetters) {
220
            return '';
221
        }
222
223
        if ($lng >= 0) {
224
            return ' E';
225
        }
226
227
        return ' W';
228
    }
229
}
230