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