2015-01-13 10:54:25 -05:00
|
|
|
#ifndef OSMIUM_GEOM_FACTORY_HPP
|
|
|
|
#define OSMIUM_GEOM_FACTORY_HPP
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
This file is part of Osmium (http://osmcode.org/libosmium).
|
|
|
|
|
2015-02-05 09:13:48 -05:00
|
|
|
Copyright 2013-2015 Jochen Topf <jochen@topf.org> and others (see README).
|
2015-01-13 10:54:25 -05:00
|
|
|
|
|
|
|
Boost Software License - Version 1.0 - August 17th, 2003
|
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person or organization
|
|
|
|
obtaining a copy of the software and accompanying documentation covered by
|
|
|
|
this license (the "Software") to use, reproduce, display, distribute,
|
|
|
|
execute, and transmit the Software, and to prepare derivative works of the
|
|
|
|
Software, and to permit third-parties to whom the Software is furnished to
|
|
|
|
do so, all subject to the following:
|
|
|
|
|
|
|
|
The copyright notices in the Software and this entire statement, including
|
|
|
|
the above license grant, this restriction and the following disclaimer,
|
|
|
|
must be included in all copies of the Software, in whole or in part, and
|
|
|
|
all derivative works of the Software, unless such copies or derivative
|
|
|
|
works are solely in the form of machine-executable object code generated by
|
|
|
|
a source language processor.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
|
|
|
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
|
|
|
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
|
|
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
|
|
DEALINGS IN THE SOFTWARE.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <cstddef>
|
|
|
|
#include <stdexcept>
|
|
|
|
#include <string>
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
#include <osmium/geom/coordinates.hpp>
|
|
|
|
#include <osmium/memory/collection.hpp>
|
|
|
|
#include <osmium/memory/item.hpp>
|
|
|
|
#include <osmium/osm/area.hpp>
|
|
|
|
#include <osmium/osm/item_type.hpp>
|
|
|
|
#include <osmium/osm/location.hpp>
|
|
|
|
#include <osmium/osm/node.hpp>
|
|
|
|
#include <osmium/osm/node_ref.hpp>
|
|
|
|
#include <osmium/osm/way.hpp>
|
|
|
|
|
|
|
|
namespace osmium {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Exception thrown when an invalid geometry is encountered. An example
|
|
|
|
* would be a linestring with less than two points.
|
|
|
|
*/
|
2015-08-28 06:42:03 -04:00
|
|
|
class geometry_error : public std::runtime_error {
|
|
|
|
|
|
|
|
std::string m_message;
|
|
|
|
osmium::object_id_type m_id;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
geometry_error(const std::string& message, const char* object_type = "", osmium::object_id_type id = 0) :
|
|
|
|
std::runtime_error(message),
|
|
|
|
m_message(message),
|
|
|
|
m_id(id) {
|
|
|
|
if (m_id != 0) {
|
|
|
|
m_message += " (";
|
|
|
|
m_message += object_type;
|
|
|
|
m_message += "_id=";
|
|
|
|
m_message += std::to_string(m_id);
|
|
|
|
m_message += ")";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void set_id(const char* object_type, osmium::object_id_type id) {
|
|
|
|
if (m_id == 0 && id != 0) {
|
|
|
|
m_message += " (";
|
|
|
|
m_message += object_type;
|
|
|
|
m_message += "_id=";
|
|
|
|
m_message += std::to_string(id);
|
|
|
|
m_message += ")";
|
|
|
|
}
|
|
|
|
m_id = id;
|
|
|
|
}
|
2015-01-13 10:54:25 -05:00
|
|
|
|
2015-08-28 06:42:03 -04:00
|
|
|
osmium::object_id_type id() const noexcept {
|
|
|
|
return m_id;
|
2015-01-13 10:54:25 -05:00
|
|
|
}
|
|
|
|
|
2015-08-28 06:42:03 -04:00
|
|
|
virtual const char* what() const noexcept override {
|
|
|
|
return m_message.c_str();
|
2015-01-13 10:54:25 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
}; // struct geometry_error
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Everything related to geometry handling.
|
|
|
|
*/
|
|
|
|
namespace geom {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Which nodes of a way to use for a linestring.
|
|
|
|
*/
|
|
|
|
enum class use_nodes : bool {
|
|
|
|
unique = true, ///< Remove consecutive nodes with same location.
|
|
|
|
all = false ///< Use all nodes.
|
|
|
|
}; // enum class use_nodes
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Which direction the linestring created from a way
|
|
|
|
* should have.
|
|
|
|
*/
|
|
|
|
enum class direction : bool {
|
|
|
|
backward = true, ///< Linestring has reverse direction.
|
|
|
|
forward = false ///< Linestring has same direction as way.
|
|
|
|
}; // enum class direction
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This pseudo projection just returns its WGS84 input unchanged.
|
|
|
|
* Used as a template parameter if a real projection is not needed.
|
|
|
|
*/
|
|
|
|
class IdentityProjection {
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
Coordinates operator()(osmium::Location location) const {
|
|
|
|
return Coordinates{location.lon(), location.lat()};
|
|
|
|
}
|
|
|
|
|
|
|
|
int epsg() const noexcept {
|
|
|
|
return 4326;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string proj_string() const {
|
|
|
|
return "+proj=longlat +datum=WGS84 +no_defs";
|
|
|
|
}
|
|
|
|
|
|
|
|
}; // class IdentityProjection
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Geometry factory.
|
|
|
|
*/
|
|
|
|
template <class TGeomImpl, class TProjection = IdentityProjection>
|
|
|
|
class GeometryFactory {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add all points of an outer or inner ring to a multipolygon.
|
|
|
|
*/
|
|
|
|
void add_points(const osmium::OuterRing& nodes) {
|
|
|
|
osmium::Location last_location;
|
|
|
|
for (const osmium::NodeRef& node_ref : nodes) {
|
|
|
|
if (last_location != node_ref.location()) {
|
|
|
|
last_location = node_ref.location();
|
|
|
|
m_impl.multipolygon_add_location(m_projection(last_location));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TProjection m_projection;
|
|
|
|
TGeomImpl m_impl;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor for default initialized projection.
|
|
|
|
*/
|
|
|
|
template <class... TArgs>
|
|
|
|
GeometryFactory<TGeomImpl, TProjection>(TArgs&&... args) :
|
|
|
|
m_projection(),
|
|
|
|
m_impl(std::forward<TArgs>(args)...) {
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor for explicitly initialized projection. Note that the
|
|
|
|
* projection is moved into the GeometryFactory.
|
|
|
|
*/
|
|
|
|
template <class... TArgs>
|
|
|
|
GeometryFactory<TGeomImpl, TProjection>(TProjection&& projection, TArgs&&... args) :
|
|
|
|
m_projection(std::move(projection)),
|
|
|
|
m_impl(std::forward<TArgs>(args)...) {
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef typename TGeomImpl::point_type point_type;
|
|
|
|
typedef typename TGeomImpl::linestring_type linestring_type;
|
|
|
|
typedef typename TGeomImpl::polygon_type polygon_type;
|
|
|
|
typedef typename TGeomImpl::multipolygon_type multipolygon_type;
|
|
|
|
typedef typename TGeomImpl::ring_type ring_type;
|
|
|
|
|
|
|
|
int epsg() const {
|
|
|
|
return m_projection.epsg();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string proj_string() const {
|
|
|
|
return m_projection.proj_string();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Point */
|
|
|
|
|
|
|
|
point_type create_point(const osmium::Location location) const {
|
|
|
|
return m_impl.make_point(m_projection(location));
|
|
|
|
}
|
|
|
|
|
|
|
|
point_type create_point(const osmium::Node& node) {
|
2015-08-28 06:42:03 -04:00
|
|
|
try {
|
|
|
|
return create_point(node.location());
|
|
|
|
} catch (osmium::geometry_error& e) {
|
|
|
|
e.set_id("node", node.id());
|
|
|
|
throw;
|
|
|
|
}
|
2015-01-13 10:54:25 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
point_type create_point(const osmium::NodeRef& node_ref) {
|
2015-08-28 06:42:03 -04:00
|
|
|
try {
|
|
|
|
return create_point(node_ref.location());
|
|
|
|
} catch (osmium::geometry_error& e) {
|
|
|
|
e.set_id("node", node_ref.ref());
|
|
|
|
throw;
|
|
|
|
}
|
2015-01-13 10:54:25 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/* LineString */
|
|
|
|
|
|
|
|
void linestring_start() {
|
|
|
|
m_impl.linestring_start();
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class TIter>
|
|
|
|
size_t fill_linestring(TIter it, TIter end) {
|
|
|
|
size_t num_points = 0;
|
|
|
|
for (; it != end; ++it, ++num_points) {
|
|
|
|
m_impl.linestring_add_location(m_projection(it->location()));
|
|
|
|
}
|
|
|
|
return num_points;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class TIter>
|
|
|
|
size_t fill_linestring_unique(TIter it, TIter end) {
|
|
|
|
size_t num_points = 0;
|
|
|
|
osmium::Location last_location;
|
|
|
|
for (; it != end; ++it) {
|
|
|
|
if (last_location != it->location()) {
|
|
|
|
last_location = it->location();
|
|
|
|
m_impl.linestring_add_location(m_projection(last_location));
|
|
|
|
++num_points;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return num_points;
|
|
|
|
}
|
|
|
|
|
|
|
|
linestring_type linestring_finish(size_t num_points) {
|
|
|
|
return m_impl.linestring_finish(num_points);
|
|
|
|
}
|
|
|
|
|
2015-04-13 09:44:38 -04:00
|
|
|
linestring_type create_linestring(const osmium::WayNodeList& wnl, use_nodes un = use_nodes::unique, direction dir = direction::forward) {
|
2015-01-13 10:54:25 -05:00
|
|
|
linestring_start();
|
|
|
|
size_t num_points = 0;
|
|
|
|
|
|
|
|
if (un == use_nodes::unique) {
|
|
|
|
osmium::Location last_location;
|
|
|
|
switch (dir) {
|
|
|
|
case direction::forward:
|
|
|
|
num_points = fill_linestring_unique(wnl.cbegin(), wnl.cend());
|
|
|
|
break;
|
|
|
|
case direction::backward:
|
|
|
|
num_points = fill_linestring_unique(wnl.crbegin(), wnl.crend());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
switch (dir) {
|
|
|
|
case direction::forward:
|
|
|
|
num_points = fill_linestring(wnl.cbegin(), wnl.cend());
|
|
|
|
break;
|
|
|
|
case direction::backward:
|
|
|
|
num_points = fill_linestring(wnl.crbegin(), wnl.crend());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (num_points < 2) {
|
2015-08-28 06:42:03 -04:00
|
|
|
throw osmium::geometry_error("need at least two points for linestring");
|
2015-01-13 10:54:25 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return linestring_finish(num_points);
|
|
|
|
}
|
|
|
|
|
|
|
|
linestring_type create_linestring(const osmium::Way& way, use_nodes un=use_nodes::unique, direction dir=direction::forward) {
|
2015-08-28 06:42:03 -04:00
|
|
|
try {
|
|
|
|
return create_linestring(way.nodes(), un, dir);
|
|
|
|
} catch (osmium::geometry_error& e) {
|
|
|
|
e.set_id("way", way.id());
|
|
|
|
throw;
|
|
|
|
}
|
2015-01-13 10:54:25 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Polygon */
|
|
|
|
|
|
|
|
void polygon_start() {
|
|
|
|
m_impl.polygon_start();
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class TIter>
|
|
|
|
size_t fill_polygon(TIter it, TIter end) {
|
|
|
|
size_t num_points = 0;
|
|
|
|
for (; it != end; ++it, ++num_points) {
|
|
|
|
m_impl.polygon_add_location(m_projection(it->location()));
|
|
|
|
}
|
|
|
|
return num_points;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class TIter>
|
|
|
|
size_t fill_polygon_unique(TIter it, TIter end) {
|
|
|
|
size_t num_points = 0;
|
|
|
|
osmium::Location last_location;
|
|
|
|
for (; it != end; ++it) {
|
|
|
|
if (last_location != it->location()) {
|
|
|
|
last_location = it->location();
|
|
|
|
m_impl.polygon_add_location(m_projection(last_location));
|
|
|
|
++num_points;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return num_points;
|
|
|
|
}
|
|
|
|
|
|
|
|
polygon_type polygon_finish(size_t num_points) {
|
|
|
|
return m_impl.polygon_finish(num_points);
|
|
|
|
}
|
|
|
|
|
2015-08-28 06:42:03 -04:00
|
|
|
polygon_type create_polygon(const osmium::WayNodeList& wnl, use_nodes un = use_nodes::unique, direction dir = direction::forward) {
|
|
|
|
polygon_start();
|
|
|
|
size_t num_points = 0;
|
|
|
|
|
|
|
|
if (un == use_nodes::unique) {
|
|
|
|
osmium::Location last_location;
|
|
|
|
switch (dir) {
|
|
|
|
case direction::forward:
|
|
|
|
num_points = fill_polygon_unique(wnl.cbegin(), wnl.cend());
|
|
|
|
break;
|
|
|
|
case direction::backward:
|
|
|
|
num_points = fill_polygon_unique(wnl.crbegin(), wnl.crend());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
switch (dir) {
|
|
|
|
case direction::forward:
|
|
|
|
num_points = fill_polygon(wnl.cbegin(), wnl.cend());
|
|
|
|
break;
|
|
|
|
case direction::backward:
|
|
|
|
num_points = fill_polygon(wnl.crbegin(), wnl.crend());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (num_points < 4) {
|
|
|
|
throw osmium::geometry_error("need at least four points for polygon");
|
|
|
|
}
|
|
|
|
|
|
|
|
return polygon_finish(num_points);
|
|
|
|
}
|
|
|
|
|
|
|
|
polygon_type create_polygon(const osmium::Way& way, use_nodes un=use_nodes::unique, direction dir=direction::forward) {
|
|
|
|
try {
|
|
|
|
return create_polygon(way.nodes(), un, dir);
|
|
|
|
} catch (osmium::geometry_error& e) {
|
|
|
|
e.set_id("way", way.id());
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-13 10:54:25 -05:00
|
|
|
/* MultiPolygon */
|
|
|
|
|
|
|
|
multipolygon_type create_multipolygon(const osmium::Area& area) {
|
2015-08-28 06:42:03 -04:00
|
|
|
try {
|
|
|
|
size_t num_polygons = 0;
|
|
|
|
size_t num_rings = 0;
|
|
|
|
m_impl.multipolygon_start();
|
|
|
|
|
|
|
|
for (auto it = area.cbegin(); it != area.cend(); ++it) {
|
|
|
|
const osmium::OuterRing& ring = static_cast<const osmium::OuterRing&>(*it);
|
|
|
|
if (it->type() == osmium::item_type::outer_ring) {
|
|
|
|
if (num_polygons > 0) {
|
|
|
|
m_impl.multipolygon_polygon_finish();
|
|
|
|
}
|
|
|
|
m_impl.multipolygon_polygon_start();
|
|
|
|
m_impl.multipolygon_outer_ring_start();
|
|
|
|
add_points(ring);
|
|
|
|
m_impl.multipolygon_outer_ring_finish();
|
|
|
|
++num_rings;
|
|
|
|
++num_polygons;
|
|
|
|
} else if (it->type() == osmium::item_type::inner_ring) {
|
|
|
|
m_impl.multipolygon_inner_ring_start();
|
|
|
|
add_points(ring);
|
|
|
|
m_impl.multipolygon_inner_ring_finish();
|
|
|
|
++num_rings;
|
2015-01-13 10:54:25 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-28 06:42:03 -04:00
|
|
|
// if there are no rings, this area is invalid
|
|
|
|
if (num_rings == 0) {
|
|
|
|
throw osmium::geometry_error("area contains no rings");
|
|
|
|
}
|
2015-01-13 10:54:25 -05:00
|
|
|
|
2015-08-28 06:42:03 -04:00
|
|
|
m_impl.multipolygon_polygon_finish();
|
|
|
|
return m_impl.multipolygon_finish();
|
|
|
|
} catch (osmium::geometry_error& e) {
|
|
|
|
e.set_id("area", area.id());
|
|
|
|
throw;
|
|
|
|
}
|
2015-01-13 10:54:25 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
}; // class GeometryFactory
|
|
|
|
|
|
|
|
} // namespace geom
|
|
|
|
|
|
|
|
} // namespace osmium
|
|
|
|
|
|
|
|
#endif // OSMIUM_GEOM_FACTORY_HPP
|