2014-08-26 10:03:44 -04:00
|
|
|
#ifndef OSMIUM_AREA_DETAIL_PROTO_RING_HPP
|
|
|
|
#define OSMIUM_AREA_DETAIL_PROTO_RING_HPP
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
This file is part of Osmium (http://osmcode.org/libosmium).
|
|
|
|
|
|
|
|
Copyright 2014 Jochen Topf <jochen@topf.org> and others (see README).
|
|
|
|
|
|
|
|
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 <algorithm>
|
|
|
|
#include <cassert>
|
|
|
|
#include <iostream>
|
|
|
|
#include <list>
|
|
|
|
#include <set>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include <osmium/osm/node_ref.hpp>
|
|
|
|
#include <osmium/area/detail/node_ref_segment.hpp>
|
|
|
|
|
|
|
|
namespace osmium {
|
|
|
|
|
|
|
|
namespace area {
|
|
|
|
|
|
|
|
namespace detail {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A ring in the process of being built by the Assembler object.
|
|
|
|
*/
|
|
|
|
class ProtoRing {
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
typedef std::vector<NodeRefSegment> segments_type;
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
// segments in this ring
|
|
|
|
segments_type m_segments;
|
|
|
|
|
|
|
|
bool m_outer {true};
|
|
|
|
|
|
|
|
// if this is an outer ring, these point to it's inner rings (if any)
|
|
|
|
std::vector<ProtoRing*> m_inner;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
2014-10-29 16:27:48 -04:00
|
|
|
explicit ProtoRing(const NodeRefSegment& segment) noexcept :
|
2014-08-26 10:03:44 -04:00
|
|
|
m_segments() {
|
|
|
|
add_segment_back(segment);
|
|
|
|
}
|
|
|
|
|
|
|
|
explicit ProtoRing(segments_type::const_iterator sbegin, segments_type::const_iterator send) :
|
|
|
|
m_segments(static_cast<size_t>(std::distance(sbegin, send))) {
|
|
|
|
std::copy(sbegin, send, m_segments.begin());
|
|
|
|
}
|
|
|
|
|
2014-10-29 16:27:48 -04:00
|
|
|
bool outer() const noexcept {
|
2014-08-26 10:03:44 -04:00
|
|
|
return m_outer;
|
|
|
|
}
|
|
|
|
|
2014-10-29 16:27:48 -04:00
|
|
|
void set_inner() noexcept {
|
2014-08-26 10:03:44 -04:00
|
|
|
m_outer = false;
|
|
|
|
}
|
|
|
|
|
2014-10-29 16:27:48 -04:00
|
|
|
segments_type& segments() noexcept {
|
2014-08-26 10:03:44 -04:00
|
|
|
return m_segments;
|
|
|
|
}
|
|
|
|
|
2014-10-29 16:27:48 -04:00
|
|
|
const segments_type& segments() const noexcept {
|
2014-08-26 10:03:44 -04:00
|
|
|
return m_segments;
|
|
|
|
}
|
|
|
|
|
|
|
|
void remove_segments(segments_type::iterator sbegin, segments_type::iterator send) {
|
|
|
|
m_segments.erase(sbegin, send);
|
|
|
|
}
|
|
|
|
|
|
|
|
void add_segment_front(const NodeRefSegment& segment) {
|
|
|
|
m_segments.insert(m_segments.begin(), segment);
|
|
|
|
}
|
|
|
|
|
|
|
|
void add_segment_back(const NodeRefSegment& segment) {
|
|
|
|
m_segments.push_back(segment);
|
|
|
|
}
|
|
|
|
|
|
|
|
const NodeRefSegment& get_segment_front() const {
|
|
|
|
return m_segments.front();
|
|
|
|
}
|
|
|
|
|
|
|
|
NodeRefSegment& get_segment_front() {
|
|
|
|
return m_segments.front();
|
|
|
|
}
|
|
|
|
|
|
|
|
const NodeRefSegment& get_segment_back() const {
|
|
|
|
return m_segments.back();
|
|
|
|
}
|
|
|
|
|
|
|
|
NodeRefSegment& get_segment_back() {
|
|
|
|
return m_segments.back();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool closed() const {
|
|
|
|
return m_segments.front().first().location() == m_segments.back().second().location();
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t sum() const {
|
|
|
|
int64_t sum = 0;
|
|
|
|
|
|
|
|
for (const auto& segment : m_segments) {
|
|
|
|
sum += static_cast<int64_t>(segment.first().location().x()) * static_cast<int64_t>(segment.second().location().y()) -
|
|
|
|
static_cast<int64_t>(segment.second().location().x()) * static_cast<int64_t>(segment.first().location().y());
|
|
|
|
}
|
|
|
|
|
|
|
|
return sum;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_cw() const {
|
|
|
|
return sum() <= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t area() const {
|
|
|
|
return std::abs(sum()) / 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
void swap_segments(ProtoRing& other) {
|
|
|
|
std::swap(m_segments, other.m_segments);
|
|
|
|
}
|
|
|
|
|
|
|
|
void add_inner_ring(ProtoRing* ring) {
|
|
|
|
m_inner.push_back(ring);
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::vector<ProtoRing*> inner_rings() const {
|
|
|
|
return m_inner;
|
|
|
|
}
|
|
|
|
|
|
|
|
void print(std::ostream& out) const {
|
|
|
|
out << "[";
|
|
|
|
bool first = true;
|
|
|
|
for (const auto& segment : m_segments) {
|
|
|
|
if (first) {
|
|
|
|
out << segment.first().ref();
|
|
|
|
}
|
|
|
|
out << ',' << segment.second().ref();
|
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
out << "]";
|
|
|
|
}
|
|
|
|
|
|
|
|
void reverse() {
|
|
|
|
std::for_each(m_segments.begin(), m_segments.end(), [](NodeRefSegment& segment) {
|
|
|
|
segment.swap_locations();
|
|
|
|
});
|
|
|
|
std::reverse(m_segments.begin(), m_segments.end());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Merge other ring to end of this ring.
|
|
|
|
*/
|
|
|
|
void merge_ring(const ProtoRing& other, bool debug) {
|
|
|
|
if (debug) {
|
|
|
|
std::cerr << " MERGE rings ";
|
|
|
|
print(std::cerr);
|
|
|
|
std::cerr << " to ";
|
|
|
|
other.print(std::cerr);
|
|
|
|
std::cerr << "\n";
|
|
|
|
}
|
|
|
|
m_segments.insert(m_segments.end(), other.m_segments.begin(), other.m_segments.end());
|
|
|
|
if (debug) {
|
|
|
|
std::cerr << " result ring: ";
|
|
|
|
print(std::cerr);
|
|
|
|
std::cerr << "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void merge_ring_reverse(const ProtoRing& other, bool debug) {
|
|
|
|
if (debug) {
|
|
|
|
std::cerr << " MERGE rings (reverse) ";
|
|
|
|
print(std::cerr);
|
|
|
|
std::cerr << " to ";
|
|
|
|
other.print(std::cerr);
|
|
|
|
std::cerr << "\n";
|
|
|
|
}
|
|
|
|
size_t n = m_segments.size();
|
|
|
|
m_segments.resize(n + other.m_segments.size());
|
|
|
|
std::transform(other.m_segments.rbegin(), other.m_segments.rend(), m_segments.begin() + static_cast<segments_type::difference_type>(n), [](NodeRefSegment segment) {
|
|
|
|
segment.swap_locations();
|
|
|
|
return segment;
|
|
|
|
});
|
|
|
|
if (debug) {
|
|
|
|
std::cerr << " result ring: ";
|
|
|
|
print(std::cerr);
|
|
|
|
std::cerr << "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const NodeRef& min_node() const {
|
|
|
|
auto it = std::min_element(m_segments.begin(), m_segments.end());
|
|
|
|
if (location_less()(it->first(), it->second())) {
|
|
|
|
return it->first();
|
|
|
|
} else {
|
|
|
|
return it->second();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_in(ProtoRing* outer) {
|
|
|
|
osmium::Location testpoint = segments().front().first().location();
|
|
|
|
bool is_in = false;
|
|
|
|
|
|
|
|
for (size_t i = 0, j = outer->segments().size()-1; i < outer->segments().size(); j = i++) {
|
|
|
|
if (((outer->segments()[i].first().location().y() > testpoint.y()) != (outer->segments()[j].first().location().y() > testpoint.y())) &&
|
|
|
|
(testpoint.x() < (outer->segments()[j].first().location().x() - outer->segments()[i].first().location().x()) * (testpoint.y() - outer->segments()[i].first().location().y()) / (outer->segments()[j].first().location().y() - outer->segments()[i].first().location().y()) + outer->segments()[i].first().location().x()) ) {
|
|
|
|
is_in = !is_in;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return is_in;
|
|
|
|
}
|
|
|
|
|
|
|
|
void get_ways(std::set<const osmium::Way*>& ways) {
|
|
|
|
for (const auto& segment : m_segments) {
|
|
|
|
ways.insert(segment.way());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool contains(const NodeRefSegment& segment) const {
|
|
|
|
for (const auto& s : m_segments) {
|
|
|
|
if (s == segment || (s.first() == segment.second() && s.second() == segment.first())) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
}; // class ProtoRing
|
|
|
|
|
|
|
|
template <typename TChar, typename TTraits>
|
|
|
|
inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const ProtoRing& ring) {
|
|
|
|
ring.print(out);
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace detail
|
|
|
|
|
|
|
|
} // namespace area
|
|
|
|
|
|
|
|
} // namespace osmium
|
|
|
|
|
|
|
|
#endif // OSMIUM_AREA_DETAIL_PROTO_RING_HPP
|