Completed
Push — dropOldAliases ( 17840d...e22dca )
by Jeroen De
12:52 queued 07:28
created

GlobeCoordinateParser::parse()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 24
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 3

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 24
ccs 14
cts 14
cp 1
rs 8.9713
cc 3
eloc 16
nc 4
nop 1
crap 3
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 Kreuz
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 126
	public function __construct( ParserOptions $options = null ) {
37 126
		$this->options = $options ?: new ParserOptions();
38
39 126
		$this->options->defaultOption( ValueParser::OPT_LANG, 'en' );
40 126
		$this->options->defaultOption( self::OPT_GLOBE, 'http://www.wikidata.org/entity/Q2' );
41
	}
42
43
	/**
44
	 * @see StringValueParser::stringParse
45
	 *
46
	 * @param string $value
47
	 *
48
	 * @throws ParseException
49
	 * @return GlobeCoordinateValue
50 119
	 */
51 119
	public function parse( $value ) {
52
		foreach ( $this->getParsers() as $precisionDetector => $parser ) {
53 119
			try {
54
				$latLong = $parser->parse( $value );
55 117
56 117
				return new GlobeCoordinateValue(
57 117
					new LatLongValue(
58 117
						$latLong->getLatitude(),
59
						$latLong->getLongitude()
60 117
					),
61 117
					$this->detectPrecision( $latLong, $precisionDetector ),
62
					$this->options->getOption( self::OPT_GLOBE )
63 86
				);
64 86
			} catch ( ParseException $parseException ) {
65
				continue;
66
			}
67
		}
68 2
69 2
		throw new ParseException(
70 2
			'The format of the coordinate could not be determined.',
71 2
			$value,
72
			self::FORMAT_NAME
73
		);
74
	}
75
76
	/**
77
	 * @param LatLongValue $latLong
78
	 * @param string $precisionDetector
79
	 *
80
	 * @return float|int
81 117
	 */
82 117
	private function detectPrecision( LatLongValue $latLong, $precisionDetector ) {
83
		if ( $this->options->hasOption( 'precision' ) ) {
84
			return $this->options->getOption( 'precision' );
85
		}
86 117
87 117
		return min(
88 117
			call_user_func( [ $this, $precisionDetector ], $latLong->getLatitude() ),
89
			call_user_func( [ $this, $precisionDetector ], $latLong->getLongitude() )
90
		);
91
	}
92
93
	/**
94
	 * @return ValueParser[]
95 119
	 */
96 119
	private function getParsers() {
97
		$parsers = [];
98 119
99 119
		$parsers['detectFloatPrecision'] = new FloatCoordinateParser( $this->options );
100 119
		$parsers['detectDmsPrecision'] = new DmsCoordinateParser( $this->options );
101 119
		$parsers['detectDmPrecision'] = new DmCoordinateParser( $this->options );
102
		$parsers['detectDdPrecision'] = new DdCoordinateParser( $this->options );
103 119
104
		return $parsers;
105
	}
106
107
	/**
108
	 * @param float $degree
109
	 *
110
	 * @return float|int
111 29
	 */
112 29
	private function detectDdPrecision( $degree ) {
113
		return $this->detectFloatPrecision( $degree );
114
	}
115
116
	/**
117
	 * @param float $degree
118
	 *
119
	 * @return float|int
120 25
	 */
121 25
	private function detectDmPrecision( $degree ) {
122 25
		$minutes = $degree * 60;
123
		$split = explode( '.', round( $minutes, 6 ) );
124 25
125 12
		if ( isset( $split[1] ) ) {
126
			return $this->detectDmsPrecision( $degree );
127
		}
128 13
129
		return 1 / 60;
130
	}
131
132
	/**
133
	 * @param float $degree
134
	 *
135
	 * @return float|int
136 42
	 */
137 42
	private function detectDmsPrecision( $degree ) {
138 42
		$seconds = $degree * 3600;
139
		$split = explode( '.', round( $seconds, 4 ) );
140 42
141 16
		if ( isset( $split[1] ) ) {
142
			return pow( 10, -strlen( $split[1] ) ) / 3600;
143
		}
144 26
145
		return 1 / 3600;
146
	}
147
148
	/**
149
	 * @param float $degree
150
	 *
151
	 * @return float|int
152 62
	 */
153 62
	private function detectFloatPrecision( $degree ) {
154
		$split = explode( '.', round( $degree, 8 ) );
155 62
156 52
		if ( isset( $split[1] ) ) {
157
			return pow( 10, -strlen( $split[1] ) );
158
		}
159 34
160
		return 1;
161
	}
162
163
}
164