Refactor Hilbert values computation

This commit is contained in:
Michael Krasnyk 2016-11-22 21:08:12 +01:00 committed by Patrick Niklaus
parent 0df0d31d83
commit e343f71541
9 changed files with 167 additions and 108 deletions

View File

@ -90,7 +90,7 @@ OSRM will use 4/5 of the projected free-flow speed.
| primary | | 3 | 60 | | 29 km/h | 32 km/h |
| primary | | | | 60 | 52 km/h | 47 km/h |
| primary | | 3 | | 60 | 32 km/h | 29 km/h |
| primary | 15 | | 60 | | 47 km/h | 12 km/h |
| primary | 15 | | 60 | | 47 km/h | 11 km/h |
| primary | 15 | 3 | 60 | | 29 km/h | 7 km/h |
| primary | 15 | | | 60 | 12 km/h | 47 km/h |
| primary | 15 | 3 | | 60 | 7 km/h | 29 km/h |
@ -109,7 +109,7 @@ OSRM will use 4/5 of the projected free-flow speed.
| primary | | 1 | 60 | | 29 km/h | 32 km/h |
| primary | | | | 60 | 52 km/h | 47 km/h |
| primary | | 1 | | 60 | 32 km/h | 29 km/h |
| primary | 15 | | 60 | | 47 km/h | 12 km/h |
| primary | 15 | | 60 | | 47 km/h | 11 km/h |
| primary | 15 | 1 | 60 | | 29 km/h | 7 km/h |
| primary | 15 | | | 60 | 12 km/h | 47 km/h |
| primary | 15 | 1 | | 60 | 7 km/h | 29 km/h |

View File

@ -27,7 +27,10 @@ Feature: Basic Routing
When I route I should get
| waypoints | route | summary |
| a,e | road,,1 st,1 st | road, 1 st |
| a,d,f | road,,,street,street | road;street |
# The via node `d` belongs to `cd`, `de`, `df`, `dg` and depending on the edge
# summary can be "road;street", "road, 1 st;1 st, street", "road, blvd;blvd, street"
# The test must be fixed by #2287
#| a,d,f | road,,,street,street | road;street |
| a,e,f | road,,1 st,1 st,1 st,street,street | road, 1 st;1 st, street |
Scenario: Name Empty

View File

@ -111,7 +111,9 @@ Feature: Traffic - turn penalties
"""
When I route I should get
| from | to | route | time |
| a | d | ad,ad | 10s +-1 |
# The target point `d` can be in `ad`, `cd`, `deh` and `dhk`
# The test must be fixed by #2287
#| a | d | ad,ad | 10s +-1 |
| a | e | ad,def,def | 10s +-1 |
| b | f | bf,bf | 10s +-1 |
| b | g | bf,fg,fg | 20s +-1 |

View File

@ -48,7 +48,7 @@ Feature: Bearing parameter
Scenario: Testbot - Initial bearing on split way
Given the node map
"""
g d 1 c f
g d 2 1 c f
h a 0 b e
"""
@ -67,18 +67,14 @@ Feature: Bearing parameter
| from | to | bearings | route | bearing |
| 0 | b | 10 10 | bc,bc | 0->0,0->0 |
| 0 | b | 90 90 | ab,ab | 0->90,90->0 |
# The returned bearing is wrong here, it's based on the snapped
# coordinates, not the acutal edge bearing. This should be
# fixed one day, but it's only a problem when we snap two vias
# to the same point - DP
#| 0 | b | 170 170 | da | 180 |
#| 0 | b | 189 189 | da | 180 |
| 0 | b | 170 170 | da,da | 0->0,0->0 |
| 0 | b | 189 189 | da,da | 0->0,0->0 |
| 0 | 1 | 90 270 | ab,bc,cd,cd | 0->90,90->0,0->270,270->0 |
| 1 | d | 10 10 | bc,bc | 0->0,0->0 |
| 1 | d | 90 90 | ab,bc,cd,da,da | 0->90,90->0,0->270,270->180,180->0 |
| 1 | 2 | 10 10 | bc,bc | 0->0,0->0 |
| 1 | 2 | 90 90 | ab,bc,cd,da,ab,ab | 0->90,90->0,0->270,270->180,180->90,90->0 |
| 1 | 0 | 189 189 | da,da | 0->180,180->0 |
| 1 | d | 270 270 | cd,cd | 0->270,270->0 |
| 1 | d | 349 349 | | |
| 1 | 2 | 270 270 | cd,cd | 0->270,270->0 |
| 1 | 2 | 349 349 | | |
Scenario: Testbot - Initial bearing in all direction
Given the node map

View File

@ -20,5 +20,5 @@ Feature: Geometry Compression
When I route I should get
| from | to | route | distance | speed |
| b | e | abcdef,abcdef | 588.8m | 36 km/h |
| e | b | abcdef,abcdef | 588.8m | 36 km/h |
| b | e | abcdef,abcdef | 588.6m | 36 km/h |
| e | b | abcdef,abcdef | 588.6m | 36 km/h |

View File

@ -3,6 +3,7 @@
#include "osrm/coordinate.hpp"
#include <climits>
#include <cstdint>
namespace osrm
@ -10,8 +11,64 @@ namespace osrm
namespace util
{
// Transform x and y to Hilbert SFC linear coordinate
// using N most significant bits of x and y.
// References:
// [1] Arndt, Jörg. Matters Computational Ideas, Algorithms, Source Code, 2010. 1.31.1 The Hilbert
// curve p. 86
// [2] FSM implementation from http://www.hackersdelight.org/hdcodetxt/hilbert/hil_s_from_xy.c.txt
// The method is to employ the following state transition table:
// ----------------------------------------------------------
// If the current And the next bits then append and enter
// state is of x and y are to result state
// ----------------------------------------------------------
// A (0, 0) 00 B
// A (0, 1) 01 A
// A (1, 0) 11 D
// A (1, 1) 10 A
// B (0, 0) 00 A
// B (0, 1) 11 C
// B (1, 0) 01 B
// B (1, 1) 10 B
// C (0, 0) 10 D
// C (0, 1) 11 B
// C (1, 0) 01 C
// C (1, 1) 00 D
// D (0, 0) 10 C
// D (0, 1) 01 D
// D (1, 0) 11 A
// D (1, 1) 00 C
template <int N = 32, typename T = std::uint32_t, typename R = std::uint64_t>
inline R HilbertToLinear(T x, T y)
{
static_assert(N <= sizeof(T) * CHAR_BIT, "input type is smaller than N");
static_assert(2 * N <= sizeof(R) * CHAR_BIT, "output type is smaller than 2N");
R result = 0;
unsigned state = 0;
for (int i = 0; i < N; ++i)
{
const unsigned xi = (x >> (sizeof(T) * CHAR_BIT - 1)) & 1;
const unsigned yi = (y >> (sizeof(T) * CHAR_BIT - 1)) & 1;
x <<= 1;
y <<= 1;
const unsigned row = 4 * state | 2 * xi | yi;
result = (result << 2) | ((0x361E9CB4 >> 2 * row) & 3);
state = (0x8FE65831 >> 2 * row) & 3;
}
return result;
}
// Computes a 64 bit value that corresponds to the hilbert space filling curve
std::uint64_t hilbertCode(const Coordinate coordinate);
inline std::uint64_t GetHilbertCode(const Coordinate &coordinate)
{
const std::uint32_t x = static_cast<std::int32_t>(coordinate.lon) +
static_cast<std::int32_t>(180 * COORDINATE_PRECISION);
const std::uint32_t y = static_cast<std::int32_t>(coordinate.lat) +
static_cast<std::int32_t>(90 * COORDINATE_PRECISION);
return HilbertToLinear(x, y);
}
}
}

View File

@ -198,7 +198,7 @@ class StaticRTree
COORDINATE_PRECISION *
web_mercator::latToY(toFloating(current_centroid.lat)))};
current_wrapper.m_hilbert_value = hilbertCode(current_centroid);
current_wrapper.m_hilbert_value = GetHilbertCode(current_centroid);
}
});

View File

@ -1,84 +0,0 @@
#include "util/hilbert_value.hpp"
namespace osrm
{
namespace util
{
namespace
{
std::uint64_t bitInterleaving(const std::uint32_t longitude, const std::uint32_t latitude)
{
std::uint64_t result = 0;
for (std::int8_t index = 31; index >= 0; --index)
{
result |= (latitude >> index) & 1;
result <<= 1;
result |= (longitude >> index) & 1;
if (0 != index)
{
result <<= 1;
}
}
return result;
}
void transposeCoordinate(std::uint32_t *x)
{
std::uint32_t M = 1u << (32 - 1), P, Q, t;
int i;
// Inverse undo
for (Q = M; Q > 1; Q >>= 1)
{
P = Q - 1;
for (i = 0; i < 2; ++i)
{
const bool condition = (x[i] & Q);
if (condition)
{
x[0] ^= P; // invert
}
else
{
t = (x[0] ^ x[i]) & P;
x[0] ^= t;
x[i] ^= t;
}
} // exchange
}
// Gray encode
for (i = 1; i < 2; ++i)
{
x[i] ^= x[i - 1];
}
t = 0;
for (Q = M; Q > 1; Q >>= 1)
{
const bool condition = (x[2 - 1] & Q);
if (condition)
{
t ^= Q - 1;
}
} // check if this for loop is wrong
for (i = 0; i < 2; ++i)
{
x[i] ^= t;
}
}
} // anonymous ns
std::uint64_t hilbertCode(const Coordinate coordinate)
{
std::uint32_t location[2];
location[0] = static_cast<std::int32_t>(coordinate.lon) +
static_cast<std::int32_t>(180 * COORDINATE_PRECISION);
location[1] = static_cast<std::int32_t>(coordinate.lat) +
static_cast<std::int32_t>(90 * COORDINATE_PRECISION);
transposeCoordinate(location);
return bitInterleaving(location[0], location[1]);
}
}
}

View File

@ -0,0 +1,85 @@
#include "util/hilbert_value.hpp"
#include <boost/test/test_case_template.hpp>
#include <boost/test/unit_test.hpp>
BOOST_AUTO_TEST_SUITE(hilbert_values_test)
using namespace osrm::util;
BOOST_AUTO_TEST_CASE(hilbert_linearization_2bit_test)
{
const std::uint8_t expected[4][4] =
// 0 1 2 3 x // y
{{5, 6, 9, 10}, // 3 ┌┐┌┐
{4, 7, 8, 11}, // 2 │└┘│
{3, 2, 13, 12}, // 1 └┐┌┘
{0, 1, 14, 15}}; // 0 ─┘└─
auto bit2_8 = HilbertToLinear<2, std::uint8_t, std::uint16_t>;
auto bit2_16 = HilbertToLinear<2, std::uint16_t, std::uint32_t>;
auto bit2_32 = HilbertToLinear<2, std::uint32_t, std::uint64_t>;
auto bit32_32 = HilbertToLinear<32, std::uint32_t, std::uint64_t>;
for (auto x = 0; x < 4; ++x)
for (auto y = 0; y < 4; ++y)
{
BOOST_CHECK_EQUAL(bit2_8(x << 6, y << 6), expected[3 - y][x]);
BOOST_CHECK_EQUAL(bit2_16(x << 14, y << 14), expected[3 - y][x]);
BOOST_CHECK_EQUAL(bit2_32(x << 30, y << 30), expected[3 - y][x]);
BOOST_CHECK_EQUAL(bit32_32(x << 30, y << 30) >> 60, expected[3 - y][x]);
}
}
BOOST_AUTO_TEST_CASE(hilbert_linearization_3bit_test)
{
const std::uint8_t expected[8][8] =
// 0 1 2 3 4 5 6 7 x // y
{{21, 22, 25, 26, 37, 38, 41, 42}, // 7 ┌┐┌┐┌┐┌┐
{20, 23, 24, 27, 36, 39, 40, 43}, // 6 │└┘││└┘│
{19, 18, 29, 28, 35, 34, 45, 44}, // 5 └┐┌┘└┐┌┘
{16, 17, 30, 31, 32, 33, 46, 47}, // 4 ┌┘└──┘└┐
{15, 12, 11, 10, 53, 52, 51, 48}, // 3 │┌─┐┌─┐│
{14, 13, 8, 9, 54, 55, 50, 49}, // 2 └┘ ││ └┘
{1, 2, 7, 6, 57, 56, 61, 62}, // 1 ┌┐ ││ ┌┐
{0, 3, 4, 5, 58, 59, 60, 63}}; // 0 │└─┘└─┘│
auto bit3_8 = HilbertToLinear<3, std::uint8_t, std::uint16_t>;
auto bit3_16 = HilbertToLinear<3, std::uint16_t, std::uint32_t>;
auto bit3_32 = HilbertToLinear<3, std::uint32_t, std::uint64_t>;
auto bit32_32 = HilbertToLinear<32, std::uint32_t, std::uint64_t>;
for (auto x = 0; x < 8; ++x)
for (auto y = 0; y < 8; ++y)
{
BOOST_CHECK_EQUAL(bit3_8(x << 5, y << 5), expected[7 - y][x]);
BOOST_CHECK_EQUAL(bit3_16(x << 13, y << 13), expected[7 - y][x]);
BOOST_CHECK_EQUAL(bit3_32(x << 29, y << 29), expected[7 - y][x]);
BOOST_CHECK_EQUAL(bit32_32(x << 29, y << 29) >> 58, expected[7 - y][x]);
}
}
BOOST_AUTO_TEST_CASE(hilbert_linearization_32bit_test)
{
auto bit32 = HilbertToLinear<32, std::uint32_t, std::uint64_t>;
BOOST_CHECK_EQUAL(bit32(0x00000000, 0x00000000), 0x0000000000000000);
BOOST_CHECK_EQUAL(bit32(0x7fffffff, 0x00000000), 0x1555555555555555);
BOOST_CHECK_EQUAL(bit32(0x80000000, 0x00000000), 0xeaaaaaaaaaaaaaaa);
BOOST_CHECK_EQUAL(bit32(0xffffffff, 0x00000000), 0xffffffffffffffff);
BOOST_CHECK_EQUAL(bit32(0x00000000, 0x7fffffff), 0x3fffffffffffffff);
BOOST_CHECK_EQUAL(bit32(0x7fffffff, 0x7fffffff), 0x2aaaaaaaaaaaaaaa);
BOOST_CHECK_EQUAL(bit32(0x80000000, 0x7fffffff), 0xd555555555555555);
BOOST_CHECK_EQUAL(bit32(0xffffffff, 0x7fffffff), 0xc000000000000000);
BOOST_CHECK_EQUAL(bit32(0x00000000, 0x80000000), 0x4000000000000000);
BOOST_CHECK_EQUAL(bit32(0x7fffffff, 0x80000000), 0x7fffffffffffffff);
BOOST_CHECK_EQUAL(bit32(0x80000000, 0x80000000), 0x8000000000000000);
BOOST_CHECK_EQUAL(bit32(0xffffffff, 0x80000000), 0xbfffffffffffffff);
BOOST_CHECK_EQUAL(bit32(0x00000000, 0xffffffff), 0x5555555555555555);
BOOST_CHECK_EQUAL(bit32(0x7fffffff, 0xffffffff), 0x6aaaaaaaaaaaaaaa);
BOOST_CHECK_EQUAL(bit32(0x80000000, 0xffffffff), 0x9555555555555555);
BOOST_CHECK_EQUAL(bit32(0xffffffff, 0xffffffff), 0xaaaaaaaaaaaaaaaa);
}
BOOST_AUTO_TEST_SUITE_END()