Completed
Push — zeroRounding ( 491d2a )
by no
05:41
created

DmCoordinateParser::parseCoordinate()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 30
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 4.2084

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 30
ccs 13
cts 17
cp 0.7647
rs 8.5806
cc 4
eloc 17
nc 6
nop 1
crap 4.2084
1
<?php
2
3
namespace DataValues\Geo\Parsers;
4
5
use ValueParsers\ParseException;
6
use ValueParsers\ParserOptions;
7
8
/**
9
 * Parser for geographical coordinates in Decimal Minute notation.
10
 *
11
 * @since 0.1
12
 *
13
 * @license GPL-2.0+
14
 * @author Jeroen De Dauw < [email protected] >
15
 * @author H. Snater < [email protected] >
16
 */
17
class DmCoordinateParser extends DdCoordinateParser {
18
19
	const FORMAT_NAME = 'dm-coordinate';
20
21
	/**
22
	 * The symbols representing minutes.
23
	 * @since 0.1
24
	 */
25
	const OPT_MINUTE_SYMBOL = 'minute';
26
27
	/**
28
	 * @param ParserOptions|null $options
29
	 */
30 25
	public function __construct( ParserOptions $options = null ) {
31 25
		parent::__construct( $options );
32
33 25
		$this->defaultOption( self::OPT_MINUTE_SYMBOL, "'" );
34
35 25
		$this->defaultDelimiters = array( $this->getOption( self::OPT_MINUTE_SYMBOL ) );
0 ignored issues
show
Documentation Bug introduced by
It seems like array($this->getOption(self::OPT_MINUTE_SYMBOL)) of type array<integer,*,{"0":"*"}> is incompatible with the declared type array<integer,string> of property $defaultDelimiters.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
36 25
	}
37
38
	/**
39
	 * @see LatLongParserBase::areValidCoordinates
40
	 *
41
	 * @param string[] $normalizedCoordinateSegments
42
	 *
43
	 * @return bool
44
	 */
45 18
	protected function areValidCoordinates( array $normalizedCoordinateSegments ) {
46
		// At least one coordinate segment needs to have minutes specified.
47
		$regExpStrict = '\d{1,3}'
48 18
			. preg_quote( $this->getOption( self::OPT_DEGREE_SYMBOL ) )
49
			// TODO: Implement localized decimal separator.
50 18
			. '(\d{1,2}(\.\d{1,20})?'
51 18
			. preg_quote( $this->getOption( self::OPT_MINUTE_SYMBOL ) )
52 18
			. ')';
53 18
		$regExpLoose = $regExpStrict . '?';
54
55
		// Cache whether minutes have been detected within the coordinate:
56 18
		$detectedMinute = false;
57
58
		// Cache whether the coordinates are specified in directional format (a mixture of
59
		// directional and non-directional is regarded invalid).
60 18
		$directional = false;
61
62 18
		foreach ( $normalizedCoordinateSegments as $i => $segment ) {
63
			$direction = '('
64 18
				. $this->getOption( self::OPT_NORTH_SYMBOL ) . '|'
65 18
				. $this->getOption( self::OPT_SOUTH_SYMBOL ) . ')';
66
67 18
			if ( $i === 1 ) {
68
				$direction = '('
69 16
					. $this->getOption( self::OPT_EAST_SYMBOL ) . '|'
70 16
					. $this->getOption( self::OPT_WEST_SYMBOL ) . ')';
71
			}
72
73 18
			$match = preg_match(
74 18
				'/^(' . $regExpStrict . $direction . '|' . $direction . $regExpStrict . ')$/i',
75 18
				$segment
76
			);
77
78 18
			if ( $match ) {
79 6
				$detectedMinute = true;
80
			} else {
81 12
				$match = preg_match(
82 12
					'/^(' . $regExpLoose . $direction . '|' . $direction . $regExpLoose . ')$/i',
83 12
					$segment
84
				);
85
			}
86
87 18
			if ( $match ) {
88 6
				$directional = true;
89 12
			} elseif ( !$directional ) {
90 12
				$match = preg_match( '/^(-)?' . $regExpStrict . '$/i', $segment );
91
92 12
				if ( $match ) {
93 10
					$detectedMinute = true;
94
				} else {
95 2
					$match = preg_match( '/^(-)?' . $regExpLoose . '$/i', $segment );
96
				}
97
			}
98
99 18
			if ( !$match ) {
100 18
				return false;
101
			}
102
		}
103
104 16
		return $detectedMinute;
105
	}
106
107
	/**
108
	 * @see DdCoordinateParser::getNormalizedNotation
109
	 *
110
	 * @param string $coordinates
111
	 *
112
	 * @return string
113
	 */
114 18
	protected function getNormalizedNotation( $coordinates ) {
115 18
		$minute = $this->getOption( self::OPT_MINUTE_SYMBOL );
116
117 18
		$coordinates = str_replace( array( '&#8242;', '&prime;', '´', '′' ), $minute, $coordinates );
118
119 18
		$coordinates = parent::getNormalizedNotation( $coordinates );
120
121 18
		$coordinates = $this->removeInvalidChars( $coordinates );
122
123 18
		return $coordinates;
124
	}
125
126
	/**
127
	 * @see DdCoordinateParser::parseCoordinate
128
	 *
129
	 * @param string $coordinateSegment
130
	 *
131
	 * @return float
132
	 */
133 16
	protected function parseCoordinate( $coordinateSegment ) {
134 16
		$isNegative = substr( $coordinateSegment, 0, 1 ) === '-';
135
136 16
		if ( $isNegative ) {
137 5
			$coordinateSegment = substr( $coordinateSegment, 1 );
138
		}
139
140 16
		$degreeSymbol = $this->getOption( self::OPT_DEGREE_SYMBOL );
141 16
		$exploded = explode( $degreeSymbol, $coordinateSegment );
142
143 16
		if ( count( $exploded ) !== 2 ) {
144
			throw new ParseException(
145
				'Unable to explode coordinate segment by degree symbol (' . $degreeSymbol . ')',
146
				$coordinateSegment,
147
				self::FORMAT_NAME
148
			);
149
		}
150
151 16
		list( $degrees, $minutes ) = $exploded;
152
153 16
		$minutes = substr( $minutes, 0, -1 );
154
155 16
		$coordinateSegment = $degrees + $minutes / 60;
156
157 16
		if ( $isNegative ) {
158 5
			$coordinateSegment *= -1;
159
		}
160
161 16
		return (float)$coordinateSegment;
162
	}
163
164
}
165