1 | <?php |
||
19 | abstract class LatLongParserBase implements ValueParser { |
||
20 | |||
21 | public const FORMAT_NAME = 'geo-coordinate'; |
||
22 | |||
23 | /** |
||
24 | * The symbols representing the different directions for usage in directional notation. |
||
25 | */ |
||
26 | public const OPT_NORTH_SYMBOL = 'north'; |
||
27 | public const OPT_EAST_SYMBOL = 'east'; |
||
28 | public const OPT_SOUTH_SYMBOL = 'south'; |
||
29 | public const OPT_WEST_SYMBOL = 'west'; |
||
30 | |||
31 | /** |
||
32 | * The symbol to use as separator between latitude and longitude. |
||
33 | */ |
||
34 | public const OPT_SEPARATOR_SYMBOL = 'separator'; |
||
35 | |||
36 | /** |
||
37 | * Delimiters used to split a coordinate string when unable to split by using the separator. |
||
38 | * @var string[] |
||
39 | */ |
||
40 | protected $defaultDelimiters; |
||
41 | |||
42 | /** |
||
43 | * @var ParserOptions |
||
44 | */ |
||
45 | private $options; |
||
46 | |||
47 | 90 | public function __construct( ParserOptions $options = null ) { |
|
48 | 90 | $this->options = $options ?: new ParserOptions(); |
|
49 | |||
50 | 90 | $this->options->defaultOption( ValueParser::OPT_LANG, 'en' ); |
|
51 | |||
52 | 90 | $this->options->defaultOption( self::OPT_NORTH_SYMBOL, 'N' ); |
|
53 | 90 | $this->options->defaultOption( self::OPT_EAST_SYMBOL, 'E' ); |
|
54 | 90 | $this->options->defaultOption( self::OPT_SOUTH_SYMBOL, 'S' ); |
|
55 | 90 | $this->options->defaultOption( self::OPT_WEST_SYMBOL, 'W' ); |
|
56 | |||
57 | 90 | $this->options->defaultOption( self::OPT_SEPARATOR_SYMBOL, ',' ); |
|
58 | 90 | } |
|
59 | |||
60 | /** |
||
61 | * Parses a single coordinate segment (either latitude or longitude) and returns it as a float. |
||
62 | * |
||
63 | * @param string $coordinateSegment |
||
64 | * |
||
65 | * @throws ParseException |
||
66 | * @return float |
||
67 | */ |
||
68 | abstract protected function getParsedCoordinate( string $coordinateSegment ): float; |
||
69 | |||
70 | /** |
||
71 | * Returns whether a coordinate split into its two segments is in the representation expected by |
||
72 | * this parser. |
||
73 | * |
||
74 | * @param string[] $normalizedCoordinateSegments |
||
75 | * |
||
76 | * @return bool |
||
77 | */ |
||
78 | abstract protected function areValidCoordinates( array $normalizedCoordinateSegments ): bool; |
||
79 | |||
80 | /** |
||
81 | * @see ValueParser::parse |
||
82 | * |
||
83 | * @param string $value |
||
84 | * |
||
85 | * @throws ParseException |
||
86 | * @return LatLongValue |
||
87 | */ |
||
88 | 81 | public function parse( $value ): LatLongValue { |
|
89 | 81 | if ( !is_string( $value ) ) { |
|
90 | 3 | throw new ParseException( 'Not a string' ); |
|
91 | } |
||
92 | |||
93 | 78 | $rawValue = $value; |
|
94 | |||
95 | 78 | $value = $this->removeInvalidChars( $value ); |
|
96 | |||
97 | 78 | $normalizedCoordinateSegments = $this->splitString( $value ); |
|
98 | |||
99 | 76 | if ( !$this->areValidCoordinates( $normalizedCoordinateSegments ) ) { |
|
100 | 6 | throw new ParseException( 'Not a valid geographical coordinate', $rawValue, static::FORMAT_NAME ); |
|
101 | } |
||
102 | |||
103 | 70 | list( $latitude, $longitude ) = $normalizedCoordinateSegments; |
|
104 | |||
105 | 70 | return new LatLongValue( |
|
106 | 70 | $this->getParsedCoordinate( $latitude ), |
|
107 | 70 | $this->getParsedCoordinate( $longitude ) |
|
108 | ); |
||
109 | } |
||
110 | |||
111 | /** |
||
112 | * Returns a string trimmed and with control characters and characters with ASCII values above |
||
113 | * 126 removed. SPACE characters within the string are not removed to retain the option to split |
||
114 | * the string using that character. |
||
115 | * |
||
116 | * @param string $string |
||
117 | * |
||
118 | * @return string |
||
119 | */ |
||
120 | 78 | protected function removeInvalidChars( string $string ): string { |
|
121 | 78 | $filtered = []; |
|
122 | |||
123 | 78 | foreach ( str_split( $string ) as $character ) { |
|
124 | 78 | $asciiValue = ord( $character ); |
|
125 | |||
126 | if ( |
||
127 | 78 | ( $asciiValue >= 32 && $asciiValue < 127 ) |
|
128 | 53 | || $asciiValue == 194 |
|
129 | 78 | || $asciiValue == 176 |
|
130 | ) { |
||
131 | 78 | $filtered[] = $character; |
|
132 | } |
||
133 | } |
||
134 | |||
135 | 78 | return trim( implode( '', $filtered ) ); |
|
136 | } |
||
137 | |||
138 | /** |
||
139 | * Splits a string into two strings using the separator specified in the options. If the string |
||
140 | * could not be split using the separator, the method will try to split the string by analyzing |
||
141 | * the used symbols. If the string could not be split into two parts, an empty array is |
||
142 | * returned. |
||
143 | * |
||
144 | * @param string $normalizedCoordinateString |
||
145 | * |
||
146 | * @throws ParseException if unable to split input string into two segments |
||
147 | * @return string[] |
||
148 | */ |
||
149 | protected function splitString( string $normalizedCoordinateString ): array { |
||
202 | |||
203 | /** |
||
204 | * Turns directional notation (N/E/S/W) of a single coordinate into non-directional notation |
||
205 | * (+/-). |
||
206 | * This method assumes there are no preceding or tailing spaces. |
||
207 | * |
||
208 | * @param string $coordinateSegment |
||
209 | * |
||
210 | * @return string |
||
211 | */ |
||
212 | 70 | protected function resolveDirection( string $coordinateSegment ): string { |
|
243 | |||
244 | 87 | protected function getOption( string $optionName ) { |
|
247 | |||
248 | } |
||
249 |