Passed
Push — master ( 954049...2e99dd )
by Leszek
07:28
created

GlobeCoordinateParser::detectPrecision()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.0185

Importance

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