1 #ifndef OSMIUM_GEOM_WKB_HPP 2 #define OSMIUM_GEOM_WKB_HPP 3 4 /* 5 6 This file is part of Osmium (https://osmcode.org/libosmium). 7 8 Copyright 2013-2021 Jochen Topf <jochen@topf.org> and others (see README). 9 10 Boost Software License - Version 1.0 - August 17th, 2003 11 12 Permission is hereby granted, free of charge, to any person or organization 13 obtaining a copy of the software and accompanying documentation covered by 14 this license (the "Software") to use, reproduce, display, distribute, 15 execute, and transmit the Software, and to prepare derivative works of the 16 Software, and to permit third-parties to whom the Software is furnished to 17 do so, all subject to the following: 18 19 The copyright notices in the Software and this entire statement, including 20 the above license grant, this restriction and the following disclaimer, 21 must be included in all copies of the Software, in whole or in part, and 22 all derivative works of the Software, unless such copies or derivative 23 works are solely in the form of machine-executable object code generated by 24 a source language processor. 25 26 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 27 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 28 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 29 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 30 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 31 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 32 DEALINGS IN THE SOFTWARE. 33 34 */ 35 36 #include <osmium/geom/coordinates.hpp> 37 #include <osmium/geom/factory.hpp> 38 #include <osmium/util/endian.hpp> 39 40 #include <algorithm> 41 #include <cstddef> 42 #include <cstdint> 43 #include <string> 44 45 namespace osmium { 46 47 namespace geom { 48 49 enum class wkb_type : bool { 50 wkb = false, 51 ewkb = true 52 }; // enum class wkb_type 53 54 enum class out_type : bool { 55 binary = false, 56 hex = true 57 }; // enum class out_type 58 59 namespace detail { 60 61 template <typename T> str_push(std::string & str,T data)62 inline void str_push(std::string& str, T data) { 63 str.append(reinterpret_cast<const char*>(&data), sizeof(T)); 64 } 65 convert_to_hex(const std::string & str)66 inline std::string convert_to_hex(const std::string& str) { 67 static const char* lookup_hex = "0123456789ABCDEF"; 68 std::string out; 69 out.reserve(str.size() * 2); 70 71 for (char c : str) { 72 out += lookup_hex[(static_cast<unsigned int>(c) >> 4U) & 0xfU]; 73 out += lookup_hex[ static_cast<unsigned int>(c) & 0xfU]; 74 } 75 76 return out; 77 } 78 79 class WKBFactoryImpl { 80 81 /** 82 * Type of WKB geometry. 83 * These definitions are from 84 * 99-049_OpenGIS_Simple_Features_Specification_For_SQL_Rev_1.1.pdf (for WKB) 85 * and https://trac.osgeo.org/postgis/browser/trunk/doc/ZMSgeoms.txt (for EWKB). 86 * They are used to encode geometries into the WKB format. 87 */ 88 enum wkbGeometryType : uint32_t { 89 wkbPoint = 1, 90 wkbLineString = 2, 91 wkbPolygon = 3, 92 wkbMultiPoint = 4, 93 wkbMultiLineString = 5, 94 wkbMultiPolygon = 6, 95 wkbGeometryCollection = 7, 96 97 // SRID-presence flag (EWKB) 98 wkbSRID = 0x20000000 99 }; // enum wkbGeometryType 100 101 /** 102 * Byte order marker in WKB geometry. 103 */ 104 enum class wkb_byte_order_type : uint8_t { 105 XDR = 0, // Big Endian 106 NDR = 1 // Little Endian 107 }; // enum class wkb_byte_order_type 108 109 std::string m_data; 110 uint32_t m_points = 0; 111 int m_srid; 112 wkb_type m_wkb_type; 113 out_type m_out_type; 114 115 std::size_t m_linestring_size_offset = 0; 116 std::size_t m_polygons = 0; 117 std::size_t m_rings = 0; 118 std::size_t m_multipolygon_size_offset = 0; 119 std::size_t m_polygon_size_offset = 0; 120 std::size_t m_ring_size_offset = 0; 121 header(std::string & str,wkbGeometryType type,bool add_length) const122 std::size_t header(std::string& str, wkbGeometryType type, bool add_length) const { 123 #if __BYTE_ORDER == __LITTLE_ENDIAN 124 str_push(str, wkb_byte_order_type::NDR); 125 #else 126 str_push(str, wkb_byte_order_type::XDR); 127 #endif 128 if (m_wkb_type == wkb_type::ewkb) { 129 str_push(str, type | wkbSRID); 130 str_push(str, m_srid); 131 } else { 132 str_push(str, type); 133 } 134 const std::size_t offset = str.size(); 135 if (add_length) { 136 str_push(str, static_cast<uint32_t>(0)); 137 } 138 return offset; 139 } 140 set_size(const std::size_t offset,const std::size_t size)141 void set_size(const std::size_t offset, const std::size_t size) { 142 if (size > std::numeric_limits<uint32_t>::max()) { 143 throw geometry_error{"Too many points in geometry"}; 144 } 145 const auto s = static_cast<uint32_t>(size); 146 std::copy_n(reinterpret_cast<const char*>(&s), sizeof(uint32_t), &m_data[offset]); 147 } 148 149 public: 150 151 using point_type = std::string; 152 using linestring_type = std::string; 153 using polygon_type = std::string; 154 using multipolygon_type = std::string; 155 using ring_type = std::string; 156 WKBFactoryImpl(int srid,wkb_type wtype=wkb_type::wkb,out_type otype=out_type::binary)157 explicit WKBFactoryImpl(int srid, wkb_type wtype = wkb_type::wkb, out_type otype = out_type::binary) : 158 m_srid(srid), 159 m_wkb_type(wtype), 160 m_out_type(otype) { 161 } 162 163 /* Point */ 164 make_point(const osmium::geom::Coordinates & xy) const165 point_type make_point(const osmium::geom::Coordinates& xy) const { 166 std::string data; 167 header(data, wkbPoint, false); 168 str_push(data, xy.x); 169 str_push(data, xy.y); 170 171 if (m_out_type == out_type::hex) { 172 return convert_to_hex(data); 173 } 174 175 return data; 176 } 177 178 /* LineString */ 179 linestring_start()180 void linestring_start() { 181 m_data.clear(); 182 m_linestring_size_offset = header(m_data, wkbLineString, true); 183 } 184 linestring_add_location(const osmium::geom::Coordinates & xy)185 void linestring_add_location(const osmium::geom::Coordinates& xy) { 186 str_push(m_data, xy.x); 187 str_push(m_data, xy.y); 188 } 189 linestring_finish(std::size_t num_points)190 linestring_type linestring_finish(std::size_t num_points) { 191 set_size(m_linestring_size_offset, num_points); 192 std::string data; 193 194 using std::swap; 195 swap(data, m_data); 196 197 if (m_out_type == out_type::hex) { 198 return convert_to_hex(data); 199 } 200 201 return data; 202 } 203 204 /* Polygon */ 205 polygon_start()206 void polygon_start() { 207 m_data.clear(); 208 set_size(header(m_data, wkbPolygon, true), 1); 209 m_ring_size_offset = m_data.size(); 210 str_push(m_data, static_cast<uint32_t>(0)); 211 } 212 polygon_add_location(const osmium::geom::Coordinates & xy)213 void polygon_add_location(const osmium::geom::Coordinates& xy) { 214 str_push(m_data, xy.x); 215 str_push(m_data, xy.y); 216 } 217 polygon_finish(std::size_t num_points)218 polygon_type polygon_finish(std::size_t num_points) { 219 set_size(m_ring_size_offset, num_points); 220 std::string data; 221 222 using std::swap; 223 swap(data, m_data); 224 225 if (m_out_type == out_type::hex) { 226 return convert_to_hex(data); 227 } 228 229 return data; 230 } 231 232 /* MultiPolygon */ 233 multipolygon_start()234 void multipolygon_start() { 235 m_data.clear(); 236 m_polygons = 0; 237 m_multipolygon_size_offset = header(m_data, wkbMultiPolygon, true); 238 } 239 multipolygon_polygon_start()240 void multipolygon_polygon_start() { 241 ++m_polygons; 242 m_rings = 0; 243 m_polygon_size_offset = header(m_data, wkbPolygon, true); 244 } 245 multipolygon_polygon_finish()246 void multipolygon_polygon_finish() { 247 set_size(m_polygon_size_offset, m_rings); 248 } 249 multipolygon_outer_ring_start()250 void multipolygon_outer_ring_start() { 251 ++m_rings; 252 m_points = 0; 253 m_ring_size_offset = m_data.size(); 254 str_push(m_data, static_cast<uint32_t>(0)); 255 } 256 multipolygon_outer_ring_finish()257 void multipolygon_outer_ring_finish() { 258 set_size(m_ring_size_offset, m_points); 259 } 260 multipolygon_inner_ring_start()261 void multipolygon_inner_ring_start() { 262 ++m_rings; 263 m_points = 0; 264 m_ring_size_offset = m_data.size(); 265 str_push(m_data, static_cast<uint32_t>(0)); 266 } 267 multipolygon_inner_ring_finish()268 void multipolygon_inner_ring_finish() { 269 set_size(m_ring_size_offset, m_points); 270 } 271 multipolygon_add_location(const osmium::geom::Coordinates & xy)272 void multipolygon_add_location(const osmium::geom::Coordinates& xy) { 273 str_push(m_data, xy.x); 274 str_push(m_data, xy.y); 275 ++m_points; 276 } 277 multipolygon_finish()278 multipolygon_type multipolygon_finish() { 279 set_size(m_multipolygon_size_offset, m_polygons); 280 std::string data; 281 282 using std::swap; 283 swap(data, m_data); 284 285 if (m_out_type == out_type::hex) { 286 return convert_to_hex(data); 287 } 288 289 return data; 290 } 291 292 }; // class WKBFactoryImpl 293 294 } // namespace detail 295 296 template <typename TProjection = IdentityProjection> 297 using WKBFactory = GeometryFactory<osmium::geom::detail::WKBFactoryImpl, TProjection>; 298 299 } // namespace geom 300 301 } // namespace osmium 302 303 #endif // OSMIUM_GEOM_WKB_HPP 304