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