ClickHouse
179 строк · 6.3 Кб
1#include "config.h"
2
3#if USE_S2_GEOMETRY
4
5#include <Columns/ColumnsNumber.h>
6#include <Columns/ColumnTuple.h>
7#include <DataTypes/DataTypesNumber.h>
8#include <DataTypes/DataTypeTuple.h>
9#include <Functions/FunctionFactory.h>
10#include <Common/typeid_cast.h>
11#include <Common/NaNUtils.h>
12#include <base/range.h>
13
14#include "s2_fwd.h"
15
16namespace DB
17{
18
19namespace ErrorCodes
20{
21extern const int ILLEGAL_TYPE_OF_ARGUMENT;
22extern const int BAD_ARGUMENTS;
23extern const int ILLEGAL_COLUMN;
24}
25
26namespace
27{
28
29/**
30* The cap represents a portion of the sphere that has been cut off by a plane.
31* See comment for s2CapContains function.
32* This function returns the smallest cap that contains both of input caps.
33* It is represented by identifier of the center and a radius.
34*/
35class FunctionS2CapUnion : public IFunction
36{
37public:
38static constexpr auto name = "s2CapUnion";
39
40static FunctionPtr create(ContextPtr)
41{
42return std::make_shared<FunctionS2CapUnion>();
43}
44
45std::string getName() const override
46{
47return name;
48}
49
50size_t getNumberOfArguments() const override { return 4; }
51
52bool useDefaultImplementationForConstants() const override { return true; }
53
54bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return true; }
55
56DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
57{
58for (size_t index = 0; index < getNumberOfArguments(); ++index)
59{
60const auto * arg = arguments[index].get();
61if (index == 1 || index == 3)
62{
63if (!WhichDataType(arg).isFloat64())
64throw Exception(
65ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
66"Illegal type {} of argument {} of function {}. Must be Float64",
67arg->getName(), index + 1, getName());
68}
69else if (!WhichDataType(arg).isUInt64())
70throw Exception(
71ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
72"Illegal type {} of argument {} of function {}. Must be UInt64",
73arg->getName(), index + 1, getName()
74);
75}
76
77DataTypePtr center = std::make_shared<DataTypeUInt64>();
78DataTypePtr radius = std::make_shared<DataTypeFloat64>();
79
80return std::make_shared<DataTypeTuple>(DataTypes{center, radius});
81}
82
83ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override
84{
85auto non_const_arguments = arguments;
86for (auto & argument : non_const_arguments)
87argument.column = argument.column->convertToFullColumnIfConst();
88
89const auto * col_center1 = checkAndGetColumn<ColumnUInt64>(non_const_arguments[0].column.get());
90if (!col_center1)
91throw Exception(
92ErrorCodes::ILLEGAL_COLUMN,
93"Illegal type {} of argument {} of function {}. Must be UInt64",
94arguments[0].type->getName(),
951,
96getName());
97const auto & data_center1 = col_center1->getData();
98
99const auto * col_radius1 = checkAndGetColumn<ColumnFloat64>(non_const_arguments[1].column.get());
100if (!col_radius1)
101throw Exception(
102ErrorCodes::ILLEGAL_COLUMN,
103"Illegal type {} of argument {} of function {}. Must be Float64",
104arguments[1].type->getName(),
1052,
106getName());
107const auto & data_radius1 = col_radius1->getData();
108
109const auto * col_center2 = checkAndGetColumn<ColumnUInt64>(non_const_arguments[2].column.get());
110if (!col_center2)
111throw Exception(
112ErrorCodes::ILLEGAL_COLUMN,
113"Illegal type {} of argument {} of function {}. Must be UInt64",
114arguments[2].type->getName(),
1153,
116getName());
117const auto & data_center2 = col_center2->getData();
118
119const auto * col_radius2 = checkAndGetColumn<ColumnFloat64>(non_const_arguments[3].column.get());
120if (!col_radius2)
121throw Exception(
122ErrorCodes::ILLEGAL_COLUMN,
123"Illegal type {} of argument {} of function {}. Must be Float64",
124arguments[3].type->getName(),
1254,
126getName());
127const auto & data_radius2 = col_radius2->getData();
128
129auto col_res_center = ColumnUInt64::create();
130auto col_res_radius = ColumnFloat64::create();
131
132auto & vec_res_center = col_res_center->getData();
133vec_res_center.reserve(input_rows_count);
134
135auto & vec_res_radius = col_res_radius->getData();
136vec_res_radius.reserve(input_rows_count);
137
138for (size_t row = 0; row < input_rows_count; ++row)
139{
140const UInt64 first_center = data_center1[row];
141const Float64 first_radius = data_radius1[row];
142const UInt64 second_center = data_center2[row];
143const Float64 second_radius = data_radius2[row];
144
145if (isNaN(first_radius) || isNaN(second_radius))
146throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Radius of the cap must not be nan");
147
148if (std::isinf(first_radius) || std::isinf(second_radius))
149throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Radius of the cap must not be infinite");
150
151auto first_center_cell = S2CellId(first_center);
152auto second_center_cell = S2CellId(second_center);
153
154if (!first_center_cell.is_valid() || !second_center_cell.is_valid())
155throw Exception(ErrorCodes::BAD_ARGUMENTS, "Center of the cap is not valid");
156
157S2Cap cap1(first_center_cell.ToPoint(), S1Angle::Degrees(first_radius));
158S2Cap cap2(second_center_cell.ToPoint(), S1Angle::Degrees(second_radius));
159
160S2Cap cap_union = cap1.Union(cap2);
161
162vec_res_center.emplace_back(S2CellId(cap_union.center()).id());
163vec_res_radius.emplace_back(cap_union.GetRadius().degrees());
164}
165
166return ColumnTuple::create(Columns{std::move(col_res_center), std::move(col_res_radius)});
167}
168};
169
170}
171
172REGISTER_FUNCTION(S2CapUnion)
173{
174factory.registerFunction<FunctionS2CapUnion>();
175}
176
177}
178
179#endif
180