blob: b4967780d2c8dc08e059d513f517505ab10e9ab1 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#include "exprs/function/geo/functions_geo.h"
#include <glog/logging.h>
#include <algorithm>
#include <boost/iterator/iterator_facade.hpp>
#include <utility>
#include "common/compiler_util.h"
#include "core/assert_cast.h"
#include "core/block/block.h"
#include "core/block/column_with_type_and_name.h"
#include "core/column/column.h"
#include "core/column/column_array.h"
#include "core/column/column_execute_util.h"
#include "core/column/column_nullable.h"
#include "core/data_type/data_type_array.h"
#include "core/data_type/data_type_nullable.h"
#include "core/data_type/data_type_number.h"
#include "core/data_type/data_type_string.h"
#include "core/data_type/define_primitive_type.h"
#include "core/string_ref.h"
#include "exprs/function/geo/geo_common.h"
#include "exprs/function/geo/geo_types.h"
#include "exprs/function/simple_function_factory.h"
namespace doris {
struct StPoint {
static constexpr auto NAME = "st_point";
static const size_t NUM_ARGS = 2;
using Type = DataTypeString;
static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) {
DCHECK_EQ(arguments.size(), 2);
auto return_type = block.get_data_type(result);
auto x_col = ColumnView<TYPE_DOUBLE>::create(block.get_by_position(arguments[0]).column);
auto y_col = ColumnView<TYPE_DOUBLE>::create(block.get_by_position(arguments[1]).column);
const auto size = x_col.size();
auto res = ColumnString::create();
auto null_map = ColumnUInt8::create(size, 0);
auto& null_map_data = null_map->get_data();
GeoPoint point;
std::string buf;
for (int row = 0; row < size; ++row) {
auto cur_res = point.from_coord(x_col.value_at(row), y_col.value_at(row));
if (cur_res != GEO_PARSE_OK) {
null_map_data[row] = 1;
res->insert_default();
continue;
}
buf.clear();
point.encode_to(&buf);
res->insert_data(buf.data(), buf.size());
}
block.replace_by_position(result,
ColumnNullable::create(std::move(res), std::move(null_map)));
return Status::OK();
}
};
struct StAsTextName {
static constexpr auto NAME = "st_astext";
};
struct StAsWktName {
static constexpr auto NAME = "st_aswkt";
};
template <typename FunctionName>
struct StAsText {
static constexpr auto NAME = FunctionName::NAME;
static const size_t NUM_ARGS = 1;
using Type = DataTypeString;
static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) {
DCHECK_EQ(arguments.size(), 1);
auto return_type = block.get_data_type(result);
auto& input = block.get_by_position(arguments[0]).column;
auto size = input->size();
auto res = ColumnString::create();
auto null_map = ColumnUInt8::create(size, 0);
auto& null_map_data = null_map->get_data();
std::unique_ptr<GeoShape> shape;
for (int row = 0; row < size; ++row) {
auto shape_value = input->get_data_at(row);
shape = GeoShape::from_encoded(shape_value.data, shape_value.size);
if (shape == nullptr) {
null_map_data[row] = 1;
res->insert_default();
continue;
}
auto wkt = shape->as_wkt();
res->insert_data(wkt.data(), wkt.size());
}
block.replace_by_position(result,
ColumnNullable::create(std::move(res), std::move(null_map)));
return Status::OK();
}
};
struct StX {
static constexpr auto NAME = "st_x";
static const size_t NUM_ARGS = 1;
using Type = DataTypeFloat64;
static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) {
DCHECK_EQ(arguments.size(), 1);
auto return_type = block.get_data_type(result);
auto& input = block.get_by_position(arguments[0]).column;
auto size = input->size();
auto res = ColumnFloat64::create();
auto null_map = ColumnUInt8::create(size, 0);
auto& null_map_data = null_map->get_data();
res->reserve(size);
GeoPoint point;
for (int row = 0; row < size; ++row) {
auto point_value = input->get_data_at(row);
auto pt = point.decode_from(point_value.data, point_value.size);
if (!pt) {
null_map_data[row] = 1;
res->insert_default();
continue;
}
auto x_value = point.x();
res->insert_value(x_value);
}
block.replace_by_position(result,
ColumnNullable::create(std::move(res), std::move(null_map)));
return Status::OK();
}
};
struct StY {
static constexpr auto NAME = "st_y";
static const size_t NUM_ARGS = 1;
using Type = DataTypeFloat64;
static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) {
DCHECK_EQ(arguments.size(), 1);
auto return_type = block.get_data_type(result);
auto& input = block.get_by_position(arguments[0]).column;
auto size = input->size();
auto res = ColumnFloat64::create();
res->reserve(size);
auto null_map = ColumnUInt8::create(size, 0);
auto& null_map_data = null_map->get_data();
GeoPoint point;
for (int row = 0; row < size; ++row) {
auto point_value = input->get_data_at(row);
auto pt = point.decode_from(point_value.data, point_value.size);
if (!pt) {
null_map_data[row] = 1;
res->insert_default();
continue;
}
auto y_value = point.y();
res->insert_value(y_value);
}
block.replace_by_position(result,
ColumnNullable::create(std::move(res), std::move(null_map)));
return Status::OK();
}
};
struct StDistanceSphere {
static constexpr auto NAME = "st_distance_sphere";
static const size_t NUM_ARGS = 4;
using Type = DataTypeFloat64;
static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) {
DCHECK_EQ(arguments.size(), 4);
auto return_type = block.get_data_type(result);
auto x_lng = ColumnView<TYPE_DOUBLE>::create(block.get_by_position(arguments[0]).column);
auto x_lat = ColumnView<TYPE_DOUBLE>::create(block.get_by_position(arguments[1]).column);
auto y_lng = ColumnView<TYPE_DOUBLE>::create(block.get_by_position(arguments[2]).column);
auto y_lat = ColumnView<TYPE_DOUBLE>::create(block.get_by_position(arguments[3]).column);
const auto size = x_lng.size();
auto res = ColumnFloat64::create();
res->reserve(size);
auto null_map = ColumnUInt8::create(size, 0);
auto& null_map_data = null_map->get_data();
for (int row = 0; row < size; ++row) {
double distance = 0;
if (!GeoPoint::ComputeDistance(x_lng.value_at(row), x_lat.value_at(row),
y_lng.value_at(row), y_lat.value_at(row), &distance)) {
null_map_data[row] = 1;
res->insert_default();
continue;
}
res->insert_value(distance);
}
block.replace_by_position(result,
ColumnNullable::create(std::move(res), std::move(null_map)));
return Status::OK();
}
};
struct StAngleSphere {
static constexpr auto NAME = "st_angle_sphere";
static const size_t NUM_ARGS = 4;
using Type = DataTypeFloat64;
static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) {
DCHECK_EQ(arguments.size(), 4);
auto return_type = block.get_data_type(result);
auto x_lng = ColumnView<TYPE_DOUBLE>::create(block.get_by_position(arguments[0]).column);
auto x_lat = ColumnView<TYPE_DOUBLE>::create(block.get_by_position(arguments[1]).column);
auto y_lng = ColumnView<TYPE_DOUBLE>::create(block.get_by_position(arguments[2]).column);
auto y_lat = ColumnView<TYPE_DOUBLE>::create(block.get_by_position(arguments[3]).column);
const auto size = x_lng.size();
auto res = ColumnFloat64::create();
res->reserve(size);
auto null_map = ColumnUInt8::create(size, 0);
auto& null_map_data = null_map->get_data();
for (int row = 0; row < size; ++row) {
double angle = 0;
if (!GeoPoint::ComputeAngleSphere(x_lng.value_at(row), x_lat.value_at(row),
y_lng.value_at(row), y_lat.value_at(row), &angle)) {
null_map_data[row] = 1;
res->insert_default();
continue;
}
res->insert_value(angle);
}
block.replace_by_position(result,
ColumnNullable::create(std::move(res), std::move(null_map)));
return Status::OK();
}
};
struct StAngle {
static constexpr auto NAME = "st_angle";
static const size_t NUM_ARGS = 3;
using Type = DataTypeFloat64;
static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) {
DCHECK_EQ(arguments.size(), 3);
auto return_type = block.get_data_type(result);
auto p1 = ColumnView<TYPE_STRING>::create(block.get_by_position(arguments[0]).column);
auto p2 = ColumnView<TYPE_STRING>::create(block.get_by_position(arguments[1]).column);
auto p3 = ColumnView<TYPE_STRING>::create(block.get_by_position(arguments[2]).column);
const auto size = p1.size();
auto res = ColumnFloat64::create();
res->reserve(size);
auto null_map = ColumnUInt8::create(size, 0);
auto& null_map_data = null_map->get_data();
GeoPoint point1;
GeoPoint point2;
GeoPoint point3;
for (int row = 0; row < size; ++row) {
auto shape_value1 = p1.value_at(row);
auto pt1 = point1.decode_from(shape_value1.data, shape_value1.size);
if (!pt1) {
null_map_data[row] = 1;
res->insert_default();
continue;
}
auto shape_value2 = p2.value_at(row);
auto pt2 = point2.decode_from(shape_value2.data, shape_value2.size);
if (!pt2) {
null_map_data[row] = 1;
res->insert_default();
continue;
}
auto shape_value3 = p3.value_at(row);
auto pt3 = point3.decode_from(shape_value3.data, shape_value3.size);
if (!pt3) {
null_map_data[row] = 1;
res->insert_default();
continue;
}
double angle = 0;
if (!GeoPoint::ComputeAngle(&point1, &point2, &point3, &angle)) {
null_map_data[row] = 1;
res->insert_default();
continue;
}
res->insert_value(angle);
}
block.replace_by_position(result,
ColumnNullable::create(std::move(res), std::move(null_map)));
return Status::OK();
}
};
struct StAzimuth {
static constexpr auto NAME = "st_azimuth";
static const size_t NUM_ARGS = 2;
using Type = DataTypeFloat64;
static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) {
DCHECK_EQ(arguments.size(), 2);
auto return_type = block.get_data_type(result);
auto left_col = ColumnView<TYPE_STRING>::create(block.get_by_position(arguments[0]).column);
auto right_col =
ColumnView<TYPE_STRING>::create(block.get_by_position(arguments[1]).column);
const auto size = left_col.size();
auto res = ColumnFloat64::create();
res->reserve(size);
auto null_map = ColumnUInt8::create(size, 0);
auto& null_map_data = null_map->get_data();
GeoPoint point1;
GeoPoint point2;
for (int row = 0; row < size; ++row) {
auto shape_value1 = left_col.value_at(row);
auto pt1 = point1.decode_from(shape_value1.data, shape_value1.size);
auto shape_value2 = right_col.value_at(row);
auto pt2 = point2.decode_from(shape_value2.data, shape_value2.size);
if (!(pt1 && pt2)) {
null_map_data[row] = 1;
res->insert_default();
continue;
}
double angle = 0;
if (!GeoPoint::ComputeAzimuth(&point1, &point2, &angle)) {
null_map_data[row] = 1;
res->insert_default();
continue;
}
res->insert_value(angle);
}
block.replace_by_position(result,
ColumnNullable::create(std::move(res), std::move(null_map)));
return Status::OK();
}
};
struct StAreaSquareMeters {
static constexpr auto NAME = "st_area_square_meters";
static const size_t NUM_ARGS = 1;
using Type = DataTypeFloat64;
static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) {
DCHECK_EQ(arguments.size(), 1);
auto return_type = block.get_data_type(result);
auto col = block.get_by_position(arguments[0]).column->convert_to_full_column_if_const();
const auto size = col->size();
auto res = ColumnFloat64::create();
res->reserve(size);
auto null_map = ColumnUInt8::create(size, 0);
auto& null_map_data = null_map->get_data();
std::unique_ptr<GeoShape> shape;
for (int row = 0; row < size; ++row) {
auto shape_value = col->get_data_at(row);
shape = GeoShape::from_encoded(shape_value.data, shape_value.size);
if (!shape) {
null_map_data[row] = 1;
res->insert_default();
continue;
}
double area = 0;
if (!GeoShape::ComputeArea(shape.get(), &area, "square_meters")) {
null_map_data[row] = 1;
res->insert_default();
continue;
}
res->insert_value(area);
}
block.replace_by_position(result,
ColumnNullable::create(std::move(res), std::move(null_map)));
return Status::OK();
}
};
struct StAreaSquareKm {
static constexpr auto NAME = "st_area_square_km";
static const size_t NUM_ARGS = 1;
using Type = DataTypeFloat64;
static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) {
DCHECK_EQ(arguments.size(), 1);
auto return_type = block.get_data_type(result);
auto col = block.get_by_position(arguments[0]).column->convert_to_full_column_if_const();
const auto size = col->size();
auto res = ColumnFloat64::create();
res->reserve(size);
auto null_map = ColumnUInt8::create(size, 0);
auto& null_map_data = null_map->get_data();
std::unique_ptr<GeoShape> shape;
for (int row = 0; row < size; ++row) {
auto shape_value = col->get_data_at(row);
shape = GeoShape::from_encoded(shape_value.data, shape_value.size);
if (!shape) {
null_map_data[row] = 1;
res->insert_default();
continue;
}
double area = 0;
if (!GeoShape::ComputeArea(shape.get(), &area, "square_km")) {
null_map_data[row] = 1;
res->insert_default();
;
continue;
}
res->insert_value(area);
}
block.replace_by_position(result,
ColumnNullable::create(std::move(res), std::move(null_map)));
return Status::OK();
}
};
struct StCircle {
static constexpr auto NAME = "st_circle";
static const size_t NUM_ARGS = 3;
using Type = DataTypeString;
static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) {
DCHECK_EQ(arguments.size(), 3);
auto return_type = block.get_data_type(result);
auto center_lng =
ColumnView<TYPE_DOUBLE>::create(block.get_by_position(arguments[0]).column);
auto center_lat =
ColumnView<TYPE_DOUBLE>::create(block.get_by_position(arguments[1]).column);
auto radius = ColumnView<TYPE_DOUBLE>::create(block.get_by_position(arguments[2]).column);
const auto size = center_lng.size();
auto res = ColumnString::create();
auto null_map = ColumnUInt8::create(size, 0);
auto& null_map_data = null_map->get_data();
GeoCircle circle;
std::string buf;
for (int row = 0; row < size; ++row) {
auto lng_value = center_lng.value_at(row);
auto lat_value = center_lat.value_at(row);
auto radius_value = radius.value_at(row);
auto value = circle.init(lng_value, lat_value, radius_value);
if (value != GEO_PARSE_OK) {
null_map_data[row] = 1;
res->insert_default();
continue;
}
buf.clear();
circle.encode_to(&buf);
res->insert_data(buf.data(), buf.size());
}
block.replace_by_position(result,
ColumnNullable::create(std::move(res), std::move(null_map)));
return Status::OK();
}
};
template <typename Func>
struct StRelationFunction {
static constexpr auto NAME = Func::NAME;
static const size_t NUM_ARGS = 2;
using Type = DataTypeUInt8;
static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) {
DCHECK_EQ(arguments.size(), 2);
auto return_type = block.get_data_type(result);
auto left_col = ColumnView<TYPE_STRING>::create(block.get_by_position(arguments[0]).column);
auto right_col =
ColumnView<TYPE_STRING>::create(block.get_by_position(arguments[1]).column);
const auto size = left_col.size();
auto res = ColumnUInt8::create(size, 0);
auto null_map = ColumnUInt8::create(size, 0);
auto& null_map_data = null_map->get_data();
for (int row = 0; row < size; ++row) {
auto lhs_value = left_col.value_at(row);
auto rhs_value = right_col.value_at(row);
std::unique_ptr<GeoShape> shape1(
GeoShape::from_encoded(lhs_value.data, lhs_value.size));
std::unique_ptr<GeoShape> shape2(
GeoShape::from_encoded(rhs_value.data, rhs_value.size));
if (!shape1 || !shape2) {
null_map_data[row] = 1;
continue;
}
auto relation_value = Func::evaluate(shape1.get(), shape2.get());
res->get_data()[row] = relation_value;
}
block.replace_by_position(result,
ColumnNullable::create(std::move(res), std::move(null_map)));
return Status::OK();
}
};
struct StContainsFunc {
static constexpr auto NAME = "st_contains";
static bool evaluate(GeoShape* shape1, GeoShape* shape2) { return shape1->contains(shape2); }
};
struct StIntersectsFunc {
static constexpr auto NAME = "st_intersects";
static bool evaluate(GeoShape* shape1, GeoShape* shape2) { return shape1->intersects(shape2); }
};
struct StDisjointFunc {
static constexpr auto NAME = "st_disjoint";
static bool evaluate(GeoShape* shape1, GeoShape* shape2) { return shape1->disjoint(shape2); }
};
struct StTouchesFunc {
static constexpr auto NAME = "st_touches";
static bool evaluate(GeoShape* shape1, GeoShape* shape2) { return shape1->touches(shape2); }
};
struct StGeometryFromText {
static constexpr auto NAME = "st_geometryfromtext";
static constexpr GeoShapeType shape_type = GEO_SHAPE_ANY;
};
struct StGeomFromText {
static constexpr auto NAME = "st_geomfromtext";
static constexpr GeoShapeType shape_type = GEO_SHAPE_ANY;
};
struct StLineFromText {
static constexpr auto NAME = "st_linefromtext";
static constexpr GeoShapeType shape_type = GEO_SHAPE_LINE_STRING;
};
struct StLineStringFromText {
static constexpr auto NAME = "st_linestringfromtext";
static constexpr GeoShapeType shape_type = GEO_SHAPE_LINE_STRING;
};
struct StPolygon {
static constexpr auto NAME = "st_polygon";
static constexpr GeoShapeType shape_type = GEO_SHAPE_POLYGON;
};
struct StPolyFromText {
static constexpr auto NAME = "st_polyfromtext";
static constexpr GeoShapeType shape_type = GEO_SHAPE_POLYGON;
};
struct StPolygonFromText {
static constexpr auto NAME = "st_polygonfromtext";
static constexpr GeoShapeType shape_type = GEO_SHAPE_POLYGON;
};
template <typename Impl>
struct StGeoFromText {
static constexpr auto NAME = Impl::NAME;
static const size_t NUM_ARGS = 1;
using Type = DataTypeString;
static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) {
DCHECK_EQ(arguments.size(), 1);
auto return_type = block.get_data_type(result);
auto& geo = block.get_by_position(arguments[0]).column;
const auto size = geo->size();
auto res = ColumnString::create();
auto null_map = ColumnUInt8::create(size, 0);
auto& null_map_data = null_map->get_data();
GeoParseStatus status;
std::string buf;
for (int row = 0; row < size; ++row) {
auto value = geo->get_data_at(row);
auto shape = GeoShape::from_wkt(value.data, value.size, status);
if (shape == nullptr || status != GEO_PARSE_OK ||
(Impl::shape_type != GEO_SHAPE_ANY && shape->type() != Impl::shape_type)) {
null_map_data[row] = 1;
res->insert_default();
continue;
}
buf.clear();
shape->encode_to(&buf);
res->insert_data(buf.data(), buf.size());
}
block.replace_by_position(result,
ColumnNullable::create(std::move(res), std::move(null_map)));
return Status::OK();
}
};
struct StGeometryFromWKB {
static constexpr auto NAME = "st_geometryfromwkb";
static constexpr GeoShapeType shape_type = GEO_SHAPE_ANY;
};
struct StGeomFromWKB {
static constexpr auto NAME = "st_geomfromwkb";
static constexpr GeoShapeType shape_type = GEO_SHAPE_ANY;
};
template <typename Impl>
struct StGeoFromWkb {
static constexpr auto NAME = Impl::NAME;
static const size_t NUM_ARGS = 1;
using Type = DataTypeString;
static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) {
DCHECK_EQ(arguments.size(), 1);
auto return_type = block.get_data_type(result);
auto& geo = block.get_by_position(arguments[0]).column;
const auto size = geo->size();
auto res = ColumnString::create();
auto null_map = ColumnUInt8::create(size, 0);
auto& null_map_data = null_map->get_data();
GeoParseStatus status;
std::string buf;
for (int row = 0; row < size; ++row) {
auto value = geo->get_data_at(row);
std::unique_ptr<GeoShape> shape = GeoShape::from_wkb(value.data, value.size, status);
if (shape == nullptr || status != GEO_PARSE_OK) {
null_map_data[row] = 1;
res->insert_default();
continue;
}
buf.clear();
shape->encode_to(&buf);
res->insert_data(buf.data(), buf.size());
}
block.replace_by_position(result,
ColumnNullable::create(std::move(res), std::move(null_map)));
return Status::OK();
}
};
struct StAsBinary {
static constexpr auto NAME = "st_asbinary";
static const size_t NUM_ARGS = 1;
using Type = DataTypeString;
static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) {
DCHECK_EQ(arguments.size(), 1);
auto return_type = block.get_data_type(result);
auto res = ColumnString::create();
auto col = block.get_by_position(arguments[0]).column;
const auto size = col->size();
auto null_map = ColumnUInt8::create(size, 0);
auto& null_map_data = null_map->get_data();
std::unique_ptr<GeoShape> shape;
for (int row = 0; row < size; ++row) {
auto shape_value = col->get_data_at(row);
shape = GeoShape::from_encoded(shape_value.data, shape_value.size);
if (!shape) {
null_map_data[row] = 1;
res->insert_default();
continue;
}
std::string binary = GeoShape::as_binary(shape.get());
if (binary.empty()) {
null_map_data[row] = 1;
res->insert_default();
continue;
}
res->insert_data(binary.data(), binary.size());
}
block.replace_by_position(result,
ColumnNullable::create(std::move(res), std::move(null_map)));
return Status::OK();
}
};
struct StLength {
static constexpr auto NAME = "st_length";
static const size_t NUM_ARGS = 1;
using Type = DataTypeFloat64;
static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) {
DCHECK_EQ(arguments.size(), 1);
auto return_type = block.get_data_type(result);
auto col = block.get_by_position(arguments[0]).column->convert_to_full_column_if_const();
const auto size = col->size();
auto res = ColumnFloat64::create();
res->reserve(size);
auto null_map = ColumnUInt8::create(size, 0);
auto& null_map_data = null_map->get_data();
std::unique_ptr<GeoShape> shape;
for (int row = 0; row < size; ++row) {
auto shape_value = col->get_data_at(row);
shape = GeoShape::from_encoded(shape_value.data, shape_value.size);
if (!shape) {
null_map_data[row] = 1;
res->insert_default();
continue;
}
double length = shape->Length();
res->insert_value(length);
}
block.replace_by_position(result,
ColumnNullable::create(std::move(res), std::move(null_map)));
return Status::OK();
}
};
struct StGeometryType {
static constexpr auto NAME = "st_geometrytype";
static const size_t NUM_ARGS = 1;
using Type = DataTypeString;
static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) {
DCHECK_EQ(arguments.size(), 1);
auto return_type = block.get_data_type(result);
auto col = block.get_by_position(arguments[0]).column->convert_to_full_column_if_const();
const auto size = col->size();
auto res = ColumnString::create();
auto null_map = ColumnUInt8::create(size, 0);
auto& null_map_data = null_map->get_data();
std::unique_ptr<GeoShape> shape;
for (int row = 0; row < size; ++row) {
auto shape_value = col->get_data_at(row);
shape = GeoShape::from_encoded(shape_value.data, shape_value.size);
if (!shape) {
null_map_data[row] = 1;
res->insert_default();
continue;
}
auto geo_type = shape->GeometryType();
res->insert_data(geo_type.data(), geo_type.size());
}
block.replace_by_position(result,
ColumnNullable::create(std::move(res), std::move(null_map)));
return Status::OK();
}
};
struct StDistance {
static constexpr auto NAME = "st_distance";
static const size_t NUM_ARGS = 2;
using Type = DataTypeFloat64;
static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) {
DCHECK_EQ(arguments.size(), 2);
auto return_type = block.get_data_type(result);
const auto& [left_column, left_const] =
unpack_if_const(block.get_by_position(arguments[0]).column);
const auto& [right_column, right_const] =
unpack_if_const(block.get_by_position(arguments[1]).column);
const auto size = std::max(left_column->size(), right_column->size());
auto res = ColumnFloat64::create();
res->reserve(size);
auto null_map = ColumnUInt8::create(size, 0);
auto& null_map_data = null_map->get_data();
if (left_const) {
const_vector(left_column, right_column, res, null_map_data, size);
} else if (right_const) {
vector_const(left_column, right_column, res, null_map_data, size);
} else {
vector_vector(left_column, right_column, res, null_map_data, size);
}
block.replace_by_position(result,
ColumnNullable::create(std::move(res), std::move(null_map)));
return Status::OK();
}
private:
static bool decode_shape(const StringRef& value, std::unique_ptr<GeoShape>& shape) {
shape = GeoShape::from_encoded(value.data, value.size);
return static_cast<bool>(shape);
}
static void loop_do(StringRef& lhs_value, StringRef& rhs_value,
std::vector<std::unique_ptr<GeoShape>>& shapes,
ColumnFloat64::MutablePtr& res, NullMap& null_map, int row) {
StringRef* strs[2] = {&lhs_value, &rhs_value};
for (int i = 0; i < 2; ++i) {
if (!decode_shape(*strs[i], shapes[i])) {
null_map[row] = 1;
res->insert_default();
return;
}
}
double distance = shapes[0]->Distance(shapes[1].get());
if (UNLIKELY(distance < 0)) {
null_map[row] = 1;
res->insert_default();
return;
}
res->insert_value(distance);
}
static void const_vector(const ColumnPtr& left_column, const ColumnPtr& right_column,
ColumnFloat64::MutablePtr& res, NullMap& null_map, const size_t size) {
const auto* left_string = assert_cast<const ColumnString*>(left_column.get());
const auto* right_string = assert_cast<const ColumnString*>(right_column.get());
auto lhs_value = left_string->get_data_at(0);
std::unique_ptr<GeoShape> lhs_shape;
if (!decode_shape(lhs_value, lhs_shape)) {
for (int row = 0; row < size; ++row) {
null_map[row] = 1;
res->insert_default();
}
return;
}
std::unique_ptr<GeoShape> rhs_shape;
for (int row = 0; row < size; ++row) {
auto rhs_value = right_string->get_data_at(row);
if (!decode_shape(rhs_value, rhs_shape)) {
null_map[row] = 1;
res->insert_default();
continue;
}
double distance = lhs_shape->Distance(rhs_shape.get());
if (UNLIKELY(distance < 0)) {
null_map[row] = 1;
res->insert_default();
continue;
}
res->insert_value(distance);
}
}
static void vector_const(const ColumnPtr& left_column, const ColumnPtr& right_column,
ColumnFloat64::MutablePtr& res, NullMap& null_map, const size_t size) {
const auto* left_string = assert_cast<const ColumnString*>(left_column.get());
const auto* right_string = assert_cast<const ColumnString*>(right_column.get());
auto rhs_value = right_string->get_data_at(0);
std::unique_ptr<GeoShape> rhs_shape;
if (!decode_shape(rhs_value, rhs_shape)) {
for (int row = 0; row < size; ++row) {
null_map[row] = 1;
res->insert_default();
}
return;
}
std::unique_ptr<GeoShape> lhs_shape;
for (int row = 0; row < size; ++row) {
auto lhs_value = left_string->get_data_at(row);
if (!decode_shape(lhs_value, lhs_shape)) {
null_map[row] = 1;
res->insert_default();
continue;
}
double distance = lhs_shape->Distance(rhs_shape.get());
if (UNLIKELY(distance < 0)) {
null_map[row] = 1;
res->insert_default();
continue;
}
res->insert_value(distance);
}
}
static void vector_vector(const ColumnPtr& left_column, const ColumnPtr& right_column,
ColumnFloat64::MutablePtr& res, NullMap& null_map,
const size_t size) {
const auto* left_string = assert_cast<const ColumnString*>(left_column.get());
const auto* right_string = assert_cast<const ColumnString*>(right_column.get());
std::vector<std::unique_ptr<GeoShape>> shapes(2);
for (int row = 0; row < size; ++row) {
auto lhs_value = left_string->get_data_at(row);
auto rhs_value = right_string->get_data_at(row);
loop_do(lhs_value, rhs_value, shapes, res, null_map, row);
}
}
};
struct StNumGeometries {
static constexpr auto NAME = "st_numgeometries";
static const size_t NUM_ARGS = 1;
using Type = DataTypeInt64;
static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) {
DCHECK_EQ(arguments.size(), 1);
auto col = ColumnView<TYPE_STRING>::create(block.get_by_position(arguments[0]).column);
const auto size = col.size();
auto res = ColumnInt64::create();
res->reserve(size);
auto null_map = ColumnUInt8::create(size, 0);
auto& null_map_data = null_map->get_data();
for (int row = 0; row < size; ++row) {
auto value = col.value_at(row);
auto shape = GeoShape::from_encoded(value.data, value.size);
if (!shape) {
null_map_data[row] = 1;
res->insert_default();
continue;
}
res->insert_value(shape->num_geometries());
}
block.replace_by_position(result,
ColumnNullable::create(std::move(res), std::move(null_map)));
return Status::OK();
}
};
class FunctionStGeometries final : public IFunction {
public:
static constexpr auto name = "st_geometries";
static FunctionPtr create() { return std::make_shared<FunctionStGeometries>(); }
String get_name() const override { return name; }
size_t get_number_of_arguments() const override { return 1; }
bool is_variadic() const override { return false; }
DataTypePtr get_return_type_impl(const DataTypes& arguments) const override {
return make_nullable(
std::make_shared<DataTypeArray>(make_nullable(std::make_shared<DataTypeString>())));
}
Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
uint32_t result, size_t input_rows_count) const override {
DCHECK_EQ(arguments.size(), 1);
auto col = ColumnView<TYPE_STRING>::create(block.get_by_position(arguments[0]).column);
const auto size = col.size();
auto nested_data = ColumnString::create();
auto offsets_col = ColumnArray::ColumnOffsets::create();
auto& offsets = offsets_col->get_data();
offsets.reserve(size);
auto null_map = ColumnUInt8::create(size, 0);
auto& null_map_data = null_map->get_data();
size_t current_offset = 0;
std::string buf;
for (size_t row = 0; row < size; ++row) {
auto shape_value = col.value_at(row);
auto shape = GeoShape::from_encoded(shape_value.data, shape_value.size);
if (!shape) {
null_map_data[row] = 1;
offsets.push_back(current_offset);
continue;
}
if (shape->type() == GEO_SHAPE_MULTI_POLYGON) {
auto* multi_polygon = static_cast<GeoMultiPolygon*>(shape.get());
const auto& polygons = multi_polygon->polygons();
if (polygons.empty()) {
null_map_data[row] = 1;
offsets.push_back(current_offset);
continue;
}
for (const auto& polygon : polygons) {
DCHECK(polygon != nullptr);
buf.clear();
polygon->encode_to(&buf);
nested_data->insert_data(buf.data(), buf.size());
++current_offset;
}
} else {
nested_data->insert_data(shape_value.data, shape_value.size);
++current_offset;
}
offsets.push_back(current_offset);
}
auto nested_null_map = ColumnUInt8::create(nested_data->size(), 0);
auto nested_nullable =
ColumnNullable::create(std::move(nested_data), std::move(nested_null_map));
auto array_col = ColumnArray::create(std::move(nested_nullable), std::move(offsets_col));
block.replace_by_position(
result, ColumnNullable::create(std::move(array_col), std::move(null_map)));
return Status::OK();
}
};
struct StNumPoints {
static constexpr auto NAME = "st_numpoints";
static const size_t NUM_ARGS = 1;
using Type = DataTypeInt64;
static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) {
DCHECK_EQ(arguments.size(), 1);
auto col = ColumnView<TYPE_STRING>::create(block.get_by_position(arguments[0]).column);
const auto size = col.size();
auto res = ColumnInt64::create();
res->reserve(size);
auto null_map = ColumnUInt8::create(size, 0);
auto& null_map_data = null_map->get_data();
for (int row = 0; row < size; ++row) {
auto value = col.value_at(row);
auto shape = GeoShape::from_encoded(value.data, value.size);
if (!shape) {
null_map_data[row] = 1;
res->insert_default();
continue;
}
auto num_points = shape->num_points();
if (num_points < 0) {
null_map_data[row] = 1;
res->insert_default();
continue;
}
res->insert_value(num_points);
}
block.replace_by_position(result,
ColumnNullable::create(std::move(res), std::move(null_map)));
return Status::OK();
}
};
void register_function_geo(SimpleFunctionFactory& factory) {
factory.register_function<GeoFunction<StPoint>>();
factory.register_function<GeoFunction<StAsText<StAsWktName>>>();
factory.register_function<GeoFunction<StAsText<StAsTextName>>>();
factory.register_function<GeoFunction<StX>>();
factory.register_function<GeoFunction<StY>>();
factory.register_function<GeoFunction<StDistanceSphere>>();
factory.register_function<GeoFunction<StAngleSphere>>();
factory.register_function<GeoFunction<StAngle>>();
factory.register_function<GeoFunction<StAzimuth>>();
factory.register_function<GeoFunction<StRelationFunction<StContainsFunc>>>();
factory.register_function<GeoFunction<StRelationFunction<StIntersectsFunc>>>();
factory.register_function<GeoFunction<StRelationFunction<StDisjointFunc>>>();
factory.register_function<GeoFunction<StRelationFunction<StTouchesFunc>>>();
factory.register_function<GeoFunction<StCircle>>();
factory.register_function<GeoFunction<StGeoFromText<StGeometryFromText>>>();
factory.register_function<GeoFunction<StGeoFromText<StGeomFromText>>>();
factory.register_function<GeoFunction<StGeoFromText<StLineFromText>>>();
factory.register_function<GeoFunction<StGeoFromText<StLineStringFromText>>>();
factory.register_function<GeoFunction<StGeoFromText<StPolygon>>>();
factory.register_function<GeoFunction<StGeoFromText<StPolygonFromText>>>();
factory.register_function<GeoFunction<StGeoFromText<StPolyFromText>>>();
factory.register_function<GeoFunction<StAreaSquareMeters>>();
factory.register_function<GeoFunction<StAreaSquareKm>>();
factory.register_function<GeoFunction<StGeoFromWkb<StGeometryFromWKB>>>();
factory.register_function<GeoFunction<StGeoFromWkb<StGeomFromWKB>>>();
factory.register_function<GeoFunction<StAsBinary>>();
factory.register_function<GeoFunction<StLength>>();
factory.register_function<GeoFunction<StGeometryType>>();
factory.register_function<GeoFunction<StDistance>>();
factory.register_function<GeoFunction<StNumGeometries>>();
factory.register_function<GeoFunction<StNumPoints>>();
factory.register_alias("st_numpoints", "st_npoints");
factory.register_function<FunctionStGeometries>();
}
} // namespace doris