1<?php
2
3namespace Elastica\Aggregation;
4
5use Elastica\Exception\InvalidException;
6
7/**
8 * Class GeoDistance.
9 *
10 * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-geodistance-aggregation.html
11 */
12class GeoDistance extends AbstractAggregation
13{
14    const DISTANCE_TYPE_ARC = 'arc';
15    const DISTANCE_TYPE_PLANE = 'plane';
16
17    const DEFAULT_DISTANCE_TYPE_VALUE = self::DISTANCE_TYPE_ARC;
18    const DEFAULT_UNIT_VALUE = 'm';
19
20    /**
21     * @param string       $name   the name if this aggregation
22     * @param string       $field  the field on which to perform this aggregation
23     * @param string|array $origin the point from which distances will be calculated
24     */
25    public function __construct(string $name, string $field, $origin)
26    {
27        parent::__construct($name);
28        $this->setField($field)->setOrigin($origin);
29    }
30
31    /**
32     * Set the field for this aggregation.
33     *
34     * @param string $field the name of the document field on which to perform this aggregation
35     *
36     * @return $this
37     */
38    public function setField(string $field): self
39    {
40        return $this->setParam('field', $field);
41    }
42
43    /**
44     * Set the origin point from which distances will be calculated.
45     *
46     * @param string|array $origin valid formats are array("lat" => 52.3760, "lon" => 4.894), "52.3760, 4.894", and array(4.894, 52.3760)
47     *
48     * @return $this
49     */
50    public function setOrigin($origin): self
51    {
52        return $this->setParam('origin', $origin);
53    }
54
55    /**
56     * Add a distance range to this aggregation.
57     *
58     * @param int $fromValue a distance
59     * @param int $toValue   a distance
60     *
61     * @throws \Elastica\Exception\InvalidException
62     *
63     * @return $this
64     */
65    public function addRange(int $fromValue = null, int $toValue = null): self
66    {
67        if (null === $fromValue && null === $toValue) {
68            throw new InvalidException('Either fromValue or toValue must be set. Both cannot be null.');
69        }
70
71        $range = [];
72
73        if (null !== $fromValue) {
74            $range['from'] = $fromValue;
75        }
76
77        if (null !== $toValue) {
78            $range['to'] = $toValue;
79        }
80
81        return $this->addParam('ranges', $range);
82    }
83
84    /**
85     * Set the unit of distance measure for this aggregation.
86     *
87     * @param string $unit defaults to m
88     *
89     * @return $this
90     */
91    public function setUnit(string $unit): self
92    {
93        return $this->setParam('unit', $unit);
94    }
95
96    /**
97     * Set the method by which distances will be calculated.
98     *
99     * @param string $distanceType see DISTANCE_TYPE_* constants for options. Defaults to arc.
100     *
101     * @return $this
102     */
103    public function setDistanceType(string $distanceType): self
104    {
105        return $this->setParam('distance_type', $distanceType);
106    }
107}
108