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