1 #ifndef OSMIUM_GEOM_GEOS_HPP
2 #define OSMIUM_GEOM_GEOS_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 <geos/version.h>
37 #if defined(GEOS_VERSION_MAJOR) && defined(GEOS_VERSION_MINOR) && (GEOS_VERSION_MAJOR < 3 || (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR <= 5))
38 
39 #define OSMIUM_WITH_GEOS
40 
41 /**
42  * @file
43  *
44  * This file contains code for conversion of OSM geometries into GEOS
45  * geometries.
46  *
47  * Note that everything in this file is deprecated and only works up to
48  * GEOS 3.5. It uses the GEOS C++ API which the GEOS project does not consider
49  * to be a stable, external API. We probably should have used the GEOS C API
50  * instead.
51  *
52  * @attention If you include this file, you'll need to link with `libgeos`.
53  */
54 
55 #include <osmium/geom/coordinates.hpp>
56 #include <osmium/geom/factory.hpp>
57 #include <osmium/util/compatibility.hpp>
58 
59 #include <geos/geom/Coordinate.h>
60 #include <geos/geom/CoordinateSequence.h>
61 #include <geos/geom/CoordinateSequenceFactory.h>
62 #include <geos/geom/GeometryFactory.h>
63 #include <geos/geom/LinearRing.h>
64 #include <geos/geom/MultiPolygon.h>
65 #include <geos/geom/Point.h>
66 #include <geos/geom/Polygon.h>
67 #include <geos/geom/PrecisionModel.h>
68 #include <geos/util/GEOSException.h>
69 
70 #include <algorithm>
71 #include <cassert>
72 #include <cstddef>
73 #include <exception>
74 #include <iterator>
75 #include <memory>
76 #include <string>
77 #include <utility>
78 #include <vector>
79 
80 namespace osmium {
81 
82     struct geos_geometry_error : public geometry_error {
83 
geos_geometry_errorosmium::geos_geometry_error84         explicit geos_geometry_error(const char* message) :
85             geometry_error(std::string{"geometry creation failed in GEOS library: "} + message) {
86         }
87 
88     }; // struct geos_geometry_error
89 
90     namespace geom {
91 
92         namespace detail {
93 
94             /// @deprecated
95             class GEOSFactoryImpl {
96 
97                 std::unique_ptr<const geos::geom::PrecisionModel> m_precision_model;
98                 std::unique_ptr<geos::geom::GeometryFactory> m_our_geos_factory;
99                 geos::geom::GeometryFactory* m_geos_factory;
100 
101                 std::unique_ptr<geos::geom::CoordinateSequence> m_coordinate_sequence;
102                 std::vector<std::unique_ptr<geos::geom::LinearRing>> m_rings;
103                 std::vector<std::unique_ptr<geos::geom::Polygon>> m_polygons;
104 
105             public:
106 
107                 using point_type        = std::unique_ptr<geos::geom::Point>;
108                 using linestring_type   = std::unique_ptr<geos::geom::LineString>;
109                 using polygon_type      = std::unique_ptr<geos::geom::Polygon>;
110                 using multipolygon_type = std::unique_ptr<geos::geom::MultiPolygon>;
111                 using ring_type         = std::unique_ptr<geos::geom::LinearRing>;
112 
GEOSFactoryImpl(int,geos::geom::GeometryFactory & geos_factory)113                 explicit GEOSFactoryImpl(int /* srid */, geos::geom::GeometryFactory& geos_factory) :
114                     m_precision_model(nullptr),
115                     m_our_geos_factory(nullptr),
116                     m_geos_factory(&geos_factory) {
117                 }
118 
119                 /**
120                  * @deprecated Do not set SRID explicitly. It will be set to the
121                  *             correct value automatically.
122                  */
GEOSFactoryImpl(int,int srid)123                 OSMIUM_DEPRECATED explicit GEOSFactoryImpl(int /* srid */, int srid) :
124                     m_precision_model(new geos::geom::PrecisionModel),
125                     m_our_geos_factory(new geos::geom::GeometryFactory{m_precision_model.get(), srid}),
126                     m_geos_factory(m_our_geos_factory.get()) {
127                 }
128 
GEOSFactoryImpl(int srid)129                 explicit GEOSFactoryImpl(int srid) :
130                     m_precision_model(new geos::geom::PrecisionModel),
131                     m_our_geos_factory(new geos::geom::GeometryFactory{m_precision_model.get(), srid}),
132                     m_geos_factory(m_our_geos_factory.get()) {
133                 }
134 
135                 /* Point */
136 
make_point(const osmium::geom::Coordinates & xy) const137                 point_type make_point(const osmium::geom::Coordinates& xy) const {
138                     try {
139                         return point_type{m_geos_factory->createPoint(geos::geom::Coordinate{xy.x, xy.y})};
140                     } catch (const geos::util::GEOSException& e) {
141                         std::throw_with_nested(osmium::geos_geometry_error(e.what()));
142                     }
143                 }
144 
145                 /* LineString */
146 
linestring_start()147                 void linestring_start() {
148                     try {
149                         m_coordinate_sequence.reset(m_geos_factory->getCoordinateSequenceFactory()->create(static_cast<std::size_t>(0), 2));
150                     } catch (const geos::util::GEOSException& e) {
151                         std::throw_with_nested(osmium::geos_geometry_error(e.what()));
152                     }
153                 }
154 
linestring_add_location(const osmium::geom::Coordinates & xy)155                 void linestring_add_location(const osmium::geom::Coordinates& xy) {
156                     try {
157                         m_coordinate_sequence->add(geos::geom::Coordinate{xy.x, xy.y});
158                     } catch (const geos::util::GEOSException& e) {
159                         std::throw_with_nested(osmium::geos_geometry_error(e.what()));
160                     }
161                 }
162 
linestring_finish(std::size_t)163                 linestring_type linestring_finish(std::size_t /* num_points */) {
164                     try {
165                         return linestring_type{m_geos_factory->createLineString(m_coordinate_sequence.release())};
166                     } catch (const geos::util::GEOSException& e) {
167                         std::throw_with_nested(osmium::geos_geometry_error(e.what()));
168                     }
169                 }
170 
171                 /* MultiPolygon */
172 
multipolygon_start()173                 void multipolygon_start() {
174                     m_polygons.clear();
175                 }
176 
multipolygon_polygon_start()177                 void multipolygon_polygon_start() {
178                     m_rings.clear();
179                 }
180 
multipolygon_polygon_finish()181                 void multipolygon_polygon_finish() {
182                     try {
183                         assert(!m_rings.empty());
184                         auto inner_rings = new std::vector<geos::geom::Geometry*>;
185                         std::transform(std::next(m_rings.begin(), 1), m_rings.end(), std::back_inserter(*inner_rings), [](std::unique_ptr<geos::geom::LinearRing>& r) {
186                             return r.release();
187                         });
188                         m_polygons.emplace_back(m_geos_factory->createPolygon(m_rings[0].release(), inner_rings));
189                         m_rings.clear();
190                     } catch (const geos::util::GEOSException& e) {
191                         std::throw_with_nested(osmium::geos_geometry_error(e.what()));
192                     }
193                 }
194 
multipolygon_outer_ring_start()195                 void multipolygon_outer_ring_start() {
196                     try {
197                         m_coordinate_sequence.reset(m_geos_factory->getCoordinateSequenceFactory()->create(static_cast<std::size_t>(0), 2));
198                     } catch (const geos::util::GEOSException& e) {
199                         std::throw_with_nested(osmium::geos_geometry_error(e.what()));
200                     }
201                 }
202 
multipolygon_outer_ring_finish()203                 void multipolygon_outer_ring_finish() {
204                     try {
205                         m_rings.emplace_back(m_geos_factory->createLinearRing(m_coordinate_sequence.release()));
206                     } catch (const geos::util::GEOSException& e) {
207                         std::throw_with_nested(osmium::geos_geometry_error(e.what()));
208                     }
209                 }
210 
multipolygon_inner_ring_start()211                 void multipolygon_inner_ring_start() {
212                     try {
213                         m_coordinate_sequence.reset(m_geos_factory->getCoordinateSequenceFactory()->create(static_cast<std::size_t>(0), 2));
214                     } catch (const geos::util::GEOSException& e) {
215                         std::throw_with_nested(osmium::geos_geometry_error(e.what()));
216                     }
217                 }
218 
multipolygon_inner_ring_finish()219                 void multipolygon_inner_ring_finish() {
220                     try {
221                         m_rings.emplace_back(m_geos_factory->createLinearRing(m_coordinate_sequence.release()));
222                     } catch (const geos::util::GEOSException& e) {
223                         std::throw_with_nested(osmium::geos_geometry_error(e.what()));
224                     }
225                 }
226 
multipolygon_add_location(const osmium::geom::Coordinates & xy)227                 void multipolygon_add_location(const osmium::geom::Coordinates& xy) {
228                     try {
229                         m_coordinate_sequence->add(geos::geom::Coordinate{xy.x, xy.y});
230                     } catch (const geos::util::GEOSException& e) {
231                         std::throw_with_nested(osmium::geos_geometry_error(e.what()));
232                     }
233                 }
234 
multipolygon_finish()235                 multipolygon_type multipolygon_finish() {
236                     try {
237                         auto polygons = new std::vector<geos::geom::Geometry*>;
238                         std::transform(m_polygons.begin(), m_polygons.end(), std::back_inserter(*polygons), [](std::unique_ptr<geos::geom::Polygon>& p) {
239                             return p.release();
240                         });
241                         m_polygons.clear();
242                         return multipolygon_type{m_geos_factory->createMultiPolygon(polygons)};
243                     } catch (const geos::util::GEOSException& e) {
244                         std::throw_with_nested(osmium::geos_geometry_error(e.what()));
245                     }
246                 }
247 
248             }; // class GEOSFactoryImpl
249 
250         } // namespace detail
251 
252         /// @deprecated
253         template <typename TProjection = IdentityProjection>
254         using GEOSFactory = GeometryFactory<osmium::geom::detail::GEOSFactoryImpl, TProjection>;
255 
256     } // namespace geom
257 
258 } // namespace osmium
259 
260 #endif
261 
262 #endif // OSMIUM_GEOM_GEOS_HPP
263