/***************************************************************************** * * This file is part of Mapnik (c++ mapping toolkit) * * Copyright (C) 2021 Artem Pavlenko * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ // mapnik #include #include #include #include #include #include #include namespace mapnik { struct wkb_reader : util::noncopyable { private: const char* wkb_; std::size_t size_; std::size_t pos_; wkbByteOrder byteOrder_; bool needSwap_; wkbFormat format_; public: enum wkbGeometryType { wkbPoint = 1, wkbLineString = 2, wkbPolygon = 3, wkbMultiPoint = 4, wkbMultiLineString = 5, wkbMultiPolygon = 6, wkbGeometryCollection = 7, // Z wkbPointZ = 1001, wkbLineStringZ = 1002, wkbPolygonZ = 1003, wkbMultiPointZ = 1004, wkbMultiLineStringZ = 1005, wkbMultiPolygonZ = 1006, wkbGeometryCollectionZ = 1007, // M wkbPointM = 2001, wkbLineStringM = 2002, wkbPolygonM = 2003, wkbMultiPointM = 2004, wkbMultiLineStringM = 2005, wkbMultiPolygonM = 2006, wkbGeometryCollectionM = 2007, // ZM wkbPointZM = 3001, wkbLineStringZM = 3002, wkbPolygonZM = 3003, wkbMultiPointZM = 3004, wkbMultiLineStringZM = 3005, wkbMultiPolygonZM = 3006, wkbGeometryCollectionZM = 3007 }; wkb_reader(const char* wkb, std::size_t size, wkbFormat format) : wkb_(wkb) , size_(size) , pos_(0) , format_(format) { // try to determine WKB format automatically if (format_ == wkbAuto) { if (size_ >= 44 && static_cast(wkb_[0]) == static_cast(0x00) && static_cast(wkb_[38]) == static_cast(0x7C) && static_cast(wkb_[size_ - 1]) == static_cast(0xFE)) { format_ = wkbSpatiaLite; } else { format_ = wkbGeneric; } } switch (format_) { case wkbSpatiaLite: byteOrder_ = static_cast(wkb_[1]); pos_ = 39; break; case wkbGeneric: default: byteOrder_ = static_cast(wkb_[0]); pos_ = 1; break; } needSwap_ = byteOrder_ ? wkbXDR : wkbNDR; } mapnik::geometry::geometry read() { mapnik::geometry::geometry geom = mapnik::geometry::geometry_empty(); int type = read_integer(); switch (type) { case wkbPoint: { auto pt = read_point(); if (!std::isnan(pt.x) && !std::isnan(pt.y)) geom = std::move(pt); break; } case wkbLineString: geom = read_linestring(); break; case wkbPolygon: geom = read_polygon(); break; case wkbMultiPoint: geom = read_multipoint(); break; case wkbMultiLineString: geom = read_multilinestring(); break; case wkbMultiPolygon: geom = read_multipolygon(); break; case wkbGeometryCollection: geom = read_collection(); break; case wkbPointZ: case wkbPointM: { auto pt = read_point(); if (!std::isnan(pt.x) && !std::isnan(pt.y)) geom = std::move(pt); break; } case wkbPointZM: { auto pt = read_point(); if (!std::isnan(pt.x) && !std::isnan(pt.y)) geom = std::move(pt); break; } case wkbLineStringZ: case wkbLineStringM: geom = read_linestring(); break; case wkbLineStringZM: geom = read_linestring(); break; case wkbPolygonZ: case wkbPolygonM: geom = read_polygon(); break; case wkbPolygonZM: geom = read_polygon(); break; case wkbMultiPointZ: case wkbMultiPointM: geom = read_multipoint(); break; case wkbMultiPointZM: geom = read_multipoint(); break; case wkbMultiLineStringZ: case wkbMultiLineStringM: geom = read_multilinestring(); break; case wkbMultiLineStringZM: geom = read_multilinestring(); break; case wkbMultiPolygonZ: case wkbMultiPolygonM: geom = read_multipolygon(); break; case wkbMultiPolygonZM: geom = read_multipolygon(); break; case wkbGeometryCollectionZ: case wkbGeometryCollectionM: case wkbGeometryCollectionZM: geom = read_collection(); break; default: break; } return geom; } private: int read_integer() { std::int32_t n; if (needSwap_) { read_int32_xdr(wkb_ + pos_, n); } else { read_int32_ndr(wkb_ + pos_, n); } pos_ += 4; return n; } double read_double() { double d; if (needSwap_) { read_double_xdr(wkb_ + pos_, d); } else { read_double_ndr(wkb_ + pos_, d); } pos_ += 8; return d; } template void read_coords(Ring& ring, std::size_t num_points) { double x, y; if (!needSwap_) { for (std::size_t i = 0; i < num_points; ++i) { read_double_ndr(wkb_ + pos_, x); read_double_ndr(wkb_ + pos_ + 8, y); ring.emplace_back(x, y); pos_ += 16; // skip XY if (Z) pos_ += 8; if (M) pos_ += 8; } } else { for (std::size_t i = 0; i < num_points; ++i) { read_double_xdr(wkb_ + pos_, x); read_double_xdr(wkb_ + pos_ + 8, y); ring.emplace_back(x, y); pos_ += 16; // skip XY if (Z) pos_ += 8; if (M) pos_ += 8; } } } template mapnik::geometry::point read_point() { double x = read_double(); double y = read_double(); if (Z) pos_ += 8; if (M) pos_ += 8; return mapnik::geometry::point(x, y); } template mapnik::geometry::multi_point read_multipoint() { mapnik::geometry::multi_point multi_point; int num_points = read_integer(); multi_point.reserve(num_points); for (int i = 0; i < num_points; ++i) { pos_ += 5; multi_point.emplace_back(read_point()); } return multi_point; } template mapnik::geometry::line_string read_linestring() { mapnik::geometry::line_string line; int num_points = read_integer(); if (num_points > 0) { line.reserve(num_points); read_coords, M, Z>(line, num_points); } return line; } template mapnik::geometry::multi_line_string read_multilinestring() { int num_lines = read_integer(); mapnik::geometry::multi_line_string multi_line; multi_line.reserve(num_lines); for (int i = 0; i < num_lines; ++i) { pos_ += 5; multi_line.push_back(read_linestring()); } return multi_line; } template mapnik::geometry::polygon read_polygon() { int num_rings = read_integer(); mapnik::geometry::polygon poly; poly.reserve(num_rings); for (int i = 0; i < num_rings; ++i) { mapnik::geometry::linear_ring ring; int num_points = read_integer(); if (num_points > 0) { ring.reserve(num_points); read_coords, M, Z>(ring, num_points); } poly.push_back(std::move(ring)); } return poly; } template mapnik::geometry::multi_polygon read_multipolygon() { int num_polys = read_integer(); mapnik::geometry::multi_polygon multi_poly; multi_poly.reserve(num_polys); for (int i = 0; i < num_polys; ++i) { pos_ += 5; multi_poly.push_back(read_polygon()); } return multi_poly; } mapnik::geometry::geometry_collection read_collection() { int num_geometries = read_integer(); mapnik::geometry::geometry_collection collection; collection.reserve(num_geometries); for (int i = 0; i < num_geometries; ++i) { pos_ += 1; // skip byte order collection.push_back(read()); } return collection; } std::string wkb_geometry_type_string(int type) { std::stringstream s; switch (type) { case wkbPoint: s << "Point"; break; case wkbPointZ: s << "PointZ"; break; case wkbPointM: s << "PointM"; break; case wkbPointZM: s << "PointZM"; break; case wkbMultiPoint: s << "MultiPoint"; break; case wkbMultiPointZ: s << "MultiPointZ"; break; case wkbMultiPointM: s << "MultiPointM"; break; case wkbMultiPointZM: s << "MultiPointZM"; break; case wkbLineString: s << "LineString"; break; case wkbLineStringZ: s << "LineStringZ"; break; case wkbLineStringM: s << "LineStringM"; break; case wkbLineStringZM: s << "LineStringZM"; break; case wkbMultiLineString: s << "MultiLineString"; break; case wkbMultiLineStringZ: s << "MultiLineStringZ"; break; case wkbMultiLineStringM: s << "MultiLineStringM"; break; case wkbMultiLineStringZM: s << "MultiLineStringZM"; break; case wkbPolygon: s << "Polygon"; break; case wkbPolygonZ: s << "PolygonZ"; break; case wkbPolygonM: s << "PolygonM"; break; case wkbPolygonZM: s << "PolygonZM"; break; case wkbMultiPolygon: s << "MultiPolygon"; break; case wkbMultiPolygonZ: s << "MultiPolygonZ"; break; case wkbMultiPolygonM: s << "MultiPolygonM"; break; case wkbMultiPolygonZM: s << "MultiPolygonZM"; break; case wkbGeometryCollection: s << "GeometryCollection"; break; case wkbGeometryCollectionZ: s << "GeometryCollectionZ"; break; case wkbGeometryCollectionM: s << "GeometryCollectionM"; break; case wkbGeometryCollectionZM: s << "GeometryCollectionZM"; break; default: s << "wkbUnknown(" << type << ")"; break; } return s.str(); } }; mapnik::geometry::geometry geometry_utils::from_wkb(const char* wkb, std::size_t size, wkbFormat format) { wkb_reader reader(wkb, size, format); mapnik::geometry::geometry geom(reader.read()); // note: this will only be applied to polygons mapnik::geometry::correct(geom); return geom; } } // namespace mapnik