Jeroen De Dauw has uploaded a new change for review.
https://gerrit.wikimedia.org/r/58981
Change subject: Added DmCoordinateParser
......................................................................
Added DmCoordinateParser
Change-Id: Icef0d92110a62f535048e079a18920d4c1b0b554
---
M ValueParsers/ValueParsers.classes.php
M ValueParsers/ValueParsers.mw.php
A ValueParsers/includes/parsers/DmCoordinateParser.php
A ValueParsers/tests/includes/parsers/DmCoordinateParserTest.php
4 files changed, 342 insertions(+), 0 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/DataValues
refs/changes/81/58981/1
diff --git a/ValueParsers/ValueParsers.classes.php
b/ValueParsers/ValueParsers.classes.php
index a93708b..922b140 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\DmCoordinateParser' =>
'includes/parsers/DmCoordinateParser.php',
'ValueParsers\DmsCoordinateParser' =>
'includes/parsers/DmsCoordinateParser.php',
'ValueParsers\FloatCoordinateParser' =>
'includes/parsers/FloatCoordinateParser.php',
'ValueParsers\GeoCoordinateParser' =>
'includes/parsers/GeoCoordinateParser.php',
diff --git a/ValueParsers/ValueParsers.mw.php b/ValueParsers/ValueParsers.mw.php
index 3fdf2da..1efbd51 100644
--- a/ValueParsers/ValueParsers.mw.php
+++ b/ValueParsers/ValueParsers.mw.php
@@ -69,6 +69,7 @@
'includes/api/ApiParseValue',
'includes/parsers/BoolParser',
+ 'includes/parsers/DmCoordinateParser',
'includes/parsers/DmsCoordinateParser',
'includes/parsers/FloatCoordinateParser',
'includes/parsers/GeoCoordinateParser',
diff --git a/ValueParsers/includes/parsers/DmCoordinateParser.php
b/ValueParsers/includes/parsers/DmCoordinateParser.php
new file mode 100644
index 0000000..8afad91
--- /dev/null
+++ b/ValueParsers/includes/parsers/DmCoordinateParser.php
@@ -0,0 +1,246 @@
+<?php
+
+namespace ValueParsers;
+
+use DataValues\GeoCoordinateValue;
+use LogicException;
+
+/**
+ * Parser for geographical coordinates in Decimal Minute 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 < [email protected] >
+ */
+class DmCoordinateParser 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';
+ const OPT_MINUTE_SYMBOL = 'minute';
+
+ /**
+ * 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_MINUTE_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->areDMCoordinates( $value ) ) {
+ throw new ParseException( 'Not a geographical
coordinate in DM 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->parseDMCoordinate( $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 ) {
+ $minute = $this->getOption( self::OPT_MINUTE_SYMBOL );
+
+ $coordinates = str_replace( array( '°', '°' ),
$this->getOption( self::OPT_DEGREE_SYMBOL ), $coordinates );
+ $coordinates = str_replace( array( '′', '′', '´',
'′' ), $minute, $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 Minute representation, and
returns them in float representation.
+ *
+ * @since 0.1
+ *
+ * @param string $coordinate
+ *
+ * @return float
+ */
+ protected function parseDMCoordinate( $coordinate ) {
+ $isNegative = $coordinate{0} == '-';
+
+ if ( $isNegative ) {
+ $coordinate = substr( $coordinate, 1 );
+ }
+
+ list( $degrees, $minutes ) = explode( $this->getOption(
self::OPT_DEGREE_SYMBOL ), $coordinate );
+
+ $minutes = substr( $minutes, 0, -1 );
+
+ $coordinate = $degrees + $minutes / 60;
+
+ if ( $isNegative ) {
+ $coordinate *= -1;
+ }
+
+ return (float)$coordinate;
+ }
+
+ /**
+ * returns whether the coordinates are in Decimal Minute representation.
+ * TODO: nicify
+ *
+ * @since 0.1
+ *
+ * @param string $coordinates
+ *
+ * @return boolean
+ */
+ protected function areDMCoordinates( $coordinates ) {
+ $sep = $this->getOption( self::OPT_SEPARATOR_SYMBOL );
+
+ $match = preg_match( '/(-)?\d{1,3}°(\d{1,2}(\.\d{1,20}\')?)?' .
$sep . '(-)?\d{1,3}°(\d{1,2}(\.\d{1,20}\')?)?$/i', $coordinates ) //
Non-directional
+ || preg_match(
'/\d{1,3}°(\d{1,2}(\.\d{1,20}\')?)?(N|S)' . $sep .
'\d{1,3}°(\d{1,2}(\.\d{1,20}\')?)?(E|W)?$/i', $coordinates ); // Directional
+
+ return $match;
+ }
+
+}
diff --git a/ValueParsers/tests/includes/parsers/DmCoordinateParserTest.php
b/ValueParsers/tests/includes/parsers/DmCoordinateParserTest.php
new file mode 100644
index 0000000..2d034b9
--- /dev/null
+++ b/ValueParsers/tests/includes/parsers/DmCoordinateParserTest.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace ValueParsers\Test;
+
+use DataValues\GeoCoordinateValue;
+use ValueParsers\DmCoordinateParser;
+
+/**
+ * Unit tests for the ValueParsers\DmCoordinateValue 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 < [email protected] >
+ */
+class DmCoordinateParserTest extends StringValueParserTest {
+
+ /**
+ * @see ValueParserTestBase::validInputProvider
+ *
+ * @since 0.1
+ *
+ * @return array
+ */
+ public function validInputProvider() {
+ $argLists = array();
+
+ // TODO: test with different parser options
+
+ $valid = array(
+ "55° 0', 37° 0'" => array( 55, 37 ),
+ "55° 30', 37° 30'" => array( 55.5, 37.5 ),
+ "0° 0', 0° 0'" => array( 0, 0 ),
+ "-55° 30', -37° 30'" => array( -55.5, -37.5 ),
+ "0° 0.3' S, 0° 0.3' W" => array( -0.005, -0.005 ),
+ );
+
+ 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\DmCoordinateParser';
+ }
+
+}
--
To view, visit https://gerrit.wikimedia.org/r/58981
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Icef0d92110a62f535048e079a18920d4c1b0b554
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/DataValues
Gerrit-Branch: master
Gerrit-Owner: Jeroen De Dauw <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits