diff --git a/include/util/coordinate_calculation.hpp b/include/util/coordinate_calculation.hpp index 2860916c6..e8d8c82a1 100644 --- a/include/util/coordinate_calculation.hpp +++ b/include/util/coordinate_calculation.hpp @@ -80,6 +80,8 @@ const constexpr double TILE_SIZE = 256.0; // Converts projected mercator degrees to PX const constexpr double DEGREE_TO_PX = detail::MAXEXTENT / 180.0; +double degreeToPixel(FloatLatitude lat, unsigned zoom); +double degreeToPixel(FloatLongitude lon, unsigned zoom); FloatLatitude yToLat(const double value); double latToY(const FloatLatitude latitude); void xyzToMercator(const int x, const int y, const int z, double &minx, double &miny, double &maxx, double &maxy); diff --git a/include/util/tiles.hpp b/include/util/tiles.hpp deleted file mode 100644 index 425993b39..000000000 --- a/include/util/tiles.hpp +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef UTIL_TILES_HPP -#define UTIL_TILES_HPP - -#include "util/coordinate.hpp" - -#include - -#include -#include - -// This is a port of the tilebelt algorithm https://github.com/mapbox/tilebelt -namespace osrm -{ -namespace util -{ -namespace tiles -{ -struct Tile -{ - unsigned x; - unsigned y; - unsigned z; -}; - -namespace detail -{ -// optimized for 32bit integers -static constexpr unsigned MAX_ZOOM = 32; - -// Returns 1-indexed 1..32 of MSB if value > 0 or 0 if value == 0 -inline unsigned getMSBPosition(std::uint32_t value) -{ - if (value == 0) - return 0; - std::uint8_t pos = 1; - while (value >>= 1) - pos++; - return pos; -} - -inline unsigned getBBMaxZoom(const Tile top_left, const Tile bottom_left) -{ - auto x_xor = top_left.x ^ bottom_left.x; - auto y_xor = top_left.y ^ bottom_left.y; - auto lon_msb = detail::getMSBPosition(x_xor); - auto lat_msb = detail::getMSBPosition(y_xor); - return MAX_ZOOM - std::max(lon_msb, lat_msb); -} -} - -inline Tile pointToTile(const FloatLongitude lon, const FloatLatitude lat) -{ - auto sin_lat = std::sin(static_cast(lat) * M_PI / 180.); - auto p2z = std::pow(2, detail::MAX_ZOOM); - unsigned x = p2z * (static_cast(lon) / 360. + 0.5); - unsigned y = p2z * (0.5 - 0.25 * std::log((1 + sin_lat) / (1 - sin_lat)) / M_PI); - - return Tile{x, y, detail::MAX_ZOOM}; -} - -inline Tile getBBMaxZoomTile(const FloatLongitude min_lon, - const FloatLatitude min_lat, - const FloatLongitude max_lon, - const FloatLatitude max_lat) -{ - const auto top_left = pointToTile(min_lon, min_lat); - const auto bottom_left = pointToTile(max_lon, max_lat); - BOOST_ASSERT(top_left.z == detail::MAX_ZOOM); - BOOST_ASSERT(bottom_left.z == detail::MAX_ZOOM); - - const auto max_zoom = detail::getBBMaxZoom(top_left, bottom_left); - - if (max_zoom == 0) - { - return Tile{0, 0, 0}; - } - - auto x = top_left.x >> (detail::MAX_ZOOM - max_zoom); - auto y = top_left.y >> (detail::MAX_ZOOM - max_zoom); - - return Tile{x, y, max_zoom}; -} -} -} -} - -#endif diff --git a/include/util/viewport.hpp b/include/util/viewport.hpp new file mode 100644 index 000000000..d16ba1462 --- /dev/null +++ b/include/util/viewport.hpp @@ -0,0 +1,48 @@ +#ifndef UTIL_VIEWPORT_HPP +#define UTIL_VIEWPORT_HPP + +#include "util/coordinate.hpp" +#include "util/coordinate_calculation.hpp" + +#include + +#include +#include + +// Port of https://github.com/mapbox/geo-viewport + +namespace osrm +{ +namespace util +{ +namespace viewport +{ + +namespace detail +{ +static constexpr unsigned MAX_ZOOM = 18; +static constexpr unsigned MIN_ZOOM = 1; +// this is an upper bound to current display sizes +static constexpr double VIEWPORT_WIDTH = 8 * coordinate_calculation::mercator::TILE_SIZE; +static constexpr double VIEWPORT_HEIGHT = 5 * coordinate_calculation::mercator::TILE_SIZE; +static double INV_LOG_2 = 1. / std::log(2); +} + +unsigned getFittedZoom(util::Coordinate south_west, util::Coordinate north_east) +{ + const auto min_x = coordinate_calculation::mercator::degreeToPixel(toFloating(south_west.lon), detail::MAX_ZOOM); + const auto max_y = coordinate_calculation::mercator::degreeToPixel(toFloating(south_west.lat), detail::MAX_ZOOM); + const auto max_x = coordinate_calculation::mercator::degreeToPixel(toFloating(north_east.lon), detail::MAX_ZOOM); + const auto min_y = coordinate_calculation::mercator::degreeToPixel(toFloating(north_east.lat), detail::MAX_ZOOM); + const double width_ratio = (max_x - min_x) / detail::VIEWPORT_WIDTH; + const double height_ratio = (max_y - min_y) / detail::VIEWPORT_HEIGHT; + const auto zoom = detail::MAX_ZOOM - std::max(std::log(width_ratio), std::log(height_ratio)) * detail::INV_LOG_2; + + return std::max(detail::MIN_ZOOM, zoom); +} + +} +} +} + +#endif diff --git a/src/engine/guidance/assemble_overview.cpp b/src/engine/guidance/assemble_overview.cpp index 6789c3305..aa25411fc 100644 --- a/src/engine/guidance/assemble_overview.cpp +++ b/src/engine/guidance/assemble_overview.cpp @@ -3,7 +3,7 @@ #include "engine/guidance/leg_geometry.hpp" #include "engine/douglas_peucker.hpp" -#include "util/tiles.hpp" +#include "util/viewport.hpp" #include #include @@ -23,25 +23,22 @@ namespace unsigned calculateOverviewZoomLevel(const std::vector &leg_geometries) { - util::FixedLongitude min_lon{std::numeric_limits::max()}; - util::FixedLongitude max_lon{std::numeric_limits::min()}; - util::FixedLatitude min_lat{std::numeric_limits::max()}; - util::FixedLatitude max_lat{std::numeric_limits::min()}; + util::Coordinate south_west{util::FixedLongitude{std::numeric_limits::max()}, util::FixedLatitude{std::numeric_limits::max()}}; + util::Coordinate north_east{util::FixedLongitude{std::numeric_limits::min()}, util::FixedLatitude{std::numeric_limits::min()}}; for (const auto &leg_geometry : leg_geometries) { for (const auto coord : leg_geometry.locations) { - min_lon = std::min(min_lon, coord.lon); - max_lon = std::max(max_lon, coord.lon); - min_lat = std::min(min_lat, coord.lat); - max_lat = std::max(max_lat, coord.lat); + south_west.lon = std::min(south_west.lon, coord.lon); + south_west.lat = std::min(south_west.lat, coord.lat); + + north_east.lon = std::max(north_east.lon, coord.lon); + north_east.lat = std::max(north_east.lat, coord.lat); } } - return util::tiles::getBBMaxZoomTile(toFloating(min_lon), toFloating(min_lat), - toFloating(max_lon), toFloating(max_lat)) - .z; + return util::viewport::getFittedZoom(south_west, north_east); } std::vector simplifyGeometry(const std::vector &leg_geometries, diff --git a/src/util/coordinate.cpp b/src/util/coordinate.cpp index a937e1e9a..0b7fed49d 100644 --- a/src/util/coordinate.cpp +++ b/src/util/coordinate.cpp @@ -29,20 +29,6 @@ Coordinate::Coordinate(const FloatLongitude lon_, const FloatLatitude lat_) Coordinate::Coordinate(const FixedLongitude lon_, const FixedLatitude lat_) : lon(lon_), lat(lat_) { -#ifndef NDEBUG - if (0 != (std::abs(static_cast(lon)) >> 30)) - { - std::bitset<32> x_coordinate_vector(static_cast(lon)); - SimpleLogger().Write(logDEBUG) << "broken lon: " << lon - << ", bits: " << x_coordinate_vector; - } - if (0 != (std::abs(static_cast(lat)) >> 30)) - { - std::bitset<32> y_coordinate_vector(static_cast(lat)); - SimpleLogger().Write(logDEBUG) << "broken lat: " << lat - << ", bits: " << y_coordinate_vector; - } -#endif } bool Coordinate::IsValid() const diff --git a/src/util/coordinate_calculation.cpp b/src/util/coordinate_calculation.cpp index 14de32e26..1f6de1003 100644 --- a/src/util/coordinate_calculation.cpp +++ b/src/util/coordinate_calculation.cpp @@ -301,9 +301,26 @@ inline void pixelToDegree(const double shift, double &x, double &y) x = (x - b) / shift * 360.0; // FIXME needs to be simplified const double g = (y - b) / -(shift / (2 * M_PI)) / DEGREE_TO_RAD; + static_assert(DEGREE_TO_RAD / (2 * M_PI) - 1/360. < 0.0001, ""); y = static_cast(util::coordinate_calculation::mercator::yToLat(g)); } +double degreeToPixel(FloatLongitude lon, unsigned zoom) +{ + const double shift = (1u << zoom) * TILE_SIZE; + const double b = shift / 2.0; + const double x = b * (1 + static_cast(lon) / 180.0); + return x; +} + +double degreeToPixel(FloatLatitude lat, unsigned zoom) +{ + const double shift = (1u << zoom) * TILE_SIZE; + const double b = shift / 2.0; + const double y = b * (1. - latToY(lat) / 180.); + return y; +} + // Converts a WMS tile coordinate (z,x,y) into a wsg84 bounding box void xyzToWSG84(const int x, const int y, const int z, double &minx, double &miny, double &maxx, double &maxy) { diff --git a/unit_tests/util/tiles.cpp b/unit_tests/util/tiles.cpp deleted file mode 100644 index 4cd41a0be..000000000 --- a/unit_tests/util/tiles.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include "util/tiles.hpp" - -using namespace osrm::util; - -#include -#include -#include - -#include - -BOOST_AUTO_TEST_SUITE(tiles_test) - -using namespace osrm; -using namespace osrm::util; - -BOOST_AUTO_TEST_CASE(point_to_tile_test) -{ - tiles::Tile tile_1_reference{2306375680, 1409941503, 32}; - tiles::Tile tile_2_reference{2308259840, 1407668224, 32}; - tiles::Tile tile_3_reference{616562688, 2805989376, 32}; - tiles::Tile tile_4_reference{1417674752, 2084569088, 32}; - tiles::Tile tile_5_reference{616562688, 2805989376, 32}; - tiles::Tile tile_6_reference{712654285, 2671662374, 32}; - - auto tile_1 = - tiles::pointToTile(FloatLongitude(13.31817626953125), FloatLatitude(52.449314140869696)); - auto tile_2 = - tiles::pointToTile(FloatLongitude(13.476104736328125), FloatLatitude(52.56529070208021)); - auto tile_3 = - tiles::pointToTile(FloatLongitude(-128.32031249999997), FloatLatitude(-48.224672649565186)); - auto tile_4 = - tiles::pointToTile(FloatLongitude(-61.17187499999999), FloatLatitude(5.266007882805498)); - auto tile_5 = - tiles::pointToTile(FloatLongitude(-128.32031249999997), FloatLatitude(-48.224672649565186)); - auto tile_6 = - tiles::pointToTile(FloatLongitude(-120.266007882805532), FloatLatitude(-40.17187499999999)); - BOOST_CHECK_EQUAL(tile_1.x, tile_1_reference.x); - BOOST_CHECK_EQUAL(tile_1.y, tile_1_reference.y); - BOOST_CHECK_EQUAL(tile_1.z, tile_1_reference.z); - BOOST_CHECK_EQUAL(tile_2.x, tile_2_reference.x); - BOOST_CHECK_EQUAL(tile_2.y, tile_2_reference.y); - BOOST_CHECK_EQUAL(tile_2.z, tile_2_reference.z); - BOOST_CHECK_EQUAL(tile_3.x, tile_3_reference.x); - BOOST_CHECK_EQUAL(tile_3.y, tile_3_reference.y); - BOOST_CHECK_EQUAL(tile_3.z, tile_3_reference.z); - BOOST_CHECK_EQUAL(tile_4.x, tile_4_reference.x); - BOOST_CHECK_EQUAL(tile_4.y, tile_4_reference.y); - BOOST_CHECK_EQUAL(tile_4.z, tile_4_reference.z); - BOOST_CHECK_EQUAL(tile_5.x, tile_5_reference.x); - BOOST_CHECK_EQUAL(tile_5.y, tile_5_reference.y); - BOOST_CHECK_EQUAL(tile_5.z, tile_5_reference.z); - BOOST_CHECK_EQUAL(tile_6.x, tile_6_reference.x); - BOOST_CHECK_EQUAL(tile_6.y, tile_6_reference.y); - BOOST_CHECK_EQUAL(tile_6.z, tile_6_reference.z); -} - -// Verify that the bearing-bounds checking function behaves as expected -BOOST_AUTO_TEST_CASE(bounding_box_to_tile_test) -{ - tiles::Tile tile_1_reference{17, 10, 5}; - tiles::Tile tile_2_reference{0, 0, 0}; - tiles::Tile tile_3_reference{0, 2, 2}; - auto tile_1 = tiles::getBBMaxZoomTile( - FloatLongitude(13.31817626953125), FloatLatitude(52.449314140869696), - FloatLongitude(13.476104736328125), FloatLatitude(52.56529070208021)); - auto tile_2 = tiles::getBBMaxZoomTile( - FloatLongitude(-128.32031249999997), FloatLatitude(-48.224672649565186), - FloatLongitude(-61.17187499999999), FloatLatitude(5.266007882805498)); - auto tile_3 = tiles::getBBMaxZoomTile( - FloatLongitude(-128.32031249999997), FloatLatitude(-48.224672649565186), - FloatLongitude(-120.2660078828055), FloatLatitude(-40.17187499999999)); - BOOST_CHECK_EQUAL(tile_1.x, tile_1_reference.x); - BOOST_CHECK_EQUAL(tile_1.y, tile_1_reference.y); - BOOST_CHECK_EQUAL(tile_1.z, tile_1_reference.z); - BOOST_CHECK_EQUAL(tile_2.x, tile_2_reference.x); - BOOST_CHECK_EQUAL(tile_2.y, tile_2_reference.y); - BOOST_CHECK_EQUAL(tile_2.z, tile_2_reference.z); - BOOST_CHECK_EQUAL(tile_3.x, tile_3_reference.x); - BOOST_CHECK_EQUAL(tile_3.y, tile_3_reference.y); - BOOST_CHECK_EQUAL(tile_3.z, tile_3_reference.z); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/unit_tests/util/viewport.cpp b/unit_tests/util/viewport.cpp new file mode 100644 index 000000000..f5f4f9a24 --- /dev/null +++ b/unit_tests/util/viewport.cpp @@ -0,0 +1,25 @@ +#include "util/viewport.hpp" + +using namespace osrm::util; + +#include +#include +#include + +#include + +BOOST_AUTO_TEST_SUITE(viewport_test) + +using namespace osrm; +using namespace osrm::util; + +BOOST_AUTO_TEST_CASE(zoom_level_test) +{ + BOOST_CHECK_EQUAL( + viewport::getFittedZoom( + Coordinate(FloatLongitude{5.668343999999995}, FloatLatitude{45.111511000000014}), + Coordinate(FloatLongitude{5.852471999999996}, FloatLatitude{45.26800200000002})), + 12); +} + +BOOST_AUTO_TEST_SUITE_END()