r64200 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r64199‎ | r64200 | r64201 >
Date:00:00, 26 March 2010
Author:jeroendedauw
Status:deferred
Tags:
Comment:
Unfinished and untested version of MapsCoordinateParser
Modified paths:
  • /trunk/extensions/Maps/Maps_CoordinateParser.php (added) (history)

Diff [purge]

Index: trunk/extensions/Maps/Maps_CoordinateParser.php
@@ -0,0 +1,308 @@
 2+<?php
 3+
 4+/**
 5+ * File holding class MapsCoordinateParser.
 6+ *
 7+ * @file Maps_CoordinateParser.php
 8+ * @ingroup Maps
 9+ *
 10+ * @author Jeroen De Dauw
 11+ */
 12+
 13+if ( !defined( 'MEDIAWIKI' ) ) {
 14+ die( 'Not an entry point.' );
 15+}
 16+
 17+define( 'COORDS_FLOAT', 0 );
 18+define( 'COORDS_DMS', 1 );
 19+define( 'COORDS_DM', 2 );
 20+define( 'COORDS_DD', 3 );
 21+
 22+/// Unicode symbols for coordinate minutes and seconds.
 23+define( 'Maps_GEO_MIN', '′' );
 24+define( 'Maps_GEO_SEC', '″' );
 25+
 26+/**
 27+ * Static class for coordinate validation and parsing.
 28+ * Supports floats, DMS, decimal degrees, and decimal minutes notations, both directional and non-directional.
 29+ *
 30+ * @ingroup Maps
 31+ *
 32+ * @author Jeroen De Dauw
 33+ *
 34+ * TODO: add functions to get a parsed value back in a non-float representatation
 35+ * TODO: hold into account all valid spacing styles
 36+ * TODO: support different coordinate seperators, currently only comma's are supported
 37+ * TODO: normalize the input before doing anything with it
 38+ */
 39+class MapsCoordinateParser {
 40+
 41+ protected static $mI18nDirections = false; // Cache for localised direction labels
 42+ protected static $mDirections; // Cache for English direction labels
 43+
 44+ /**
 45+ * Takes in a set of coordinates and checks if they are a supported format.
 46+ * If they are, they will be parsed to the given notation, which defaults to
 47+ * non-directional floats, and returned in an associative array with keys lat and lon.
 48+ *
 49+ * @param string $coordinates The coordinates to be formatted.
 50+ * @param coordinate type $targetType The notation to which they should be formatted. Defaults to floats.
 51+ * @param boolean $directional Indicates if the target notation should be directional. Defaults to false.
 52+ *
 53+ * @return array or false
 54+ *
 55+ * TODO: hold into account type and directional parameters
 56+ */
 57+ public static function formatCoordinates( $coordinates, $targetType = COORDS_FLOAT, $directional = false ) {
 58+ $coordinates = trim( $coordinates );
 59+
 60+ $coordinates = self::handleI18nLabels( $coordinates );
 61+
 62+ $coordsType = self::getCoordinatesType( $coordinates );
 63+
 64+ // If getCoordinatesType returned false, the provided value is invalid.
 65+ if ( $coordsType === false ) {
 66+ return false;
 67+ }
 68+
 69+ // Split the coodrinates string into a lat and lon part.
 70+ $coordinates = explode( ',', $coordinates );
 71+ $coordinates = array(
 72+ 'lat' => trim ( $coordinates[0] ),
 73+ 'lon' => trim ( $coordinates[1] ),
 74+ );
 75+
 76+ $coordinates = self::resolveAngles( $coordinates );
 77+
 78+ $coordinates = array(
 79+ 'lat' => self::parseCoordinate( $coordinates['lat'], $coordsType ),
 80+ 'lon' => self::parseCoordinate( $coordinates['lon'], $coordsType ),
 81+ );
 82+
 83+ return $floatCoordinates;
 84+ }
 85+
 86+ /**
 87+ * Returns the type of the provided coordinates, or flase if they are invalid.
 88+ * You can use this as validation function, but be sure to use ===, since 0 can be returned.
 89+ *
 90+ * @param string $coordinates
 91+ *
 92+ * @return Integer or false
 93+ */
 94+ public static function getCoordinatesType( $coordinates ) {
 95+ switch ( true ) {
 96+ case self::areFloatCoordinates( $coordinates ):
 97+ return COORDS_FLOAT;
 98+ break;
 99+ case self::areDMSCoordinates( $coordinates ):
 100+ return COORDS_DMS;
 101+ break;
 102+ case self::areDDCoordinates( $coordinates ):
 103+ return COORDS_DD;
 104+ break;
 105+ case self::areDMCoordinates( $coordinates ):
 106+ return COORDS_DM;
 107+ break;
 108+ default:
 109+ return false;
 110+ }
 111+ }
 112+
 113+ private static function parseCoordinate( $coordinate, $coordType ) {
 114+ switch ( $coordType ) {
 115+ case COORDS_FLOAT:
 116+ return self::parseFloatCoordinate( $coordinate );
 117+ break;
 118+ case COORDS_DMS:
 119+ return self::parseDMSCoordinate( $coordinate );
 120+ break;
 121+ case COORDS_DD:
 122+ return self::parseDDCoordinate( $coordinate );
 123+ break;
 124+ case COORDS_DM:
 125+ return self::parseDMCoordinate( $coordinate );
 126+ }
 127+ }
 128+
 129+ /**
 130+ * returns whether the coordinates are in float representataion.
 131+ *
 132+ * @param string $coordinates
 133+ *
 134+ * @return boolean
 135+ */
 136+ private static function areFloatCoordinates( $coordinates ) {
 137+ return preg_match( '/^(-)?\d{1,3}(\.\d{1,20})?,(\s)?(-)?\d{1,3}(\.\d{1,20})?$/', $coordinates ) // Non-directional
 138+ || preg_match( '/^\d{1,3}(\.\d{1,20})?(\s)?(N|S),(\s)?\d{1,3}(\.\d{1,20})?(\s)?(E|W)$/', $coordinates ); // Directional
 139+ }
 140+
 141+ /**
 142+ * returns whether the coordinates are in DMS representataion.
 143+ *
 144+ * @param string $coordinates
 145+ *
 146+ * @return boolean
 147+ */
 148+ private static function areDMSCoordinates( $coordinates ) {
 149+ return preg_match( '/^(-)?(\d{1,3}°)(\d{1,2}(\′|\'))?((\d{1,2}(″|"))?|(\d{1,2}\.\d{1,2}(″|"))?),(\s)?(-)?(\d{1,3}°)(\d{1,2}(\′|\'))?((\d{1,2}(″|"))?|(\d{1,2}\.\d{1,2}(″|"))?)$/', $coordinates ) // Non-directional
 150+ || preg_match( '/^(\d{1,3}°)(\d{1,2}(\′|\'))?((\d{1,2}(″|"))?|(\d{1,2}\.\d{1,2}(″|"))?)(\s)?(N|S),(\s)?(\d{1,3}°)(\d{1,2}(\′|\'))?((\d{1,2}(″|"))?|(\d{1,2}\.\d{1,2}(″|"))?)(\s)?(E|W)$/', $coordinates ); // Directional
 151+ }
 152+
 153+ /**
 154+ * returns whether the coordinates are in Decimal Degree representataion.
 155+ *
 156+ * @param string $coordinates
 157+ *
 158+ * @return boolean
 159+ */
 160+ private static function areDDCoordinates( $coordinates ) {
 161+ return preg_match( '/^(-)?\d{1,3}(|\.\d{1,20})°,(\s)?(-)?(\s)?\d{1,3}(|\.\d{1,20})°$/', $coordinates ) // Non-directional
 162+ || preg_match( '/^\d{1,3}(|\.\d{1,20})°(\s)?(N|S),(\s)?(\s)?\d{1,3}(|\.\d{1,20})°(\s)?(E|W)?$/', $coordinates ); // Directional
 163+ }
 164+
 165+ /**
 166+ * returns whether the coordinates are in Decimal Minute representataion.
 167+ *
 168+ * @param string $coordinates
 169+ *
 170+ * @return boolean
 171+ */
 172+ private static function areDMCoordinates( $coordinates ) {
 173+ return preg_match( '/(-)?\d{1,3}°\d{1,3}(\.\d{1,20}\')?,(\s)?(-)?\d{1,3}°\d{1,3}(\.\d{1,20}\')?$/', $coordinates ) // Non-directional
 174+ || preg_match( '/\d{1,3}°\d{1,3}(\.\d{1,20}\')?(\s)?(N|S),(\s)?\d{1,3}°\d{1,3}(\.\d{1,20}\')?(\s)?(E|W)?$/', $coordinates ); // Directional
 175+ }
 176+
 177+ /**
 178+ * Turn i18n labels into English ones, for both validation and ease of handling.
 179+ *
 180+ * @param string $coordinates
 181+ */
 182+ private static function handleI18nLabels( $coordinates ) {
 183+ self::initializeDirectionLabels();
 184+ $coordinates = str_replace( self::$mI18nDirections, self::$mDirections, $coordinates );
 185+ }
 186+
 187+ /**
 188+ * Initialize the cache for internationalized direction labels if not done yet.
 189+ */
 190+ private static function initializeDirectionLabels() {
 191+ if ( !$this->mI18nDirections ) {
 192+ self::$mI18nDirections = array(
 193+ 'N' => wfMsgForContent( 'maps-abb-north' ),
 194+ 'E' => wfMsgForContent( 'maps-abb-east' ),
 195+ 'S' => wfMsgForContent( 'maps-abb-south' ),
 196+ 'W' => wfMsgForContent( 'maps-abb-west' ),
 197+ );
 198+ self::$mDirections = array_keys( self::$mI18nDirections );
 199+ }
 200+ }
 201+
 202+ /**
 203+ * Turns directional notation (N/E/S/W) of a coordinate set into non-directional notation (+/-).
 204+ *
 205+ * @param array $coordinates
 206+ *
 207+ * @return array
 208+ */
 209+ private static function resolveAngles( array $coordinates ) {
 210+ return array(
 211+ 'lat' => self::resolveAngle( $coordinates['lat'] ),
 212+ 'lon' => self::resolveAngle( $coordinates['lon'] ),
 213+ );
 214+ }
 215+
 216+ /**
 217+ * Turns directional notation (N/E/S/W) of a single coordinate into non-directional notation (+/-).
 218+ *
 219+ * @param string $coordinates
 220+ *
 221+ * @return string
 222+ */
 223+ private static function resolveAngle( $coordinate ) {
 224+ // Get the last char, which could be a direction indicator
 225+ $lastChar = substr( $coordinate, -1 );
 226+
 227+ // If there is a direction indicator, remove it, and prepend a minus sign for south and west directions.
 228+ // If there is no direction indicator, the coordinate is already non-directional and no work is required.
 229+ if ( in_array( $lastChar, self::$mDirections ) ) {
 230+ $coordinate = substr( $coordinate, 0, -1 );
 231+ if ( ( $lastChar == "S" ) or ( $lastChar == "W" ) ) {
 232+ $coordinate = '-' . trim( $coordinate );
 233+ }
 234+ }
 235+
 236+ return $coordinate;
 237+ }
 238+
 239+ /**
 240+ * Takes a set of coordinates in float representataion, and returns them in float representataion.
 241+ *
 242+ * @param string $coordinate
 243+ *
 244+ * @return string
 245+ */
 246+ private static function parseFloatCoordinate( $coordinate ) {
 247+ return $coordinate; // No parsing needed?
 248+ }
 249+
 250+ /**
 251+ * Takes a set of coordinates in DMS representataion, and returns them in float representataion.
 252+ *
 253+ * @param string $coordinate
 254+ *
 255+ * @return string
 256+ */
 257+ private static function parseDMSCoordinate( $coordinate ) {
 258+ $isNegative = substr( $coordinate, 0, 1 ) == '-';
 259+ if ( $isNegative ) $coordinate = substr( $coordinate, 1 );
 260+
 261+ $degreePosition = strpos( $coordinate, '°' );
 262+ $minutePosition = strpos( $coordinate, "'" );
 263+ $secondPosition = strpos( $coordinate, '"' );
 264+
 265+ $minuteLength = $minutePosition - $degreePosition - 1;
 266+ $secondLength = $secondPosition - $minutePosition - 1;
 267+
 268+ $degrees = substr ( $coordinate, 0, $degreePosition );
 269+ $minutes = substr ( $coordinate, $degreePosition + 1, $minuteLength );
 270+ $seconds = substr ( $coordinate, $minutePosition + 1, $secondLength );
 271+
 272+ $coordinate = $degrees + ( $minutes + $seconds / 60 ) / 60;
 273+ if ( $isNegative ) $coordinate *= -1;
 274+
 275+ return $coordinate;
 276+ }
 277+
 278+ /**
 279+ * Takes a set of coordinates in Decimal Degree representataion, and returns them in float representataion.
 280+ *
 281+ * @param string $coordinate
 282+ *
 283+ * @return string
 284+ */
 285+ private static function parseDDCoordinate( $coordinate ) {
 286+ return (int)str_replace( '°', '', $coordinate );
 287+ }
 288+
 289+ /**
 290+ * Takes a set of coordinates in Decimal Minute representataion, and returns them in float representataion.
 291+ *
 292+ * @param string $coordinate
 293+ *
 294+ * @return string
 295+ */
 296+ private static function parseDMCoordinate( $coordinate ) {
 297+ $isNegative = substr( $coordinate, 0, 1 ) == '-';
 298+ if ( $isNegative ) $coordinate = substr( $coordinate, 1 );
 299+
 300+ list( $degrees, $minutes ) = explode( '°', $coordinate );
 301+ $minutes = substr( $minutes, -1 );
 302+
 303+ $coordinate = $degrees + $minutes / 60;
 304+ if ( $isNegative ) $coordinate *= -1;
 305+
 306+ return $coordinate;
 307+ }
 308+
 309+}
\ No newline at end of file
Property changes on: trunk/extensions/Maps/Maps_CoordinateParser.php
___________________________________________________________________
Name: svn:eol-style
1310 + native

Status & tagging log