Passed
Push — php73 ( 4b71d8 )
by Jeroen De
05:36
created

GlobeCoordinateParser::detectFloatPrecision()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 5
cts 5
cp 1
rs 9.9666
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 2
1
<?php
2
3
declare( strict_types = 1 );
4
5
namespace DataValues\Geo\Parsers;
6
7
use DataValues\Geo\Values\GlobeCoordinateValue;
8
use DataValues\Geo\Values\LatLongValue;
9
use ValueParsers\ParseException;
10
use ValueParsers\ParserOptions;
11
use ValueParsers\ValueParser;
12
13
/**
14
 * Extends the LatLongParser by adding precision detection support.
15
 *
16
 * The object that gets constructed is a GlobeCoordinateValue rather then a LatLongValue.
17
 *
18
 * @since 0.1
19
 *
20
 * @license GPL-2.0-or-later
21
 * @author Jeroen De Dauw < [email protected] >
22
 * @author H. Snater < [email protected] >
23
 * @author Thiemo Kreuz
24
 */
25
class GlobeCoordinateParser implements ValueParser {
26
27
	public const FORMAT_NAME = 'globe-coordinate';
28
29
	/**
30
	 * Option specifying the globe. Should be a string containing a Wikidata concept URI. Defaults
31
	 * to Earth.
32
	 */
33
	public const OPT_GLOBE = 'globe';
34
35
	private $options;
36
37 122
	public function __construct( ParserOptions $options = null ) {
38 122
		$this->options = $options ?: new ParserOptions();
39
40 122
		$this->options->defaultOption( ValueParser::OPT_LANG, 'en' );
41 122
		$this->options->defaultOption( self::OPT_GLOBE, 'http://www.wikidata.org/entity/Q2' );
42 122
	}
43
44
	/**
45
	 * @see StringValueParser::stringParse
46
	 *
47
	 * @param string $value
48
	 *
49
	 * @throws ParseException
50
	 * @return GlobeCoordinateValue
51
	 */
52 122
	public function parse( $value ): GlobeCoordinateValue {
53 122
		foreach ( $this->getParsers() as $precisionDetector => $parser ) {
54
			try {
55 122
				$latLong = $parser->parse( $value );
56
57 117
				return new GlobeCoordinateValue(
58 117
					new LatLongValue(
59 117
						$latLong->getLatitude(),
60 117
						$latLong->getLongitude()
61
					),
62 117
					$this->detectPrecision( $latLong, $precisionDetector ),
63 117
					$this->options->getOption( self::OPT_GLOBE )
64
				);
65 89
			} catch ( ParseException $parseException ) {
66 89
				continue;
67
			}
68
		}
69
70 5
		throw new ParseException(
71 5
			'The format of the coordinate could not be determined.',
72
			$value,
73 5
			self::FORMAT_NAME
74
		);
75
	}
76
77 117
	private function detectPrecision( LatLongValue $latLong, string $precisionDetector ): float {
78 117
		if ( $this->options->hasOption( 'precision' ) ) {
79
			return $this->options->getOption( 'precision' );
80
		}
81
82 117
		return min(
83 117
			call_user_func( [ $this, $precisionDetector ], $latLong->getLatitude() ),
84 117
			call_user_func( [ $this, $precisionDetector ], $latLong->getLongitude() )
85
		);
86
	}
87
88
	/**
89
	 * @return ValueParser[]
90
	 */
91 122
	private function getParsers(): array {
92 122
		$parsers = [];
93
94 122
		$parsers['detectFloatPrecision'] = new FloatCoordinateParser( $this->options );
95 122
		$parsers['detectDmsPrecision'] = new DmsCoordinateParser( $this->options );
96 122
		$parsers['detectDmPrecision'] = new DmCoordinateParser( $this->options );
97 122
		$parsers['detectDdPrecision'] = new DdCoordinateParser( $this->options );
98
99 122
		return $parsers;
100
	}
101
102 29
	private function detectDdPrecision( float $degree ): float {
103 29
		return $this->detectFloatPrecision( $degree );
104
	}
105
106 25
	private function detectDmPrecision( float $degree ): float {
107 25
		$minutes = $degree * 60;
108 25
		$split = explode( '.', (string)round( $minutes, 6 ) );
109
110 25
		if ( isset( $split[1] ) ) {
111 12
			return $this->detectDmsPrecision( $degree );
112
		}
113
114 13
		return 1 / 60;
115
	}
116
117 42
	private function detectDmsPrecision( float $degree ): float {
118 42
		$seconds = $degree * 3600;
119 42
		$split = explode( '.', (string)round( $seconds, 4 ) );
120
121 42
		if ( isset( $split[1] ) ) {
122 16
			return pow( 10, -strlen( $split[1] ) ) / 3600;
123
		}
124
125 26
		return 1 / 3600;
126
	}
127
128 62
	private function detectFloatPrecision( float $degree ): float {
129 62
		$split = explode( '.', (string)round( $degree, 8 ) );
130
131 62
		if ( isset( $split[1] ) ) {
132 52
			return pow( 10, -strlen( $split[1] ) );
133
		}
134
135 34
		return 1;
136
	}
137
138
}
139