Passed
Push — rm-common ( 88d3f9 )
by Jeroen De
02:57
created

GlobeCoordinateParser::detectFloatPrecision()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

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