#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; } template using Ring = boost::geometry::model::ring; template using Polygon = boost::geometry::model::polygon; template using MultiPolygon = boost::geometry::model::multi_polygon>; using CartesianPoint = boost::geometry::model::d2::point_xy; using CartesianRing = Ring; using CartesianPolygon = Polygon; using CartesianMultiPolygon = MultiPolygon; using SphericalPoint = boost::geometry::model::point>; using SphericalRing = Ring; using SphericalPolygon = Polygon; using SphericalMultiPolygon = MultiPolygon; /** * Class which takes converts Column with type Tuple(Float64, Float64) to a vector of boost point type. * They are (x,y) in case of cartesian coordinated and (lon,lat) in case of Spherical. */ template struct ColumnToPointsConverter { static std::vector convert(ColumnPtr col) { const auto * tuple = typeid_cast(col.get()); const auto & tuple_columns = tuple->getColumns(); const auto * x_data = typeid_cast(tuple_columns[0].get()); const auto * y_data = typeid_cast(tuple_columns[1].get()); const auto * first_container = x_data->getData().data(); const auto * second_container = y_data->getData().data(); std::vector answer(col->size()); for (size_t i = 0; i < col->size(); ++i) { const Float64 first = first_container[i]; const Float64 second = second_container[i]; if (isNaN(first) || isNaN(second)) throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Point's component must not be NaN"); if (std::isinf(first) || std::isinf(second)) throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Point's component must not be infinite"); answer[i] = Point(first, second); } return answer; } }; /** * Class which converts Column with type Array(Tuple(Float64, Float64)) to a vector of boost ring type. */ template struct ColumnToRingsConverter { static std::vector> convert(ColumnPtr col) { const IColumn::Offsets & offsets = typeid_cast(*col).getOffsets(); size_t prev_offset = 0; std::vector> answer; answer.reserve(offsets.size()); auto tmp = ColumnToPointsConverter::convert(typeid_cast(*col).getDataPtr()); for (size_t offset : offsets) { answer.emplace_back(tmp.begin() + prev_offset, tmp.begin() + offset); prev_offset = offset; } return answer; } }; /** * Class which converts Column with type Array(Array(Tuple(Float64, Float64))) to a vector of boost polygon type. */ template struct ColumnToPolygonsConverter { static std::vector> convert(ColumnPtr col) { const IColumn::Offsets & offsets = typeid_cast(*col).getOffsets(); std::vector> answer(offsets.size()); auto all_rings = ColumnToRingsConverter::convert(typeid_cast(*col).getDataPtr()); size_t prev_offset = 0; for (size_t iter = 0; iter < offsets.size(); ++iter) { const auto current_array_size = offsets[iter] - prev_offset; if (current_array_size == 0) { answer.emplace_back(); continue; } answer[iter].outer() = std::move(all_rings[prev_offset]); answer[iter].inners().reserve(current_array_size); for (size_t inner_holes = prev_offset + 1; inner_holes < offsets[iter]; ++inner_holes) answer[iter].inners().emplace_back(std::move(all_rings[inner_holes])); prev_offset = offsets[iter]; } return answer; } }; /** * Class which converts Column with type Array(Array(Array(Tuple(Float64, Float64)))) to a vector of boost multi_polygon type. */ template struct ColumnToMultiPolygonsConverter { static std::vector> convert(ColumnPtr col) { const IColumn::Offsets & offsets = typeid_cast(*col).getOffsets(); size_t prev_offset = 0; std::vector> answer(offsets.size()); auto all_polygons = ColumnToPolygonsConverter::convert(typeid_cast(*col).getDataPtr()); for (size_t iter = 0; iter < offsets.size() && iter < all_polygons.size(); ++iter) { for (size_t polygon_iter = prev_offset; polygon_iter < offsets[iter]; ++polygon_iter) answer[iter].emplace_back(std::move(all_polygons[polygon_iter])); prev_offset = offsets[iter]; } return answer; } }; /// To serialize Spherical 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) { /// Outer ring + all inner rings (holes). 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) { const auto & factory = DataTypeFactory::instance(); /// There is no Point type, because for most of geometry functions it is useless. if (factory.get("Point")->equals(*type)) return f(ConverterType>()); else if (factory.get("Ring")->equals(*type)) return f(ConverterType>()); else if (factory.get("Polygon")->equals(*type)) return f(ConverterType>()); else if (factory.get("MultiPolygon")->equals(*type)) return f(ConverterType>()); throw Exception(ErrorCodes::BAD_ARGUMENTS, "Unknown geometry type {}", type->getName()); } 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()); }); }); } }