Current File : /home/jvzmxxx/wiki1/vendor/data-values/geo/src/Parsers/GlobeCoordinateParser.php
<?php

namespace DataValues\Geo\Parsers;

use DataValues\Geo\Values\GlobeCoordinateValue;
use DataValues\Geo\Values\LatLongValue;
use ValueParsers\ParseException;
use ValueParsers\ParserOptions;
use ValueParsers\StringValueParser;

/**
 * Extends the GeoCoordinateParser by adding precision detection support.
 *
 * The object that gets constructed is a GlobeCoordinateValue rather then a LatLongValue.
 *
 * @since 0.1
 *
 * @license GPL-2.0+
 * @author Jeroen De Dauw < jeroendedauw@gmail.com >
 * @author H. Snater < mediawiki@snater.com >
 * @author Thiemo Mättig
 */
class GlobeCoordinateParser extends StringValueParser {

	const FORMAT_NAME = 'globe-coordinate';

	/**
	 * Option specifying the globe. Should be a string containing a Wikidata concept URI. Defaults
	 * to Earth.
	 */
	const OPT_GLOBE = 'globe';

	/**
	 * @param ParserOptions|null $options
	 */
	public function __construct( ParserOptions $options = null ) {
		parent::__construct( $options );

		$this->defaultOption( self::OPT_GLOBE, 'http://www.wikidata.org/entity/Q2' );
	}

	/**
	 * @see StringValueParser::stringParse
	 *
	 * @param string $value
	 *
	 * @return GlobeCoordinateValue
	 * @throws ParseException
	 */
	protected function stringParse( $value ) {
		foreach ( $this->getParsers() as $precisionDetector => $parser ) {
			try {
				$latLong = $parser->parse( $value );

				return new GlobeCoordinateValue(
					new LatLongValue(
						$latLong->getLatitude(),
						$latLong->getLongitude()
					),
					$this->detectPrecision( $latLong, $precisionDetector ),
					$this->getOption( self::OPT_GLOBE )
				);
			} catch ( ParseException $parseException ) {
				continue;
			}
		}

		throw new ParseException(
			'The format of the coordinate could not be determined.',
			$value,
			self::FORMAT_NAME
		);
	}

	/**
	 * @param LatLongValue $latLong
	 * @param string $precisionDetector
	 *
	 * @return float|int
	 */
	private function detectPrecision( LatLongValue $latLong, $precisionDetector ) {
		if ( $this->options->hasOption( 'precision' ) ) {
			return $this->getOption( 'precision' );
		}

		return min(
			call_user_func( array( $this, $precisionDetector ), $latLong->getLatitude() ),
			call_user_func( array( $this, $precisionDetector ), $latLong->getLongitude() )
		);
	}

	/**
	 * @return  StringValueParser[]
	 */
	private function getParsers() {
		$parsers = array();

		$parsers['detectFloatPrecision'] = new FloatCoordinateParser( $this->options );
		$parsers['detectDmsPrecision'] = new DmsCoordinateParser( $this->options );
		$parsers['detectDmPrecision'] = new DmCoordinateParser( $this->options );
		$parsers['detectDdPrecision'] = new DdCoordinateParser( $this->options );

		return $parsers;
	}

	/**
	 * @param float $degree
	 *
	 * @return float|int
	 */
	protected function detectDdPrecision( $degree ) {
		return $this->detectFloatPrecision( $degree );
	}

	/**
	 * @param float $degree
	 *
	 * @return float|int
	 */
	protected function detectDmPrecision( $degree ) {
		$minutes = $degree * 60;
		$split = explode( '.', round( $minutes, 6 ) );

		if ( isset( $split[1] ) ) {
			return $this->detectDmsPrecision( $degree );
		}

		return 1 / 60;
	}

	/**
	 * @param float $degree
	 *
	 * @return float|int
	 */
	protected function detectDmsPrecision( $degree ) {
		$seconds = $degree * 3600;
		$split = explode( '.', round( $seconds, 4 ) );

		if ( isset( $split[1] ) ) {
			return pow( 10, -strlen( $split[1] ) ) / 3600;
		}

		return 1 / 3600;
	}

	/**
	 * @param float $degree
	 *
	 * @return float|int
	 */
	protected function detectFloatPrecision( $degree ) {
		$split = explode( '.', round( $degree, 8 ) );

		if ( isset( $split[1] ) ) {
			return pow( 10, -strlen( $split[1] ) );
		}

		return 1;
	}

}