Passed
Push — php71 ( 95a24c...2c708a )
by Jeroen De
07:19
created

GlobeCoordinateParser::detectDmsPrecision()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 10
ccs 6
cts 6
cp 1
rs 9.4285
cc 2
eloc 6
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\ValueParser;
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-or-later
19
 * @author Jeroen De Dauw < [email protected] >
20
 * @author H. Snater < [email protected] >
21
 * @author Thiemo Kreuz
22
 */
23
class GlobeCoordinateParser implements ValueParser {
24
25
	public 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
	public const OPT_GLOBE = 'globe';
32
33
	private $options;
34
35 122
	public function __construct( ParserOptions $options = null ) {
36 122
		$this->options = $options ?: new ParserOptions();
37
38 122
		$this->options->defaultOption( ValueParser::OPT_LANG, 'en' );
39 122
		$this->options->defaultOption( self::OPT_GLOBE, 'http://www.wikidata.org/entity/Q2' );
40 122
	}
41
42
	/**
43
	 * @see StringValueParser::stringParse
44
	 *
45
	 * @param string $value
46
	 *
47
	 * @throws ParseException
48
	 * @return GlobeCoordinateValue
49
	 */
50 122
	public function parse( $value ): GlobeCoordinateValue {
51 122
		foreach ( $this->getParsers() as $precisionDetector => $parser ) {
52
			try {
53 122
				$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->options->getOption( self::OPT_GLOBE )
62
				);
63 89
			} catch ( ParseException $parseException ) {
64 89
				continue;
65
			}
66
		}
67
68 5
		throw new ParseException(
69 5
			'The format of the coordinate could not be determined.',
70 5
			$value,
71 5
			self::FORMAT_NAME
72
		);
73
	}
74
75 117
	private function detectPrecision( LatLongValue $latLong, string $precisionDetector ): float {
76 117
		if ( $this->options->hasOption( 'precision' ) ) {
77
			return $this->options->getOption( 'precision' );
78
		}
79
80 117
		return min(
81 117
			call_user_func( [ $this, $precisionDetector ], $latLong->getLatitude() ),
82 117
			call_user_func( [ $this, $precisionDetector ], $latLong->getLongitude() )
83
		);
84
	}
85
86
	/**
87
	 * @return ValueParser[]
88
	 */
89 122
	private function getParsers(): array {
90 122
		$parsers = [];
91
92 122
		$parsers['detectFloatPrecision'] = new FloatCoordinateParser( $this->options );
93 122
		$parsers['detectDmsPrecision'] = new DmsCoordinateParser( $this->options );
94 122
		$parsers['detectDmPrecision'] = new DmCoordinateParser( $this->options );
95 122
		$parsers['detectDdPrecision'] = new DdCoordinateParser( $this->options );
96
97 122
		return $parsers;
98
	}
99
100 29
	private function detectDdPrecision( float $degree ): float {
101 29
		return $this->detectFloatPrecision( $degree );
102
	}
103
104 25
	private function detectDmPrecision( float $degree ): float {
105 25
		$minutes = $degree * 60;
106 25
		$split = explode( '.', round( $minutes, 6 ) );
107
108 25
		if ( isset( $split[1] ) ) {
109 12
			return $this->detectDmsPrecision( $degree );
110
		}
111
112 13
		return 1 / 60;
113
	}
114
115 42
	private function detectDmsPrecision( float $degree ): float {
116 42
		$seconds = $degree * 3600;
117 42
		$split = explode( '.', round( $seconds, 4 ) );
118
119 42
		if ( isset( $split[1] ) ) {
120 16
			return pow( 10, -strlen( $split[1] ) ) / 3600;
121
		}
122
123 26
		return 1 / 3600;
124
	}
125
126 62
	private function detectFloatPrecision( float $degree ): float {
127 62
		$split = explode( '.', round( $degree, 8 ) );
128
129 62
		if ( isset( $split[1] ) ) {
130 52
			return pow( 10, -strlen( $split[1] ) );
131
		}
132
133 34
		return 1;
134
	}
135
136
}
137