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    public const LOCATION_TYPE_GEOHASH = 'geohash';
17    public 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|null
40     */
41    protected $_latitude;
42
43    /**
44     * Longitude.
45     *
46     * @var float|null
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 array|string $location Location as array or geohash: array('lat' => 48.86, 'lon' => 2.35) OR 'drm3btev3e86'
61     *
62     * @internal param string $distance Distance
63     */
64    public function __construct(string $key, $location)
65    {
66        $this->setKey($key);
67        $this->setLocation($location);
68    }
69
70    /**
71     * @return $this
72     */
73    public function setKey(string $key): self
74    {
75        $this->_key = $key;
76
77        return $this;
78    }
79
80    /**
81     * @param array|string $location
82     *
83     * @throws InvalidException
84     *
85     * @return $this
86     */
87    public function setLocation($location): self
88    {
89        // Location
90        if (\is_array($location)) { // Latitude/Longitude
91            // Latitude
92            if (isset($location['lat'])) {
93                $this->setLatitude($location['lat']);
94            } else {
95                throw new InvalidException('$location[\'lat\'] has to be set');
96            }
97
98            // Longitude
99            if (isset($location['lon'])) {
100                $this->setLongitude($location['lon']);
101            } else {
102                throw new InvalidException('$location[\'lon\'] has to be set');
103            }
104        } elseif (\is_string($location)) { // Geohash
105            $this->setGeohash($location);
106        } else { // Invalid location
107            throw new InvalidException('$location has to be an array (latitude/longitude) or a string (geohash)');
108        }
109
110        return $this;
111    }
112
113    /**
114     * @return $this
115     */
116    public function setLatitude(float $latitude): self
117    {
118        $this->_latitude = $latitude;
119        $this->_locationType = self::LOCATION_TYPE_LATLON;
120
121        return $this;
122    }
123
124    /**
125     * @return $this
126     */
127    public function setLongitude(float $longitude): self
128    {
129        $this->_longitude = $longitude;
130        $this->_locationType = self::LOCATION_TYPE_LATLON;
131
132        return $this;
133    }
134
135    /**
136     * @return $this
137     */
138    public function setGeohash(string $geohash): self
139    {
140        $this->_geohash = $geohash;
141        $this->_locationType = self::LOCATION_TYPE_GEOHASH;
142
143        return $this;
144    }
145
146    /**
147     * {@inheritdoc}
148     */
149    public function toArray(): array
150    {
151        $this->setParam($this->_key, $this->_getLocationData());
152
153        return parent::toArray();
154    }
155
156    /**
157     * @throws InvalidException
158     *
159     * @return array|string
160     */
161    protected function _getLocationData()
162    {
163        if (self::LOCATION_TYPE_LATLON === $this->_locationType) { // Latitude/longitude
164            $location = [];
165
166            if (isset($this->_latitude)) { // Latitude
167                $location['lat'] = $this->_latitude;
168            } else {
169                throw new InvalidException('Latitude has to be set');
170            }
171
172            if (isset($this->_longitude)) { // Geohash
173                $location['lon'] = $this->_longitude;
174            } else {
175                throw new InvalidException('Longitude has to be set');
176            }
177        } elseif (self::LOCATION_TYPE_GEOHASH === $this->_locationType) { // Geohash
178            $location = $this->_geohash;
179        } else { // Invalid location type
180            throw new InvalidException('Invalid location type');
181        }
182
183        return $location;
184    }
185}
186