GlobeCoordinateHandler::getInsertValues()   B
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 25
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
dl 0
loc 25
rs 8.8571
c 2
b 0
f 0
cc 2
eloc 17
nc 2
nop 1
1
<?php
2
3
namespace Wikibase\QueryEngine\SQLStore\DVHandler;
4
5
use Ask\Language\Description\ValueDescription;
6
use DataValues\DataValue;
7
use DataValues\Geo\GlobeMath;
8
use DataValues\Geo\Values\GlobeCoordinateValue;
9
use Doctrine\DBAL\Query\QueryBuilder;
10
use Doctrine\DBAL\Schema\Table;
11
use Doctrine\DBAL\Types\Type;
12
use InvalidArgumentException;
13
use Wikibase\QueryEngine\QueryNotSupportedException;
14
use Wikibase\QueryEngine\SQLStore\DataValueHandler;
15
16
/**
17
 * @since 0.3
18
 *
19
 * @licence GNU GPL v2+
20
 * @author Thiemo Kreuz
21
 */
22
class GlobeCoordinateHandler extends DataValueHandler {
23
24
	/**
25
	 * @var GlobeMath
26
	 */
27
	private $math;
28
29
	public function __construct() {
30
		$this->math = new GlobeMath();
31
	}
32
33
	/**
34
	 * @see DataValueHandler::getBaseTableName
35
	 *
36
	 * @return string
37
	 */
38
	protected function getBaseTableName() {
39
		return 'globecoordinate';
40
	}
41
42
	/**
43
	 * @see DataValueHandler::completeTable
44
	 *
45
	 * @param Table $table
46
	 */
47
	protected function completeTable( Table $table ) {
48
		$table->addColumn( 'value_globe',   Type::STRING, array( 'length' => 255, 'notnull' => false ) );
49
		$table->addColumn( 'value_lat',     Type::FLOAT );
50
		$table->addColumn( 'value_lon',     Type::FLOAT );
51
		$table->addColumn( 'value_min_lat', Type::FLOAT );
52
		$table->addColumn( 'value_max_lat', Type::FLOAT );
53
		$table->addColumn( 'value_min_lon', Type::FLOAT );
54
		$table->addColumn( 'value_max_lon', Type::FLOAT );
55
		$table->addColumn( 'hash',          Type::STRING, array( 'length' => 32 ) );
56
57
		// TODO: We still need to find out if combined indexes are better or not.
58
		$table->addIndex( array( 'value_lon', 'value_lat' ) );
59
		$table->addIndex( array( 'value_min_lat', 'value_max_lat', 'value_min_lon', 'value_max_lon' ) );
60
	}
61
62
	/**
63
	 * @see DataValueHandler::getSortFieldNames
64
	 *
65
	 * @return string[]
66
	 */
67
	public function getSortFieldNames() {
68
		// Order by West-East first
69
		return array( 'value_lon', 'value_lat' );
70
	}
71
72
	/**
73
	 * @see DataValueHandler::getInsertValues
74
	 *
75
	 * @param DataValue $value
76
	 *
77
	 * @return array
78
	 * @throws InvalidArgumentException
79
	 */
80
	public function getInsertValues( DataValue $value ) {
81
		if ( !( $value instanceof GlobeCoordinateValue ) ) {
82
			throw new InvalidArgumentException( 'Value is not a GlobeCoordinateValue.' );
83
		}
84
85
		$normalized = $this->math->normalizeGlobeCoordinate( $value );
86
		$lat = $normalized->getLatitude();
87
		$lon = $normalized->getLongitude();
88
		$precision = abs( $value->getPrecision() );
89
90
		$values = array(
91
			'value_globe'   => $this->normalizeGlobe( $value->getGlobe() ),
92
			'value_lat'     => $lat,
93
			'value_lon'     => $lon,
94
			'value_min_lat' => $lat - $precision,
95
			'value_max_lat' => $lat + $precision,
96
			'value_min_lon' => $lon - $precision,
97
			'value_max_lon' => $lon + $precision,
98
99
			// No special human-readable hash needed, everything required is in the other fields.
100
			'hash' => $this->getEqualityFieldValue( $value ),
101
		);
102
103
		return $values;
104
	}
105
106
	/**
107
	 * @see DataValueHandler::addMatchConditions
108
	 *
109
	 * @param QueryBuilder $builder
110
	 * @param ValueDescription $description
111
	 *
112
	 * @throws InvalidArgumentException
113
	 * @throws QueryNotSupportedException
114
	 */
115
	public function addMatchConditions( QueryBuilder $builder, ValueDescription $description ) {
116
		$value = $description->getValue();
117
118
		if ( !( $value instanceof GlobeCoordinateValue ) ) {
119
			throw new InvalidArgumentException( 'Value is not a GlobeCoordinateValue.' );
120
		}
121
122
		if ( $description->getComparator() === ValueDescription::COMP_EQUAL ) {
123
			$this->addInRangeConditions( $builder, $value );
124
		} else {
125
			parent::addMatchConditions( $builder, $description );
126
		}
127
	}
128
129
	/**
130
	 * @param QueryBuilder $builder
131
	 * @param GlobeCoordinateValue $value
132
	 */
133
	private function addInRangeConditions( QueryBuilder $builder, GlobeCoordinateValue $value ) {
134
		$value = $this->math->normalizeGlobeCoordinate( $value );
135
		$globe = $this->normalizeGlobe( $value->getGlobe() );
136
		$lat = $value->getLatitude();
137
		$lon = $value->getLongitude();
138
		$epsilon = abs( $value->getPrecision() );
139
140
		if ( $globe === null ) {
141
			$builder->andWhere( 'value_globe IS NULL' );
142
		} else {
143
			$builder->andWhere( 'value_globe = :globe' );
144
			$builder->setParameter( ':globe', $globe );
145
		}
146
147
		$builder->andWhere( 'value_lat >= :min_lat' );
148
		$builder->andWhere( 'value_lat <= :max_lat' );
149
		$builder->andWhere( 'value_lon >= :min_lon' );
150
		$builder->andWhere( 'value_lon <= :max_lon' );
151
152
		$builder->setParameter( ':min_lat', $lat - $epsilon );
153
		$builder->setParameter( ':max_lat', $lat + $epsilon );
154
		$builder->setParameter( ':min_lon', $lon - $epsilon );
155
		$builder->setParameter( ':max_lon', $lon + $epsilon );
156
	}
157
158
	/**
159
	 * @param string $globe
160
	 *
161
	 * @return string|null
162
	 */
163
	private function normalizeGlobe( $globe ) {
164
		$globe = $this->math->normalizeGlobe( $globe );
165
166
		if ( $globe === GlobeCoordinateValue::GLOBE_EARTH ) {
167
			$globe = null;
168
		}
169
170
		return $globe;
171
	}
172
173
}
174