2020-06-14 17:04:10 +00:00
|
|
|
#include <Functions/geometryConverters.h>
|
2020-06-07 16:47:56 +00:00
|
|
|
#include <DataTypes/DataTypeCustomGeo.h>
|
2020-06-07 12:33:49 +00:00
|
|
|
|
2020-06-21 17:59:18 +00:00
|
|
|
#include <common/logger_useful.h>
|
2020-06-07 12:33:49 +00:00
|
|
|
|
2021-01-19 17:16:10 +00:00
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
|
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
|
|
|
extern const int ILLEGAL_COLUMN;
|
2021-02-15 19:22:13 +00:00
|
|
|
extern const int BAD_ARGUMENTS;
|
|
|
|
extern const int LOGICAL_ERROR;
|
2021-01-19 17:16:10 +00:00
|
|
|
}
|
2020-06-07 13:42:09 +00:00
|
|
|
|
2021-02-19 18:09:38 +00:00
|
|
|
template <typename Point>
|
|
|
|
std::vector<Point> PointFromColumnParser<Point>::parse(size_t shift, size_t count) const
|
|
|
|
{
|
|
|
|
const auto * tuple = typeid_cast<const ColumnTuple *>(col.get());
|
|
|
|
const auto & tuple_columns = tuple->getColumns();
|
|
|
|
|
|
|
|
const auto * x_data = typeid_cast<const ColumnFloat64 *>(tuple_columns[0].get());
|
|
|
|
const auto * y_data = typeid_cast<const ColumnFloat64 *>(tuple_columns[1].get());
|
|
|
|
|
|
|
|
const auto * first_container = x_data->getData().data() + shift;
|
|
|
|
const auto * second_container = y_data->getData().data() + shift;
|
|
|
|
|
|
|
|
std::vector<Point> answer(count);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < count; ++i)
|
|
|
|
{
|
|
|
|
const Float64 first = first_container[i];
|
|
|
|
const Float64 second = second_container[i];
|
|
|
|
|
|
|
|
if (isNaN(first) || isNaN(second))
|
|
|
|
throw Exception("Point's component must not be NaN", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
|
|
|
|
|
|
|
if (isinf(first) || isinf(second))
|
|
|
|
throw Exception("Point's component must not be infinite", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
|
|
|
|
|
|
|
answer[i] = Point(first, second);
|
|
|
|
}
|
|
|
|
|
|
|
|
return answer;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Point>
|
|
|
|
std::vector<Ring<Point>> RingFromColumnParser<Point>::parse(size_t /*shift*/, size_t /*size*/) const
|
|
|
|
{
|
|
|
|
size_t prev_offset = 0;
|
|
|
|
std::vector<Ring<Point>> answer;
|
|
|
|
answer.reserve(offsets.size());
|
|
|
|
for (size_t offset : offsets)
|
|
|
|
{
|
|
|
|
auto tmp = point_parser.parse(prev_offset, offset - prev_offset);
|
|
|
|
answer.emplace_back(tmp.begin(), tmp.end());
|
|
|
|
prev_offset = offset;
|
|
|
|
}
|
|
|
|
return answer;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Point>
|
|
|
|
std::vector<Polygon<Point>> PolygonFromColumnParser<Point>::parse(size_t /*shift*/, size_t /*size*/) const
|
|
|
|
{
|
|
|
|
std::vector<Polygon<Point>> answer(offsets.size());
|
|
|
|
auto all_rings = ring_parser.parse(0, 0);
|
|
|
|
|
|
|
|
auto prev_offset = 0;
|
|
|
|
for (size_t iter = 0; iter < offsets.size(); ++iter)
|
|
|
|
{
|
|
|
|
const auto current_array_size = offsets[iter] - prev_offset;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template <typename Point>
|
|
|
|
std::vector<MultiPolygon<Point>> MultiPolygonFromColumnParser<Point>::parse(size_t /*shift*/, size_t /*size*/) const
|
|
|
|
{
|
|
|
|
size_t prev_offset = 0;
|
|
|
|
std::vector<MultiPolygon<Point>> answer(offsets.size());
|
|
|
|
|
|
|
|
auto all_polygons = polygon_parser.parse(0, 0);
|
|
|
|
|
|
|
|
for (size_t iter = 0; iter < offsets.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;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename ContainterWithFigures>
|
|
|
|
class ParserVisitor : public boost::static_visitor<ContainterWithFigures>
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
template <class T>
|
|
|
|
ContainterWithFigures operator()(const T & parser) const
|
|
|
|
{
|
|
|
|
auto parsed = parser.parse(0, 0);
|
|
|
|
ContainterWithFigures figures;
|
|
|
|
figures.reserve(parsed.size());
|
|
|
|
for (auto & value : parsed)
|
|
|
|
figures.emplace_back(value);
|
|
|
|
return figures;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename Point>
|
|
|
|
std::vector<Figure<Point>> parseFigure(const GeometryFromColumnParser<Point> & parser)
|
|
|
|
{
|
|
|
|
static ParserVisitor<std::vector<Figure<Point>>> creator;
|
|
|
|
return boost::apply_visitor(creator, parser);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template std::vector<Figure<CartesianPoint>> parseFigure(const GeometryFromColumnParser<CartesianPoint> &);
|
|
|
|
template std::vector<Figure<GeographicPoint>> parseFigure(const GeometryFromColumnParser<GeographicPoint> &);
|
2020-06-07 16:04:35 +00:00
|
|
|
|
|
|
|
|
2021-02-15 19:22:13 +00:00
|
|
|
template <typename Point, template<typename> typename Desired>
|
|
|
|
void checkColumnTypeOrThrow(const ColumnWithTypeAndName & column)
|
|
|
|
{
|
|
|
|
DataTypePtr desired_type;
|
|
|
|
if constexpr (std::is_same_v<Desired<Point>, Ring<Point>>)
|
|
|
|
desired_type = DataTypeCustomRingSerialization::nestedDataType();
|
|
|
|
else if constexpr (std::is_same_v<Desired<Point>, Polygon<Point>>)
|
|
|
|
desired_type = DataTypeCustomPolygonSerialization::nestedDataType();
|
|
|
|
else if constexpr (std::is_same_v<Desired<Point>, MultiPolygon<Point>>)
|
|
|
|
desired_type = DataTypeCustomMultiPolygonSerialization::nestedDataType();
|
|
|
|
else
|
|
|
|
throw Exception("Unexpected Desired type.", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
|
|
|
|
if (!desired_type->equals(*column.type))
|
|
|
|
throw Exception(fmt::format("Expected type {} (MultiPolygon), but got {}", desired_type->getName(), column.type->getName()), ErrorCodes::BAD_ARGUMENTS);
|
|
|
|
}
|
|
|
|
|
|
|
|
template void checkColumnTypeOrThrow<CartesianPoint, Ring>(const ColumnWithTypeAndName &);
|
|
|
|
template void checkColumnTypeOrThrow<CartesianPoint, Polygon>(const ColumnWithTypeAndName &);
|
|
|
|
template void checkColumnTypeOrThrow<CartesianPoint, MultiPolygon>(const ColumnWithTypeAndName &);
|
|
|
|
template void checkColumnTypeOrThrow<GeographicPoint, Ring>(const ColumnWithTypeAndName &);
|
|
|
|
template void checkColumnTypeOrThrow<GeographicPoint, Polygon>(const ColumnWithTypeAndName &);
|
|
|
|
template void checkColumnTypeOrThrow<GeographicPoint, MultiPolygon>(const ColumnWithTypeAndName &);
|
|
|
|
|
2021-02-19 18:09:38 +00:00
|
|
|
template <typename Point>
|
|
|
|
GeometryFromColumnParser<Point> getConverterBasedOnType(const ColumnWithTypeAndName & column)
|
|
|
|
{
|
|
|
|
if (DataTypeCustomRingSerialization::nestedDataType()->equals(*column.type))
|
|
|
|
{
|
|
|
|
return RingFromColumnParser<Point>(std::move(column.column->convertToFullColumnIfConst()));
|
|
|
|
}
|
|
|
|
else if (DataTypeCustomPolygonSerialization::nestedDataType()->equals(*column.type))
|
|
|
|
{
|
|
|
|
return PolygonFromColumnParser<Point>(std::move(column.column->convertToFullColumnIfConst()));
|
|
|
|
}
|
|
|
|
else if (DataTypeCustomMultiPolygonSerialization::nestedDataType()->equals(*column.type))
|
|
|
|
{
|
|
|
|
return MultiPolygonFromColumnParser<Point>(std::move(column.column->convertToFullColumnIfConst()));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
throw Exception(fmt::format("Unexpected type of column {}", column.type->getName()), ErrorCodes::BAD_ARGUMENTS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template GeometryFromColumnParser<CartesianPoint> getConverterBasedOnType(const ColumnWithTypeAndName & column);
|
|
|
|
template GeometryFromColumnParser<GeographicPoint> getConverterBasedOnType(const ColumnWithTypeAndName & column);
|
|
|
|
|
2020-06-07 12:33:49 +00:00
|
|
|
}
|