ClickHouse
119 строк · 3.7 Кб
1#include <Functions/FunctionFactory.h>
2#include <Functions/GeoHash.h>
3#include <Functions/FunctionHelpers.h>
4
5#include <Columns/ColumnString.h>
6#include <DataTypes/DataTypeString.h>
7#include <DataTypes/DataTypesNumber.h>
8
9#include <string>
10
11#define GEOHASH_MAX_TEXT_LENGTH 16
12
13
14namespace DB
15{
16
17namespace ErrorCodes
18{
19extern const int LOGICAL_ERROR;
20extern const int TOO_MANY_ARGUMENTS_FOR_FUNCTION;
21}
22
23namespace
24{
25
26// geohashEncode(lon float32/64, lat float32/64, length UInt8) => string
27class FunctionGeohashEncode : public IFunction
28{
29public:
30static constexpr auto name = "geohashEncode";
31static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionGeohashEncode>(); }
32
33String getName() const override
34{
35return name;
36}
37
38bool isVariadic() const override { return true; }
39size_t getNumberOfArguments() const override { return 0; }
40bool useDefaultImplementationForConstants() const override { return true; }
41bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return true; }
42
43DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
44{
45validateArgumentType(*this, arguments, 0, isFloat, "float");
46validateArgumentType(*this, arguments, 1, isFloat, "float");
47if (arguments.size() == 3)
48{
49validateArgumentType(*this, arguments, 2, isInteger, "integer");
50}
51if (arguments.size() > 3)
52{
53throw Exception(ErrorCodes::TOO_MANY_ARGUMENTS_FOR_FUNCTION, "Too many arguments for function {} expected at most 3",
54getName());
55}
56
57return std::make_shared<DataTypeString>();
58}
59
60ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const override
61{
62const IColumn * longitude = arguments[0].column.get();
63const IColumn * latitude = arguments[1].column.get();
64
65ColumnPtr precision;
66if (arguments.size() < 3)
67precision = DataTypeUInt8().createColumnConst(longitude->size(), GEOHASH_MAX_TEXT_LENGTH);
68else
69precision = arguments[2].column;
70
71ColumnPtr res_column;
72vector(longitude, latitude, precision.get(), res_column);
73return res_column;
74}
75
76private:
77void vector(const IColumn * lon_column, const IColumn * lat_column, const IColumn * precision_column, ColumnPtr & result) const
78{
79auto col_str = ColumnString::create();
80ColumnString::Chars & out_vec = col_str->getChars();
81ColumnString::Offsets & out_offsets = col_str->getOffsets();
82
83const size_t size = lat_column->size();
84
85out_offsets.resize(size);
86out_vec.resize(size * (GEOHASH_MAX_TEXT_LENGTH + 1));
87
88char * begin = reinterpret_cast<char *>(out_vec.data());
89char * pos = begin;
90
91for (size_t i = 0; i < size; ++i)
92{
93const Float64 longitude_value = lon_column->getFloat64(i);
94const Float64 latitude_value = lat_column->getFloat64(i);
95const UInt64 precision_value = std::min<UInt64>(precision_column->get64(i), GEOHASH_MAX_TEXT_LENGTH);
96
97const size_t encoded_size = geohashEncode(longitude_value, latitude_value, precision_value, pos);
98
99pos += encoded_size;
100*pos = '\0';
101out_offsets[i] = ++pos - begin;
102}
103out_vec.resize(pos - begin);
104
105if (!out_offsets.empty() && out_offsets.back() != out_vec.size())
106throw Exception(ErrorCodes::LOGICAL_ERROR, "Column size mismatch (internal logical error)");
107
108result = std::move(col_str);
109}
110};
111
112}
113
114REGISTER_FUNCTION(GeohashEncode)
115{
116factory.registerFunction<FunctionGeohashEncode>();
117}
118
119}
120