#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace DB { namespace ErrorCodes { extern const int BAD_ARGUMENTS; } template using Ring = boost::geometry::model::ring; template using Polygon = boost::geometry::model::polygon; template using MultiPolygon = boost::geometry::model::multi_polygon>; template using Geometry = boost::variant, Polygon, MultiPolygon>; template using Figure = boost::variant, Polygon, MultiPolygon>; using CartesianPoint = boost::geometry::model::d2::point_xy; using CartesianRing = Ring; using CartesianPolygon = Polygon; using CartesianMultiPolygon = MultiPolygon; using CartesianGeometry = Geometry; using GeographicPoint = boost::geometry::model::point>; using GeographicRing = Ring; using GeographicPolygon = Polygon; using GeographicMultiPolygon = MultiPolygon; using GeographicGeometry = Geometry; template class RingFromColumnConverter; template class PolygonFromColumnConverter; template class MultiPolygonFromColumnConverter; /** * Class which takes some boost type and returns a pair of numbers. * They are (x,y) in case of cartesian coordinated and (lon,lat) in case of geographic. */ template class PointFromColumnConverter { public: explicit PointFromColumnConverter(ColumnPtr col_) : col(col_) { } std::vector convert() const { return convertImpl(0, col->size()); } private: std::vector convertImpl(size_t shift, size_t count) const; friend class RingFromColumnConverter; ColumnPtr col{nullptr}; }; template class RingFromColumnConverter { public: explicit RingFromColumnConverter(ColumnPtr col_) : col(col_) , point_converter(typeid_cast(*col_).getDataPtr()) { } std::vector> convert() const; private: friend class PointFromColumnConverter; /// To prevent use-after-free and increase column lifetime. ColumnPtr col{nullptr}; const PointFromColumnConverter point_converter{}; }; template class PolygonFromColumnConverter { public: explicit PolygonFromColumnConverter(ColumnPtr col_) : col(col_) , ring_converter(typeid_cast(*col_).getDataPtr()) { } std::vector> convert() const; private: friend class MultiPolygonFromColumnConverter; /// To prevent use-after-free and increase column lifetime. ColumnPtr col{nullptr}; const RingFromColumnConverter ring_converter{}; }; template class MultiPolygonFromColumnConverter { public: explicit MultiPolygonFromColumnConverter(ColumnPtr col_) : col(col_) , polygon_converter(typeid_cast(*col_).getDataPtr()) {} std::vector> convert() const; private: /// To prevent use-after-free and increase column lifetime. ColumnPtr col{nullptr}; const PolygonFromColumnConverter polygon_converter{}; }; extern template class PointFromColumnConverter; extern template class PointFromColumnConverter; extern template class RingFromColumnConverter; extern template class RingFromColumnConverter; extern template class PolygonFromColumnConverter; extern template class PolygonFromColumnConverter; extern template class MultiPolygonFromColumnConverter; extern template class MultiPolygonFromColumnConverter; /// To serialize Geographic or Cartesian point (a pair of numbers in both cases). template class PointSerializer { public: PointSerializer() : first(ColumnFloat64::create()) , second(ColumnFloat64::create()) , first_container(first->getData()) , second_container(second->getData()) {} explicit PointSerializer(size_t n) : first(ColumnFloat64::create(n)) , second(ColumnFloat64::create(n)) , first_container(first->getData()) , second_container(second->getData()) {} void add(const Point & point) { first_container.emplace_back(point.template get<0>()); second_container.emplace_back(point.template get<1>()); } ColumnPtr finalize() { Columns columns(2); columns[0] = std::move(first); columns[1] = std::move(second); return ColumnTuple::create(columns); } private: ColumnFloat64::MutablePtr first; ColumnFloat64::MutablePtr second; ColumnFloat64::Container & first_container; ColumnFloat64::Container & second_container; }; /// Serialize Point, Ring as Ring template class RingSerializer { public: RingSerializer() : offsets(ColumnUInt64::create()) {} explicit RingSerializer(size_t n) : offsets(ColumnUInt64::create(n)) {} void add(const Ring & ring) { size += ring.size(); offsets->insertValue(size); for (const auto & point : ring) point_serializer.add(point); } ColumnPtr finalize() { return ColumnArray::create(point_serializer.finalize(), std::move(offsets)); } private: size_t size = 0; PointSerializer point_serializer; ColumnUInt64::MutablePtr offsets; }; /// Serialize Point, Ring, Polygon as Polygon template class PolygonSerializer { public: PolygonSerializer() : offsets(ColumnUInt64::create()) {} explicit PolygonSerializer(size_t n) : offsets(ColumnUInt64::create(n)) {} void add(const Ring & ring) { size++; offsets->insertValue(size); ring_serializer.add(ring); } void add(const Polygon & polygon) { size += 1 + polygon.inners().size(); offsets->insertValue(size); ring_serializer.add(polygon.outer()); for (const auto & ring : polygon.inners()) ring_serializer.add(ring); } ColumnPtr finalize() { return ColumnArray::create(ring_serializer.finalize(), std::move(offsets)); } private: size_t size = 0; RingSerializer ring_serializer; ColumnUInt64::MutablePtr offsets; }; /// Serialize Point, Ring, Polygon, MultiPolygon as MultiPolygon template class MultiPolygonSerializer { public: MultiPolygonSerializer() : offsets(ColumnUInt64::create()) {} explicit MultiPolygonSerializer(size_t n) : offsets(ColumnUInt64::create(n)) {} void add(const Ring & ring) { size++; offsets->insertValue(size); polygon_serializer.add(ring); } void add(const Polygon & polygon) { size++; offsets->insertValue(size); polygon_serializer.add(polygon); } void add(const MultiPolygon & multi_polygon) { size += multi_polygon.size(); offsets->insertValue(size); for (const auto & polygon : multi_polygon) { polygon_serializer.add(polygon); } } ColumnPtr finalize() { return ColumnArray::create(polygon_serializer.finalize(), std::move(offsets)); } private: size_t size = 0; PolygonSerializer polygon_serializer; ColumnUInt64::MutablePtr offsets; }; template struct ConverterType { using Type = PType; }; template static void callOnGeometryDataType(DataTypePtr type, F && f) { /// There is no Point type, because for most of geometry functions it is useless. if (DataTypeCustomPointSerialization::nestedDataType()->equals(*type)) return f(ConverterType>()); else if (DataTypeCustomRingSerialization::nestedDataType()->equals(*type)) return f(ConverterType>()); else if (DataTypeCustomPolygonSerialization::nestedDataType()->equals(*type)) return f(ConverterType>()); else if (DataTypeCustomMultiPolygonSerialization::nestedDataType()->equals(*type)) return f(ConverterType>()); throw Exception(fmt::format("Unknown geometry type {}", type->getName()), ErrorCodes::BAD_ARGUMENTS); } template static void callOnTwoGeometryDataTypes(DataTypePtr left_type, DataTypePtr right_type, F && func) { return callOnGeometryDataType(left_type, [&](const auto & left_types) { using LeftConverterType = std::decay_t; return callOnGeometryDataType(right_type, [&](const auto & right_types) { using RightConverterType = std::decay_t; return func(LeftConverterType(), RightConverterType()); }); }); } }