Daniel Werner has submitted this change and it was merged. Change subject: Added DdCoordinateParser ......................................................................
Added DdCoordinateParser Change-Id: I8ba120f111775885e350adc3c65c4d556844bf2c --- M ValueParsers/ValueParsers.classes.php M ValueParsers/ValueParsers.mw.php A ValueParsers/includes/parsers/DdCoordinateParser.php A ValueParsers/tests/includes/parsers/DdCoordinateParserTest.php M ValueParsers/tests/includes/parsers/DmCoordinateParserTest.php M ValueParsers/tests/includes/parsers/DmsCoordinateParserTest.php M ValueParsers/tests/includes/parsers/FloatCoordinateParserTest.php 7 files changed, 324 insertions(+), 3 deletions(-) Approvals: Daniel Werner: Verified; Looks good to me, approved jenkins-bot: Verified diff --git a/ValueParsers/ValueParsers.classes.php b/ValueParsers/ValueParsers.classes.php index 922b140..f14bc3f 100644 --- a/ValueParsers/ValueParsers.classes.php +++ b/ValueParsers/ValueParsers.classes.php @@ -37,6 +37,7 @@ 'ValueParsers\ApiParseValue' => 'includes/api/ApiParseValue.php', 'ValueParsers\BoolParser' => 'includes/parsers/BoolParser.php', + 'ValueParsers\DdCoordinateParser' => 'includes/parsers/DdCoordinateParser.php', 'ValueParsers\DmCoordinateParser' => 'includes/parsers/DmCoordinateParser.php', 'ValueParsers\DmsCoordinateParser' => 'includes/parsers/DmsCoordinateParser.php', 'ValueParsers\FloatCoordinateParser' => 'includes/parsers/FloatCoordinateParser.php', diff --git a/ValueParsers/ValueParsers.mw.php b/ValueParsers/ValueParsers.mw.php index 1efbd51..143c6a2 100644 --- a/ValueParsers/ValueParsers.mw.php +++ b/ValueParsers/ValueParsers.mw.php @@ -69,6 +69,7 @@ 'includes/api/ApiParseValue', 'includes/parsers/BoolParser', + 'includes/parsers/DdCoordinateParser', 'includes/parsers/DmCoordinateParser', 'includes/parsers/DmsCoordinateParser', 'includes/parsers/FloatCoordinateParser', diff --git a/ValueParsers/includes/parsers/DdCoordinateParser.php b/ValueParsers/includes/parsers/DdCoordinateParser.php new file mode 100644 index 0000000..f14203f --- /dev/null +++ b/ValueParsers/includes/parsers/DdCoordinateParser.php @@ -0,0 +1,224 @@ +<?php + +namespace ValueParsers; + +use DataValues\GeoCoordinateValue; +use LogicException; + +/** + * Parser for geographical coordinates in Decimal Degree notation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @since 0.1 + * + * @file + * @ingroup ValueParsers + * + * @licence GNU GPL v2+ + * @author Jeroen De Dauw < jeroended...@gmail.com > + */ +class DdCoordinateParser extends StringValueParser { + + /** + * The symbols representing the different directions for usage in directional notation. + * @since 0.1 + */ + const OPT_NORTH_SYMBOL = 'north'; + const OPT_EAST_SYMBOL = 'east'; + const OPT_SOUTH_SYMBOL = 'south'; + const OPT_WEST_SYMBOL = 'west'; + + /** + * The symbols representing degrees, minutes and seconds. + * @since 0.1 + */ + const OPT_DEGREE_SYMBOL = 'degree'; + + /** + * The symbol to use as separator between latitude and longitude. + * @since 0.1 + */ + const OPT_SEPARATOR_SYMBOL = 'separator'; + + /** + * @since 0.1 + * + * @param ParserOptions|null $options + */ + public function __construct( ParserOptions $options = null ) { + parent::__construct( $options ); + + $this->defaultOption( self::OPT_NORTH_SYMBOL, 'N' ); + $this->defaultOption( self::OPT_EAST_SYMBOL, 'E' ); + $this->defaultOption( self::OPT_SOUTH_SYMBOL, 'S' ); + $this->defaultOption( self::OPT_WEST_SYMBOL, 'W' ); + + $this->defaultOption( self::OPT_DEGREE_SYMBOL, '°' ); + + $this->defaultOption( self::OPT_SEPARATOR_SYMBOL, ',' ); + } + + /** + * @see StringValueParser::stringParse + * + * @since 0.1 + * + * @param string $value + * + * @return GeoCoordinateValue + * @throws ParseException + * @throws LogicException + */ + protected function stringParse( $value ) { + $value = $this->getNormalizedNotation( $value ); + + if ( !$this->areDDCoordinates( $value ) ) { + throw new ParseException( 'Not a geographical coordinate in DD format' ); + } + + $coordinates = explode( $this->getOption( self::OPT_SEPARATOR_SYMBOL ), $value ); + + if ( count( $coordinates ) !== 2 ) { + throw new LogicException( 'A coordinates string with an incorrect segment count has made it through validation' ); + } + + list( $latitude, $longitude ) = $coordinates; + + $latitude = $this->getParsedCoordinate( $latitude ); + $longitude = $this->getParsedCoordinate( $longitude ); + + return new GeoCoordinateValue( $latitude, $longitude ); + } + + /** + * Parsers a single coordinate (either latitude or longitude) and returns it as a float. + * + * @since 0.1 + * + * @param string $coordinate + * + * @return float + */ + protected function getParsedCoordinate( $coordinate ) { + $coordinate = $this->resolveDirection( $coordinate ); + return $this->parseDDCoordinate( $coordinate ); + } + + /** + * Turns directional notation (N/E/S/W) of a single coordinate into non-directional notation (+/-). + * This method assumes there are no preceding or tailing spaces. + * + * @since 0.1 + * + * @param string $coordinate + * + * @return string + */ + protected function resolveDirection( $coordinate ) { + // Get the last char, which could be a direction indicator + $lastChar = strtoupper( substr( $coordinate, -1 ) ); + + $n = $this->getOption( self::OPT_NORTH_SYMBOL ); + $e = $this->getOption( self::OPT_EAST_SYMBOL ); + $s = $this->getOption( self::OPT_SOUTH_SYMBOL ); + $w = $this->getOption( self::OPT_WEST_SYMBOL ); + + // If there is a direction indicator, remove it, and prepend a minus sign for south and west directions. + // If there is no direction indicator, the coordinate is already non-directional and no work is required. + if ( in_array( $lastChar, array( $n, $e, $s, $w ) ) ) { + $coordinate = substr( $coordinate, 0, -1 ); + + if ( in_array( $lastChar, array( $s, $w ) ) ) { + $coordinate = '-' . $coordinate; + } + } + + return $coordinate; + } + + /** + * Returns a normalized version of the coordinate string. + * + * @since 0.1 + * + * @param string $coordinates + * + * @return string + */ + protected function getNormalizedNotation( $coordinates ) { + $coordinates = str_replace( array( '°', '°' ), $this->getOption( self::OPT_DEGREE_SYMBOL ), $coordinates ); + + $coordinates = $this->removeInvalidChars( $coordinates ); + + return $coordinates; + } + + /** + * Returns a string with whitespace, control characters and characters with ASCII values above 126 removed. + * + * @since 0.1 + * + * @param string $string + * + * @return string + */ + protected function removeInvalidChars( $string ) { + $filtered = array(); + + foreach ( str_split( $string ) as $character ) { + $asciiValue = ord( $character ); + + if ( ( $asciiValue > 32 && $asciiValue < 127 ) || $asciiValue == 194 || $asciiValue == 176 ) { + $filtered[] = $character; + } + } + + return implode( '', $filtered ); + } + + /** + * Takes a set of coordinates in Decimal Degree representation, and returns them in float representation. + * + * @since 0.1 + * + * @param string $coordinate + * + * @return float + */ + protected function parseDDCoordinate( $coordinate ) { + return (float)str_replace( $this->getOption( self::OPT_DEGREE_SYMBOL ), '', $coordinate ); + } + + /** + * returns whether the coordinates are in Decimal Degree representation. + * + * @since 0.1 + * + * @param string $coordinates + * + * @return boolean + */ + protected function areDDCoordinates( $coordinates ) { + $sep = $this->getOption( self::OPT_SEPARATOR_SYMBOL ); + + $match = preg_match( '/^(-)?\d{1,3}(|\.\d{1,20})°' . $sep . '(-)?\d{1,3}(|\.\d{1,20})°$/i', $coordinates ) // Non-directional + || preg_match( '/^\d{1,3}(|\.\d{1,20})°(N|S)' . $sep . '\d{1,3}(|\.\d{1,20})°(E|W)?$/i', $coordinates ); // Directional + + return $match; + } + +} diff --git a/ValueParsers/tests/includes/parsers/DdCoordinateParserTest.php b/ValueParsers/tests/includes/parsers/DdCoordinateParserTest.php new file mode 100644 index 0000000..ed7659d --- /dev/null +++ b/ValueParsers/tests/includes/parsers/DdCoordinateParserTest.php @@ -0,0 +1,95 @@ +<?php + +namespace ValueParsers\Test; + +use DataValues\GeoCoordinateValue; +use ValueParsers\DdCoordinateParser; + +/** + * Unit tests for the ValueParsers\DdCoordinateParser class. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @since 0.1 + * + * @ingroup ValueParsersTest + * + * @group ValueParsers + * @group DataValueExtensions + * @group GeoCoordinateParserTest + * + * @licence GNU GPL v2+ + * @author Jeroen De Dauw < jeroended...@gmail.com > + */ +class DdCoordinateParserTest extends StringValueParserTest { + + /** + * @see ValueParserTestBase::validInputProvider + * + * @since 0.1 + * + * @return array + */ + public function validInputProvider() { + $argLists = array(); + + // TODO: test with different parser options + + $valid = array( + '55.7557860° N, 37.6176330° W' => array( 55.7557860, -37.6176330 ), + '55.7557860°, -37.6176330°' => array( 55.7557860, -37.6176330 ), + '55° S, 37.6176330 ° W' => array( -55, -37.6176330 ), + '-55°, -37.6176330 °' => array( -55, -37.6176330 ), + '5.5°S,37°W ' => array( -5.5, -37 ), + '-5.5°,-37° ' => array( -5.5, -37 ), + ); + + foreach ( $valid as $value => $expected ) { + $expected = new GeoCoordinateValue( $expected[0], $expected[1] ); + $argLists[] = array( (string)$value, $expected ); + } + + return $argLists; + } + + public function invalidInputProvider() { + $argLists = parent::invalidInputProvider(); + + $invalid = array( + '~=[,,_,,]:3', + 'ohi there', + ); + + foreach ( $invalid as $value ) { + $argLists[] = array( $value ); + } + + return $argLists; + } + + /** + * @see ValueParserTestBase::getParserClass + * + * @since 0.1 + * + * @return string + */ + protected function getParserClass() { + return 'ValueParsers\DdCoordinateParser'; + } + +} diff --git a/ValueParsers/tests/includes/parsers/DmCoordinateParserTest.php b/ValueParsers/tests/includes/parsers/DmCoordinateParserTest.php index 2d034b9..deca8ed 100644 --- a/ValueParsers/tests/includes/parsers/DmCoordinateParserTest.php +++ b/ValueParsers/tests/includes/parsers/DmCoordinateParserTest.php @@ -6,7 +6,7 @@ use ValueParsers\DmCoordinateParser; /** - * Unit tests for the ValueParsers\DmCoordinateValue class. + * Unit tests for the ValueParsers\DmCoordinateParser class. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/ValueParsers/tests/includes/parsers/DmsCoordinateParserTest.php b/ValueParsers/tests/includes/parsers/DmsCoordinateParserTest.php index 3b3ea25..b41ce3a 100644 --- a/ValueParsers/tests/includes/parsers/DmsCoordinateParserTest.php +++ b/ValueParsers/tests/includes/parsers/DmsCoordinateParserTest.php @@ -6,7 +6,7 @@ use ValueParsers\DmsCoordinateParser; /** - * Unit tests for the ValueParsers\DmsCoordinateValue class. + * Unit tests for the ValueParsers\DmsCoordinateParser class. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/ValueParsers/tests/includes/parsers/FloatCoordinateParserTest.php b/ValueParsers/tests/includes/parsers/FloatCoordinateParserTest.php index 7d29318..c1676c8 100644 --- a/ValueParsers/tests/includes/parsers/FloatCoordinateParserTest.php +++ b/ValueParsers/tests/includes/parsers/FloatCoordinateParserTest.php @@ -6,7 +6,7 @@ use ValueParsers\FloatCoordinateParser; /** - * Unit tests for the ValueParsers\FloatCoordinateValue class. + * Unit tests for the ValueParsers\FloatCoordinateParser class. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by -- To view, visit https://gerrit.wikimedia.org/r/59075 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I8ba120f111775885e350adc3c65c4d556844bf2c Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/DataValues Gerrit-Branch: master Gerrit-Owner: Jeroen De Dauw <jeroended...@gmail.com> Gerrit-Reviewer: Daniel Werner <daniel.wer...@wikimedia.de> Gerrit-Reviewer: John Erling Blad <jeb...@gmail.com> Gerrit-Reviewer: Tobias Gritschacher <tobias.gritschac...@wikimedia.de> Gerrit-Reviewer: jenkins-bot _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits