#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace DB { namespace ErrorCodes { extern const int BAD_ARGUMENTS; extern const int ILLEGAL_TYPE_OF_ARGUMENT; } namespace bg = boost::geometry; template using Ring = bg::model::ring; template using Polygon = bg::model::polygon; template using MultiPolygon = bg::model::multi_polygon>; template using Geometry = boost::variant, Polygon, MultiPolygon>; using CartesianPoint = bg::model::d2::point_xy; using CartesianRing = Ring; using CartesianPolygon = Polygon; using CartesianMultiPolygon = MultiPolygon; using CartesianGeometry = Geometry; using GeographicPoint = bg::model::point>; using GeographicRing = Ring; using GeographicPolygon = Polygon; using GeographicMultiPolygon = MultiPolygon; using GeographicGeometry = Geometry; /** * 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 PointFromColumnParser { public: explicit PointFromColumnParser(ColumnPtr col_) : col(col_) { const auto & tuple = dynamic_cast(*col_); const auto & tuple_columns = tuple.getColumns(); #ifndef NDEBUG size = tuple.size(); #endif const auto & x_data = dynamic_cast(*tuple_columns[0]); first = x_data.getData().data(); const auto & y_data = dynamic_cast(*tuple_columns[1]); second = y_data.getData().data(); } template typename std::enable_if_t, CartesianGeometry> createContainer() const { return CartesianPoint(); } template typename std::enable_if_t, GeographicGeometry> createContainer() const { return GeographicPoint(); } template void get(std::enable_if_t, CartesianGeometry> & container, size_t i) const { #ifndef NDEBUG assert(i < size); #endif get(boost::get(container), i); } template void get(std::enable_if_t, GeographicGeometry> & container, size_t i) const { #ifndef NDEBUG assert(i < size); #endif get(boost::get(container), i); } void get(PointType & container, size_t i) const { #ifndef NDEBUG assert(i < size); #endif if (isNaN(first[i]) || isNaN(second[i])) throw Exception("Point's component must not be NaN", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); boost::geometry::set<0>(container, first[i]); boost::geometry::set<1>(container, second[i]); } private: /// To prevent use-after-free and increase column lifetime. ColumnPtr col; #ifndef NDEBUG size_t size; #endif const Float64 * first; const Float64 * second; }; template class RingFromColumnParser { public: explicit RingFromColumnParser(ColumnPtr col_) : col(col_) , offsets(dynamic_cast(*col_).getOffsets()) , point_parser(dynamic_cast(*col_).getDataPtr()) { } Geometry createContainer() const { return Ring(); } void get(Geometry & container, size_t i) const { get(boost::get>(container), i); } void get(Ring & container, size_t i) const { size_t left = i == 0 ? 0 : offsets[i - 1]; size_t right = offsets[i]; if (left == right) throw Exception("Empty polygons are not allowed in line " + toString(i), ErrorCodes::BAD_ARGUMENTS); // reserve extra point for case when polygon is open container.reserve(right - left + 1); container.resize(right - left); for (size_t j = left; j < right; j++) point_parser.get(container[j - left], j); // make ring closed if (!boost::geometry::equals(container[0], container.back())) { container.push_back(container[0]); } } private: /// To prevent use-after-free and increase column lifetime. ColumnPtr col; const IColumn::Offsets & offsets; const PointFromColumnParser point_parser; }; template class PolygonFromColumnParser { public: explicit PolygonFromColumnParser(ColumnPtr col_) : col(col_) , offsets(static_cast(*col_).getOffsets()) , ring_parser(static_cast(*col_).getDataPtr()) {} Geometry createContainer() const { return Polygon(); } void get(Geometry & container, size_t i) const { get(boost::get>(container), i); } void get(Polygon & container, size_t i) const { size_t l = offsets[i - 1]; size_t r = offsets[i]; ring_parser.get(container.outer(), l); container.inners().resize(r - l - 1); for (size_t j = l + 1; j < r; j++) { ring_parser.get(container.inners()[j - l - 1], j); } } private: /// To prevent use-after-free and increase column lifetime. ColumnPtr col; const IColumn::Offsets & offsets; const RingFromColumnParser ring_parser; }; template class MultiPolygonFromColumnParser { public: explicit MultiPolygonFromColumnParser(ColumnPtr col_) : col(col_) , offsets(static_cast(*col_).getOffsets()) , polygon_parser(static_cast(*col_).getDataPtr()) {} Geometry createContainer() const { return MultiPolygon(); } void get(Geometry & container, size_t i) const { get(boost::get>(container), i); } void get(MultiPolygon & container, size_t i) const { size_t l = offsets[i - 1]; size_t r = offsets[i]; container.resize(r - l); for (size_t j = l; j < r; j++) { polygon_parser.get(container[j - l], j); } } private: /// To prevent use-after-free and increase column lifetime. ColumnPtr col; const IColumn::Offsets & offsets; const PolygonFromColumnParser polygon_parser; }; template using GeometryFromColumnParser = boost::variant< PointFromColumnParser, RingFromColumnParser, PolygonFromColumnParser, MultiPolygonFromColumnParser >; template Geometry createContainer(const GeometryFromColumnParser & parser); template void get(const GeometryFromColumnParser & parser, Geometry & container, size_t i); template GeometryFromColumnParser makeGeometryFromColumnParser(const ColumnWithTypeAndName & col); extern template Geometry createContainer(const GeometryFromColumnParser & parser); extern template Geometry createContainer(const GeometryFromColumnParser & parser); extern template void get(const GeometryFromColumnParser & parser, Geometry & container, size_t i); extern template void get(const GeometryFromColumnParser & parser, Geometry & container, size_t i); extern template GeometryFromColumnParser makeGeometryFromColumnParser(const ColumnWithTypeAndName & col); extern template GeometryFromColumnParser makeGeometryFromColumnParser(const ColumnWithTypeAndName & col); /// To serialize Geographic or Cartesian point (a pair of numbers in both cases). template class PointSerializerVisitor : public boost::static_visitor { public: PointSerializerVisitor() : first(ColumnFloat64::create()) , second(ColumnFloat64::create()) {} explicit PointSerializerVisitor(size_t n) : first(ColumnFloat64::create(n)) , second(ColumnFloat64::create(n)) {} void operator()(const Point & point) { first->insertValue(point.template get<0>()); second->insertValue(point.template get<1>()); } void operator()(const Ring & ring) { if (ring.size() != 1) throw Exception("Unable to write ring of size " + toString(ring.size()) + " != 1 to point column", ErrorCodes::BAD_ARGUMENTS); (*this)(ring[0]); } void operator()(const Polygon & polygon) { if (polygon.inners().size() != 0) throw Exception("Unable to write polygon with holes to point column", ErrorCodes::BAD_ARGUMENTS); (*this)(polygon.outer()); } void operator()(const MultiPolygon & multi_polygon) { if (multi_polygon.size() != 1) throw Exception("Unable to write multi-polygon of size " + toString(multi_polygon.size()) + " != 1 to point column", ErrorCodes::BAD_ARGUMENTS); (*this)(multi_polygon[0]); } 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; }; template class RingSerializerVisitor : public boost::static_visitor { public: RingSerializerVisitor() : offsets(ColumnUInt64::create()) {} explicit RingSerializerVisitor(size_t n) : offsets(ColumnUInt64::create(n)) {} void operator()(const Point & point) { size++; offsets->insertValue(size); point_serializer(point); } void operator()(const Ring & ring) { size += ring.size(); offsets->insertValue(size); for (const auto & point : ring) { point_serializer(point); } } void operator()(const Polygon & polygon) { if (polygon.inners().size() != 0) throw Exception("Unable to write polygon with holes to ring column", ErrorCodes::BAD_ARGUMENTS); (*this)(polygon.outer()); } void operator()(const MultiPolygon & multi_polygon) { if (multi_polygon.size() != 1) throw Exception("Unable to write multi-polygon of size " + toString(multi_polygon.size()) + " != 1 to ring column", ErrorCodes::BAD_ARGUMENTS); (*this)(multi_polygon[0]); } ColumnPtr finalize() { return ColumnArray::create(point_serializer.finalize(), std::move(offsets)); } private: size_t size = 0; PointSerializerVisitor point_serializer; ColumnUInt64::MutablePtr offsets; }; template class PolygonSerializerVisitor : public boost::static_visitor { public: PolygonSerializerVisitor() : offsets(ColumnUInt64::create()) {} explicit PolygonSerializerVisitor(size_t n) : offsets(ColumnUInt64::create(n)) {} void operator()(const Point & point) { size++; offsets->insertValue(size); ring_serializer(point); } void operator()(const Ring & ring) { size++; offsets->insertValue(size); ring_serializer(ring); } void operator()(const Polygon & polygon) { size += 1 + polygon.inners().size(); offsets->insertValue(size); ring_serializer(polygon.outer()); for (const auto & ring : polygon.inners()) { ring_serializer(ring); } } void operator()(const MultiPolygon & multi_polygon) { if (multi_polygon.size() != 1) throw Exception("Unable to write multi-polygon of size " + toString(multi_polygon.size()) + " != 1 to polygon column", ErrorCodes::BAD_ARGUMENTS); (*this)(multi_polygon[0]); } ColumnPtr finalize() { return ColumnArray::create(ring_serializer.finalize(), std::move(offsets)); } private: size_t size = 0; RingSerializerVisitor ring_serializer; ColumnUInt64::MutablePtr offsets; }; template class MultiPolygonSerializerVisitor : public boost::static_visitor { public: MultiPolygonSerializerVisitor() : offsets(ColumnUInt64::create()) {} explicit MultiPolygonSerializerVisitor(size_t n) : offsets(ColumnUInt64::create(n)) {} void operator()(const Point & point) { size++; offsets->insertValue(size); polygon_serializer(point); } void operator()(const Ring & ring) { size++; offsets->insertValue(size); polygon_serializer(ring); } void operator()(const Polygon & polygon) { size++; offsets->insertValue(size); polygon_serializer(polygon); } void operator()(const MultiPolygon & multi_polygon) { size += multi_polygon.size(); offsets->insertValue(size); for (const auto & polygon : multi_polygon) { polygon_serializer(polygon); } } ColumnPtr finalize() { return ColumnArray::create(polygon_serializer.finalize(), std::move(offsets)); } private: size_t size = 0; PolygonSerializerVisitor polygon_serializer; ColumnUInt64::MutablePtr offsets; }; template class GeometrySerializer { public: void add(const Geometry & geometry) { boost::apply_visitor(visitor, geometry); } ColumnPtr finalize() { return visitor.finalize(); } private: Visitor visitor; }; template using PointSerializer = GeometrySerializer, PointSerializerVisitor>; template using RingSerializer = GeometrySerializer, RingSerializerVisitor>; template using PolygonSerializer = GeometrySerializer, PolygonSerializerVisitor>; template using MultiPolygonSerializer = GeometrySerializer, MultiPolygonSerializerVisitor>; template typename Desired> void checkColumnTypeOrThrow(const ColumnWithTypeAndName & column); }