Merge branch 'TheMarex-boost-test' into develop
This commit is contained in:
commit
0c3713f7e5
118
Benchmarks/StaticRTreeBench.cpp
Normal file
118
Benchmarks/StaticRTreeBench.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
#include "../DataStructures/OriginalEdgeData.h"
|
||||
#include "../DataStructures/QueryNode.h"
|
||||
#include "../DataStructures/SharedMemoryVectorWrapper.h"
|
||||
#include "../DataStructures/StaticRTree.h"
|
||||
#include "../Util/BoostFileSystemFix.h"
|
||||
#include "../DataStructures/EdgeBasedNode.h"
|
||||
|
||||
#include <osrm/Coordinate.h>
|
||||
|
||||
#include <random>
|
||||
|
||||
// Choosen by a fair W20 dice roll (this value is completely arbitrary)
|
||||
constexpr unsigned RANDOM_SEED = 13;
|
||||
constexpr int32_t WORLD_MIN_LAT = -90*COORDINATE_PRECISION;
|
||||
constexpr int32_t WORLD_MAX_LAT = 90*COORDINATE_PRECISION;
|
||||
constexpr int32_t WORLD_MIN_LON = -180*COORDINATE_PRECISION;
|
||||
constexpr int32_t WORLD_MAX_LON = 180*COORDINATE_PRECISION;
|
||||
|
||||
typedef EdgeBasedNode RTreeLeaf;
|
||||
typedef std::shared_ptr<std::vector<FixedPointCoordinate>> FixedPointCoordinateListPtr;
|
||||
typedef StaticRTree<RTreeLeaf, ShM<FixedPointCoordinate, false>::vector, false> BenchStaticRTree;
|
||||
|
||||
FixedPointCoordinateListPtr LoadCoordinates(const boost::filesystem::path& nodes_file)
|
||||
{
|
||||
boost::filesystem::ifstream nodes_input_stream(nodes_file, std::ios::binary);
|
||||
|
||||
NodeInfo current_node;
|
||||
unsigned number_of_coordinates = 0;
|
||||
nodes_input_stream.read((char *)&number_of_coordinates, sizeof(unsigned));
|
||||
auto coords = std::make_shared<std::vector<FixedPointCoordinate>>(number_of_coordinates);
|
||||
for (unsigned i = 0; i < number_of_coordinates; ++i)
|
||||
{
|
||||
nodes_input_stream.read((char *)¤t_node, sizeof(NodeInfo));
|
||||
coords->at(i) = FixedPointCoordinate(current_node.lat, current_node.lon);
|
||||
BOOST_ASSERT((std::abs(coords->at(i).lat) >> 30) == 0);
|
||||
BOOST_ASSERT((std::abs(coords->at(i).lon) >> 30) == 0);
|
||||
}
|
||||
nodes_input_stream.close();
|
||||
return coords;
|
||||
}
|
||||
|
||||
void Benchmark(BenchStaticRTree& rtree, unsigned num_queries)
|
||||
{
|
||||
std::mt19937 g(RANDOM_SEED);
|
||||
std::uniform_int_distribution<> lat_udist(WORLD_MIN_LAT, WORLD_MAX_LAT);
|
||||
std::uniform_int_distribution<> lon_udist(WORLD_MIN_LON, WORLD_MAX_LON);
|
||||
std::vector<FixedPointCoordinate> queries;
|
||||
for (unsigned i = 0; i < num_queries; i++)
|
||||
{
|
||||
queries.emplace_back(
|
||||
FixedPointCoordinate(lat_udist(g), lon_udist(g))
|
||||
);
|
||||
}
|
||||
|
||||
const unsigned num_results = 5;
|
||||
std::cout << "#### IncrementalFindPhantomNodeForCoordinate : " << num_results << " phantom nodes" << std::endl;
|
||||
|
||||
TIMER_START(query_phantom);
|
||||
std::vector<PhantomNode> resulting_phantom_node_vector;
|
||||
for (const auto& q : queries)
|
||||
{
|
||||
resulting_phantom_node_vector.clear();
|
||||
rtree.IncrementalFindPhantomNodeForCoordinate(q, resulting_phantom_node_vector, 3, num_results);
|
||||
resulting_phantom_node_vector.clear();
|
||||
rtree.IncrementalFindPhantomNodeForCoordinate(q, resulting_phantom_node_vector, 17, num_results);
|
||||
}
|
||||
TIMER_STOP(query_phantom);
|
||||
|
||||
std::cout << "Took " << TIMER_MSEC(query_phantom) << " msec for " << num_queries << " queries." << std::endl;
|
||||
std::cout << TIMER_MSEC(query_phantom)/((double) num_queries) << " msec/query." << std::endl;
|
||||
|
||||
std::cout << "#### LocateClosestEndPointForCoordinate" << std::endl;
|
||||
|
||||
TIMER_START(query_endpoint);
|
||||
FixedPointCoordinate result;
|
||||
for (const auto& q : queries)
|
||||
{
|
||||
rtree.LocateClosestEndPointForCoordinate(q, result, 3);
|
||||
}
|
||||
TIMER_STOP(query_endpoint);
|
||||
|
||||
std::cout << "Took " << TIMER_MSEC(query_endpoint) << " msec for " << num_queries << " queries." << std::endl;
|
||||
std::cout << TIMER_MSEC(query_endpoint)/((double) num_queries) << " msec/query." << std::endl;
|
||||
|
||||
std::cout << "#### FindPhantomNodeForCoordinate" << std::endl;
|
||||
|
||||
TIMER_START(query_phantomnode);
|
||||
for (const auto& q : queries)
|
||||
{
|
||||
PhantomNode phantom;
|
||||
rtree.FindPhantomNodeForCoordinate(q, phantom, 3);
|
||||
}
|
||||
TIMER_STOP(query_phantomnode);
|
||||
|
||||
std::cout << "Took " << TIMER_MSEC(query_phantomnode) << " msec for " << num_queries << " queries." << std::endl;
|
||||
std::cout << TIMER_MSEC(query_phantomnode)/((double) num_queries) << " msec/query." << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc < 4)
|
||||
{
|
||||
std::cout << "./rtree-bench file.ramIndex file.fileIndx file.nodes" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* ramPath = argv[1];
|
||||
const char* filePath = argv[2];
|
||||
const char* nodesPath = argv[3];
|
||||
|
||||
auto coords = LoadCoordinates(nodesPath);
|
||||
|
||||
BenchStaticRTree rtree(ramPath, filePath, coords);
|
||||
|
||||
Benchmark(rtree, 10000);
|
||||
|
||||
return 0;
|
||||
}
|
@ -41,8 +41,10 @@ add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/Util/FingerPrint.cpp FingerPrint.c
|
||||
VERBATIM)
|
||||
|
||||
add_custom_target(FingerPrintConfigure DEPENDS ${CMAKE_SOURCE_DIR}/Util/FingerPrint.cpp)
|
||||
add_custom_target(tests DEPENDS datastructure-tests)
|
||||
add_custom_target(benchmarks DEPENDS rtree-bench)
|
||||
|
||||
set(BOOST_COMPONENTS date_time filesystem iostreams program_options regex system thread)
|
||||
set(BOOST_COMPONENTS date_time filesystem iostreams program_options regex system thread unit_test_framework)
|
||||
|
||||
configure_file(
|
||||
${CMAKE_SOURCE_DIR}/Util/GitDescription.cpp.in
|
||||
@ -65,6 +67,7 @@ file(GLOB CoordinateGlob DataStructures/Coordinate.cpp)
|
||||
file(GLOB AlgorithmGlob Algorithms/*.cpp)
|
||||
file(GLOB HttpGlob Server/Http/*.cpp)
|
||||
file(GLOB LibOSRMGlob Library/*.cpp)
|
||||
file(GLOB DataStructureTestsGlob UnitTests/DataStructures/*.cpp DataStructures/HilbertValue.cpp)
|
||||
|
||||
set(
|
||||
OSRMSources
|
||||
@ -84,6 +87,12 @@ add_dependencies(FINGERPRINT FingerPrintConfigure)
|
||||
add_executable(osrm-routed routed.cpp ${ServerGlob})
|
||||
add_executable(osrm-datastore datastore.cpp)
|
||||
|
||||
# Unit tests
|
||||
add_executable(datastructure-tests EXCLUDE_FROM_ALL UnitTests/datastructure_tests.cpp ${DataStructureTestsGlob})
|
||||
|
||||
# Benchmarks
|
||||
add_executable(rtree-bench EXCLUDE_FROM_ALL Benchmarks/StaticRTreeBench.cpp)
|
||||
|
||||
# Check the release mode
|
||||
if(NOT CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
@ -114,6 +123,10 @@ if(CMAKE_BUILD_TYPE MATCHES Release)
|
||||
endif (HAS_LTO_FLAG)
|
||||
endif()
|
||||
|
||||
if (NOT WIN32)
|
||||
add_definitions(-DBOOST_TEST_DYN_LINK)
|
||||
endif()
|
||||
|
||||
# Configuring compilers
|
||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
# using Clang
|
||||
@ -189,12 +202,16 @@ target_link_libraries(osrm-extract ${Boost_LIBRARIES} FINGERPRINT GITDESCRIPTION
|
||||
target_link_libraries(osrm-prepare ${Boost_LIBRARIES} FINGERPRINT GITDESCRIPTION COORDLIB IMPORT)
|
||||
target_link_libraries(osrm-routed ${Boost_LIBRARIES} ${OPTIONAL_SOCKET_LIBS} OSRM FINGERPRINT GITDESCRIPTION)
|
||||
target_link_libraries(osrm-datastore ${Boost_LIBRARIES} FINGERPRINT GITDESCRIPTION COORDLIB)
|
||||
target_link_libraries(datastructure-tests ${Boost_LIBRARIES} COORDLIB)
|
||||
target_link_libraries(rtree-bench ${Boost_LIBRARIES} COORDLIB)
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(osrm-extract ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_link_libraries(osrm-datastore ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_link_libraries(osrm-prepare ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_link_libraries(OSRM ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_link_libraries(datastructure-tests ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_link_libraries(rtree-bench ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
find_package(TBB REQUIRED)
|
||||
if(WIN32 AND CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
@ -204,6 +221,8 @@ target_link_libraries(osrm-datastore ${TBB_LIBRARIES})
|
||||
target_link_libraries(osrm-extract ${TBB_LIBRARIES})
|
||||
target_link_libraries(osrm-prepare ${TBB_LIBRARIES})
|
||||
target_link_libraries(osrm-routed ${TBB_LIBRARIES})
|
||||
target_link_libraries(datastructure-tests ${TBB_LIBRARIES})
|
||||
target_link_libraries(rtree-bench ${TBB_LIBRARIES})
|
||||
include_directories(${TBB_INCLUDE_DIR})
|
||||
|
||||
find_package(Lua52)
|
||||
|
@ -36,6 +36,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
|
||||
template <typename NodeID, typename Key> class ArrayStorage
|
||||
{
|
||||
|
@ -159,10 +159,10 @@ FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordinate &s
|
||||
// initialize values
|
||||
const float x_value = lat2y(point.lat / COORDINATE_PRECISION);
|
||||
const float y_value = point.lon / COORDINATE_PRECISION;
|
||||
const float a = lat2y(source_coordinate.lat / COORDINATE_PRECISION);
|
||||
const float b = source_coordinate.lon / COORDINATE_PRECISION;
|
||||
const float c = lat2y(target_coordinate.lat / COORDINATE_PRECISION);
|
||||
const float d = target_coordinate.lon / COORDINATE_PRECISION;
|
||||
float a = lat2y(source_coordinate.lat / COORDINATE_PRECISION);
|
||||
float b = source_coordinate.lon / COORDINATE_PRECISION;
|
||||
float c = lat2y(target_coordinate.lat / COORDINATE_PRECISION);
|
||||
float d = target_coordinate.lon / COORDINATE_PRECISION;
|
||||
float p, q;
|
||||
if (std::abs(a - c) > std::numeric_limits<float>::epsilon())
|
||||
{
|
||||
@ -178,15 +178,35 @@ FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordinate &s
|
||||
q = y_value;
|
||||
}
|
||||
|
||||
float nY = (d * p - c * q) / (a * d - b * c);
|
||||
// discretize the result to coordinate precision. it's a hack!
|
||||
if (std::abs(nY) < (1.f / COORDINATE_PRECISION))
|
||||
float ratio;
|
||||
bool inverse_ratio = false;
|
||||
|
||||
// straight line segment on equator
|
||||
if (std::abs(c) < std::numeric_limits<float>::epsilon() && std::abs(a) < std::numeric_limits<float>::epsilon())
|
||||
{
|
||||
nY = 0.f;
|
||||
ratio = (q - b) / (d - b);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (std::abs(c) < std::numeric_limits<float>::epsilon())
|
||||
{
|
||||
// swap start/end
|
||||
std::swap(a, c);
|
||||
std::swap(b, d);
|
||||
inverse_ratio = true;
|
||||
}
|
||||
|
||||
float nY = (d * p - c * q) / (a * d - b * c);
|
||||
// discretize the result to coordinate precision. it's a hack!
|
||||
if (std::abs(nY) < (1.f / COORDINATE_PRECISION))
|
||||
{
|
||||
nY = 0.f;
|
||||
}
|
||||
|
||||
// compute ratio
|
||||
ratio = (p - nY * a) / c;
|
||||
}
|
||||
|
||||
// compute ratio
|
||||
float ratio = (p - nY * a) / c;
|
||||
if (std::isnan(ratio))
|
||||
{
|
||||
ratio = (target_coordinate == point ? 1.f : 0.f);
|
||||
@ -200,6 +220,12 @@ FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordinate &s
|
||||
ratio = 1.f;
|
||||
}
|
||||
|
||||
// we need to do this, if we switched start/end coordinates
|
||||
if (inverse_ratio)
|
||||
{
|
||||
ratio = 1.0f - ratio;
|
||||
}
|
||||
|
||||
//compute the nearest location
|
||||
FixedPointCoordinate nearest_location;
|
||||
BOOST_ASSERT(!std::isnan(ratio));
|
||||
@ -216,6 +242,7 @@ FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordinate &s
|
||||
nearest_location.lat = static_cast<int>(y2lat(p) * COORDINATE_PRECISION);
|
||||
nearest_location.lon = static_cast<int>(q * COORDINATE_PRECISION);
|
||||
}
|
||||
|
||||
BOOST_ASSERT(nearest_location.isValid());
|
||||
return FixedPointCoordinate::ApproximateEuclideanDistance(point, nearest_location);
|
||||
}
|
||||
|
@ -117,7 +117,8 @@ public:
|
||||
{
|
||||
// the last value is used as sentinel
|
||||
block_offsets.push_back(lengths_prefix_sum);
|
||||
block_idx = (block_idx + 1) % BLOCK_SIZE;
|
||||
block_idx = 1;
|
||||
last_length = 0;
|
||||
}
|
||||
|
||||
while (0 != block_idx)
|
||||
|
@ -162,11 +162,11 @@ template <typename EdgeDataT, bool UseSharedMemory = false> class StaticGraph
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned GetNumberOfNodes() const { return number_of_nodes -1; }
|
||||
unsigned GetNumberOfNodes() const { return number_of_nodes; }
|
||||
|
||||
unsigned GetNumberOfEdges() const { return number_of_edges; }
|
||||
|
||||
unsigned GetOutDegree(const NodeIterator n) const { return BeginEdges(n) - EndEdges(n) - 1; }
|
||||
unsigned GetOutDegree(const NodeIterator n) const { return EndEdges(n) - BeginEdges(n); }
|
||||
|
||||
inline NodeIterator GetTarget(const EdgeIterator e) const
|
||||
{
|
||||
|
@ -60,16 +60,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// tuning parameters
|
||||
const static uint32_t RTREE_BRANCHING_FACTOR = 64;
|
||||
const static uint32_t RTREE_LEAF_NODE_SIZE = 1024;
|
||||
|
||||
static boost::thread_specific_ptr<boost::filesystem::ifstream> thread_local_rtree_stream;
|
||||
|
||||
// Implements a static, i.e. packed, R-tree
|
||||
template <class EdgeDataT,
|
||||
class CoordinateListT = std::vector<FixedPointCoordinate>,
|
||||
bool UseSharedMemory = false>
|
||||
bool UseSharedMemory = false,
|
||||
uint32_t BRANCHING_FACTOR=64,
|
||||
uint32_t LEAF_NODE_SIZE=1024>
|
||||
class StaticRTree
|
||||
{
|
||||
public:
|
||||
@ -80,7 +76,7 @@ class StaticRTree
|
||||
int32_t min_lon, max_lon;
|
||||
int32_t min_lat, max_lat;
|
||||
|
||||
inline void InitializeMBRectangle(const std::array<EdgeDataT, RTREE_LEAF_NODE_SIZE> &objects,
|
||||
inline void InitializeMBRectangle(const std::array<EdgeDataT, LEAF_NODE_SIZE> &objects,
|
||||
const uint32_t element_count,
|
||||
const std::vector<NodeInfo> &coordinate_list)
|
||||
{
|
||||
@ -147,19 +143,64 @@ class StaticRTree
|
||||
return 0.;
|
||||
}
|
||||
|
||||
enum Direction
|
||||
{
|
||||
INVALID = 0,
|
||||
NORTH = 1,
|
||||
SOUTH = 2,
|
||||
EAST = 4,
|
||||
NORTH_EAST = 5,
|
||||
SOUTH_EAST = 6,
|
||||
WEST = 8,
|
||||
NORTH_WEST = 9,
|
||||
SOUTH_WEST = 10
|
||||
};
|
||||
|
||||
Direction d = INVALID;
|
||||
if (location.lat > max_lat)
|
||||
d = (Direction) (d | NORTH);
|
||||
else if (location.lat < min_lat)
|
||||
d = (Direction) (d | SOUTH);
|
||||
if (location.lon > max_lon)
|
||||
d = (Direction) (d | EAST);
|
||||
else if (location.lon < min_lon)
|
||||
d = (Direction) (d | WEST);
|
||||
|
||||
BOOST_ASSERT(d != INVALID);
|
||||
|
||||
float min_dist = std::numeric_limits<float>::max();
|
||||
min_dist = std::min(min_dist,
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(
|
||||
location.lat, location.lon, max_lat, min_lon));
|
||||
min_dist = std::min(min_dist,
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(
|
||||
location.lat, location.lon, max_lat, max_lon));
|
||||
min_dist = std::min(min_dist,
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(
|
||||
location.lat, location.lon, min_lat, max_lon));
|
||||
min_dist = std::min(min_dist,
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(
|
||||
location.lat, location.lon, min_lat, min_lon));
|
||||
switch (d)
|
||||
{
|
||||
case NORTH:
|
||||
min_dist = FixedPointCoordinate::ApproximateEuclideanDistance(location, FixedPointCoordinate(max_lat, location.lon));
|
||||
break;
|
||||
case SOUTH:
|
||||
min_dist = FixedPointCoordinate::ApproximateEuclideanDistance(location, FixedPointCoordinate(min_lat, location.lon));
|
||||
break;
|
||||
case WEST:
|
||||
min_dist = FixedPointCoordinate::ApproximateEuclideanDistance(location, FixedPointCoordinate(location.lat, min_lon));
|
||||
break;
|
||||
case EAST:
|
||||
min_dist = FixedPointCoordinate::ApproximateEuclideanDistance(location, FixedPointCoordinate(location.lat, max_lon));
|
||||
break;
|
||||
case NORTH_EAST:
|
||||
min_dist = FixedPointCoordinate::ApproximateEuclideanDistance(location, FixedPointCoordinate(max_lat, max_lon));
|
||||
break;
|
||||
case NORTH_WEST:
|
||||
min_dist = FixedPointCoordinate::ApproximateEuclideanDistance(location, FixedPointCoordinate(max_lat, min_lon));
|
||||
break;
|
||||
case SOUTH_EAST:
|
||||
min_dist = FixedPointCoordinate::ApproximateEuclideanDistance(location, FixedPointCoordinate(min_lat, max_lon));
|
||||
break;
|
||||
case SOUTH_WEST:
|
||||
min_dist = FixedPointCoordinate::ApproximateEuclideanDistance(location, FixedPointCoordinate(min_lat, min_lon));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(min_dist != std::numeric_limits<float>::max());
|
||||
|
||||
return min_dist;
|
||||
}
|
||||
|
||||
@ -167,10 +208,10 @@ class StaticRTree
|
||||
{
|
||||
float min_max_dist = std::numeric_limits<float>::max();
|
||||
// Get minmax distance to each of the four sides
|
||||
FixedPointCoordinate upper_left(max_lat, min_lon);
|
||||
FixedPointCoordinate upper_right(max_lat, max_lon);
|
||||
FixedPointCoordinate lower_right(min_lat, max_lon);
|
||||
FixedPointCoordinate lower_left(min_lat, min_lon);
|
||||
const FixedPointCoordinate upper_left(max_lat, min_lon);
|
||||
const FixedPointCoordinate upper_right(max_lat, max_lon);
|
||||
const FixedPointCoordinate lower_right(min_lat, max_lon);
|
||||
const FixedPointCoordinate lower_left(min_lat, min_lon);
|
||||
|
||||
min_max_dist = std::min(
|
||||
min_max_dist,
|
||||
@ -198,8 +239,8 @@ class StaticRTree
|
||||
|
||||
inline bool Contains(const FixedPointCoordinate &location) const
|
||||
{
|
||||
const bool lats_contained = (location.lat > min_lat) && (location.lat < max_lat);
|
||||
const bool lons_contained = (location.lon > min_lon) && (location.lon < max_lon);
|
||||
const bool lats_contained = (location.lat >= min_lat) && (location.lat <= max_lat);
|
||||
const bool lons_contained = (location.lon >= min_lon) && (location.lon <= max_lon);
|
||||
return lats_contained && lons_contained;
|
||||
}
|
||||
|
||||
@ -220,7 +261,7 @@ class StaticRTree
|
||||
RectangleT minimum_bounding_rectangle;
|
||||
uint32_t child_count : 31;
|
||||
bool child_is_on_disk : 1;
|
||||
uint32_t children[RTREE_BRANCHING_FACTOR];
|
||||
uint32_t children[BRANCHING_FACTOR];
|
||||
};
|
||||
|
||||
private:
|
||||
@ -244,9 +285,9 @@ class StaticRTree
|
||||
|
||||
struct LeafNode
|
||||
{
|
||||
LeafNode() : object_count(0) {}
|
||||
LeafNode() : object_count(0), objects() {}
|
||||
uint32_t object_count;
|
||||
std::array<EdgeDataT, RTREE_LEAF_NODE_SIZE> objects;
|
||||
std::array<EdgeDataT, LEAF_NODE_SIZE> objects;
|
||||
};
|
||||
|
||||
struct QueryCandidate
|
||||
@ -304,6 +345,7 @@ class StaticRTree
|
||||
uint64_t m_element_count;
|
||||
const std::string m_leaf_node_filename;
|
||||
std::shared_ptr<CoordinateListT> m_coordinate_list;
|
||||
boost::filesystem::ifstream leaves_stream;
|
||||
|
||||
public:
|
||||
StaticRTree() = delete;
|
||||
@ -369,7 +411,7 @@ class StaticRTree
|
||||
TreeNode current_node;
|
||||
// SimpleLogger().Write() << "reading " << tree_size << " tree nodes in " <<
|
||||
// (sizeof(TreeNode)*tree_size) << " bytes";
|
||||
for (uint32_t current_element_index = 0; RTREE_LEAF_NODE_SIZE > current_element_index;
|
||||
for (uint32_t current_element_index = 0; LEAF_NODE_SIZE > current_element_index;
|
||||
++current_element_index)
|
||||
{
|
||||
if (m_element_count > (processed_objects_count + current_element_index))
|
||||
@ -406,9 +448,9 @@ class StaticRTree
|
||||
while (processed_tree_nodes_in_level < tree_nodes_in_level.size())
|
||||
{
|
||||
TreeNode parent_node;
|
||||
// pack RTREE_BRANCHING_FACTOR elements into tree_nodes each
|
||||
// pack BRANCHING_FACTOR elements into tree_nodes each
|
||||
for (uint32_t current_child_node_index = 0;
|
||||
RTREE_BRANCHING_FACTOR > current_child_node_index;
|
||||
BRANCHING_FACTOR > current_child_node_index;
|
||||
++current_child_node_index)
|
||||
{
|
||||
if (processed_tree_nodes_in_level < tree_nodes_in_level.size())
|
||||
@ -507,9 +549,8 @@ class StaticRTree
|
||||
throw OSRMException("mem index file is empty");
|
||||
}
|
||||
|
||||
boost::filesystem::ifstream leaf_node_file(leaf_file, std::ios::binary);
|
||||
leaf_node_file.read((char *)&m_element_count, sizeof(uint64_t));
|
||||
leaf_node_file.close();
|
||||
leaves_stream.open(leaf_file, std::ios::binary);
|
||||
leaves_stream.read((char *)&m_element_count, sizeof(uint64_t));
|
||||
|
||||
// SimpleLogger().Write() << tree_size << " nodes in search tree";
|
||||
// SimpleLogger().Write() << m_element_count << " elements in leafs";
|
||||
@ -532,14 +573,8 @@ class StaticRTree
|
||||
throw OSRMException("mem index file is empty");
|
||||
}
|
||||
|
||||
boost::filesystem::ifstream leaf_node_file(leaf_file, std::ios::binary);
|
||||
leaf_node_file.read((char *)&m_element_count, sizeof(uint64_t));
|
||||
leaf_node_file.close();
|
||||
|
||||
if (thread_local_rtree_stream.get())
|
||||
{
|
||||
thread_local_rtree_stream->close();
|
||||
}
|
||||
leaves_stream.open(leaf_file, std::ios::binary);
|
||||
leaves_stream.read((char *)&m_element_count, sizeof(uint64_t));
|
||||
|
||||
// SimpleLogger().Write() << tree_size << " nodes in search tree";
|
||||
// SimpleLogger().Write() << m_element_count << " elements in leafs";
|
||||
@ -628,7 +663,7 @@ class StaticRTree
|
||||
std::vector<PhantomNode> &result_phantom_node_vector,
|
||||
const unsigned zoom_level,
|
||||
const unsigned number_of_results,
|
||||
const unsigned max_checked_segments = 4*RTREE_LEAF_NODE_SIZE)
|
||||
const unsigned max_checked_segments = 4*LEAF_NODE_SIZE)
|
||||
{
|
||||
// TIMER_START(samet);
|
||||
// SimpleLogger().Write(logDEBUG) << "searching for " << number_of_results << " results";
|
||||
@ -988,22 +1023,21 @@ class StaticRTree
|
||||
|
||||
inline void LoadLeafFromDisk(const uint32_t leaf_id, LeafNode &result_node)
|
||||
{
|
||||
if (!thread_local_rtree_stream.get() || !thread_local_rtree_stream->is_open())
|
||||
if (!leaves_stream.is_open())
|
||||
{
|
||||
thread_local_rtree_stream.reset(new boost::filesystem::ifstream(
|
||||
m_leaf_node_filename, std::ios::in | std::ios::binary));
|
||||
leaves_stream.open(m_leaf_node_filename, std::ios::in | std::ios::binary);
|
||||
}
|
||||
if (!thread_local_rtree_stream->good())
|
||||
if (!leaves_stream.good())
|
||||
{
|
||||
thread_local_rtree_stream->clear(std::ios::goodbit);
|
||||
leaves_stream.clear(std::ios::goodbit);
|
||||
SimpleLogger().Write(logDEBUG) << "Resetting stale filestream";
|
||||
}
|
||||
const uint64_t seek_pos = sizeof(uint64_t) + leaf_id * sizeof(LeafNode);
|
||||
thread_local_rtree_stream->seekg(seek_pos);
|
||||
BOOST_ASSERT_MSG(thread_local_rtree_stream->good(),
|
||||
leaves_stream.seekg(seek_pos);
|
||||
BOOST_ASSERT_MSG(leaves_stream.good(),
|
||||
"Seeking to position in leaf file failed.");
|
||||
thread_local_rtree_stream->read((char *)&result_node, sizeof(LeafNode));
|
||||
BOOST_ASSERT_MSG(thread_local_rtree_stream->good(), "Reading from leaf file failed.");
|
||||
leaves_stream.read((char *)&result_node, sizeof(LeafNode));
|
||||
BOOST_ASSERT_MSG(leaves_stream.good(), "Reading from leaf file failed.");
|
||||
}
|
||||
|
||||
inline bool EdgesAreEquivalent(const FixedPointCoordinate &a,
|
||||
|
@ -95,16 +95,16 @@ template <class EdgeDataT> class BaseDataFacade
|
||||
|
||||
virtual bool LocateClosestEndPointForCoordinate(const FixedPointCoordinate &input_coordinate,
|
||||
FixedPointCoordinate &result,
|
||||
const unsigned zoom_level = 18) const = 0;
|
||||
const unsigned zoom_level = 18) = 0;
|
||||
|
||||
virtual bool FindPhantomNodeForCoordinate(const FixedPointCoordinate &input_coordinate,
|
||||
PhantomNode &resulting_phantom_node,
|
||||
const unsigned zoom_level) const = 0;
|
||||
const unsigned zoom_level) = 0;
|
||||
|
||||
virtual bool IncrementalFindPhantomNodeForCoordinate(const FixedPointCoordinate &input_coordinate,
|
||||
std::vector<PhantomNode> &resulting_phantom_node_vector,
|
||||
const unsigned zoom_level,
|
||||
const unsigned number_of_results) const = 0;
|
||||
const unsigned number_of_results) = 0;
|
||||
|
||||
virtual unsigned GetCheckSum() const = 0;
|
||||
|
||||
|
@ -71,8 +71,10 @@ template <class EdgeDataT> class InternalDataFacade : public BaseDataFacade<Edge
|
||||
ShM<unsigned, false>::vector m_geometry_indices;
|
||||
ShM<unsigned, false>::vector m_geometry_list;
|
||||
|
||||
std::shared_ptr<StaticRTree<RTreeLeaf, ShM<FixedPointCoordinate, false>::vector, false>>
|
||||
boost::thread_specific_ptr<StaticRTree<RTreeLeaf, ShM<FixedPointCoordinate, false>::vector, false>>
|
||||
m_static_rtree;
|
||||
boost::filesystem::path ram_index_path;
|
||||
boost::filesystem::path file_index_path;
|
||||
RangeTable<16, false> m_name_table;
|
||||
|
||||
void LoadTimestamp(const boost::filesystem::path ×tamp_path)
|
||||
@ -192,13 +194,13 @@ template <class EdgeDataT> class InternalDataFacade : public BaseDataFacade<Edge
|
||||
geometry_stream.close();
|
||||
}
|
||||
|
||||
void LoadRTree(const boost::filesystem::path &ram_index_path,
|
||||
const boost::filesystem::path &file_index_path)
|
||||
void LoadRTree()
|
||||
{
|
||||
BOOST_ASSERT_MSG(!m_coordinate_list->empty(), "coordinates must be loaded before r-tree");
|
||||
|
||||
m_static_rtree = std::make_shared<StaticRTree<RTreeLeaf>>(
|
||||
ram_index_path, file_index_path, m_coordinate_list);
|
||||
m_static_rtree.reset(
|
||||
new StaticRTree<RTreeLeaf>(ram_index_path, file_index_path, m_coordinate_list)
|
||||
);
|
||||
}
|
||||
|
||||
void LoadStreetNames(const boost::filesystem::path &names_file)
|
||||
@ -266,10 +268,10 @@ template <class EdgeDataT> class InternalDataFacade : public BaseDataFacade<Edge
|
||||
const boost::filesystem::path ×tamp_path = paths_iterator->second;
|
||||
paths_iterator = server_paths.find("ramindex");
|
||||
BOOST_ASSERT(server_paths.end() != paths_iterator);
|
||||
const boost::filesystem::path &ram_index_path = paths_iterator->second;
|
||||
ram_index_path = paths_iterator->second;
|
||||
paths_iterator = server_paths.find("fileindex");
|
||||
BOOST_ASSERT(server_paths.end() != paths_iterator);
|
||||
const boost::filesystem::path &file_index_path = paths_iterator->second;
|
||||
file_index_path = paths_iterator->second;
|
||||
paths_iterator = server_paths.find("nodesdata");
|
||||
BOOST_ASSERT(server_paths.end() != paths_iterator);
|
||||
const boost::filesystem::path &nodes_data_path = paths_iterator->second;
|
||||
@ -297,7 +299,6 @@ template <class EdgeDataT> class InternalDataFacade : public BaseDataFacade<Edge
|
||||
SimpleLogger().Write() << "loading r-tree";
|
||||
AssertPathExists(ram_index_path);
|
||||
AssertPathExists(file_index_path);
|
||||
LoadRTree(ram_index_path, file_index_path);
|
||||
SimpleLogger().Write() << "loading timestamp";
|
||||
LoadTimestamp(timestamp_path);
|
||||
SimpleLogger().Write() << "loading street names";
|
||||
@ -358,16 +359,26 @@ template <class EdgeDataT> class InternalDataFacade : public BaseDataFacade<Edge
|
||||
|
||||
bool LocateClosestEndPointForCoordinate(const FixedPointCoordinate &input_coordinate,
|
||||
FixedPointCoordinate &result,
|
||||
const unsigned zoom_level = 18) const
|
||||
const unsigned zoom_level = 18)
|
||||
{
|
||||
if (!m_static_rtree.get())
|
||||
{
|
||||
LoadRTree();
|
||||
}
|
||||
|
||||
return m_static_rtree->LocateClosestEndPointForCoordinate(
|
||||
input_coordinate, result, zoom_level);
|
||||
}
|
||||
|
||||
bool FindPhantomNodeForCoordinate(const FixedPointCoordinate &input_coordinate,
|
||||
PhantomNode &resulting_phantom_node,
|
||||
const unsigned zoom_level) const
|
||||
const unsigned zoom_level)
|
||||
{
|
||||
if (!m_static_rtree.get())
|
||||
{
|
||||
LoadRTree();
|
||||
}
|
||||
|
||||
return m_static_rtree->FindPhantomNodeForCoordinate(
|
||||
input_coordinate, resulting_phantom_node, zoom_level);
|
||||
}
|
||||
@ -376,8 +387,13 @@ template <class EdgeDataT> class InternalDataFacade : public BaseDataFacade<Edge
|
||||
IncrementalFindPhantomNodeForCoordinate(const FixedPointCoordinate &input_coordinate,
|
||||
std::vector<PhantomNode> &resulting_phantom_node_vector,
|
||||
const unsigned zoom_level,
|
||||
const unsigned number_of_results) const
|
||||
const unsigned number_of_results)
|
||||
{
|
||||
if (!m_static_rtree.get())
|
||||
{
|
||||
LoadRTree();
|
||||
}
|
||||
|
||||
return m_static_rtree->IncrementalFindPhantomNodeForCoordinate(
|
||||
input_coordinate, resulting_phantom_node_vector, zoom_level, number_of_results);
|
||||
}
|
||||
|
@ -83,8 +83,9 @@ template <class EdgeDataT> class SharedDataFacade : public BaseDataFacade<EdgeDa
|
||||
ShM<unsigned, true>::vector m_geometry_indices;
|
||||
ShM<unsigned, true>::vector m_geometry_list;
|
||||
|
||||
std::shared_ptr<StaticRTree<RTreeLeaf, ShM<FixedPointCoordinate, true>::vector, true>>
|
||||
boost::thread_specific_ptr<StaticRTree<RTreeLeaf, ShM<FixedPointCoordinate, true>::vector, true>>
|
||||
m_static_rtree;
|
||||
boost::filesystem::path file_index_path;
|
||||
|
||||
std::shared_ptr<RangeTable<16, true>> m_name_table;
|
||||
|
||||
@ -105,18 +106,19 @@ template <class EdgeDataT> class SharedDataFacade : public BaseDataFacade<EdgeDa
|
||||
m_timestamp.begin());
|
||||
}
|
||||
|
||||
void LoadRTree(const boost::filesystem::path &file_index_path)
|
||||
void LoadRTree()
|
||||
{
|
||||
BOOST_ASSERT_MSG(!m_coordinate_list->empty(), "coordinates must be loaded before r-tree");
|
||||
|
||||
RTreeNode *tree_ptr =
|
||||
data_layout->GetBlockPtr<RTreeNode>(shared_memory, SharedDataLayout::R_SEARCH_TREE);
|
||||
m_static_rtree =
|
||||
std::make_shared<StaticRTree<RTreeLeaf, ShM<FixedPointCoordinate, true>::vector, true>>(
|
||||
m_static_rtree.reset(
|
||||
new StaticRTree<RTreeLeaf, ShM<FixedPointCoordinate, true>::vector, true>(
|
||||
tree_ptr,
|
||||
data_layout->num_entries[SharedDataLayout::R_SEARCH_TREE],
|
||||
file_index_path,
|
||||
m_coordinate_list);
|
||||
m_coordinate_list)
|
||||
);
|
||||
}
|
||||
|
||||
void LoadGraph()
|
||||
@ -248,7 +250,7 @@ template <class EdgeDataT> class SharedDataFacade : public BaseDataFacade<EdgeDa
|
||||
|
||||
const char *file_index_ptr =
|
||||
data_layout->GetBlockPtr<char>(shared_memory, SharedDataLayout::FILE_INDEX_PATH);
|
||||
boost::filesystem::path file_index_path(file_index_ptr);
|
||||
file_index_path = boost::filesystem::path(file_index_ptr);
|
||||
if (!boost::filesystem::exists(file_index_path))
|
||||
{
|
||||
SimpleLogger().Write(logDEBUG) << "Leaf file name " << file_index_path.string();
|
||||
@ -260,7 +262,6 @@ template <class EdgeDataT> class SharedDataFacade : public BaseDataFacade<EdgeDa
|
||||
LoadChecksum();
|
||||
LoadNodeAndEdgeInformation();
|
||||
LoadGeometries();
|
||||
LoadRTree(file_index_path);
|
||||
LoadTimestamp();
|
||||
LoadViaNodeList();
|
||||
LoadNames();
|
||||
@ -349,16 +350,26 @@ template <class EdgeDataT> class SharedDataFacade : public BaseDataFacade<EdgeDa
|
||||
|
||||
bool LocateClosestEndPointForCoordinate(const FixedPointCoordinate &input_coordinate,
|
||||
FixedPointCoordinate &result,
|
||||
const unsigned zoom_level = 18) const
|
||||
const unsigned zoom_level = 18)
|
||||
{
|
||||
if (!m_static_rtree.get())
|
||||
{
|
||||
LoadRTree();
|
||||
}
|
||||
|
||||
return m_static_rtree->LocateClosestEndPointForCoordinate(
|
||||
input_coordinate, result, zoom_level);
|
||||
}
|
||||
|
||||
bool FindPhantomNodeForCoordinate(const FixedPointCoordinate &input_coordinate,
|
||||
PhantomNode &resulting_phantom_node,
|
||||
const unsigned zoom_level) const
|
||||
const unsigned zoom_level)
|
||||
{
|
||||
if (!m_static_rtree.get())
|
||||
{
|
||||
LoadRTree();
|
||||
}
|
||||
|
||||
return m_static_rtree->FindPhantomNodeForCoordinate(
|
||||
input_coordinate, resulting_phantom_node, zoom_level);
|
||||
}
|
||||
@ -367,8 +378,13 @@ template <class EdgeDataT> class SharedDataFacade : public BaseDataFacade<EdgeDa
|
||||
IncrementalFindPhantomNodeForCoordinate(const FixedPointCoordinate &input_coordinate,
|
||||
std::vector<PhantomNode> &resulting_phantom_node_vector,
|
||||
const unsigned zoom_level,
|
||||
const unsigned number_of_results) const
|
||||
const unsigned number_of_results)
|
||||
{
|
||||
if (!m_static_rtree.get())
|
||||
{
|
||||
LoadRTree();
|
||||
}
|
||||
|
||||
return m_static_rtree->IncrementalFindPhantomNodeForCoordinate(
|
||||
input_coordinate, resulting_phantom_node_vector, zoom_level, number_of_results);
|
||||
}
|
||||
|
154
UnitTests/DataStructures/BinaryHeapTest.cpp
Normal file
154
UnitTests/DataStructures/BinaryHeapTest.cpp
Normal file
@ -0,0 +1,154 @@
|
||||
#include "../../DataStructures/BinaryHeap.h"
|
||||
#include "../../typedefs.h"
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <boost/test/test_case_template.hpp>
|
||||
#include <boost/mpl/list.hpp>
|
||||
|
||||
#include <random>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(binary_heap)
|
||||
|
||||
struct TestData
|
||||
{
|
||||
unsigned value;
|
||||
};
|
||||
|
||||
typedef NodeID TestNodeID;
|
||||
typedef int TestKey;
|
||||
typedef int TestWeight;
|
||||
typedef boost::mpl::list<ArrayStorage<TestNodeID, TestKey>,
|
||||
MapStorage<TestNodeID, TestKey>,
|
||||
UnorderedMapStorage<TestNodeID, TestKey>> storage_types;
|
||||
|
||||
|
||||
template<unsigned NUM_ELEM>
|
||||
struct RandomDataFixture
|
||||
{
|
||||
RandomDataFixture()
|
||||
{
|
||||
for (unsigned i = 0; i < NUM_ELEM; i++)
|
||||
{
|
||||
data.push_back(TestData {i*3});
|
||||
weights.push_back((i+1)*100);
|
||||
ids.push_back(i);
|
||||
order.push_back(i);
|
||||
}
|
||||
|
||||
// Choosen by a fair W20 dice roll
|
||||
std::mt19937 g(15);
|
||||
|
||||
std::shuffle(order.begin(), order.end(), g);
|
||||
}
|
||||
|
||||
std::vector<TestData> data;
|
||||
std::vector<TestWeight> weights;
|
||||
std::vector<TestNodeID> ids;
|
||||
std::vector<unsigned> order;
|
||||
};
|
||||
|
||||
constexpr unsigned NUM_NODES = 100;
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE_TEMPLATE(insert_test, T, storage_types, RandomDataFixture<NUM_NODES>)
|
||||
{
|
||||
BinaryHeap<TestNodeID, TestKey, TestWeight, TestData, T> heap(NUM_NODES);
|
||||
|
||||
TestWeight min_weight = std::numeric_limits<TestWeight>::max();
|
||||
TestNodeID min_id;
|
||||
|
||||
for (unsigned idx : order)
|
||||
{
|
||||
BOOST_CHECK(!heap.WasInserted(ids[idx]));
|
||||
|
||||
heap.Insert(ids[idx], weights[idx], data[idx]);
|
||||
|
||||
BOOST_CHECK(heap.WasInserted(ids[idx]));
|
||||
|
||||
if (weights[idx] < min_weight)
|
||||
{
|
||||
min_weight = weights[idx];
|
||||
min_id = ids[idx];
|
||||
}
|
||||
BOOST_CHECK_EQUAL(min_id, heap.Min());
|
||||
}
|
||||
|
||||
for (auto id : ids)
|
||||
{
|
||||
const auto& d = heap.GetData(id);
|
||||
BOOST_CHECK_EQUAL(d.value, data[id].value);
|
||||
|
||||
const auto& w = heap.GetKey(id);
|
||||
BOOST_CHECK_EQUAL(w, weights[id]);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE_TEMPLATE(delete_min_test, T, storage_types, RandomDataFixture<NUM_NODES>)
|
||||
{
|
||||
BinaryHeap<TestNodeID, TestKey, TestWeight, TestData, T> heap(NUM_NODES);
|
||||
|
||||
for (unsigned idx : order)
|
||||
{
|
||||
heap.Insert(ids[idx], weights[idx], data[idx]);
|
||||
}
|
||||
|
||||
for (auto id : ids)
|
||||
{
|
||||
BOOST_CHECK(!heap.WasRemoved(id));
|
||||
|
||||
BOOST_CHECK_EQUAL(heap.Min(), id);
|
||||
BOOST_CHECK_EQUAL(id, heap.DeleteMin());
|
||||
if (id+1 < NUM_NODES)
|
||||
BOOST_CHECK_EQUAL(heap.Min(), id+1);
|
||||
|
||||
BOOST_CHECK(heap.WasRemoved(id));
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE_TEMPLATE(delete_all_test, T, storage_types, RandomDataFixture<NUM_NODES>)
|
||||
{
|
||||
BinaryHeap<TestNodeID, TestKey, TestWeight, TestData, T> heap(NUM_NODES);
|
||||
|
||||
for (unsigned idx : order)
|
||||
{
|
||||
heap.Insert(ids[idx], weights[idx], data[idx]);
|
||||
}
|
||||
|
||||
heap.DeleteAll();
|
||||
|
||||
BOOST_CHECK(heap.Empty());
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE_TEMPLATE(decrease_key_test, T, storage_types, RandomDataFixture<10>)
|
||||
{
|
||||
BinaryHeap<TestNodeID, TestKey, TestWeight, TestData, T> heap(10);
|
||||
|
||||
for (unsigned idx : order)
|
||||
{
|
||||
heap.Insert(ids[idx], weights[idx], data[idx]);
|
||||
}
|
||||
|
||||
std::vector<TestNodeID> rids(ids);
|
||||
std::reverse(rids.begin(), rids.end());
|
||||
|
||||
for (auto id : rids)
|
||||
{
|
||||
TestNodeID min_id = heap.Min();
|
||||
TestWeight min_weight = heap.GetKey(min_id);
|
||||
|
||||
// decrease weight until we reach min weight
|
||||
while (weights[id] > min_weight)
|
||||
{
|
||||
heap.DecreaseKey(id, weights[id]);
|
||||
BOOST_CHECK_EQUAL(heap.Min(), min_id);
|
||||
weights[id]--;
|
||||
}
|
||||
|
||||
// make weight smaller than min
|
||||
weights[id] -= 2;
|
||||
heap.DecreaseKey(id, weights[id]);
|
||||
BOOST_CHECK_EQUAL(heap.Min(), id);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
103
UnitTests/DataStructures/RangeTableTest.cpp
Normal file
103
UnitTests/DataStructures/RangeTableTest.cpp
Normal file
@ -0,0 +1,103 @@
|
||||
#include "../../DataStructures/RangeTable.h"
|
||||
#include "../../typedefs.h"
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <boost/test/test_case_template.hpp>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
constexpr unsigned BLOCK_SIZE = 16;
|
||||
typedef RangeTable<BLOCK_SIZE, false> TestRangeTable;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(range_table)
|
||||
|
||||
void ConstructionTest(std::vector<unsigned> lengths, std::vector<unsigned> offsets)
|
||||
{
|
||||
BOOST_ASSERT(lengths.size() == offsets.size() - 1);
|
||||
|
||||
TestRangeTable table(lengths);
|
||||
|
||||
for (unsigned i = 0; i < lengths.size(); i++)
|
||||
{
|
||||
auto range = table.GetRange(i);
|
||||
BOOST_CHECK_EQUAL(range.front(), offsets[i]);
|
||||
BOOST_CHECK_EQUAL(range.back()+1, offsets[i+1]);
|
||||
}
|
||||
}
|
||||
|
||||
void ComputeLengthsOffsets(std::vector<unsigned>& lengths, std::vector<unsigned>& offsets, unsigned num)
|
||||
{
|
||||
lengths.resize(num);
|
||||
offsets.resize(num+1);
|
||||
std::iota(lengths.begin(), lengths.end(), 1);
|
||||
offsets[0] = 0;
|
||||
std::partial_sum(lengths.begin(), lengths.end(), offsets.begin()+1);
|
||||
|
||||
std::stringstream l_ss;
|
||||
l_ss << "Lengths: ";
|
||||
for (auto l : lengths)
|
||||
l_ss << l << ", ";
|
||||
BOOST_TEST_MESSAGE(l_ss.str());
|
||||
std::stringstream o_ss;
|
||||
o_ss << "Offsets: ";
|
||||
for (auto o : offsets)
|
||||
o_ss << o << ", ";
|
||||
BOOST_TEST_MESSAGE(o_ss.str());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(serialization_test)
|
||||
{
|
||||
std::vector<unsigned> lengths;
|
||||
std::vector<unsigned> offsets;
|
||||
ComputeLengthsOffsets(lengths, offsets, (BLOCK_SIZE+1)*10);
|
||||
|
||||
TestRangeTable in_table(lengths);
|
||||
TestRangeTable out_table;
|
||||
|
||||
std::stringstream ss;
|
||||
ss << in_table;
|
||||
ss >> out_table;
|
||||
|
||||
for (unsigned i = 0; i < lengths.size(); i++)
|
||||
{
|
||||
auto range = out_table.GetRange(i);
|
||||
BOOST_CHECK_EQUAL(range.front(), offsets[i]);
|
||||
BOOST_CHECK_EQUAL(range.back()+1, offsets[i+1]);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(construction_test)
|
||||
{
|
||||
// only offset empty block
|
||||
ConstructionTest({1}, {0, 1});
|
||||
// first block almost full => sentinel is last element of block
|
||||
// [0] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, (16)}
|
||||
std::vector<unsigned> almost_full_lengths;
|
||||
std::vector<unsigned> almost_full_offsets;
|
||||
ComputeLengthsOffsets(almost_full_lengths, almost_full_offsets, BLOCK_SIZE);
|
||||
ConstructionTest(almost_full_lengths, almost_full_offsets);
|
||||
|
||||
// first block full => sentinel is offset of new block, next block empty
|
||||
// [0] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
|
||||
// [(153)] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
std::vector<unsigned> full_lengths;
|
||||
std::vector<unsigned> full_offsets;
|
||||
ComputeLengthsOffsets(full_lengths, full_offsets, BLOCK_SIZE+1);
|
||||
ConstructionTest(full_lengths, full_offsets);
|
||||
|
||||
// first block full and offset of next block not sentinel, but the first differential value
|
||||
// [0] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
|
||||
// [153] {(17), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
std::vector<unsigned> over_full_lengths;
|
||||
std::vector<unsigned> over_full_offsets;
|
||||
ComputeLengthsOffsets(over_full_lengths, over_full_offsets, BLOCK_SIZE+2);
|
||||
ConstructionTest(over_full_lengths, over_full_offsets);
|
||||
|
||||
// test multiple blocks
|
||||
std::vector<unsigned> multiple_lengths;
|
||||
std::vector<unsigned> multiple_offsets;
|
||||
ComputeLengthsOffsets(multiple_lengths, multiple_offsets, (BLOCK_SIZE+1)*10);
|
||||
ConstructionTest(multiple_lengths, multiple_offsets);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
175
UnitTests/DataStructures/StaticGraphTest.cpp
Normal file
175
UnitTests/DataStructures/StaticGraphTest.cpp
Normal file
@ -0,0 +1,175 @@
|
||||
#include "../../DataStructures/StaticGraph.h"
|
||||
#include "../../typedefs.h"
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <boost/test/test_case_template.hpp>
|
||||
#include <boost/mpl/list.hpp>
|
||||
|
||||
#include <random>
|
||||
#include <unordered_map>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(static_graph)
|
||||
|
||||
struct TestData
|
||||
{
|
||||
EdgeID id;
|
||||
bool shortcut;
|
||||
unsigned distance;
|
||||
};
|
||||
|
||||
struct TestEdge
|
||||
{
|
||||
unsigned source;
|
||||
unsigned target;
|
||||
unsigned distance;
|
||||
};
|
||||
|
||||
typedef StaticGraph<TestData> TestStaticGraph;
|
||||
typedef TestStaticGraph::NodeArrayEntry TestNodeArrayEntry;
|
||||
typedef TestStaticGraph::EdgeArrayEntry TestEdgeArrayEntry;
|
||||
typedef TestStaticGraph::InputEdge TestInputEdge;
|
||||
|
||||
constexpr unsigned TEST_NUM_NODES = 100;
|
||||
constexpr unsigned TEST_NUM_EDGES = 500;
|
||||
// Choosen by a fair W20 dice roll (this value is completely arbitrary)
|
||||
constexpr unsigned RANDOM_SEED = 15;
|
||||
|
||||
template<unsigned NUM_NODES, unsigned NUM_EDGES>
|
||||
struct RandomArrayEntryFixture
|
||||
{
|
||||
RandomArrayEntryFixture()
|
||||
{
|
||||
std::mt19937 g(RANDOM_SEED);
|
||||
|
||||
std::uniform_int_distribution<> edge_udist(0, NUM_EDGES-1);
|
||||
std::vector<unsigned> offsets;
|
||||
for (unsigned i = 0; i < NUM_NODES; i++)
|
||||
{
|
||||
offsets.push_back(edge_udist(g));
|
||||
}
|
||||
std::sort(offsets.begin(), offsets.end());
|
||||
// add sentinel
|
||||
offsets.push_back(offsets.back());
|
||||
|
||||
// extract interval lengths
|
||||
for(unsigned i = 0; i < offsets.size()-1; i++)
|
||||
{
|
||||
lengths.push_back(offsets[i+1] - offsets[i]);
|
||||
}
|
||||
lengths.push_back(NUM_EDGES - offsets[NUM_NODES-1]);
|
||||
|
||||
for (auto offset : offsets)
|
||||
{
|
||||
nodes.emplace_back(TestNodeArrayEntry {offset});
|
||||
}
|
||||
|
||||
std::uniform_int_distribution<> lengths_udist(0, 100000);
|
||||
std::uniform_int_distribution<> node_udist(0, NUM_NODES-1);
|
||||
for (unsigned i = 0; i < NUM_EDGES; i++)
|
||||
{
|
||||
edges.emplace_back(
|
||||
TestEdgeArrayEntry {
|
||||
static_cast<unsigned>(node_udist(g)),
|
||||
TestData {i, false, static_cast<unsigned>(lengths_udist(g))}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < NUM_NODES; i++)
|
||||
order.push_back(i);
|
||||
std::shuffle(order.begin(), order.end(), g);
|
||||
}
|
||||
|
||||
typename ShM<TestNodeArrayEntry, false>::vector nodes;
|
||||
typename ShM<TestEdgeArrayEntry, false>::vector edges;
|
||||
std::vector<unsigned> lengths;
|
||||
std::vector<unsigned> order;
|
||||
};
|
||||
|
||||
typedef RandomArrayEntryFixture<TEST_NUM_NODES, TEST_NUM_EDGES> TestRandomArrayEntryFixture;
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(array_test, TestRandomArrayEntryFixture)
|
||||
{
|
||||
auto nodes_copy = nodes;
|
||||
|
||||
TestStaticGraph graph(nodes, edges);
|
||||
|
||||
BOOST_CHECK_EQUAL(graph.GetNumberOfEdges(), TEST_NUM_EDGES);
|
||||
BOOST_CHECK_EQUAL(graph.GetNumberOfNodes(), TEST_NUM_NODES);
|
||||
|
||||
for (auto idx : order)
|
||||
{
|
||||
BOOST_CHECK_EQUAL(graph.BeginEdges((NodeID) idx), nodes_copy[idx].first_edge);
|
||||
BOOST_CHECK_EQUAL(graph.EndEdges((NodeID) idx), nodes_copy[idx+1].first_edge);
|
||||
BOOST_CHECK_EQUAL(graph.GetOutDegree((NodeID) idx), lengths[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
TestStaticGraph GraphFromEdgeList(const std::vector<TestEdge>& edges)
|
||||
{
|
||||
std::vector<TestInputEdge> input_edges;
|
||||
unsigned i = 0;
|
||||
unsigned num_nodes = 0;
|
||||
for (const auto& e : edges)
|
||||
{
|
||||
input_edges.push_back(TestInputEdge {
|
||||
e.source,
|
||||
e.target,
|
||||
TestData {i++, false, e.distance}
|
||||
});
|
||||
|
||||
num_nodes = std::max(num_nodes, std::max(e.source, e.target));
|
||||
}
|
||||
|
||||
return TestStaticGraph(num_nodes, input_edges);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(find_test)
|
||||
{
|
||||
/*
|
||||
* (0) -1-> (1)
|
||||
* ^ ^
|
||||
* 2 1
|
||||
* | |
|
||||
* (3) -4-> (4)
|
||||
* <-3-
|
||||
*/
|
||||
TestStaticGraph simple_graph = GraphFromEdgeList({
|
||||
TestEdge {0, 1, 1},
|
||||
TestEdge {3, 0, 2},
|
||||
TestEdge {3, 4, 4},
|
||||
TestEdge {4, 3, 3},
|
||||
TestEdge {3, 0, 1}
|
||||
});
|
||||
|
||||
auto eit = simple_graph.FindEdge(0, 1);
|
||||
BOOST_CHECK_EQUAL(simple_graph.GetEdgeData(eit).id, 0);
|
||||
|
||||
eit = simple_graph.FindEdge(1, 0);
|
||||
BOOST_CHECK_EQUAL(eit, SPECIAL_EDGEID);
|
||||
|
||||
eit = simple_graph.FindEdgeInEitherDirection(1, 0);
|
||||
BOOST_CHECK_EQUAL(simple_graph.GetEdgeData(eit).id, 0);
|
||||
|
||||
bool reverse = false;
|
||||
eit = simple_graph.FindEdgeIndicateIfReverse(1, 0, reverse);
|
||||
BOOST_CHECK_EQUAL(simple_graph.GetEdgeData(eit).id, 0);
|
||||
BOOST_CHECK(reverse);
|
||||
|
||||
eit = simple_graph.FindEdge(3, 1);
|
||||
BOOST_CHECK_EQUAL(eit, SPECIAL_EDGEID);
|
||||
eit = simple_graph.FindEdge(0, 4);
|
||||
BOOST_CHECK_EQUAL(eit, SPECIAL_EDGEID);
|
||||
|
||||
eit = simple_graph.FindEdge(3, 4);
|
||||
BOOST_CHECK_EQUAL(simple_graph.GetEdgeData(eit).id, 2);
|
||||
eit = simple_graph.FindEdgeInEitherDirection(3, 4);
|
||||
// I think this is wrong behaviour! Should be 3.
|
||||
BOOST_CHECK_EQUAL(simple_graph.GetEdgeData(eit).id, 2);
|
||||
|
||||
eit = simple_graph.FindEdge(3, 0);
|
||||
BOOST_CHECK_EQUAL(simple_graph.GetEdgeData(eit).id, 4);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
503
UnitTests/DataStructures/StaticRTreeTest.cpp
Normal file
503
UnitTests/DataStructures/StaticRTreeTest.cpp
Normal file
@ -0,0 +1,503 @@
|
||||
#include "../../DataStructures/StaticRTree.h"
|
||||
#include "../../DataStructures/QueryNode.h"
|
||||
#include "../../DataStructures/EdgeBasedNode.h"
|
||||
#include "../../Include/osrm/Coordinate.h"
|
||||
#include "../../typedefs.h"
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <boost/test/test_case_template.hpp>
|
||||
#include <boost/mpl/list.hpp>
|
||||
|
||||
#include <random>
|
||||
#include <unordered_set>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(static_rtree)
|
||||
|
||||
constexpr uint32_t TEST_BRANCHING_FACTOR = 8;
|
||||
constexpr uint32_t TEST_LEAF_NODE_SIZE = 64;
|
||||
|
||||
typedef EdgeBasedNode TestData;
|
||||
typedef StaticRTree<TestData,
|
||||
std::vector<FixedPointCoordinate>,
|
||||
false,
|
||||
TEST_BRANCHING_FACTOR,
|
||||
TEST_LEAF_NODE_SIZE>
|
||||
TestStaticRTree;
|
||||
|
||||
// Choosen by a fair W20 dice roll (this value is completely arbitrary)
|
||||
constexpr unsigned RANDOM_SEED = 15;
|
||||
static const int32_t WORLD_MIN_LAT = -90*COORDINATE_PRECISION;
|
||||
static const int32_t WORLD_MAX_LAT = 90*COORDINATE_PRECISION;
|
||||
static const int32_t WORLD_MIN_LON = -180*COORDINATE_PRECISION;
|
||||
static const int32_t WORLD_MAX_LON = 180*COORDINATE_PRECISION;
|
||||
|
||||
class LinearSearchNN
|
||||
{
|
||||
public:
|
||||
LinearSearchNN(const std::shared_ptr<std::vector<FixedPointCoordinate>>& coords,
|
||||
const std::vector<TestData>& edges)
|
||||
: coords(coords)
|
||||
, edges(edges)
|
||||
{ }
|
||||
|
||||
bool LocateClosestEndPointForCoordinate(const FixedPointCoordinate &input_coordinate,
|
||||
FixedPointCoordinate &result_coordinate,
|
||||
const unsigned zoom_level)
|
||||
{
|
||||
bool ignore_tiny_components = (zoom_level <= 14);
|
||||
|
||||
float min_dist = std::numeric_limits<float>::max();
|
||||
FixedPointCoordinate min_coord;
|
||||
for (const TestData& e : edges)
|
||||
{
|
||||
if (ignore_tiny_components && e.is_in_tiny_cc)
|
||||
continue;
|
||||
|
||||
const FixedPointCoordinate& start = coords->at(e.u);
|
||||
const FixedPointCoordinate& end = coords->at(e.v);
|
||||
float distance = FixedPointCoordinate::ApproximateEuclideanDistance(
|
||||
input_coordinate.lat,
|
||||
input_coordinate.lon,
|
||||
start.lat,
|
||||
start.lon);
|
||||
if (distance < min_dist)
|
||||
{
|
||||
min_coord = start;
|
||||
min_dist = distance;
|
||||
}
|
||||
|
||||
distance = FixedPointCoordinate::ApproximateEuclideanDistance(
|
||||
input_coordinate.lat,
|
||||
input_coordinate.lon,
|
||||
end.lat,
|
||||
end.lon);
|
||||
if (distance < min_dist)
|
||||
{
|
||||
min_coord = end;
|
||||
min_dist = distance;
|
||||
}
|
||||
}
|
||||
|
||||
result_coordinate = min_coord;
|
||||
return result_coordinate.isValid();
|
||||
}
|
||||
|
||||
bool FindPhantomNodeForCoordinate(const FixedPointCoordinate &input_coordinate,
|
||||
PhantomNode &result_phantom_node,
|
||||
const unsigned zoom_level)
|
||||
{
|
||||
bool ignore_tiny_components = (zoom_level <= 14);
|
||||
|
||||
float min_dist = std::numeric_limits<float>::max();
|
||||
TestData nearest_edge;
|
||||
for (const TestData& e : edges)
|
||||
{
|
||||
if (ignore_tiny_components && e.is_in_tiny_cc)
|
||||
continue;
|
||||
|
||||
float current_ratio = 0.;
|
||||
FixedPointCoordinate nearest;
|
||||
const float current_perpendicular_distance =
|
||||
FixedPointCoordinate::ComputePerpendicularDistance(
|
||||
coords->at(e.u),
|
||||
coords->at(e.v),
|
||||
input_coordinate,
|
||||
nearest,
|
||||
current_ratio);
|
||||
|
||||
if ((current_perpendicular_distance < min_dist) &&
|
||||
!EpsilonCompare(current_perpendicular_distance, min_dist))
|
||||
{ // found a new minimum
|
||||
min_dist = current_perpendicular_distance;
|
||||
result_phantom_node = { e.forward_edge_based_node_id,
|
||||
e.reverse_edge_based_node_id,
|
||||
e.name_id,
|
||||
e.forward_weight,
|
||||
e.reverse_weight,
|
||||
e.forward_offset,
|
||||
e.reverse_offset,
|
||||
e.packed_geometry_id,
|
||||
nearest,
|
||||
e.fwd_segment_position};
|
||||
nearest_edge = e;
|
||||
}
|
||||
}
|
||||
|
||||
if (result_phantom_node.location.isValid())
|
||||
{
|
||||
// Hack to fix rounding errors and wandering via nodes.
|
||||
if (1 == std::abs(input_coordinate.lon - result_phantom_node.location.lon))
|
||||
{
|
||||
result_phantom_node.location.lon = input_coordinate.lon;
|
||||
}
|
||||
if (1 == std::abs(input_coordinate.lat - result_phantom_node.location.lat))
|
||||
{
|
||||
result_phantom_node.location.lat = input_coordinate.lat;
|
||||
}
|
||||
|
||||
const float distance_1 = FixedPointCoordinate::ApproximateEuclideanDistance(
|
||||
coords->at(nearest_edge.u),
|
||||
result_phantom_node.location);
|
||||
const float distance_2 = FixedPointCoordinate::ApproximateEuclideanDistance(
|
||||
coords->at(nearest_edge.u),
|
||||
coords->at(nearest_edge.v));
|
||||
const float ratio = std::min(1.f, distance_1 / distance_2);
|
||||
|
||||
if (SPECIAL_NODEID != result_phantom_node.forward_node_id)
|
||||
{
|
||||
result_phantom_node.forward_weight *= ratio;
|
||||
}
|
||||
if (SPECIAL_NODEID != result_phantom_node.reverse_node_id)
|
||||
{
|
||||
result_phantom_node.reverse_weight *= (1. - ratio);
|
||||
}
|
||||
}
|
||||
|
||||
return result_phantom_node.location.isValid();
|
||||
}
|
||||
|
||||
template<typename FloatT>
|
||||
inline bool EpsilonCompare(const FloatT d1, const FloatT d2) const
|
||||
{
|
||||
return (std::abs(d1 - d2) < std::numeric_limits<FloatT>::epsilon());
|
||||
}
|
||||
|
||||
private:
|
||||
const std::shared_ptr<std::vector<FixedPointCoordinate>>& coords;
|
||||
const std::vector<TestData>& edges;
|
||||
};
|
||||
|
||||
|
||||
template<unsigned NUM_NODES, unsigned NUM_EDGES>
|
||||
struct RandomGraphFixture
|
||||
{
|
||||
struct TupleHash
|
||||
{
|
||||
typedef std::pair<unsigned, unsigned> argument_type;
|
||||
typedef std::size_t result_type;
|
||||
|
||||
result_type operator()(const argument_type & t) const
|
||||
{
|
||||
std::size_t val { 0 };
|
||||
boost::hash_combine(val, t.first);
|
||||
boost::hash_combine(val, t.second);
|
||||
return val;
|
||||
}
|
||||
};
|
||||
|
||||
RandomGraphFixture()
|
||||
: coords(std::make_shared<std::vector<FixedPointCoordinate>>())
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Constructing " << NUM_NODES << " nodes and " << NUM_EDGES << " edges.");
|
||||
|
||||
std::mt19937 g(RANDOM_SEED);
|
||||
|
||||
std::uniform_int_distribution<> lat_udist(WORLD_MIN_LAT, WORLD_MAX_LAT);
|
||||
std::uniform_int_distribution<> lon_udist(WORLD_MIN_LON, WORLD_MAX_LON);
|
||||
|
||||
for (unsigned i = 0; i < NUM_NODES; i++)
|
||||
{
|
||||
int lat = lat_udist(g);
|
||||
int lon = lon_udist(g);
|
||||
nodes.emplace_back(NodeInfo(lat, lon, i));
|
||||
coords->emplace_back(FixedPointCoordinate(lat, lon));
|
||||
}
|
||||
|
||||
std::uniform_int_distribution<> edge_udist(0, nodes.size() - 1);
|
||||
|
||||
std::unordered_set<std::pair<unsigned, unsigned>, TupleHash> used_edges;
|
||||
|
||||
while(edges.size() < NUM_EDGES)
|
||||
{
|
||||
TestData data;
|
||||
data.u = edge_udist(g);
|
||||
data.v = edge_udist(g);
|
||||
if (used_edges.find(std::pair<unsigned, unsigned>(std::min(data.u, data.v), std::max(data.u, data.v))) == used_edges.end())
|
||||
{
|
||||
data.is_in_tiny_cc = false;
|
||||
edges.emplace_back(data);
|
||||
used_edges.emplace(std::min(data.u, data.v), std::max(data.u, data.v));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::vector<NodeInfo> nodes;
|
||||
std::shared_ptr<std::vector<FixedPointCoordinate>> coords;
|
||||
std::vector<TestData> edges;
|
||||
};
|
||||
|
||||
struct GraphFixture
|
||||
{
|
||||
GraphFixture(const std::vector<std::pair<float, float>>& input_coords,
|
||||
const std::vector<std::pair<unsigned, unsigned>>& input_edges)
|
||||
: coords(std::make_shared<std::vector<FixedPointCoordinate>>())
|
||||
{
|
||||
|
||||
for (unsigned i = 0; i < input_coords.size(); i++)
|
||||
{
|
||||
FixedPointCoordinate c(input_coords[i].first * COORDINATE_PRECISION, input_coords[i].second * COORDINATE_PRECISION);
|
||||
coords->emplace_back(c);
|
||||
nodes.emplace_back(NodeInfo(c.lat, c.lon, i));
|
||||
}
|
||||
|
||||
for (const auto& pair : input_edges)
|
||||
{
|
||||
TestData d;
|
||||
d.u = pair.first;
|
||||
d.v = pair.second;
|
||||
edges.emplace_back(d);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::vector<NodeInfo> nodes;
|
||||
std::shared_ptr<std::vector<FixedPointCoordinate>> coords;
|
||||
std::vector<TestData> edges;
|
||||
};
|
||||
|
||||
|
||||
typedef RandomGraphFixture<TEST_LEAF_NODE_SIZE*3, TEST_LEAF_NODE_SIZE/2> TestRandomGraphFixture_LeafHalfFull;
|
||||
typedef RandomGraphFixture<TEST_LEAF_NODE_SIZE*5, TEST_LEAF_NODE_SIZE> TestRandomGraphFixture_LeafFull;
|
||||
typedef RandomGraphFixture<TEST_LEAF_NODE_SIZE*10, TEST_LEAF_NODE_SIZE*2> TestRandomGraphFixture_TwoLeaves;
|
||||
typedef RandomGraphFixture<TEST_LEAF_NODE_SIZE*TEST_BRANCHING_FACTOR*3,
|
||||
TEST_LEAF_NODE_SIZE*TEST_BRANCHING_FACTOR> TestRandomGraphFixture_Branch;
|
||||
typedef RandomGraphFixture<TEST_LEAF_NODE_SIZE*TEST_BRANCHING_FACTOR*3,
|
||||
TEST_LEAF_NODE_SIZE*TEST_BRANCHING_FACTOR*2> TestRandomGraphFixture_MultipleLevels;
|
||||
|
||||
template<typename RTreeT>
|
||||
void simple_verify_rtree(RTreeT& rtree, const std::shared_ptr<std::vector<FixedPointCoordinate>>& coords, const std::vector<TestData>& edges)
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Verify end points");
|
||||
for (const auto& e : edges)
|
||||
{
|
||||
FixedPointCoordinate result_u, result_v;
|
||||
const FixedPointCoordinate& pu = coords->at(e.u);
|
||||
const FixedPointCoordinate& pv = coords->at(e.v);
|
||||
bool found_u = rtree.LocateClosestEndPointForCoordinate(pu, result_u, 1);
|
||||
bool found_v = rtree.LocateClosestEndPointForCoordinate(pv, result_v, 1);
|
||||
BOOST_CHECK(found_u && found_v);
|
||||
float dist_u = FixedPointCoordinate::ApproximateEuclideanDistance(
|
||||
result_u.lat,
|
||||
result_u.lon,
|
||||
pu.lat,
|
||||
pu.lon);
|
||||
BOOST_CHECK_LE(dist_u, std::numeric_limits<float>::epsilon());
|
||||
float dist_v = FixedPointCoordinate::ApproximateEuclideanDistance(
|
||||
result_v.lat,
|
||||
result_v.lon,
|
||||
pv.lat,
|
||||
pv.lon);
|
||||
BOOST_CHECK_LE(dist_v, std::numeric_limits<float>::epsilon());
|
||||
}
|
||||
}
|
||||
|
||||
template<typename RTreeT>
|
||||
void sampling_verify_rtree(RTreeT& rtree, LinearSearchNN& lsnn, unsigned num_samples)
|
||||
{
|
||||
std::mt19937 g(RANDOM_SEED);
|
||||
std::uniform_int_distribution<> lat_udist(WORLD_MIN_LAT, WORLD_MAX_LAT);
|
||||
std::uniform_int_distribution<> lon_udist(WORLD_MIN_LON, WORLD_MAX_LON);
|
||||
std::vector<FixedPointCoordinate> queries;
|
||||
for (unsigned i = 0; i < num_samples; i++)
|
||||
{
|
||||
queries.emplace_back(
|
||||
FixedPointCoordinate(lat_udist(g), lon_udist(g))
|
||||
);
|
||||
}
|
||||
|
||||
BOOST_TEST_MESSAGE("Sampling queries");
|
||||
for (const auto& q : queries)
|
||||
{
|
||||
FixedPointCoordinate result_rtree;
|
||||
rtree.LocateClosestEndPointForCoordinate(q, result_rtree, 1);
|
||||
FixedPointCoordinate result_ln;
|
||||
lsnn.LocateClosestEndPointForCoordinate(q, result_ln, 1);
|
||||
BOOST_CHECK_EQUAL(result_ln, result_rtree);
|
||||
|
||||
PhantomNode phantom_rtree;
|
||||
rtree.FindPhantomNodeForCoordinate(q, phantom_rtree, 1);
|
||||
PhantomNode phantom_ln;
|
||||
lsnn.FindPhantomNodeForCoordinate(q, phantom_ln, 1);
|
||||
BOOST_CHECK_EQUAL(phantom_rtree, phantom_ln);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FixtureT, typename RTreeT=TestStaticRTree>
|
||||
void build_rtree(const std::string& prefix, FixtureT* fixture, std::string& leaves_path, std::string& nodes_path)
|
||||
{
|
||||
nodes_path = prefix + ".ramIndex";
|
||||
leaves_path = prefix + ".fileIndex";
|
||||
const std::string coords_path = prefix + ".nodes";
|
||||
|
||||
boost::filesystem::ofstream node_stream(coords_path, std::ios::binary);
|
||||
const unsigned num_nodes = fixture->nodes.size();
|
||||
node_stream.write((char *)&num_nodes, sizeof(unsigned));
|
||||
node_stream.write((char *)&(fixture->nodes[0]), num_nodes * sizeof(NodeInfo));
|
||||
node_stream.close();
|
||||
|
||||
RTreeT r(fixture->edges, nodes_path, leaves_path, fixture->nodes);
|
||||
}
|
||||
|
||||
template<typename FixtureT, typename RTreeT=TestStaticRTree>
|
||||
void construction_test(const std::string& prefix, FixtureT* fixture)
|
||||
{
|
||||
std::string leaves_path;
|
||||
std::string nodes_path;
|
||||
build_rtree<FixtureT, RTreeT>(prefix, fixture, leaves_path, nodes_path);
|
||||
RTreeT rtree(nodes_path, leaves_path, fixture->coords);
|
||||
LinearSearchNN lsnn(fixture->coords, fixture->edges);
|
||||
|
||||
simple_verify_rtree(rtree, fixture->coords, fixture->edges);
|
||||
sampling_verify_rtree(rtree, lsnn, 100);
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(construct_half_leaf_test, TestRandomGraphFixture_LeafHalfFull)
|
||||
{
|
||||
construction_test("test_1", this);
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(construct_full_leaf_test, TestRandomGraphFixture_LeafFull)
|
||||
{
|
||||
construction_test("test_2", this);
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(construct_two_leaves_test, TestRandomGraphFixture_TwoLeaves)
|
||||
{
|
||||
construction_test("test_3", this);
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(construct_branch_test, TestRandomGraphFixture_Branch)
|
||||
{
|
||||
construction_test("test_4", this);
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(construct_multiple_levels_test, TestRandomGraphFixture_MultipleLevels)
|
||||
{
|
||||
construction_test("test_5", this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Bug: If you querry a point that lies between two BBs that have a gap,
|
||||
* one BB will be pruned, even if it could contain a nearer match.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(regression_test)
|
||||
{
|
||||
typedef std::pair<float, float> Coord;
|
||||
typedef std::pair<unsigned, unsigned> Edge;
|
||||
GraphFixture fixture({
|
||||
Coord(40.0, 0.0),
|
||||
Coord(35.0, 5.0),
|
||||
|
||||
Coord(5.0, 5.0),
|
||||
Coord(0.0, 10.0),
|
||||
|
||||
Coord(20.0, 10.0),
|
||||
Coord(20.0, 5.0),
|
||||
|
||||
Coord(40.0, 100.0),
|
||||
Coord(35.0, 105.0),
|
||||
|
||||
Coord(5.0, 105.0),
|
||||
Coord(0.0, 110.0),
|
||||
},
|
||||
{
|
||||
Edge(0, 1),
|
||||
Edge(2, 3),
|
||||
Edge(4, 5),
|
||||
Edge(6, 7),
|
||||
Edge(8, 9)
|
||||
}
|
||||
);
|
||||
|
||||
typedef StaticRTree<TestData,
|
||||
std::vector<FixedPointCoordinate>,
|
||||
false,
|
||||
2,
|
||||
3> MiniStaticRTree;
|
||||
|
||||
std::string leaves_path;
|
||||
std::string nodes_path;
|
||||
build_rtree<GraphFixture, MiniStaticRTree>("test_regression", &fixture, leaves_path, nodes_path);
|
||||
MiniStaticRTree rtree(nodes_path, leaves_path, fixture.coords);
|
||||
|
||||
// query a node just right of the center of the gap
|
||||
FixedPointCoordinate input(20.0 * COORDINATE_PRECISION, 55.1 * COORDINATE_PRECISION);
|
||||
FixedPointCoordinate result;
|
||||
rtree.LocateClosestEndPointForCoordinate(input, result, 1);
|
||||
FixedPointCoordinate result_ln;
|
||||
LinearSearchNN lsnn(fixture.coords, fixture.edges);
|
||||
lsnn.LocateClosestEndPointForCoordinate(input, result_ln, 1);
|
||||
|
||||
BOOST_CHECK_EQUAL(result_ln, result);
|
||||
}
|
||||
|
||||
void TestRectangle(double width, double height, double center_lat, double center_lon)
|
||||
{
|
||||
FixedPointCoordinate center(center_lat*COORDINATE_PRECISION, center_lon*COORDINATE_PRECISION);
|
||||
|
||||
TestStaticRTree::RectangleT rect;
|
||||
rect.min_lat = center.lat - height/2.0 * COORDINATE_PRECISION;
|
||||
rect.max_lat = center.lat + height/2.0 * COORDINATE_PRECISION;
|
||||
rect.min_lon = center.lon - width/2.0 * COORDINATE_PRECISION;
|
||||
rect.max_lon = center.lon + width/2.0 * COORDINATE_PRECISION;
|
||||
|
||||
unsigned offset = 5*COORDINATE_PRECISION;
|
||||
FixedPointCoordinate north(rect.max_lat + offset, center.lon);
|
||||
FixedPointCoordinate south(rect.min_lat - offset, center.lon);
|
||||
FixedPointCoordinate west(center.lat, rect.min_lon - offset);
|
||||
FixedPointCoordinate east(center.lat, rect.max_lon + offset);
|
||||
FixedPointCoordinate north_east(rect.max_lat + offset, rect.max_lon + offset);
|
||||
FixedPointCoordinate north_west(rect.max_lat + offset, rect.min_lon - offset);
|
||||
FixedPointCoordinate south_east(rect.min_lat - offset, rect.max_lon + offset);
|
||||
FixedPointCoordinate south_west(rect.min_lat - offset, rect.min_lon - offset);
|
||||
|
||||
|
||||
/* Distance to line segments of rectangle */
|
||||
BOOST_CHECK_EQUAL(
|
||||
rect.GetMinDist(north),
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(north, FixedPointCoordinate(rect.max_lat, north.lon))
|
||||
);
|
||||
BOOST_CHECK_EQUAL(
|
||||
rect.GetMinDist(south),
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(south, FixedPointCoordinate(rect.min_lat, south.lon))
|
||||
);
|
||||
BOOST_CHECK_EQUAL(
|
||||
rect.GetMinDist(west),
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(west, FixedPointCoordinate(west.lat, rect.min_lon))
|
||||
);
|
||||
BOOST_CHECK_EQUAL(
|
||||
rect.GetMinDist(east),
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(east, FixedPointCoordinate(east.lat, rect.max_lon))
|
||||
);
|
||||
|
||||
/* Distance to corner points */
|
||||
BOOST_CHECK_EQUAL(
|
||||
rect.GetMinDist(north_east),
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(north_east, FixedPointCoordinate(rect.max_lat, rect.max_lon))
|
||||
);
|
||||
BOOST_CHECK_EQUAL(
|
||||
rect.GetMinDist(north_west),
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(north_west, FixedPointCoordinate(rect.max_lat, rect.min_lon))
|
||||
);
|
||||
BOOST_CHECK_EQUAL(
|
||||
rect.GetMinDist(south_east),
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(south_east, FixedPointCoordinate(rect.min_lat, rect.max_lon))
|
||||
);
|
||||
BOOST_CHECK_EQUAL(
|
||||
rect.GetMinDist(south_west),
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(south_west, FixedPointCoordinate(rect.min_lat, rect.min_lon))
|
||||
);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(rectangle_test)
|
||||
{
|
||||
TestRectangle(10, 10, 5, 5);
|
||||
TestRectangle(10, 10, -5, 5);
|
||||
TestRectangle(10, 10, 5, -5);
|
||||
TestRectangle(10, 10, -5, -5);
|
||||
TestRectangle(10, 10, 0, 0);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
8
UnitTests/datastructure_tests.cpp
Normal file
8
UnitTests/datastructure_tests.cpp
Normal file
@ -0,0 +1,8 @@
|
||||
#define BOOST_TEST_MODULE datastructure tests
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
/*
|
||||
* This file will contain an automatically generated main function.
|
||||
*/
|
||||
|
@ -417,7 +417,7 @@ unsigned readHSGRFromStream(const boost::filesystem::path &hsgr_file,
|
||||
<< ", number_of_edges: " << number_of_edges;
|
||||
|
||||
// BOOST_ASSERT_MSG( 0 != number_of_edges, "number of edges is zero");
|
||||
node_list.resize(number_of_nodes + 1);
|
||||
node_list.resize(number_of_nodes);
|
||||
hsgr_input_stream.read((char *)&(node_list[0]), number_of_nodes * sizeof(NodeT));
|
||||
|
||||
edge_list.resize(number_of_edges);
|
||||
|
@ -38,7 +38,12 @@ build_script:
|
||||
- set TBB_ARCH_PLATFORM=intel64/vc12
|
||||
- cmake .. -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=%Configuration% -DBZIP2_INCLUDE_DIR=%P%/libs/include -DBZIP2_LIBRARIES=%P%/libs/lib/libbz2.lib -DCMAKE_INSTALL_PREFIX=%P%/libs -DBOOST_ROOT=%P%/boost_min -DBoost_USE_STATIC_LIBS=ON
|
||||
- nmake
|
||||
- nmake tests
|
||||
- nmake benchmarks
|
||||
- if "%APPVEYOR_REPO_BRANCH%"=="develop" (7z a %P%/osrm_%Configuration%.zip *.exe *.pdb %P%/libs/bin/*.dll -tzip)
|
||||
- set PATH=%PATH%;c:/projects/osrm/libs/bin
|
||||
- cd c:/projects/osrm/build
|
||||
- datastructure-tests.exe
|
||||
|
||||
test: off
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user