2014-11-28 06:13:18 -05:00
|
|
|
#ifndef STATIC_RTREE_HPP
|
|
|
|
#define STATIC_RTREE_HPP
|
2013-06-26 09:43:13 -04:00
|
|
|
|
2018-03-21 13:09:59 -04:00
|
|
|
#include "storage/tar_fwd.hpp"
|
|
|
|
|
2016-05-27 15:05:04 -04:00
|
|
|
#include "util/bearing.hpp"
|
|
|
|
#include "util/coordinate_calculation.hpp"
|
2016-01-02 11:13:44 -05:00
|
|
|
#include "util/deallocating_vector.hpp"
|
2016-05-27 15:05:04 -04:00
|
|
|
#include "util/exception.hpp"
|
2016-01-02 11:13:44 -05:00
|
|
|
#include "util/hilbert_value.hpp"
|
2016-05-27 15:05:04 -04:00
|
|
|
#include "util/integer_range.hpp"
|
2017-05-19 18:28:01 -04:00
|
|
|
#include "util/mmap_file.hpp"
|
2016-01-02 11:13:44 -05:00
|
|
|
#include "util/rectangle.hpp"
|
|
|
|
#include "util/typedefs.hpp"
|
2017-04-04 19:01:00 -04:00
|
|
|
#include "util/vector_view.hpp"
|
2016-04-08 20:18:47 -04:00
|
|
|
#include "util/web_mercator.hpp"
|
2013-06-26 09:43:13 -04:00
|
|
|
|
2016-01-02 11:13:44 -05:00
|
|
|
#include "osrm/coordinate.hpp"
|
2013-12-17 11:59:44 -05:00
|
|
|
|
2017-04-04 03:52:00 -04:00
|
|
|
#include "storage/shared_memory_ownership.hpp"
|
2017-03-29 08:06:52 -04:00
|
|
|
|
2013-06-26 09:43:13 -04:00
|
|
|
#include <boost/assert.hpp>
|
2013-08-09 11:47:11 -04:00
|
|
|
#include <boost/filesystem.hpp>
|
2016-09-09 09:22:26 -04:00
|
|
|
#include <boost/format.hpp>
|
2016-05-01 07:48:56 -04:00
|
|
|
#include <boost/iostreams/device/mapped_file.hpp>
|
2013-06-26 09:43:13 -04:00
|
|
|
|
2018-01-04 17:28:19 -05:00
|
|
|
#include <tbb/blocked_range.h>
|
2014-05-20 17:59:30 -04:00
|
|
|
#include <tbb/parallel_for.h>
|
2014-05-22 13:07:29 -04:00
|
|
|
#include <tbb/parallel_sort.h>
|
2014-05-20 17:59:30 -04:00
|
|
|
|
2013-06-26 09:43:13 -04:00
|
|
|
#include <algorithm>
|
2014-05-06 09:20:31 -04:00
|
|
|
#include <array>
|
2013-10-02 05:22:42 -04:00
|
|
|
#include <limits>
|
2014-05-09 10:47:42 -04:00
|
|
|
#include <memory>
|
2013-06-26 09:43:13 -04:00
|
|
|
#include <queue>
|
2013-07-22 10:32:19 -04:00
|
|
|
#include <string>
|
2013-06-26 09:43:13 -04:00
|
|
|
#include <vector>
|
|
|
|
|
2022-12-11 04:10:26 -05:00
|
|
|
namespace osrm::util
|
2016-01-05 10:51:13 -05:00
|
|
|
{
|
2018-03-21 13:09:59 -04:00
|
|
|
template <class EdgeDataT,
|
|
|
|
storage::Ownership Ownership = storage::Ownership::Container,
|
|
|
|
std::uint32_t BRANCHING_FACTOR = 64,
|
|
|
|
std::uint32_t LEAF_PAGE_SIZE = 4096>
|
|
|
|
class StaticRTree;
|
|
|
|
|
|
|
|
namespace serialization
|
|
|
|
{
|
|
|
|
template <class EdgeDataT,
|
|
|
|
storage::Ownership Ownership,
|
|
|
|
std::uint32_t BRANCHING_FACTOR,
|
|
|
|
std::uint32_t LEAF_PAGE_SIZE>
|
|
|
|
inline void read(storage::tar::FileReader &reader,
|
|
|
|
const std::string &name,
|
|
|
|
util::StaticRTree<EdgeDataT, Ownership, BRANCHING_FACTOR, LEAF_PAGE_SIZE> &rtree);
|
|
|
|
|
|
|
|
template <class EdgeDataT,
|
|
|
|
storage::Ownership Ownership,
|
|
|
|
std::uint32_t BRANCHING_FACTOR,
|
|
|
|
std::uint32_t LEAF_PAGE_SIZE>
|
|
|
|
inline void
|
|
|
|
write(storage::tar::FileWriter &writer,
|
|
|
|
const std::string &name,
|
|
|
|
const util::StaticRTree<EdgeDataT, Ownership, BRANCHING_FACTOR, LEAF_PAGE_SIZE> &rtree);
|
2020-11-26 10:21:39 -05:00
|
|
|
} // namespace serialization
|
2016-01-05 10:51:13 -05:00
|
|
|
|
2017-05-17 04:32:06 -04:00
|
|
|
/***
|
|
|
|
* Static RTree for serving nearest neighbour queries
|
2022-08-27 06:36:20 -04:00
|
|
|
* // All coordinates are projected first to Web Mercator before the bounding boxes
|
2017-05-17 04:32:06 -04:00
|
|
|
* // are computed, this means the internal distance metric doesn not represent meters!
|
|
|
|
*/
|
|
|
|
|
2014-06-23 10:54:31 -04:00
|
|
|
template <class EdgeDataT,
|
2018-03-21 13:09:59 -04:00
|
|
|
storage::Ownership Ownership,
|
|
|
|
std::uint32_t BRANCHING_FACTOR,
|
|
|
|
std::uint32_t LEAF_PAGE_SIZE>
|
2014-05-07 12:39:16 -04:00
|
|
|
class StaticRTree
|
2014-05-06 09:20:31 -04:00
|
|
|
{
|
2017-05-17 04:32:06 -04:00
|
|
|
/**********************************************************
|
|
|
|
* Example RTree construction:
|
|
|
|
*
|
|
|
|
* 30 elements (EdgeDataT objects)
|
|
|
|
* LEAF_NODE_SIZE = 3
|
|
|
|
* BRANCHING_FACTOR = 2
|
|
|
|
*
|
|
|
|
* 012 345 678 901 234 567 890 123 456 789 <- EdgeDataT objects in .fileIndex data, sorted by
|
|
|
|
* \|/ \|/ \|/ \|/ \|/ \|/ \|/ \|/ \|/ \|/ Hilbert Code of the centroid coordinate
|
|
|
|
* A B C D E F G H I J <- Everything from here down is a Rectangle in
|
|
|
|
* \ / \ / \ / \ / \ / .ramIndex
|
|
|
|
* K L M N O
|
|
|
|
* \ / \ / /
|
|
|
|
* \ / \ / /
|
|
|
|
* \ / \ / /
|
|
|
|
* P Q R
|
|
|
|
* \ / /
|
|
|
|
* \ / /
|
|
|
|
* \ / /
|
|
|
|
* \ / /
|
|
|
|
* \ / /
|
|
|
|
* \ / /
|
|
|
|
* \ / /
|
|
|
|
* U V
|
|
|
|
* \ /
|
|
|
|
* \ /
|
|
|
|
* \ /
|
|
|
|
* W
|
|
|
|
*
|
|
|
|
* Step 1 - objects 01234567... are sorted by Hilbert code (these are the line
|
|
|
|
* segments of the OSM roads)
|
|
|
|
* Step 2 - we grab LEAF_NODE_SIZE of them at a time and create TreeNode A with a
|
|
|
|
* bounding-box that surrounds the first LEAF_NODE_SIZE objects
|
|
|
|
* Step 2a- continue grabbing LEAF_NODE_SIZE objects, creating TreeNodes B,C,D,E...J
|
|
|
|
* until we run out of objects. The last TreeNode J may not have
|
|
|
|
* LEAF_NODE_SIZE entries. Our math later on caters for this.
|
|
|
|
* Step 3 - Now start grabbing nodes from A..J in groups of BRANCHING_FACTOR,
|
|
|
|
* and create K..O with bounding boxes surrounding the groups of
|
|
|
|
* BRANCHING_FACTOR. Again, O, the last entry, may have fewer than
|
|
|
|
* BRANCHING_FACTOR entries.
|
|
|
|
* Step 3a- Repeat this process for each level, until you only create 1 TreeNode
|
|
|
|
* to contain its children (in this case, W).
|
|
|
|
*
|
|
|
|
* As we create TreeNodes, we append them to the m_search_tree vector.
|
|
|
|
*
|
|
|
|
* After this part of the building process, m_search_tree will contain TreeNode
|
|
|
|
* objects in this order:
|
|
|
|
*
|
|
|
|
* ABCDEFGHIJ KLMNO PQR UV W
|
|
|
|
* 10 5 3 2 1 <- number of nodes in the level
|
|
|
|
*
|
|
|
|
* In order to make our math easy later on, we reverse the whole array,
|
|
|
|
* then reverse the nodes within each level:
|
|
|
|
*
|
|
|
|
* Reversed: W VU RQP ONMKL JIHGFEDCBA
|
|
|
|
* Levels reversed: W UV PQR KLMNO ABCDEFGHIJ
|
|
|
|
*
|
|
|
|
* We also now have the following information:
|
|
|
|
*
|
|
|
|
* level sizes = {1,2,3,5,10}
|
|
|
|
*
|
|
|
|
* and we can calculate the array position the nodes for each level
|
|
|
|
* start (based on the sum of the previous level sizes):
|
|
|
|
*
|
|
|
|
* level starts = {0,1,3,6,11}
|
|
|
|
*
|
|
|
|
* Now, some basic math can be used to navigate around the tree. See
|
|
|
|
* the body of the `child_indexes` function for the details.
|
|
|
|
*
|
|
|
|
***********************************************/
|
2017-04-04 18:00:17 -04:00
|
|
|
template <typename T> using Vector = ViewOrVector<T, Ownership>;
|
2017-04-02 19:58:06 -04:00
|
|
|
|
2014-05-06 09:20:31 -04:00
|
|
|
public:
|
2015-12-03 14:04:23 -05:00
|
|
|
using Rectangle = RectangleInt2D;
|
|
|
|
using EdgeData = EdgeDataT;
|
2017-04-02 19:58:06 -04:00
|
|
|
using CoordinateList = Vector<util::Coordinate>;
|
2014-08-29 06:37:07 -04:00
|
|
|
|
2017-05-17 04:32:06 -04:00
|
|
|
static_assert(LEAF_PAGE_SIZE >= sizeof(EdgeDataT), "page size is too small");
|
2016-05-28 04:36:25 -04:00
|
|
|
static_assert(((LEAF_PAGE_SIZE - 1) & LEAF_PAGE_SIZE) == 0, "page size is not a power of 2");
|
2017-05-17 04:32:06 -04:00
|
|
|
static constexpr std::uint32_t LEAF_NODE_SIZE = (LEAF_PAGE_SIZE / sizeof(EdgeDataT));
|
2016-05-01 11:40:58 -04:00
|
|
|
|
2016-03-28 14:38:19 -04:00
|
|
|
struct CandidateSegment
|
|
|
|
{
|
|
|
|
Coordinate fixed_projected_coordinate;
|
|
|
|
EdgeDataT data;
|
|
|
|
};
|
2013-06-26 09:43:13 -04:00
|
|
|
|
2017-05-17 04:32:06 -04:00
|
|
|
/**
|
|
|
|
* Represents a node position somewhere in our tree. This is purely a navigation
|
|
|
|
* class used to find children of each node - the actual data for each node
|
|
|
|
* is in the m_search_tree vector of TreeNode objects.
|
|
|
|
*/
|
2016-05-27 05:18:25 -04:00
|
|
|
struct TreeIndex
|
2016-05-27 03:37:57 -04:00
|
|
|
{
|
2017-05-17 04:32:06 -04:00
|
|
|
TreeIndex() : level(0), offset(0) {}
|
|
|
|
TreeIndex(std::uint32_t level_, std::uint32_t offset_) : level(level_), offset(offset_) {}
|
|
|
|
std::uint32_t level; // Which level of the tree is this node in
|
|
|
|
std::uint32_t offset; // Which node on this level is this (0=leftmost)
|
2016-05-27 03:37:57 -04:00
|
|
|
};
|
|
|
|
|
2017-05-17 04:32:06 -04:00
|
|
|
/**
|
|
|
|
* An actual node in the tree. It's pretty minimal, we use the TreeIndex
|
|
|
|
* classes to navigate around. The TreeNode is packed into m_search_tree
|
|
|
|
* in a specific order so we can calculate positions of children
|
|
|
|
* (see the children_indexes function)
|
|
|
|
*/
|
2014-05-06 09:20:31 -04:00
|
|
|
struct TreeNode
|
|
|
|
{
|
2016-05-27 03:37:57 -04:00
|
|
|
Rectangle minimum_bounding_rectangle;
|
2016-01-29 20:52:20 -05:00
|
|
|
};
|
|
|
|
|
2014-05-06 09:20:31 -04:00
|
|
|
private:
|
2017-05-17 04:32:06 -04:00
|
|
|
/**
|
|
|
|
* A lightweight wrapper for the Hilbert Code for each EdgeDataT object
|
|
|
|
* A vector of these is used to sort the EdgeDataT input onto the
|
|
|
|
* Hilbert Curve.
|
|
|
|
* The sorting doesn't modify the original array, so this struct
|
|
|
|
* maintains a pointer to the original index position (m_original_index)
|
|
|
|
* so we can fetch the original data from the sorted position.
|
|
|
|
*/
|
2014-05-06 09:20:31 -04:00
|
|
|
struct WrappedInputElement
|
|
|
|
{
|
2016-05-28 04:36:25 -04:00
|
|
|
explicit WrappedInputElement(const uint64_t _hilbert_value,
|
2017-05-17 04:32:06 -04:00
|
|
|
const std::uint32_t _original_index)
|
|
|
|
: m_hilbert_value(_hilbert_value), m_original_index(_original_index)
|
2014-05-06 09:20:31 -04:00
|
|
|
{
|
|
|
|
}
|
2013-06-27 10:57:40 -04:00
|
|
|
|
2017-05-17 04:32:06 -04:00
|
|
|
WrappedInputElement() : m_hilbert_value(0), m_original_index(UINT_MAX) {}
|
2013-06-26 09:43:13 -04:00
|
|
|
|
|
|
|
uint64_t m_hilbert_value;
|
2017-05-17 04:32:06 -04:00
|
|
|
std::uint32_t m_original_index;
|
2013-06-26 09:43:13 -04:00
|
|
|
|
2014-10-21 12:34:50 -04:00
|
|
|
inline bool operator<(const WrappedInputElement &other) const
|
2014-05-06 09:20:31 -04:00
|
|
|
{
|
2013-06-26 09:43:13 -04:00
|
|
|
return m_hilbert_value < other.m_hilbert_value;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-05-06 09:20:31 -04:00
|
|
|
struct QueryCandidate
|
|
|
|
{
|
2016-05-27 05:18:25 -04:00
|
|
|
QueryCandidate(std::uint64_t squared_min_dist, TreeIndex tree_index)
|
2016-05-28 04:36:25 -04:00
|
|
|
: squared_min_dist(squared_min_dist), tree_index(tree_index),
|
2017-06-02 05:38:46 -04:00
|
|
|
segment_index(std::numeric_limits<std::uint32_t>::max())
|
2016-05-28 04:36:25 -04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QueryCandidate(std::uint64_t squared_min_dist,
|
|
|
|
TreeIndex tree_index,
|
|
|
|
std::uint32_t segment_index,
|
|
|
|
const Coordinate &coordinate)
|
|
|
|
: squared_min_dist(squared_min_dist), tree_index(tree_index),
|
2017-06-02 05:38:46 -04:00
|
|
|
fixed_projected_coordinate(coordinate), segment_index(segment_index)
|
2016-05-28 04:36:25 -04:00
|
|
|
{
|
|
|
|
}
|
2016-05-27 05:18:25 -04:00
|
|
|
|
|
|
|
inline bool is_segment() const
|
|
|
|
{
|
2017-06-02 05:38:46 -04:00
|
|
|
return segment_index != std::numeric_limits<std::uint32_t>::max();
|
2016-05-27 05:18:25 -04:00
|
|
|
}
|
|
|
|
|
2014-10-21 12:34:50 -04:00
|
|
|
inline bool operator<(const QueryCandidate &other) const
|
2014-05-06 09:20:31 -04:00
|
|
|
{
|
2017-05-17 04:32:06 -04:00
|
|
|
// Attn: this is reversed order. std::priority_queue is a
|
|
|
|
// max pq (biggest item at the front)!
|
2016-03-28 14:38:19 -04:00
|
|
|
return other.squared_min_dist < squared_min_dist;
|
2013-06-26 09:43:13 -04:00
|
|
|
}
|
2014-06-10 11:26:05 -04:00
|
|
|
|
2016-04-08 19:24:58 -04:00
|
|
|
std::uint64_t squared_min_dist;
|
2016-05-27 05:18:25 -04:00
|
|
|
TreeIndex tree_index;
|
|
|
|
Coordinate fixed_projected_coordinate;
|
2017-06-02 05:38:46 -04:00
|
|
|
std::uint32_t segment_index;
|
2014-06-23 10:54:31 -04:00
|
|
|
};
|
2014-06-10 11:26:05 -04:00
|
|
|
|
2018-03-21 13:09:59 -04:00
|
|
|
// Representation of the in-memory search tree
|
|
|
|
Vector<TreeNode> m_search_tree;
|
2017-05-17 04:32:06 -04:00
|
|
|
// Reference to the actual lon/lat data we need for doing math
|
2018-04-03 11:16:53 -04:00
|
|
|
util::vector_view<const Coordinate> m_coordinate_list;
|
2017-05-17 04:32:06 -04:00
|
|
|
// Holds the start indexes of each level in m_search_tree
|
2018-03-21 13:09:59 -04:00
|
|
|
Vector<std::uint64_t> m_tree_level_starts;
|
2017-05-17 04:32:06 -04:00
|
|
|
// mmap'd .fileIndex file
|
2018-03-27 06:20:06 -04:00
|
|
|
boost::iostreams::mapped_file_source m_objects_region;
|
2017-05-17 04:32:06 -04:00
|
|
|
// This is a view of the EdgeDataT data mmap'd from the .fileIndex file
|
2018-03-27 06:20:06 -04:00
|
|
|
util::vector_view<const EdgeDataT> m_objects;
|
2014-02-21 10:55:41 -05:00
|
|
|
|
2014-05-06 09:20:31 -04:00
|
|
|
public:
|
2018-04-03 11:16:53 -04:00
|
|
|
StaticRTree() = default;
|
2014-05-07 12:39:16 -04:00
|
|
|
StaticRTree(const StaticRTree &) = delete;
|
Takes care of proper special member generation globally, fixes #1689
Phew, a lot of classes were affected by this. The rationale for the
changes are as follows:
- When a type X declares any constructor, the default constructor is
not declared, so there is no need for X() = delete there. In fact,
there is brutal difference between those two: deleted members
participate in overload resolution, but not-declared members do not!
- When a type X wants to be non-copyable (e.g. to be only movable, like
threads, unique_ptrs, and so on), you can either do it by inheriting
from boost::noncopyable (the old way), or better declare both (!) the
copy constructor _and_ the copy assignment operator as deleted:
X(X const&) = delete;
X& operator=(X const&) = delete;
We had tons of types with deleted copy constructors that were lacking
a corresponding deleted copy assignment operator, making them still
copyable and you wouldn't even notice (read: scary)!
References:
- http://accu.org/content/conf2014/Howard_Hinnant_Accu_2014.pdf
- http://www.boost.org/doc/libs/master/libs/core/doc/html/core/noncopyable.html
Note: I know, I'm quoting Hinnant's extraordinary slides a lot, but
getting the sematic right here is so incredibly important.
2016-01-27 05:20:55 -05:00
|
|
|
StaticRTree &operator=(const StaticRTree &) = delete;
|
2018-03-21 13:09:59 -04:00
|
|
|
StaticRTree(StaticRTree &&) = default;
|
|
|
|
StaticRTree &operator=(StaticRTree &&) = default;
|
2014-05-07 12:39:16 -04:00
|
|
|
|
2014-05-06 09:20:31 -04:00
|
|
|
// Construct a packed Hilbert-R-Tree with Kamel-Faloutsos algorithm [1]
|
2015-04-21 04:43:02 -04:00
|
|
|
explicit StaticRTree(const std::vector<EdgeDataT> &input_data_vector,
|
2018-03-21 13:09:59 -04:00
|
|
|
const Vector<Coordinate> &coordinate_list,
|
|
|
|
const boost::filesystem::path &on_disk_file_name)
|
2018-04-03 11:16:53 -04:00
|
|
|
: m_coordinate_list(coordinate_list.data(), coordinate_list.size())
|
2013-06-27 10:57:40 -04:00
|
|
|
{
|
2017-05-17 04:32:06 -04:00
|
|
|
const auto element_count = input_data_vector.size();
|
2016-05-01 11:40:58 -04:00
|
|
|
std::vector<WrappedInputElement> input_wrapper_vector(element_count);
|
2013-06-26 09:43:13 -04:00
|
|
|
|
2017-05-17 04:32:06 -04:00
|
|
|
// Step 1 - create a vector of Hilbert Code/original position pairs
|
2016-05-28 04:36:25 -04:00
|
|
|
tbb::parallel_for(
|
|
|
|
tbb::blocked_range<uint64_t>(0, element_count),
|
|
|
|
[&input_data_vector, &input_wrapper_vector, this](
|
2024-05-06 03:14:46 -04:00
|
|
|
const tbb::blocked_range<uint64_t> &range)
|
|
|
|
{
|
2016-05-28 04:36:25 -04:00
|
|
|
for (uint64_t element_counter = range.begin(), end = range.end();
|
|
|
|
element_counter != end;
|
2016-05-27 15:05:04 -04:00
|
|
|
++element_counter)
|
2014-05-20 17:59:30 -04:00
|
|
|
{
|
|
|
|
WrappedInputElement ¤t_wrapper = input_wrapper_vector[element_counter];
|
2017-05-17 04:32:06 -04:00
|
|
|
current_wrapper.m_original_index = element_counter;
|
2014-05-20 17:59:30 -04:00
|
|
|
|
2014-06-23 10:54:31 -04:00
|
|
|
EdgeDataT const ¤t_element = input_data_vector[element_counter];
|
2014-05-20 17:59:30 -04:00
|
|
|
|
|
|
|
// Get Hilbert-Value for centroid in mercartor projection
|
2016-05-06 20:43:37 -04:00
|
|
|
BOOST_ASSERT(current_element.u < m_coordinate_list.size());
|
|
|
|
BOOST_ASSERT(current_element.v < m_coordinate_list.size());
|
2016-02-17 21:20:27 -05:00
|
|
|
|
2016-03-26 12:28:22 -04:00
|
|
|
Coordinate current_centroid = coordinate_calculation::centroid(
|
2016-05-06 20:43:37 -04:00
|
|
|
m_coordinate_list[current_element.u], m_coordinate_list[current_element.v]);
|
2016-07-26 09:00:58 -04:00
|
|
|
current_centroid.lat = FixedLatitude{static_cast<std::int32_t>(
|
|
|
|
COORDINATE_PRECISION *
|
|
|
|
web_mercator::latToY(toFloating(current_centroid.lat)))};
|
2014-05-20 17:59:30 -04:00
|
|
|
|
2016-11-22 15:08:12 -05:00
|
|
|
current_wrapper.m_hilbert_value = GetHilbertCode(current_centroid);
|
2014-05-20 17:59:30 -04:00
|
|
|
}
|
2014-06-23 10:54:31 -04:00
|
|
|
});
|
2013-11-22 12:05:47 -05:00
|
|
|
|
2014-05-06 09:20:31 -04:00
|
|
|
// sort the hilbert-value representatives
|
2015-09-10 05:36:02 -04:00
|
|
|
tbb::parallel_sort(input_wrapper_vector.begin(), input_wrapper_vector.end());
|
2014-05-06 09:20:31 -04:00
|
|
|
{
|
2018-03-27 06:20:06 -04:00
|
|
|
boost::iostreams::mapped_file out_objects_region;
|
|
|
|
auto out_objects = mmapFile<EdgeDataT>(on_disk_file_name,
|
|
|
|
out_objects_region,
|
|
|
|
input_data_vector.size() * sizeof(EdgeDataT));
|
2018-03-21 13:09:59 -04:00
|
|
|
|
2017-05-17 04:32:06 -04:00
|
|
|
// Note, we can't just write everything in one go, because the input_data_vector
|
|
|
|
// is not sorted by hilbert code, only the input_wrapper_vector is in the correct
|
|
|
|
// order. Instead, we iterate over input_wrapper_vector, copy the hilbert-indexed
|
|
|
|
// entries from input_data_vector into a temporary contiguous array, then write
|
|
|
|
// that array to disk.
|
|
|
|
|
|
|
|
// Create the first level of TreeNodes - each bounding LEAF_NODE_COUNT EdgeDataT
|
|
|
|
// objects.
|
|
|
|
std::size_t wrapped_element_index = 0;
|
2018-03-27 06:20:06 -04:00
|
|
|
auto objects_iter = out_objects.begin();
|
2018-03-21 13:09:59 -04:00
|
|
|
|
2017-05-17 04:32:06 -04:00
|
|
|
while (wrapped_element_index < element_count)
|
2014-05-06 09:20:31 -04:00
|
|
|
{
|
2017-05-28 01:37:53 -04:00
|
|
|
TreeNode current_node;
|
2016-05-27 03:37:57 -04:00
|
|
|
|
2017-05-17 04:32:06 -04:00
|
|
|
// Loop over the next block of EdgeDataT, calculate the bounding box
|
|
|
|
// for the block, and save the data to write to disk in the correct
|
|
|
|
// order.
|
|
|
|
for (std::uint32_t object_index = 0;
|
|
|
|
object_index < LEAF_NODE_SIZE && wrapped_element_index < element_count;
|
|
|
|
++object_index, ++wrapped_element_index)
|
|
|
|
{
|
|
|
|
const std::uint32_t input_object_index =
|
|
|
|
input_wrapper_vector[wrapped_element_index].m_original_index;
|
|
|
|
const EdgeDataT &object = input_data_vector[input_object_index];
|
|
|
|
|
2018-03-21 13:09:59 -04:00
|
|
|
*objects_iter++ = object;
|
2017-05-17 04:32:06 -04:00
|
|
|
|
|
|
|
Coordinate projected_u{
|
|
|
|
web_mercator::fromWGS84(Coordinate{m_coordinate_list[object.u]})};
|
|
|
|
Coordinate projected_v{
|
|
|
|
web_mercator::fromWGS84(Coordinate{m_coordinate_list[object.v]})};
|
|
|
|
|
|
|
|
BOOST_ASSERT(std::abs(toFloating(projected_u.lon).operator double()) <= 180.);
|
|
|
|
BOOST_ASSERT(std::abs(toFloating(projected_u.lat).operator double()) <= 180.);
|
|
|
|
BOOST_ASSERT(std::abs(toFloating(projected_v.lon).operator double()) <= 180.);
|
|
|
|
BOOST_ASSERT(std::abs(toFloating(projected_v.lat).operator double()) <= 180.);
|
|
|
|
|
|
|
|
Rectangle rectangle;
|
|
|
|
rectangle.min_lon =
|
|
|
|
std::min(rectangle.min_lon, std::min(projected_u.lon, projected_v.lon));
|
|
|
|
rectangle.max_lon =
|
|
|
|
std::max(rectangle.max_lon, std::max(projected_u.lon, projected_v.lon));
|
|
|
|
|
|
|
|
rectangle.min_lat =
|
|
|
|
std::min(rectangle.min_lat, std::min(projected_u.lat, projected_v.lat));
|
|
|
|
rectangle.max_lat =
|
|
|
|
std::max(rectangle.max_lat, std::max(projected_u.lat, projected_v.lat));
|
|
|
|
|
|
|
|
BOOST_ASSERT(rectangle.IsValid());
|
|
|
|
current_node.minimum_bounding_rectangle.MergeBoundingBoxes(rectangle);
|
2017-05-28 01:37:53 -04:00
|
|
|
}
|
2013-06-26 09:43:13 -04:00
|
|
|
|
2017-05-17 04:32:06 -04:00
|
|
|
m_search_tree.emplace_back(current_node);
|
2017-05-28 01:37:53 -04:00
|
|
|
}
|
2013-06-26 09:43:13 -04:00
|
|
|
}
|
2018-03-27 06:20:06 -04:00
|
|
|
// mmap as read-only now
|
|
|
|
m_objects = mmapFile<EdgeDataT>(on_disk_file_name, m_objects_region);
|
2013-06-26 09:43:13 -04:00
|
|
|
|
2017-05-17 04:32:06 -04:00
|
|
|
// Should hold the number of nodes at the lowest level of the graph (closest
|
|
|
|
// to the data)
|
|
|
|
std::uint32_t nodes_in_previous_level = m_search_tree.size();
|
2018-03-21 13:09:59 -04:00
|
|
|
|
|
|
|
// Holds the number of TreeNodes in each level.
|
|
|
|
// We always start with the root node, so
|
|
|
|
// m_tree_level_sizes[0] should always be 1
|
|
|
|
std::vector<std::uint64_t> tree_level_sizes;
|
|
|
|
tree_level_sizes.push_back(nodes_in_previous_level);
|
2017-05-17 04:32:06 -04:00
|
|
|
|
|
|
|
// Now, repeatedly create levels of nodes that contain BRANCHING_FACTOR
|
|
|
|
// nodes from the previous level.
|
|
|
|
while (nodes_in_previous_level > 1)
|
2014-05-06 09:20:31 -04:00
|
|
|
{
|
2017-05-17 04:32:06 -04:00
|
|
|
auto previous_level_start_pos = m_search_tree.size() - nodes_in_previous_level;
|
|
|
|
|
|
|
|
// We can calculate how many nodes will be in this level, we divide by
|
|
|
|
// BRANCHING_FACTOR
|
|
|
|
// and round up
|
|
|
|
std::uint32_t nodes_in_current_level =
|
|
|
|
std::ceil(static_cast<double>(nodes_in_previous_level) / BRANCHING_FACTOR);
|
|
|
|
|
2017-06-02 05:38:46 -04:00
|
|
|
for (auto current_node_idx : irange<std::size_t>(0, nodes_in_current_level))
|
2014-05-06 09:20:31 -04:00
|
|
|
{
|
2013-06-26 09:43:13 -04:00
|
|
|
TreeNode parent_node;
|
2017-05-17 04:32:06 -04:00
|
|
|
auto first_child_index =
|
|
|
|
current_node_idx * BRANCHING_FACTOR + previous_level_start_pos;
|
|
|
|
auto last_child_index =
|
|
|
|
first_child_index +
|
2017-06-02 05:38:46 -04:00
|
|
|
std::min<std::size_t>(BRANCHING_FACTOR,
|
|
|
|
nodes_in_previous_level -
|
|
|
|
current_node_idx * BRANCHING_FACTOR);
|
2017-05-17 04:32:06 -04:00
|
|
|
|
|
|
|
// Calculate the bounding box for BRANCHING_FACTOR nodes in the previous
|
|
|
|
// level, then save that box as a new TreeNode in the new level.
|
2017-06-02 05:38:46 -04:00
|
|
|
for (auto child_node_idx : irange<std::size_t>(first_child_index, last_child_index))
|
2014-05-06 09:20:31 -04:00
|
|
|
{
|
2017-05-17 04:32:06 -04:00
|
|
|
parent_node.minimum_bounding_rectangle.MergeBoundingBoxes(
|
|
|
|
m_search_tree[child_node_idx].minimum_bounding_rectangle);
|
2013-06-26 09:43:13 -04:00
|
|
|
}
|
2017-05-17 04:32:06 -04:00
|
|
|
m_search_tree.emplace_back(parent_node);
|
2013-06-26 09:43:13 -04:00
|
|
|
}
|
2017-05-17 04:32:06 -04:00
|
|
|
nodes_in_previous_level = nodes_in_current_level;
|
2018-03-21 13:09:59 -04:00
|
|
|
tree_level_sizes.push_back(nodes_in_previous_level);
|
2013-06-26 09:43:13 -04:00
|
|
|
}
|
2017-05-17 04:32:06 -04:00
|
|
|
// At this point, we've got our tree built, but the nodes are in a weird order.
|
|
|
|
// Next thing we'll do is flip it around so that we don't end up with a lot of
|
|
|
|
// `size - n` math later on.
|
2013-06-26 09:43:13 -04:00
|
|
|
|
2017-05-17 04:32:06 -04:00
|
|
|
// Flip the tree so that the root node is at 0.
|
|
|
|
// This just makes our math during search a bit more intuitive
|
2014-07-20 18:24:40 -04:00
|
|
|
std::reverse(m_search_tree.begin(), m_search_tree.end());
|
2013-11-22 12:05:47 -05:00
|
|
|
|
2017-05-17 04:32:06 -04:00
|
|
|
// Same for the level sizes - root node / base level is at 0
|
2018-03-21 13:09:59 -04:00
|
|
|
std::reverse(tree_level_sizes.begin(), tree_level_sizes.end());
|
2017-05-17 04:32:06 -04:00
|
|
|
|
|
|
|
// The first level starts at 0
|
|
|
|
m_tree_level_starts = {0};
|
|
|
|
// The remaining levels start at the partial sum of the preceeding level sizes
|
2018-03-21 13:09:59 -04:00
|
|
|
std::partial_sum(tree_level_sizes.begin(),
|
|
|
|
tree_level_sizes.end(),
|
2017-05-17 04:32:06 -04:00
|
|
|
std::back_inserter(m_tree_level_starts));
|
2018-03-21 13:09:59 -04:00
|
|
|
BOOST_ASSERT(m_tree_level_starts.size() >= 2);
|
2017-05-17 04:32:06 -04:00
|
|
|
|
|
|
|
// Now we have to flip the coordinates within each level so that math is easier
|
|
|
|
// later on. The workflow here is:
|
|
|
|
// The initial order of tree nodes in the m_search_tree array is roughly:
|
|
|
|
// 6789 345 12 0 (each block here is a level of the tree)
|
|
|
|
// Then we reverse it and get:
|
|
|
|
// 0 21 543 9876
|
|
|
|
// Now the loop below reverses each level to give us the final result
|
|
|
|
// 0 12 345 6789
|
|
|
|
// This ordering keeps the position math easy to understand during later
|
|
|
|
// searches
|
2018-03-21 13:09:59 -04:00
|
|
|
for (auto i : irange<std::size_t>(0, tree_level_sizes.size()))
|
2017-05-17 04:32:06 -04:00
|
|
|
{
|
|
|
|
std::reverse(m_search_tree.begin() + m_tree_level_starts[i],
|
2018-03-21 13:09:59 -04:00
|
|
|
m_search_tree.begin() + m_tree_level_starts[i] + tree_level_sizes[i]);
|
2017-05-17 04:32:06 -04:00
|
|
|
}
|
2013-06-26 09:43:13 -04:00
|
|
|
}
|
|
|
|
|
2017-05-17 04:32:06 -04:00
|
|
|
/**
|
2018-03-21 13:09:59 -04:00
|
|
|
* Constructs an empty RTree for de-serialization.
|
2017-05-17 04:32:06 -04:00
|
|
|
*/
|
2018-03-21 13:09:59 -04:00
|
|
|
template <typename = std::enable_if<Ownership == storage::Ownership::Container>>
|
|
|
|
explicit StaticRTree(const boost::filesystem::path &on_disk_file_name,
|
2017-04-02 19:58:06 -04:00
|
|
|
const Vector<Coordinate> &coordinate_list)
|
2022-07-04 16:46:59 -04:00
|
|
|
: m_coordinate_list(coordinate_list.data(), coordinate_list.size()),
|
|
|
|
m_objects(mmapFile<EdgeDataT>(on_disk_file_name, m_objects_region))
|
2014-05-06 09:20:31 -04:00
|
|
|
{
|
2013-09-23 12:03:07 -04:00
|
|
|
}
|
|
|
|
|
2017-05-17 04:32:06 -04:00
|
|
|
/**
|
|
|
|
* Constructs an r-tree from blocks of memory loaded by someone else
|
|
|
|
* (usually a shared memory block created by osrm-datastore)
|
|
|
|
* These memory blocks basically just contain the files read into RAM,
|
|
|
|
* excep the .fileIndex file always stays on disk, and we mmap() it as usual
|
|
|
|
*/
|
2018-03-21 13:09:59 -04:00
|
|
|
explicit StaticRTree(Vector<TreeNode> search_tree_,
|
|
|
|
Vector<std::uint64_t> tree_level_starts,
|
|
|
|
const boost::filesystem::path &on_disk_file_name,
|
2017-04-02 19:58:06 -04:00
|
|
|
const Vector<Coordinate> &coordinate_list)
|
2018-04-03 11:16:53 -04:00
|
|
|
: m_search_tree(std::move(search_tree_)),
|
|
|
|
m_coordinate_list(coordinate_list.data(), coordinate_list.size()),
|
2018-03-21 13:09:59 -04:00
|
|
|
m_tree_level_starts(std::move(tree_level_starts))
|
2013-09-24 06:07:34 -04:00
|
|
|
{
|
2018-03-21 13:09:59 -04:00
|
|
|
BOOST_ASSERT(m_tree_level_starts.size() >= 2);
|
|
|
|
m_objects = mmapFile<EdgeDataT>(on_disk_file_name, m_objects_region);
|
2013-08-26 08:16:34 -04:00
|
|
|
}
|
|
|
|
|
2016-03-28 14:38:19 -04:00
|
|
|
/* Returns all features inside the bounding box.
|
|
|
|
Rectangle needs to be projected!*/
|
2016-05-06 20:56:08 -04:00
|
|
|
std::vector<EdgeDataT> SearchInBox(const Rectangle &search_rectangle) const
|
2016-02-16 13:51:04 -05:00
|
|
|
{
|
2016-03-28 14:38:19 -04:00
|
|
|
const Rectangle projected_rectangle{
|
2016-05-28 04:36:25 -04:00
|
|
|
search_rectangle.min_lon,
|
|
|
|
search_rectangle.max_lon,
|
|
|
|
toFixed(FloatLatitude{
|
|
|
|
web_mercator::latToY(toFloating(FixedLatitude(search_rectangle.min_lat)))}),
|
|
|
|
toFixed(FloatLatitude{
|
|
|
|
web_mercator::latToY(toFloating(FixedLatitude(search_rectangle.max_lat)))})};
|
2016-02-16 13:51:04 -05:00
|
|
|
std::vector<EdgeDataT> results;
|
|
|
|
|
2016-05-27 05:18:25 -04:00
|
|
|
std::queue<TreeIndex> traversal_queue;
|
|
|
|
traversal_queue.push(TreeIndex{});
|
2016-02-16 13:51:04 -05:00
|
|
|
|
|
|
|
while (!traversal_queue.empty())
|
|
|
|
{
|
2016-05-27 03:37:57 -04:00
|
|
|
auto const current_tree_index = traversal_queue.front();
|
2016-02-16 13:51:04 -05:00
|
|
|
traversal_queue.pop();
|
|
|
|
|
2017-05-17 04:32:06 -04:00
|
|
|
// If we're at the bottom of the tree, we need to explore the
|
|
|
|
// element array
|
|
|
|
if (is_leaf(current_tree_index))
|
2016-02-16 13:51:04 -05:00
|
|
|
{
|
|
|
|
|
2017-05-17 04:32:06 -04:00
|
|
|
// Note: irange is [start,finish), so we need to +1 to make sure we visit the
|
|
|
|
// last
|
|
|
|
for (const auto current_child_index : child_indexes(current_tree_index))
|
2016-02-16 13:51:04 -05:00
|
|
|
{
|
2017-05-17 04:32:06 -04:00
|
|
|
const auto ¤t_edge = m_objects[current_child_index];
|
2016-02-16 13:51:04 -05:00
|
|
|
|
2016-04-03 05:41:46 -04:00
|
|
|
// we don't need to project the coordinates here,
|
|
|
|
// because we use the unprojected rectangle to test against
|
2016-05-28 04:36:25 -04:00
|
|
|
const Rectangle bbox{std::min(m_coordinate_list[current_edge.u].lon,
|
|
|
|
m_coordinate_list[current_edge.v].lon),
|
|
|
|
std::max(m_coordinate_list[current_edge.u].lon,
|
|
|
|
m_coordinate_list[current_edge.v].lon),
|
|
|
|
std::min(m_coordinate_list[current_edge.u].lat,
|
|
|
|
m_coordinate_list[current_edge.v].lat),
|
|
|
|
std::max(m_coordinate_list[current_edge.u].lat,
|
|
|
|
m_coordinate_list[current_edge.v].lat)};
|
2016-02-16 13:51:04 -05:00
|
|
|
|
2016-04-03 05:41:46 -04:00
|
|
|
// use the _unprojected_ input rectangle here
|
2016-02-16 13:51:04 -05:00
|
|
|
if (bbox.Intersects(search_rectangle))
|
|
|
|
{
|
|
|
|
results.push_back(current_edge);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-05-17 04:32:06 -04:00
|
|
|
BOOST_ASSERT(current_tree_index.level + 1 < m_tree_level_starts.size());
|
2016-05-27 03:37:57 -04:00
|
|
|
|
2017-05-17 04:32:06 -04:00
|
|
|
for (const auto child_index : child_indexes(current_tree_index))
|
2016-02-16 13:51:04 -05:00
|
|
|
{
|
2016-05-28 04:36:25 -04:00
|
|
|
const auto &child_rectangle =
|
2017-05-17 04:32:06 -04:00
|
|
|
m_search_tree[child_index].minimum_bounding_rectangle;
|
2016-02-16 13:51:04 -05:00
|
|
|
|
2016-04-03 05:41:46 -04:00
|
|
|
if (child_rectangle.Intersects(projected_rectangle))
|
2016-02-16 13:51:04 -05:00
|
|
|
{
|
2017-05-17 04:32:06 -04:00
|
|
|
traversal_queue.push(TreeIndex(
|
|
|
|
current_tree_index.level + 1,
|
|
|
|
child_index - m_tree_level_starts[current_tree_index.level + 1]));
|
2016-02-16 13:51:04 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
2015-12-03 14:04:23 -05:00
|
|
|
// Override filter and terminator for the desired behaviour.
|
2022-08-27 06:36:20 -04:00
|
|
|
std::vector<CandidateSegment> Nearest(const Coordinate input_coordinate,
|
|
|
|
const std::size_t max_results) const
|
2015-09-21 21:34:37 -04:00
|
|
|
{
|
2020-11-26 10:21:39 -05:00
|
|
|
return Nearest(
|
|
|
|
input_coordinate,
|
|
|
|
[](const CandidateSegment &) { return std::make_pair(true, true); },
|
2024-05-06 03:14:46 -04:00
|
|
|
[max_results](const std::size_t num_results, const CandidateSegment &)
|
|
|
|
{ return num_results >= max_results; });
|
2015-09-21 21:34:37 -04:00
|
|
|
}
|
|
|
|
|
2022-08-27 06:36:20 -04:00
|
|
|
// Return edges in distance order with the coordinate of the closest point on the edge.
|
2015-12-03 14:04:23 -05:00
|
|
|
template <typename FilterT, typename TerminationT>
|
2022-08-27 06:36:20 -04:00
|
|
|
std::vector<CandidateSegment> Nearest(const Coordinate input_coordinate,
|
|
|
|
const FilterT filter,
|
|
|
|
const TerminationT terminate) const
|
2014-06-10 11:26:05 -04:00
|
|
|
{
|
2022-08-27 06:36:20 -04:00
|
|
|
std::vector<CandidateSegment> results;
|
2016-04-08 20:18:47 -04:00
|
|
|
auto projected_coordinate = web_mercator::fromWGS84(input_coordinate);
|
2016-03-28 14:38:19 -04:00
|
|
|
Coordinate fixed_projected_coordinate{projected_coordinate};
|
2014-06-23 10:54:31 -04:00
|
|
|
// initialize queue with root element
|
2015-12-03 14:04:23 -05:00
|
|
|
std::priority_queue<QueryCandidate> traversal_queue;
|
2016-05-27 05:18:25 -04:00
|
|
|
traversal_queue.push(QueryCandidate{0, TreeIndex{}});
|
2016-04-29 22:19:03 -04:00
|
|
|
|
2014-06-23 10:54:31 -04:00
|
|
|
while (!traversal_queue.empty())
|
|
|
|
{
|
2016-03-28 14:38:19 -04:00
|
|
|
QueryCandidate current_query_node = traversal_queue.top();
|
2014-07-23 13:25:09 -04:00
|
|
|
traversal_queue.pop();
|
|
|
|
|
2016-05-27 05:18:25 -04:00
|
|
|
const TreeIndex ¤t_tree_index = current_query_node.tree_index;
|
|
|
|
if (!current_query_node.is_segment())
|
2014-09-23 12:46:14 -04:00
|
|
|
{ // current object is a tree node
|
2017-05-17 04:32:06 -04:00
|
|
|
if (is_leaf(current_tree_index))
|
2014-07-23 13:25:09 -04:00
|
|
|
{
|
2016-05-28 04:36:25 -04:00
|
|
|
ExploreLeafNode(current_tree_index,
|
|
|
|
fixed_projected_coordinate,
|
|
|
|
projected_coordinate,
|
|
|
|
traversal_queue);
|
2014-07-23 13:25:09 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-05-28 04:36:25 -04:00
|
|
|
ExploreTreeNode(
|
|
|
|
current_tree_index, fixed_projected_coordinate, traversal_queue);
|
2014-07-23 13:25:09 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2016-05-28 04:36:25 -04:00
|
|
|
{ // current candidate is an actual road segment
|
2022-08-27 06:36:20 -04:00
|
|
|
const auto &edge_data = m_objects[current_query_node.segment_index];
|
|
|
|
// We deliberately make an edge data copy here, we mutate the value below
|
|
|
|
CandidateSegment current_candidate{current_query_node.fixed_projected_coordinate,
|
|
|
|
edge_data};
|
2014-07-23 13:25:09 -04:00
|
|
|
|
2016-05-27 15:05:04 -04:00
|
|
|
// to allow returns of no-results if too restrictive filtering, this needs to be
|
2016-05-28 04:36:25 -04:00
|
|
|
// done here even though performance would indicate that we want to stop after
|
|
|
|
// adding the first candidate
|
2016-04-13 11:18:40 -04:00
|
|
|
if (terminate(results.size(), current_candidate))
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-03-28 14:38:19 -04:00
|
|
|
auto use_segment = filter(current_candidate);
|
2015-12-03 14:04:23 -05:00
|
|
|
if (!use_segment.first && !use_segment.second)
|
2014-12-08 17:46:31 -05:00
|
|
|
{
|
2015-09-21 21:34:37 -04:00
|
|
|
continue;
|
|
|
|
}
|
2022-08-27 06:36:20 -04:00
|
|
|
current_candidate.data.forward_segment_id.enabled &= use_segment.first;
|
|
|
|
current_candidate.data.reverse_segment_id.enabled &= use_segment.second;
|
2015-09-21 21:34:37 -04:00
|
|
|
|
2014-09-23 12:46:14 -04:00
|
|
|
// store phantom node in result vector
|
2022-08-27 06:36:20 -04:00
|
|
|
results.push_back(std::move(current_candidate));
|
2014-12-23 11:18:32 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-03 14:04:23 -05:00
|
|
|
return results;
|
2014-12-23 11:18:32 -05:00
|
|
|
}
|
2014-06-23 10:54:31 -04:00
|
|
|
|
|
|
|
private:
|
2017-05-17 04:32:06 -04:00
|
|
|
/**
|
|
|
|
* Iterates over all the objects in a leaf node and inserts them into our
|
|
|
|
* search priority queue. The speed of this function is very much governed
|
|
|
|
* by the value of LEAF_NODE_SIZE, as we'll calculate the euclidean distance
|
|
|
|
* for every child of each leaf node visited.
|
|
|
|
*/
|
2015-12-03 14:04:23 -05:00
|
|
|
template <typename QueueT>
|
2016-05-27 05:18:25 -04:00
|
|
|
void ExploreLeafNode(const TreeIndex &leaf_id,
|
2016-05-28 04:36:25 -04:00
|
|
|
const Coordinate &projected_input_coordinate_fixed,
|
2016-03-28 14:38:19 -04:00
|
|
|
const FloatCoordinate &projected_input_coordinate,
|
2016-05-06 20:56:08 -04:00
|
|
|
QueueT &traversal_queue) const
|
2014-06-23 10:54:31 -04:00
|
|
|
{
|
2017-05-17 04:32:06 -04:00
|
|
|
// Check that we're actually looking at the bottom level of the tree
|
|
|
|
BOOST_ASSERT(is_leaf(leaf_id));
|
2014-06-23 10:54:31 -04:00
|
|
|
|
2017-05-17 04:32:06 -04:00
|
|
|
for (const auto i : child_indexes(leaf_id))
|
2015-01-27 11:44:46 -05:00
|
|
|
{
|
2017-05-17 04:32:06 -04:00
|
|
|
const auto ¤t_edge = m_objects[i];
|
|
|
|
|
2016-05-06 20:43:37 -04:00
|
|
|
const auto projected_u = web_mercator::fromWGS84(m_coordinate_list[current_edge.u]);
|
|
|
|
const auto projected_v = web_mercator::fromWGS84(m_coordinate_list[current_edge.v]);
|
2016-03-28 14:38:19 -04:00
|
|
|
|
|
|
|
FloatCoordinate projected_nearest;
|
|
|
|
std::tie(std::ignore, projected_nearest) =
|
2016-05-28 04:36:25 -04:00
|
|
|
coordinate_calculation::projectPointOnSegment(
|
|
|
|
projected_u, projected_v, projected_input_coordinate);
|
2016-03-28 14:38:19 -04:00
|
|
|
|
2016-05-28 04:36:25 -04:00
|
|
|
const auto squared_distance = coordinate_calculation::squaredEuclideanDistance(
|
|
|
|
projected_input_coordinate_fixed, projected_nearest);
|
2015-12-03 14:04:23 -05:00
|
|
|
// distance must be non-negative
|
2016-03-28 14:38:19 -04:00
|
|
|
BOOST_ASSERT(0. <= squared_distance);
|
2017-06-02 05:38:46 -04:00
|
|
|
BOOST_ASSERT(i < std::numeric_limits<std::uint32_t>::max());
|
|
|
|
traversal_queue.push(QueryCandidate{squared_distance,
|
|
|
|
leaf_id,
|
|
|
|
static_cast<std::uint32_t>(i),
|
|
|
|
Coordinate{projected_nearest}});
|
2015-01-27 11:44:46 -05:00
|
|
|
}
|
2013-06-26 09:43:13 -04:00
|
|
|
}
|
2013-08-26 08:16:34 -04:00
|
|
|
|
2017-05-17 04:32:06 -04:00
|
|
|
/**
|
|
|
|
* Iterates over all the children of a TreeNode and inserts them into the search
|
|
|
|
* priority queue using their distance from the search coordinate as the
|
|
|
|
* priority metric.
|
2022-08-27 06:36:20 -04:00
|
|
|
* The closest distance to a box from our point is also the closest distance
|
2017-05-17 04:32:06 -04:00
|
|
|
* to the closest line in that box (assuming the boxes hug their contents).
|
|
|
|
*/
|
2014-05-29 09:36:14 -04:00
|
|
|
template <class QueueT>
|
2017-05-17 04:32:06 -04:00
|
|
|
void ExploreTreeNode(const TreeIndex &parent,
|
2016-05-28 04:36:25 -04:00
|
|
|
const Coordinate &fixed_projected_input_coordinate,
|
2016-05-06 20:56:08 -04:00
|
|
|
QueueT &traversal_queue) const
|
2014-05-29 09:36:14 -04:00
|
|
|
{
|
2017-05-17 04:32:06 -04:00
|
|
|
// Figure out which_id level the parent is on, and it's offset
|
|
|
|
// in that level.
|
|
|
|
// Check that we're actually looking at the bottom level of the tree
|
|
|
|
BOOST_ASSERT(!is_leaf(parent));
|
|
|
|
|
|
|
|
for (const auto child_index : child_indexes(parent))
|
2014-05-29 09:36:14 -04:00
|
|
|
{
|
2017-05-17 04:32:06 -04:00
|
|
|
const auto &child = m_search_tree[child_index];
|
|
|
|
|
2016-03-28 14:38:19 -04:00
|
|
|
const auto squared_lower_bound_to_element =
|
2017-05-17 04:32:06 -04:00
|
|
|
child.minimum_bounding_rectangle.GetMinSquaredDist(
|
|
|
|
fixed_projected_input_coordinate);
|
|
|
|
|
|
|
|
traversal_queue.push(QueryCandidate{
|
|
|
|
squared_lower_bound_to_element,
|
|
|
|
TreeIndex(parent.level + 1, child_index - m_tree_level_starts[parent.level + 1])});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-21 13:09:59 -04:00
|
|
|
std::uint64_t GetLevelSize(const std::size_t level) const
|
|
|
|
{
|
|
|
|
BOOST_ASSERT(m_tree_level_starts.size() > level + 1);
|
|
|
|
BOOST_ASSERT(m_tree_level_starts[level + 1] >= m_tree_level_starts[level]);
|
|
|
|
return m_tree_level_starts[level + 1] - m_tree_level_starts[level];
|
|
|
|
}
|
|
|
|
|
2017-05-17 04:32:06 -04:00
|
|
|
/**
|
|
|
|
* Calculates the absolute position of child data in our packed data
|
|
|
|
* vectors.
|
|
|
|
*
|
|
|
|
* when given a TreeIndex that is a leaf node (i.e. at the bottom of the tree),
|
|
|
|
* this function returns indexes valid for `m_objects`
|
|
|
|
*
|
|
|
|
* otherwise, the indexes are to be used with m_search_tree to iterate over
|
|
|
|
* the children of `parent`
|
|
|
|
*
|
|
|
|
* This function assumes we pack nodes as described in the big comment
|
|
|
|
* at the top of this class. All nodes are fully filled except for the last
|
|
|
|
* one in each level.
|
|
|
|
*/
|
2017-06-02 05:38:46 -04:00
|
|
|
range<std::size_t> child_indexes(const TreeIndex &parent) const
|
2017-05-17 04:32:06 -04:00
|
|
|
{
|
|
|
|
// If we're looking at a leaf node, the index is from 0 to m_objects.size(),
|
|
|
|
// there is only 1 level of object data in the m_objects array
|
|
|
|
if (is_leaf(parent))
|
|
|
|
{
|
|
|
|
const std::uint64_t first_child_index = parent.offset * LEAF_NODE_SIZE;
|
|
|
|
const std::uint64_t end_child_index = std::min(
|
|
|
|
first_child_index + LEAF_NODE_SIZE, static_cast<std::uint64_t>(m_objects.size()));
|
|
|
|
|
|
|
|
BOOST_ASSERT(first_child_index < std::numeric_limits<std::uint32_t>::max());
|
|
|
|
BOOST_ASSERT(end_child_index < std::numeric_limits<std::uint32_t>::max());
|
|
|
|
BOOST_ASSERT(end_child_index <= m_objects.size());
|
|
|
|
|
2017-06-02 05:38:46 -04:00
|
|
|
return irange<std::size_t>(first_child_index, end_child_index);
|
2017-05-17 04:32:06 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const std::uint64_t first_child_index =
|
|
|
|
m_tree_level_starts[parent.level + 1] + parent.offset * BRANCHING_FACTOR;
|
|
|
|
|
2018-03-21 13:09:59 -04:00
|
|
|
const std::uint64_t end_child_index =
|
|
|
|
std::min(first_child_index + BRANCHING_FACTOR,
|
|
|
|
m_tree_level_starts[parent.level + 1] + GetLevelSize(parent.level + 1));
|
2017-05-17 04:32:06 -04:00
|
|
|
BOOST_ASSERT(first_child_index < std::numeric_limits<std::uint32_t>::max());
|
|
|
|
BOOST_ASSERT(end_child_index < std::numeric_limits<std::uint32_t>::max());
|
|
|
|
BOOST_ASSERT(end_child_index <= m_search_tree.size());
|
2018-03-21 13:09:59 -04:00
|
|
|
BOOST_ASSERT(end_child_index <=
|
|
|
|
m_tree_level_starts[parent.level + 1] + GetLevelSize(parent.level + 1));
|
2017-06-02 05:38:46 -04:00
|
|
|
return irange<std::size_t>(first_child_index, end_child_index);
|
2014-10-28 19:33:43 -04:00
|
|
|
}
|
|
|
|
}
|
2017-05-17 04:32:06 -04:00
|
|
|
|
|
|
|
bool is_leaf(const TreeIndex &treeindex) const
|
|
|
|
{
|
2018-03-21 13:09:59 -04:00
|
|
|
BOOST_ASSERT(m_tree_level_starts.size() >= 2);
|
|
|
|
return treeindex.level == m_tree_level_starts.size() - 2;
|
2017-05-17 04:32:06 -04:00
|
|
|
}
|
2018-03-21 13:09:59 -04:00
|
|
|
|
|
|
|
friend void serialization::read<EdgeDataT, Ownership, BRANCHING_FACTOR, LEAF_PAGE_SIZE>(
|
2018-03-22 14:26:40 -04:00
|
|
|
storage::tar::FileReader &reader, const std::string &name, StaticRTree &rtree);
|
2018-03-21 13:09:59 -04:00
|
|
|
|
|
|
|
friend void serialization::write<EdgeDataT, Ownership, BRANCHING_FACTOR, LEAF_PAGE_SIZE>(
|
|
|
|
storage::tar::FileWriter &writer, const std::string &name, const StaticRTree &rtree);
|
2013-06-26 09:43:13 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
//[1] "On Packing R-Trees"; I. Kamel, C. Faloutsos; 1993; DOI: 10.1145/170088.170403
|
|
|
|
//[2] "Nearest Neighbor Queries", N. Roussopulos et al; 1995; DOI: 10.1145/223784.223794
|
2014-06-23 10:54:31 -04:00
|
|
|
//[3] "Distance Browsing in Spatial Databases"; G. Hjaltason, H. Samet; 1999; ACM Trans. DB Sys
|
|
|
|
// Vol.24 No.2, pp.265-318
|
2022-12-20 12:00:11 -05:00
|
|
|
} // namespace osrm::util
|
2016-01-05 10:51:13 -05:00
|
|
|
|
2014-11-28 06:13:18 -05:00
|
|
|
#endif // STATIC_RTREE_HPP
|