1<?php
2
3namespace Elastica\Query;
4
5use Elastica\Exception\InvalidException;
6
7/**
8 * Geo distance query.
9 *
10 * @author Nicolas Ruflin <spam@ruflin.com>
11 *
12 * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-geo-distance-query.html
13 */
14abstract class AbstractGeoDistance extends AbstractQuery
15{
16    const LOCATION_TYPE_GEOHASH = 'geohash';
17    const LOCATION_TYPE_LATLON = 'latlon';
18
19    /**
20     * Location type.
21     *
22     * Decides if this query uses latitude/longitude or geohash for the location.
23     * Values are "latlon" or "geohash".
24     *
25     * @var string
26     */
27    protected $_locationType;
28
29    /**
30     * Key.
31     *
32     * @var string
33     */
34    protected $_key;
35
36    /**
37     * Latitude.
38     *
39     * @var float
40     */
41    protected $_latitude;
42
43    /**
44     * Longitude.
45     *
46     * @var float
47     */
48    protected $_longitude;
49
50    /**
51     * Geohash.
52     *
53     * @var string
54     */
55    protected $_geohash;
56
57    /**
58     * Create GeoDistance object.
59     *
60     * @param string       $key      Key
61     * @param array|string $location Location as array or geohash: array('lat' => 48.86, 'lon' => 2.35) OR 'drm3btev3e86'
62     *
63     * @internal param string $distance Distance
64     */
65    public function __construct(string $key, $location)
66    {
67        $this->setKey($key);
68        $this->setLocation($location);
69    }
70
71    /**
72     * @param string $key
73     *
74     * @return $this
75     */
76    public function setKey(string $key): self
77    {
78        $this->_key = $key;
79
80        return $this;
81    }
82
83    /**
84     * @param array|string $location
85     *
86     * @throws InvalidException
87     *
88     * @return $this
89     */
90    public function setLocation($location): self
91    {
92        // Location
93        if (\is_array($location)) { // Latitude/Longitude
94            // Latitude
95            if (isset($location['lat'])) {
96                $this->setLatitude($location['lat']);
97            } else {
98                throw new InvalidException('$location[\'lat\'] has to be set');
99            }
100
101            // Longitude
102            if (isset($location['lon'])) {
103                $this->setLongitude($location['lon']);
104            } else {
105                throw new InvalidException('$location[\'lon\'] has to be set');
106            }
107        } elseif (\is_string($location)) { // Geohash
108            $this->setGeohash($location);
109        } else { // Invalid location
110            throw new InvalidException('$location has to be an array (latitude/longitude) or a string (geohash)');
111        }
112
113        return $this;
114    }
115
116    /**
117     * @param float $latitude
118     *
119     * @return $this
120     */
121    public function setLatitude(float $latitude): self
122    {
123        $this->_latitude = $latitude;
124        $this->_locationType = self::LOCATION_TYPE_LATLON;
125
126        return $this;
127    }
128
129    /**
130     * @param float $longitude
131     *
132     * @return $this
133     */
134    public function setLongitude(float $longitude): self
135    {
136        $this->_longitude = $longitude;
137        $this->_locationType = self::LOCATION_TYPE_LATLON;
138
139        return $this;
140    }
141
142    /**
143     * @param string $geohash
144     *
145     * @return $this
146     */
147    public function setGeohash(string $geohash): self
148    {
149        $this->_geohash = $geohash;
150        $this->_locationType = self::LOCATION_TYPE_GEOHASH;
151
152        return $this;
153    }
154
155    /**
156     * @throws InvalidException
157     *
158     * @return array|string
159     */
160    protected function _getLocationData()
161    {
162        if (self::LOCATION_TYPE_LATLON === $this->_locationType) { // Latitude/longitude
163            $location = [];
164
165            if (isset($this->_latitude)) { // Latitude
166                $location['lat'] = $this->_latitude;
167            } else {
168                throw new InvalidException('Latitude has to be set');
169            }
170
171            if (isset($this->_longitude)) { // Geohash
172                $location['lon'] = $this->_longitude;
173            } else {
174                throw new InvalidException('Longitude has to be set');
175            }
176        } elseif (self::LOCATION_TYPE_GEOHASH === $this->_locationType) { // Geohash
177            $location = $this->_geohash;
178        } else { // Invalid location type
179            throw new InvalidException('Invalid location type');
180        }
181
182        return $location;
183    }
184
185    /**
186     * {@inheritdoc}
187     */
188    public function toArray(): array
189    {
190        $this->setParam($this->_key, $this->_getLocationData());
191
192        return parent::toArray();
193    }
194}
195