Passed
Push — master ( 9bfee7...008502 )
by no
03:35 queued 01:22
created

DmsCoordinateParser::areValidCoordinates()   C

Complexity

Conditions 8
Paths 33

Size

Total Lines 66
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 36
CRAP Score 8

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 66
ccs 36
cts 36
cp 1
rs 6.7081
cc 8
eloc 41
nc 33
nop 1
crap 8

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 Degree Minute Second notation.
10
 *
11
 * @since 0.1
12
 *
13
 * @license GPL-2.0-or-later
14
 * @author Jeroen De Dauw < [email protected] >
15
 * @author H. Snater < [email protected] >
16
 */
17
class DmsCoordinateParser extends DmCoordinateParser {
18
19
	const FORMAT_NAME = 'dms-coordinate';
20
21
	/**
22
	 * The symbol representing seconds.
23
	 * @since 0.1
24
	 */
25
	const OPT_SECOND_SYMBOL = 'second';
26
27
	/**
28
	 * @param ParserOptions|null $options
29
	 */
30 24
	public function __construct( ParserOptions $options = null ) {
31 24
		$options = $options ?: new ParserOptions();
32 24
		$options->defaultOption( self::OPT_SECOND_SYMBOL, '"' );
33
34 24
		parent::__construct( $options );
35
36 24
		$this->defaultDelimiters = [ $this->getOption( self::OPT_SECOND_SYMBOL ) ];
0 ignored issues
show
Documentation Bug introduced by
It seems like array($this->getOption(self::OPT_SECOND_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...
37 24
	}
38
39
	/**
40
	 * @see LatLongParserBase::areValidCoordinates
41
	 *
42
	 * @param string[] $normalizedCoordinateSegments
43
	 *
44
	 * @return bool
45
	 */
46 24
	protected function areValidCoordinates( array $normalizedCoordinateSegments ) {
47
		// At least one coordinate segment needs to have seconds specified (which additionally
48
		// requires minutes to be specified).
49
		$regExpLoose = '(\d{1,3}'
50 24
			. preg_quote( $this->getOption( self::OPT_DEGREE_SYMBOL ) )
51 24
			. ')(\d{1,2}'
52 24
			. preg_quote( $this->getOption( self::OPT_MINUTE_SYMBOL ) )
53 24
			. ')?((\d{1,2}'
54 24
			. preg_quote( $this->getOption( self::OPT_SECOND_SYMBOL ) )
55
			// TODO: Implement localized decimal separator.
56 24
			. ')?|(\d{1,2}\.\d{1,20}'
57 24
			. preg_quote( $this->getOption( self::OPT_SECOND_SYMBOL ) )
58 24
			. ')?)';
59 24
		$regExpStrict = str_replace( '?', '', $regExpLoose );
60
61
		// Cache whether seconds have been detected within the coordinate:
62 24
		$detectedSecond = false;
63
64
		// Cache whether the coordinates are specified in directional format (a mixture of
65
		// directional and non-directional is regarded invalid).
66 24
		$directional = false;
67
68 24
		foreach ( $normalizedCoordinateSegments as $i => $segment ) {
69
			$direction = '('
70 24
				. $this->getOption( self::OPT_NORTH_SYMBOL ) . '|'
71 24
				. $this->getOption( self::OPT_SOUTH_SYMBOL ) . ')';
72
73 24
			if ( $i === 1 ) {
74
				$direction = '('
75 19
					. $this->getOption( self::OPT_EAST_SYMBOL ) . '|'
76 19
					. $this->getOption( self::OPT_WEST_SYMBOL ) . ')';
77
			}
78
79 24
			$match = preg_match(
80 24
				'/^(' . $regExpStrict . $direction . '|' . $direction . $regExpStrict . ')$/i',
81 24
				$segment
82
			);
83
84 24
			if ( $match ) {
85 6
				$detectedSecond = true;
86
			} else {
87 18
				$match = preg_match(
88 18
					'/^(' . $regExpLoose . $direction . '|' . $direction . $regExpLoose . ')$/i',
89 18
					$segment
90
				);
91
			}
92
93 24
			if ( $match ) {
94 6
				$directional = true;
95 18
			} elseif ( !$directional ) {
96 18
				$match = preg_match( '/^(-)?' . $regExpStrict . '$/i', $segment );
97
98 18
				if ( $match ) {
99 13
					$detectedSecond = true;
100
				} else {
101 5
					$match = preg_match( '/^(-)?' . $regExpLoose . '$/i', $segment );
102
				}
103
			}
104
105 24
			if ( !$match ) {
106 24
				return false;
107
			}
108
		}
109
110 19
		return $detectedSecond;
111
	}
112
113
	/**
114
	 * @see DdCoordinateParser::getNormalizedNotation
115
	 *
116
	 * @param string $coordinates
117
	 *
118
	 * @return string
119
	 */
120 24
	protected function getNormalizedNotation( $coordinates ) {
121 24
		$second = $this->getOption( self::OPT_SECOND_SYMBOL );
122 24
		$minute = $this->getOption( self::OPT_MINUTE_SYMBOL );
123
124 24
		$coordinates = str_replace(
125 24
			[ '&#8243;', '&Prime;', $minute . $minute, '´´', '′′', '″' ],
126 24
			$second,
127 24
			$coordinates
128
		);
129 24
		$coordinates = str_replace( [ '&acute;', '&#180;' ], $second, $coordinates );
130
131 24
		$coordinates = parent::getNormalizedNotation( $coordinates );
132
133 24
		$coordinates = $this->removeInvalidChars( $coordinates );
134
135 24
		return $coordinates;
136
	}
137
138
	/**
139
	 * @see DdCoordinateParser::parseCoordinate
140
	 *
141
	 * @param string $coordinateSegment
142
	 *
143
	 * @return float
144
	 */
145 19
	protected function parseCoordinate( $coordinateSegment ) {
146 19
		$isNegative = substr( $coordinateSegment, 0, 1 ) === '-';
147
148 19
		if ( $isNegative ) {
149 7
			$coordinateSegment = substr( $coordinateSegment, 1 );
150
		}
151
152 19
		$degreeSymbol = $this->getOption( self::OPT_DEGREE_SYMBOL );
153 19
		$degreePosition = strpos( $coordinateSegment, $degreeSymbol );
154
155 19
		if ( $degreePosition === false ) {
156
			throw new ParseException(
157
				'Did not find degree symbol (' . $degreeSymbol . ')',
158
				$coordinateSegment,
159
				self::FORMAT_NAME
160
			);
161
		}
162
163 19
		$degrees = (float)substr( $coordinateSegment, 0, $degreePosition );
164
165 19
		$minutePosition = strpos( $coordinateSegment, $this->getOption( self::OPT_MINUTE_SYMBOL ) );
166
167 19
		if ( $minutePosition === false ) {
168
			$minutes = 0;
169
		} else {
170 19
			$degSignLength = strlen( $this->getOption( self::OPT_DEGREE_SYMBOL ) );
171 19
			$minuteLength = $minutePosition - $degreePosition - $degSignLength;
172 19
			$minutes = substr( $coordinateSegment, $degreePosition + $degSignLength, $minuteLength );
173
		}
174
175 19
		$secondPosition = strpos( $coordinateSegment, $this->getOption( self::OPT_SECOND_SYMBOL ) );
176
177 19
		if ( $secondPosition === false ) {
178
			$seconds = 0;
179
		} else {
180 19
			$secondLength = $secondPosition - $minutePosition - 1;
181 19
			$seconds = substr( $coordinateSegment, $minutePosition + 1, $secondLength );
182
		}
183
184 19
		$coordinateSegment = $degrees + ( $minutes + $seconds / 60 ) / 60;
185
186 19
		if ( $isNegative ) {
187 7
			$coordinateSegment *= -1;
188
		}
189
190 19
		return (float)$coordinateSegment;
191
	}
192
193
}
194