diff --git a/src/Functions/geometryFromColumn.cpp b/src/Functions/geometryFromColumn.cpp index 03013266caa..e4cdb3e22b7 100644 --- a/src/Functions/geometryFromColumn.cpp +++ b/src/Functions/geometryFromColumn.cpp @@ -5,16 +5,13 @@ namespace DB { namespace { -size_t getArrayDepth(const ColumnWithTypeAndName & col, size_t max_depth) +size_t getArrayDepth(DataTypePtr data_type, size_t max_depth) { size_t depth = 0; - DataTypePtr data_type = col.type; - while (isArray(data_type) && depth != max_depth + 1) { data_type = static_cast(*data_type).getNestedType(); } - return max_depth; } @@ -22,11 +19,11 @@ size_t getArrayDepth(const ColumnWithTypeAndName & col, size_t max_depth) GeometryFromColumnParser makeGeometryFromColumnParser(const ColumnWithTypeAndName & col) { - switch (getArrayDepth(col, 3)) { + switch (getArrayDepth(col.type, 3)) { case 0: return Float64PointFromColumnParser(*col.column); case 1: return Float64RingFromColumnParser(*col.column); - // case 2: return parsePolygon(col, i); - // case 3: return parseMultyPoligon(col, i); + case 2: return Float64PolygonFromColumnParser(*col.column); + case 3: return Float64MultiPolygonFromColumnParser(*col.column); default: throw Exception("Cannot parse geometry from column with type " + col.type->getName() + ", array depth is too big", ErrorCodes::ILLEGAL_COLUMN); } diff --git a/src/Functions/geometryFromColumn.h b/src/Functions/geometryFromColumn.h index cc9e743ccc3..1042c4312fb 100644 --- a/src/Functions/geometryFromColumn.h +++ b/src/Functions/geometryFromColumn.h @@ -75,8 +75,7 @@ public: RingFromColumnParser(const IColumn & col) : offsets(static_cast(col).getOffsets()) , pointParser(static_cast(col).getData()) - { - } + {} RingType createContainer() const { @@ -88,21 +87,103 @@ public: size_t l = offsets[i - 1]; size_t r = offsets[i]; + // reserve extra point for case when polygon is open + container.reserve(r - l + 1); container.resize(r - l); for (size_t j = l; j < r; j++) { - pointParser.parse(container[j - l], l); + pointParser.get(container[j - l], j); + } + + // make ring closed + if (!boost::geometry::equals(container[0], container.back())) + { + container.push_back(container[0]); } } private: const IColumn::Offsets & offsets; - PointParser pointParser; + const PointParser pointParser; }; using Float64RingFromColumnParser = RingFromColumnParser; -using GeometryFromColumnParser = boost::variant; +template +class PolygonFromColumnParser +{ +public: + PolygonFromColumnParser(const IColumn & col) + : offsets(static_cast(col).getOffsets()) + , ringParser(static_cast(col).getData()) + {} + + PolygonType createContainer() const + { + return PolygonType(); + } + + void get(PolygonType & container, size_t i) const + { + size_t l = offsets[i - 1]; + size_t r = offsets[i]; + + container.resize(r - l); + ringParser.get(container.outer(), l); + + container.inners().resize(r - l - 1); + for (size_t j = l + 1; j < r; j++) + { + ringParser.get(container.inners()[j - l - 1], j); + } + } + +private: + const IColumn::Offsets & offsets; + const RingParser ringParser; +}; + +using Float64PolygonFromColumnParser = PolygonFromColumnParser; + +template +class MultiPolygonFromColumnParser +{ +public: + MultiPolygonFromColumnParser(const IColumn & col) + : offsets(static_cast(col).getOffsets()) + , polygonParser(static_cast(col).getData()) + {} + + MultiPolygonType createContainer() const + { + return MultiPolygonType(); + } + + void get(MultiPolygonType & 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++) + { + polygonParser.get(container[j - l], j - l); + } + } + +private: + const IColumn::Offsets & offsets; + const PolygonParser polygonParser; +}; + +using Float64MultiPolygonFromColumnParser = MultiPolygonFromColumnParser; + +using GeometryFromColumnParser = boost::variant< + Float64PointFromColumnParser, + Float64RingFromColumnParser, + Float64PolygonFromColumnParser, + Float64MultiPolygonFromColumnParser +>; GeometryFromColumnParser makeGeometryFromColumnParser(const ColumnWithTypeAndName & col);