Implements a vector tileserver so you can see what's going on inside
OSRM.
This commit is contained in:
parent
33403efc8e
commit
5dc7b79bb6
@ -76,6 +76,9 @@ template <class EdgeDataT> class BaseDataFacade
|
||||
|
||||
virtual extractor::TravelMode GetTravelModeForEdgeID(const unsigned id) const = 0;
|
||||
|
||||
virtual std::vector<RTreeLeaf> GetEdgesInBox(const util::FixedPointCoordinate & south_west,
|
||||
const util::FixedPointCoordinate & north_east) = 0;
|
||||
|
||||
virtual std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodesInRange(const util::FixedPointCoordinate input_coordinate,
|
||||
const float max_distance,
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "util/range_table.hpp"
|
||||
#include "util/graph_loader.hpp"
|
||||
#include "util/simple_logger.hpp"
|
||||
#include "util/rectangle.hpp"
|
||||
|
||||
#include "osrm/coordinate.hpp"
|
||||
|
||||
@ -357,6 +358,20 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
|
||||
return m_travel_mode_list.at(id);
|
||||
}
|
||||
|
||||
std::vector<RTreeLeaf> GetEdgesInBox(const util::FixedPointCoordinate & south_west,
|
||||
const util::FixedPointCoordinate & north_east)
|
||||
override final
|
||||
{
|
||||
if (!m_static_rtree.get())
|
||||
{
|
||||
LoadRTree();
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
}
|
||||
util::RectangleInt2D bbox = {south_west.lon, north_east.lon,
|
||||
south_west.lat, north_east.lat};
|
||||
return m_geospatial_query->Search(bbox);
|
||||
}
|
||||
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodesInRange(const util::FixedPointCoordinate input_coordinate,
|
||||
const float max_distance,
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "util/static_rtree.hpp"
|
||||
#include "util/make_unique.hpp"
|
||||
#include "util/simple_logger.hpp"
|
||||
#include "util/rectangle.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
@ -407,6 +408,20 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
|
||||
return m_travel_mode_list.at(id);
|
||||
}
|
||||
|
||||
std::vector<RTreeLeaf> GetEdgesInBox(const util::FixedPointCoordinate & south_west,
|
||||
const util::FixedPointCoordinate & north_east)
|
||||
override final
|
||||
{
|
||||
if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
|
||||
{
|
||||
LoadRTree();
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
}
|
||||
util::RectangleInt2D bbox = {south_west.lon, north_east.lon,
|
||||
south_west.lat, north_east.lat};
|
||||
return m_geospatial_query->Search(bbox);
|
||||
}
|
||||
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodesInRange(const util::FixedPointCoordinate input_coordinate,
|
||||
const float max_distance,
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "util/typedefs.hpp"
|
||||
#include "engine/phantom_node.hpp"
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/rectangle.hpp"
|
||||
|
||||
#include "osrm/coordinate.hpp"
|
||||
|
||||
@ -32,6 +33,14 @@ template <typename RTreeT> class GeospatialQuery
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<EdgeData>
|
||||
Search(const util::RectangleInt2D & bbox)
|
||||
{
|
||||
return rtree.SearchInBox(bbox);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Returns nearest PhantomNodes in the given bearing range within max_distance.
|
||||
// Does not filter by small/big component!
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
|
349
include/engine/plugins/tile.hpp
Normal file
349
include/engine/plugins/tile.hpp
Normal file
@ -0,0 +1,349 @@
|
||||
#ifndef TILEPLUGIN_HPP
|
||||
#define TILEPLUGIN_HPP
|
||||
|
||||
#include "engine/plugins/plugin_base.hpp"
|
||||
#include "osrm/json_container.hpp"
|
||||
#include "util/tile_bbox.hpp"
|
||||
|
||||
#include <protozero/varint.hpp>
|
||||
#include <protozero/pbf_writer.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <cmath>
|
||||
|
||||
/*
|
||||
* This plugin generates Mapbox Vector tiles that show the internal
|
||||
* routing geometry and speed values on all road segments.
|
||||
* You can use this along with a vector-tile viewer, like Mapbox GL,
|
||||
* to display maps that show the exact road network that
|
||||
* OSRM is routing. This is very useful for debugging routing
|
||||
* errors
|
||||
*/
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace plugins
|
||||
{
|
||||
|
||||
// from mapnik/well_known_srs.hpp
|
||||
static const double EARTH_RADIUS = 6378137.0;
|
||||
static const double EARTH_DIAMETER = EARTH_RADIUS * 2.0;
|
||||
static const double EARTH_CIRCUMFERENCE = EARTH_DIAMETER * M_PI;
|
||||
static const double MAXEXTENT = EARTH_CIRCUMFERENCE / 2.0;
|
||||
static const double M_PI_by2 = M_PI / 2;
|
||||
static const double D2R = M_PI / 180;
|
||||
static const double R2D = 180 / M_PI;
|
||||
static const double M_PIby360 = M_PI / 360;
|
||||
static const double MAXEXTENTby180 = MAXEXTENT / 180;
|
||||
static const double MAX_LATITUDE = R2D * (2 * std::atan(std::exp(180 * D2R)) - M_PI_by2);
|
||||
|
||||
|
||||
// from mapnik-vector-tile
|
||||
namespace detail_pbf {
|
||||
|
||||
inline unsigned encode_length(unsigned len)
|
||||
{
|
||||
return (len << 3u) | 2u;
|
||||
}
|
||||
}
|
||||
|
||||
inline void lonlat2merc(double & x, double & y)
|
||||
{
|
||||
if (x > 180) x = 180;
|
||||
else if (x < -180) x = -180;
|
||||
if (y > MAX_LATITUDE) y = MAX_LATITUDE;
|
||||
else if (y < -MAX_LATITUDE) y = -MAX_LATITUDE;
|
||||
x = x * MAXEXTENTby180;
|
||||
y = std::log(std::tan((90 + y) * M_PIby360)) * R2D;
|
||||
y = y * MAXEXTENTby180;
|
||||
}
|
||||
|
||||
const static int tile_size_ = 256;
|
||||
|
||||
void from_pixels(double shift, double & x, double & y)
|
||||
{
|
||||
double b = shift/2.0;
|
||||
x = (x - b)/(shift/360.0);
|
||||
double g = (y - b)/-(shift/(2 * M_PI));
|
||||
y = R2D * (2.0 * std::atan(std::exp(g)) - M_PI_by2);
|
||||
}
|
||||
|
||||
void xyz(int x,
|
||||
int y,
|
||||
int z,
|
||||
double & minx,
|
||||
double & miny,
|
||||
double & maxx,
|
||||
double & maxy)
|
||||
{
|
||||
minx = x * tile_size_;
|
||||
miny = (y + 1.0) * tile_size_;
|
||||
maxx = (x + 1.0) * tile_size_;
|
||||
maxy = y * tile_size_;
|
||||
double shift = std::pow(2.0,z) * tile_size_;
|
||||
from_pixels(shift,minx,miny);
|
||||
from_pixels(shift,maxx,maxy);
|
||||
lonlat2merc(minx,miny);
|
||||
lonlat2merc(maxx,maxy);
|
||||
}
|
||||
|
||||
void xyz2wsg84(int x,
|
||||
int y,
|
||||
int z,
|
||||
double & minx,
|
||||
double & miny,
|
||||
double & maxx,
|
||||
double & maxy)
|
||||
{
|
||||
minx = x * tile_size_;
|
||||
miny = (y + 1.0) * tile_size_;
|
||||
maxx = (x + 1.0) * tile_size_;
|
||||
maxy = y * tile_size_;
|
||||
double shift = std::pow(2.0,z) * tile_size_;
|
||||
from_pixels(shift,minx,miny);
|
||||
from_pixels(shift,maxx,maxy);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// emulates mapbox::box2d
|
||||
class bbox {
|
||||
public:
|
||||
double minx;
|
||||
double miny;
|
||||
double maxx;
|
||||
double maxy;
|
||||
bbox(double _minx,double _miny,double _maxx,double _maxy) :
|
||||
minx(_minx),
|
||||
miny(_miny),
|
||||
maxx(_maxx),
|
||||
maxy(_maxy) { }
|
||||
|
||||
double width() const {
|
||||
return maxx - minx;
|
||||
}
|
||||
|
||||
double height() const {
|
||||
return maxy - miny;
|
||||
}
|
||||
};
|
||||
|
||||
// should start using core geometry class across mapnik, osrm, mapbox-gl-native
|
||||
class point_type_d {
|
||||
public:
|
||||
double x;
|
||||
double y;
|
||||
point_type_d(double _x, double _y) :
|
||||
x(_x),
|
||||
y(_y) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
class point_type_i {
|
||||
public:
|
||||
std::int64_t x;
|
||||
std::int64_t y;
|
||||
point_type_i(std::int64_t _x, std::int64_t _y) :
|
||||
x(_x),
|
||||
y(_y) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
using line_type = std::vector<point_type_i>;
|
||||
using line_typed = std::vector<point_type_d>;
|
||||
|
||||
// from mapnik-vector-tile
|
||||
inline bool encode_linestring(line_type line,
|
||||
protozero::packed_field_uint32 & geometry,
|
||||
int32_t & start_x,
|
||||
int32_t & start_y) {
|
||||
std::size_t line_size = line.size();
|
||||
//line_size -= detail_pbf::repeated_point_count(line);
|
||||
if (line_size < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned line_to_length = static_cast<unsigned>(line_size) - 1;
|
||||
|
||||
auto pt = line.begin();
|
||||
geometry.add_element(9); // move_to | (1 << 3)
|
||||
geometry.add_element(protozero::encode_zigzag32(pt->x - start_x));
|
||||
geometry.add_element(protozero::encode_zigzag32(pt->y - start_y));
|
||||
start_x = pt->x;
|
||||
start_y = pt->y;
|
||||
geometry.add_element(detail_pbf::encode_length(line_to_length));
|
||||
for (++pt; pt != line.end(); ++pt)
|
||||
{
|
||||
int32_t dx = pt->x - start_x;
|
||||
int32_t dy = pt->y - start_y;
|
||||
/*if (dx == 0 && dy == 0)
|
||||
{
|
||||
continue;
|
||||
}*/
|
||||
geometry.add_element(protozero::encode_zigzag32(dx));
|
||||
geometry.add_element(protozero::encode_zigzag32(dy));
|
||||
start_x = pt->x;
|
||||
start_y = pt->y;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class DataFacadeT> class TilePlugin final : public BasePlugin
|
||||
{
|
||||
public:
|
||||
explicit TilePlugin(DataFacadeT *facade) : facade(facade), descriptor_string("tile") {}
|
||||
|
||||
const std::string GetDescriptor() const override final { return descriptor_string; }
|
||||
|
||||
Status HandleRequest(const RouteParameters &route_parameters,
|
||||
util::json::Object &json_result) override final
|
||||
{
|
||||
|
||||
const unsigned tile_extent = 4096;
|
||||
double min_lon, min_lat, max_lon, max_lat;
|
||||
|
||||
xyz2wsg84(route_parameters.x, route_parameters.y, route_parameters.z, min_lon, min_lat, max_lon, max_lat);
|
||||
|
||||
FixedPointCoordinate southwest = { static_cast<int32_t>(min_lat * COORDINATE_PRECISION), static_cast<int32_t>(min_lon * COORDINATE_PRECISION) };
|
||||
FixedPointCoordinate northeast = { static_cast<int32_t>(max_lat * COORDINATE_PRECISION), static_cast<int32_t>(max_lon * COORDINATE_PRECISION) };
|
||||
|
||||
auto edges = facade->GetEdgesInBox(southwest, northeast);
|
||||
|
||||
xyz(route_parameters.x, route_parameters.y, route_parameters.z, min_lon, min_lat, max_lon, max_lat);
|
||||
|
||||
bbox tile_bbox(min_lon, min_lat, max_lon, max_lat);
|
||||
|
||||
std::string buffer;
|
||||
protozero::pbf_writer tile_writer(buffer);
|
||||
{
|
||||
protozero::pbf_writer layer_writer(tile_writer,3);
|
||||
// TODO: don't write a layer if there are no features
|
||||
layer_writer.add_uint32(15,2); // version
|
||||
layer_writer.add_string(1,"speeds"); // name
|
||||
layer_writer.add_uint32(5,4096); // extent
|
||||
|
||||
std::vector<double> speeds;
|
||||
std::vector<bool> is_smalls;
|
||||
{
|
||||
unsigned id = 1;
|
||||
for (const auto & edge : edges)
|
||||
{
|
||||
const auto a = facade->GetCoordinateOfNode(edge.u);
|
||||
const auto b = facade->GetCoordinateOfNode(edge.v);
|
||||
double length = osrm::util::coordinate_calculation::haversineDistance( a.lon, a.lat, b.lon, b.lat );
|
||||
if (edge.forward_weight != 0 && edge.forward_edge_based_node_id != SPECIAL_NODEID) {
|
||||
std::int32_t start_x = 0;
|
||||
std::int32_t start_y = 0;
|
||||
|
||||
line_typed geo_line;
|
||||
geo_line.emplace_back(a.lon / COORDINATE_PRECISION, a.lat / COORDINATE_PRECISION);
|
||||
geo_line.emplace_back(b.lon / COORDINATE_PRECISION, b.lat / COORDINATE_PRECISION);
|
||||
|
||||
double speed = round(length / edge.forward_weight * 10 ) * 3.6;
|
||||
|
||||
speeds.push_back(speed);
|
||||
is_smalls.push_back(edge.component.is_tiny);
|
||||
|
||||
line_type tile_line;
|
||||
for (auto const & pt : geo_line) {
|
||||
double px_merc = pt.x;
|
||||
double py_merc = pt.y;
|
||||
lonlat2merc(px_merc,py_merc);
|
||||
// convert to integer tile coordinat
|
||||
std::int64_t px = std::round((px_merc - tile_bbox.minx) * tile_extent/16 / tile_bbox.width());
|
||||
std::int64_t py = std::round((tile_bbox.maxy - py_merc) * tile_extent/16 / tile_bbox.height());
|
||||
tile_line.emplace_back(px*tile_extent/256,py*tile_extent/256);
|
||||
}
|
||||
|
||||
protozero::pbf_writer feature_writer(layer_writer,2);
|
||||
feature_writer.add_enum(3,2); // geometry type
|
||||
feature_writer.add_uint64(1,id++); // id
|
||||
{
|
||||
protozero::packed_field_uint32 field(feature_writer, 2);
|
||||
field.add_element(0); // "speed" tag key offset
|
||||
field.add_element((speeds.size()-1)*2); // "speed" tag value offset
|
||||
field.add_element(1); // "is_small" tag key offset
|
||||
field.add_element((is_smalls.size()-1)*2+1); // "is_small" tag value offset
|
||||
}
|
||||
{
|
||||
protozero::packed_field_uint32 geometry(feature_writer,4);
|
||||
encode_linestring(tile_line,geometry,start_x,start_y);
|
||||
}
|
||||
}
|
||||
if (edge.reverse_weight != 0 && edge.reverse_edge_based_node_id != SPECIAL_NODEID) {
|
||||
std::int32_t start_x = 0;
|
||||
std::int32_t start_y = 0;
|
||||
|
||||
line_typed geo_line;
|
||||
geo_line.emplace_back(b.lon / COORDINATE_PRECISION, b.lat / COORDINATE_PRECISION);
|
||||
geo_line.emplace_back(a.lon / COORDINATE_PRECISION, a.lat / COORDINATE_PRECISION);
|
||||
|
||||
double speed = round(length / edge.reverse_weight * 10 ) * 3.6;
|
||||
|
||||
speeds.push_back(speed);
|
||||
is_smalls.push_back(edge.component.is_tiny);
|
||||
|
||||
line_type tile_line;
|
||||
for (auto const & pt : geo_line) {
|
||||
double px_merc = pt.x;
|
||||
double py_merc = pt.y;
|
||||
lonlat2merc(px_merc,py_merc);
|
||||
// convert to integer tile coordinat
|
||||
std::int64_t px = std::round((px_merc - tile_bbox.minx) * tile_extent/16 / tile_bbox.width());
|
||||
std::int64_t py = std::round((tile_bbox.maxy - py_merc) * tile_extent/16 / tile_bbox.height());
|
||||
tile_line.emplace_back(px*tile_extent/256,py*tile_extent/256);
|
||||
}
|
||||
|
||||
protozero::pbf_writer feature_writer(layer_writer,2);
|
||||
feature_writer.add_enum(3,2); // geometry type
|
||||
feature_writer.add_uint64(1,id++); // id
|
||||
{
|
||||
protozero::packed_field_uint32 field(feature_writer, 2);
|
||||
field.add_element(0); // "speed" tag key offset
|
||||
field.add_element((speeds.size()-1)*2); // "speed" tag value offset
|
||||
field.add_element(1); // "is_small" tag key offset
|
||||
field.add_element((is_smalls.size()-1)*2+1); // "is_small" tag value offset
|
||||
}
|
||||
{
|
||||
protozero::packed_field_uint32 geometry(feature_writer,4);
|
||||
encode_linestring(tile_line,geometry,start_x,start_y);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
layer_writer.add_string(3,"speed");
|
||||
layer_writer.add_string(3,"is_small");
|
||||
|
||||
for (size_t i=0; i<speeds.size(); i++) {
|
||||
{
|
||||
protozero::pbf_writer values_writer(layer_writer,4);
|
||||
values_writer.add_double(3, speeds[i]);
|
||||
}
|
||||
{
|
||||
protozero::pbf_writer values_writer(layer_writer,4);
|
||||
values_writer.add_bool(7, is_smalls[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
json_result.values["pbf"] = buffer;
|
||||
|
||||
return Status::Ok;
|
||||
}
|
||||
|
||||
private:
|
||||
DataFacadeT *facade;
|
||||
std::string descriptor_string;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* TILEPLUGIN_HPP */
|
@ -92,6 +92,10 @@ struct RouteParameters
|
||||
|
||||
void SetCoordinatesFromGeometry(const std::string &geometry_string);
|
||||
|
||||
void SetX(const int &x);
|
||||
void SetZ(const int &z);
|
||||
void SetY(const int &y);
|
||||
|
||||
short zoom_level;
|
||||
bool print_instructions;
|
||||
bool alternate_route;
|
||||
@ -115,6 +119,9 @@ struct RouteParameters
|
||||
std::vector<FixedPointCoordinate> coordinates;
|
||||
std::vector<bool> is_destination;
|
||||
std::vector<bool> is_source;
|
||||
int z;
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ template <typename Iterator, class HandlerT> struct APIGrammar : qi::grammar<Ite
|
||||
query = ('?') >> +(zoom | output | jsonp | checksum | uturns | location_with_options |
|
||||
destination_with_options | source_with_options | cmp | language |
|
||||
instruction | geometry | alt_route | old_API | num_results |
|
||||
matching_beta | gps_precision | classify | locs);
|
||||
matching_beta | gps_precision | classify | locs | x | y | z);
|
||||
// all combinations of timestamp, uturn, hint and bearing without duplicates
|
||||
t_u = (u >> -timestamp) | (timestamp >> -u);
|
||||
t_h = (hint >> -timestamp) | (timestamp >> -hint);
|
||||
@ -110,6 +110,11 @@ template <typename Iterator, class HandlerT> struct APIGrammar : qi::grammar<Ite
|
||||
locs = (-qi::lit('&')) >> qi::lit("locs") >> '=' >>
|
||||
stringforPolyline[boost::bind(&HandlerT::SetCoordinatesFromGeometry, handler, ::_1)];
|
||||
|
||||
|
||||
z = (-qi::lit('&')) >> qi::lit("tz") >> '=' >> qi::int_[boost::bind<void>(&HandlerT::SetZ, handler, ::_1)];
|
||||
x = (-qi::lit('&')) >> qi::lit("tx") >> '=' >> qi::int_[boost::bind<void>(&HandlerT::SetX, handler, ::_1)];
|
||||
y = (-qi::lit('&')) >> qi::lit("ty") >> '=' >> qi::int_[boost::bind<void>(&HandlerT::SetY, handler, ::_1)];
|
||||
|
||||
string = +(qi::char_("a-zA-Z"));
|
||||
stringwithDot = +(qi::char_("a-zA-Z0-9_.-"));
|
||||
stringwithPercent = +(qi::char_("a-zA-Z0-9_.-") | qi::char_('[') | qi::char_(']') |
|
||||
@ -122,7 +127,7 @@ template <typename Iterator, class HandlerT> struct APIGrammar : qi::grammar<Ite
|
||||
qi::rule<Iterator, std::string()> service, zoom, output, string, jsonp, checksum, location,
|
||||
destination, source, hint, timestamp, bearing, stringwithDot, stringwithPercent, language,
|
||||
geometry, cmp, alt_route, u, uturns, old_API, num_results, matching_beta, gps_precision,
|
||||
classify, locs, instruction, stringforPolyline;
|
||||
classify, locs, instruction, stringforPolyline, x, y, z;
|
||||
|
||||
HandlerT *handler;
|
||||
};
|
||||
|
@ -15,13 +15,13 @@ class exception final : public std::exception
|
||||
public:
|
||||
explicit exception(const char *message) : message(message) {}
|
||||
explicit exception(std::string message) : message(std::move(message)) {}
|
||||
const char *what() const noexcept override { return message.c_str(); }
|
||||
|
||||
private:
|
||||
// This function exists to 'anchor' the class, and stop the compiler from
|
||||
// copying vtable and RTTI info into every object file that includes
|
||||
// this header. (Caught by -Wweak-vtables under Clang.)
|
||||
virtual void anchor() const;
|
||||
const char *what() const noexcept override { return message.c_str(); }
|
||||
const std::string message;
|
||||
};
|
||||
}
|
||||
|
@ -26,6 +26,10 @@ struct RectangleInt2D
|
||||
{
|
||||
}
|
||||
|
||||
RectangleInt2D(int32_t min_lon_, int32_t max_lon_, int32_t min_lat_, int32_t max_lat_) :
|
||||
min_lon(min_lon_), max_lon(max_lon_),
|
||||
min_lat(min_lat_), max_lat(max_lat_) {}
|
||||
|
||||
int32_t min_lon, max_lon;
|
||||
int32_t min_lat, max_lat;
|
||||
|
||||
@ -53,13 +57,10 @@ struct RectangleInt2D
|
||||
|
||||
bool Intersects(const RectangleInt2D &other) const
|
||||
{
|
||||
FixedPointCoordinate upper_left(other.max_lat, other.min_lon);
|
||||
FixedPointCoordinate upper_right(other.max_lat, other.max_lon);
|
||||
FixedPointCoordinate lower_right(other.min_lat, other.max_lon);
|
||||
FixedPointCoordinate lower_left(other.min_lat, other.min_lon);
|
||||
|
||||
return (Contains(upper_left) || Contains(upper_right) || Contains(lower_right) ||
|
||||
Contains(lower_left));
|
||||
// Standard box intersection test - check if boxes *don't* overlap,
|
||||
// and return the negative of that
|
||||
return ! (max_lon < other.min_lon || min_lon > other.max_lon
|
||||
|| max_lat < other.min_lat || min_lat > other.max_lat);
|
||||
}
|
||||
|
||||
double GetMinDist(const FixedPointCoordinate location) const
|
||||
|
@ -321,6 +321,62 @@ class StaticRTree
|
||||
leaves_stream.read((char *)&m_element_count, sizeof(uint64_t));
|
||||
}
|
||||
|
||||
/* Returns all features inside the bounding box */
|
||||
std::vector<EdgeDataT> SearchInBox(const Rectangle & search_rectangle)
|
||||
{
|
||||
|
||||
std::vector<EdgeDataT> results;
|
||||
|
||||
std::queue<TreeNode> traversal_queue;
|
||||
|
||||
traversal_queue.push(m_search_tree[0]);
|
||||
|
||||
while (!traversal_queue.empty())
|
||||
{
|
||||
auto const current_tree_node = traversal_queue.front();
|
||||
traversal_queue.pop();
|
||||
|
||||
if (current_tree_node.child_is_on_disk)
|
||||
{
|
||||
LeafNode current_leaf_node;
|
||||
LoadLeafFromDisk(current_tree_node.children[0], current_leaf_node);
|
||||
|
||||
for (const auto i : irange(0u, current_leaf_node.object_count))
|
||||
{
|
||||
const auto ¤t_edge = current_leaf_node.objects[i];
|
||||
|
||||
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)};
|
||||
|
||||
if (bbox.Intersects(search_rectangle))
|
||||
{
|
||||
results.push_back(current_edge);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If it's a tree node, look at all children and add them
|
||||
// to the search queue if their bounding boxes intersect
|
||||
for (uint32_t i = 0; i < current_tree_node.child_count; ++i)
|
||||
{
|
||||
const int32_t child_id = current_tree_node.children[i];
|
||||
const auto &child_tree_node = m_search_tree[child_id];
|
||||
const auto &child_rectangle = child_tree_node.minimum_bounding_rectangle;
|
||||
|
||||
if (child_rectangle.Intersects(search_rectangle))
|
||||
{
|
||||
traversal_queue.push(m_search_tree[child_id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
// Override filter and terminator for the desired behaviour.
|
||||
std::vector<EdgeDataT> Nearest(const FixedPointCoordinate input_coordinate,
|
||||
const std::size_t max_results)
|
||||
|
32
include/util/tile_bbox.hpp
Normal file
32
include/util/tile_bbox.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef TILE_BBOX
|
||||
#define TILE_BBOX
|
||||
|
||||
#include "util/rectangle.hpp"
|
||||
#include <cmath>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
|
||||
inline RectangleInt2D TileToBBOX(int z, int x, int y)
|
||||
{
|
||||
double minx = x / pow(2.0, z) * 360 - 180;
|
||||
double n = M_PI - 2.0 * M_PI * y / pow(2.0, z);
|
||||
double miny = 180.0 / M_PI * atan(0.5 * (exp(n) - exp(-n)));
|
||||
|
||||
double maxx = (x + 1) / pow(2.0, z) * 360 - 180;
|
||||
double mn = M_PI - 2.0 * M_PI * (y + 1) / pow(2.0, z);
|
||||
double maxy = 180.0 / M_PI * atan(0.5 * (exp(mn) - exp(-mn)));
|
||||
|
||||
return {
|
||||
static_cast<int32_t>(std::min(minx,maxx) * COORDINATE_PRECISION),
|
||||
static_cast<int32_t>(std::max(minx,maxx) * COORDINATE_PRECISION),
|
||||
static_cast<int32_t>(std::min(miny,maxy) * COORDINATE_PRECISION),
|
||||
static_cast<int32_t>(std::min(miny,maxy) * COORDINATE_PRECISION)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -9,6 +9,7 @@
|
||||
#include "engine/plugins/trip.hpp"
|
||||
#include "engine/plugins/viaroute.hpp"
|
||||
#include "engine/plugins/match.hpp"
|
||||
#include "engine/plugins/tile.hpp"
|
||||
|
||||
#include "engine/datafacade/datafacade_base.hpp"
|
||||
#include "engine/datafacade/internal_datafacade.hpp"
|
||||
@ -63,6 +64,7 @@ Engine::Engine(EngineConfig &config)
|
||||
config.max_locations_viaroute));
|
||||
RegisterPlugin(
|
||||
new plugins::RoundTripPlugin<DataFacade>(query_data_facade, config.max_locations_trip));
|
||||
RegisterPlugin(new plugins::TilePlugin<DataFacade>(query_data_facade));
|
||||
}
|
||||
|
||||
void Engine::RegisterPlugin(plugins::BasePlugin *raw_plugin_ptr)
|
||||
|
@ -146,5 +146,10 @@ void RouteParameters::SetCoordinatesFromGeometry(const std::string &geometry_str
|
||||
{
|
||||
coordinates = polylineDecode(geometry_string);
|
||||
}
|
||||
|
||||
void RouteParameters::SetX(const int &x_) { x = x_; }
|
||||
void RouteParameters::SetZ(const int &z_) { z = z_; }
|
||||
void RouteParameters::SetY(const int &y_) { y = y_; }
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,10 @@
|
||||
#include "util/json_container.hpp"
|
||||
#include "osrm/osrm.hpp"
|
||||
|
||||
#include <boost/iostreams/filtering_streambuf.hpp>
|
||||
#include <boost/iostreams/copy.hpp>
|
||||
#include <boost/iostreams/filter/gzip.hpp>
|
||||
|
||||
#include <ctime>
|
||||
|
||||
#include <algorithm>
|
||||
@ -113,10 +117,32 @@ void RequestHandler::handle_request(const http::request ¤t_request,
|
||||
current_reply.headers.emplace_back("Access-Control-Allow-Headers",
|
||||
"X-Requested-With, Content-Type");
|
||||
|
||||
// set headers
|
||||
current_reply.headers.emplace_back("Content-Length",
|
||||
std::to_string(current_reply.content.size()));
|
||||
if (route_parameters.jsonp_parameter.empty())
|
||||
if (route_parameters.service == "tile") {
|
||||
|
||||
/*
|
||||
std::istringstream is(json_result.values["pbf"].get<osrm::util::json::String>().value);
|
||||
boost::iostreams::filtering_streambuf<boost::iostreams::input> in;
|
||||
//in.push(boost::iostreams::gzip_compressor());
|
||||
in.push(boost::iostreams::zlib_compressor());
|
||||
in.push(is);
|
||||
|
||||
std::ostringstream os;
|
||||
|
||||
boost::iostreams::copy(in,os);
|
||||
|
||||
auto s = os.str();
|
||||
|
||||
std::copy(s.cbegin(),s.cend(), std::back_inserter(current_reply.content));
|
||||
*/
|
||||
std::copy(json_result.values["pbf"].get<osrm::util::json::String>().value.cbegin(),
|
||||
json_result.values["pbf"].get<osrm::util::json::String>().value.cend(),
|
||||
std::back_inserter(current_reply.content));
|
||||
|
||||
//current_reply.content.append(json_result.values["pbf"].get<osrm::util::json::String>().value
|
||||
current_reply.headers.emplace_back("Content-Type",
|
||||
"application/x-protobuf");
|
||||
}
|
||||
else if (route_parameters.jsonp_parameter.empty())
|
||||
{ // json file
|
||||
util::json::render(current_reply.content, json_result);
|
||||
current_reply.headers.emplace_back("Content-Type", "application/json; charset=UTF-8");
|
||||
@ -130,6 +156,8 @@ void RequestHandler::handle_request(const http::request ¤t_request,
|
||||
current_reply.headers.emplace_back("Content-Disposition",
|
||||
"inline; filename=\"response.js\"");
|
||||
}
|
||||
current_reply.headers.emplace_back("Content-Length",
|
||||
std::to_string(current_reply.content.size()));
|
||||
if (!route_parameters.jsonp_parameter.empty())
|
||||
{ // append brace to jsonp response
|
||||
current_reply.content.push_back(')');
|
||||
|
94
src/tools/tiler.cpp
Normal file
94
src/tools/tiler.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
#include "util/typedefs.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include "util/dynamic_graph.hpp"
|
||||
#include "util/static_graph.hpp"
|
||||
#include "util/fingerprint.hpp"
|
||||
#include "util/graph_loader.hpp"
|
||||
#include "util/make_unique.hpp"
|
||||
#include "util/exception.hpp"
|
||||
#include "util/simple_logger.hpp"
|
||||
#include "util/binary_heap.hpp"
|
||||
|
||||
#include "engine/datafacade/internal_datafacade.hpp"
|
||||
|
||||
#include "util/routed_options.hpp"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/timer/timer.hpp>
|
||||
|
||||
#include "osrm/coordinate.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace tools
|
||||
{
|
||||
|
||||
struct BoundingBox {
|
||||
FixedPointCoordinate southwest;
|
||||
FixedPointCoordinate northeast;
|
||||
};
|
||||
|
||||
BoundingBox TileToBBOX(int z, int x, int y)
|
||||
{
|
||||
// A small box in SF near the marina covering the intersection
|
||||
// of Powell and Embarcadero
|
||||
return { { static_cast<int32_t>(37.80781742045232 * COORDINATE_PRECISION) , static_cast<int32_t>(-122.4139380455017 * COORDINATE_PRECISION) },
|
||||
{ static_cast<int32_t>(37.809410993963944 * COORDINATE_PRECISION), static_cast<int32_t>(-122.41186738014221 * COORDINATE_PRECISION) } };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
std::vector<osrm::extractor::QueryNode> coordinate_list;
|
||||
osrm::util::LogPolicy::GetInstance().Unmute();
|
||||
|
||||
// enable logging
|
||||
if (argc < 5)
|
||||
{
|
||||
osrm::util::SimpleLogger().Write(logWARNING) << "usage:\n" << argv[0] << " <filename.osrm> <z> <x> <y>";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Set up the datafacade for querying
|
||||
std::unordered_map<std::string, boost::filesystem::path> server_paths;
|
||||
server_paths["base"] = std::string(argv[1]);
|
||||
osrm::util::populate_base_path(server_paths);
|
||||
osrm::engine::datafacade::InternalDataFacade<osrm::contractor::QueryEdge::EdgeData> datafacade(server_paths);
|
||||
|
||||
|
||||
// Step 1 - convert z,x,y into tile bounds
|
||||
//
|
||||
|
||||
int z = std::stoi(argv[2]);
|
||||
int x = std::stoi(argv[3]);
|
||||
int y = std::stoi(argv[4]);
|
||||
|
||||
auto bbox = util::coordinate_calculation::mercator::TileToBBOX(z,x,y); // @karenzshea - implement this function!!
|
||||
|
||||
// Step 2 - Get all the features from those bounds
|
||||
//
|
||||
//
|
||||
auto edges = datafacade.GetEdgesInBox(bbox.southwest, bbox.northeast);
|
||||
|
||||
// Step 3 - Encode those features as Mapbox Vector Tiles
|
||||
//
|
||||
//
|
||||
for (const auto & edge : edges)
|
||||
{
|
||||
const auto a = datafacade.GetCoordinateOfNode(edge.u);
|
||||
const auto b = datafacade.GetCoordinateOfNode(edge.v);
|
||||
std::cout << "Feature: " << a << " to " << b << std::endl;
|
||||
}
|
||||
|
||||
// Step 4 - Output the result
|
||||
//
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include "util/rectangle.hpp"
|
||||
|
||||
#include "util/string_util.hpp"
|
||||
#include "util/trigonometry_table.hpp"
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include "extractor/query_node.hpp"
|
||||
#include "extractor/edge_based_node.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
#include "util/rectangle.hpp"
|
||||
#include "util/exception.hpp"
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
@ -455,4 +457,42 @@ BOOST_AUTO_TEST_CASE(bearing_tests)
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(bbox_search_tests)
|
||||
{
|
||||
using Coord = std::pair<double, double>;
|
||||
using Edge = std::pair<unsigned, unsigned>;
|
||||
|
||||
GraphFixture fixture(
|
||||
{
|
||||
Coord(0.0,0.0),
|
||||
Coord(1.0,1.0),
|
||||
Coord(2.0,2.0),
|
||||
Coord(3.0,3.0),
|
||||
Coord(4.0,4.0),
|
||||
},
|
||||
{Edge(0,1), Edge(1,2), Edge(2,3), Edge(3,4)});
|
||||
|
||||
std::string leaves_path;
|
||||
std::string nodes_path;
|
||||
build_rtree<GraphFixture, MiniStaticRTree>("test_bbox", &fixture, leaves_path, nodes_path);
|
||||
MiniStaticRTree rtree(nodes_path, leaves_path, fixture.coords);
|
||||
engine::GeospatialQuery<MiniStaticRTree> query(rtree, fixture.coords);
|
||||
|
||||
{
|
||||
RectangleInt2D bbox = {static_cast<uint32_t>(0.5 * COORDINATE_PRECISION), static_cast<uint32_t>(1.5 * COORDINATE_PRECISION),
|
||||
static_cast<uint32_t>(0.5 * COORDINATE_PRECISION), static_cast<uint32_t>(1.5 * COORDINATE_PRECISION)};
|
||||
auto results = query.Search(bbox);
|
||||
BOOST_CHECK_EQUAL(results.size(), 2);
|
||||
}
|
||||
|
||||
{
|
||||
RectangleInt2D bbox = {static_cast<uint32_t>(1.5 * COORDINATE_PRECISION), static_cast<uint32_t>(3.5 * COORDINATE_PRECISION),
|
||||
static_cast<uint32_t>(1.5 * COORDINATE_PRECISION), static_cast<uint32_t>(3.5 * COORDINATE_PRECISION)};
|
||||
auto results = query.Search(bbox);
|
||||
BOOST_CHECK_EQUAL(results.size(), 3);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
|
||||
|
33
unit_tests/util/tile_to_bbox.cpp
Normal file
33
unit_tests/util/tile_to_bbox.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
#include "util/tile_bbox.hpp"
|
||||
#include "util/rectangle.hpp"
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <boost/test/test_case_template.hpp>
|
||||
#include <iostream>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(tile_to_bbox_test)
|
||||
|
||||
using namespace osrm;
|
||||
using namespace osrm::util;
|
||||
|
||||
// Check that this osm tile to coordinate bbox converter works
|
||||
struct R
|
||||
{
|
||||
bool operator()(const RectangleInt2D lhs, const RectangleInt2D rhs)
|
||||
{
|
||||
return std::tie(lhs.min_lon, lhs.max_lon, lhs.min_lat, lhs.max_lat) == std::tie(rhs.min_lon, rhs.max_lon, rhs.min_lat, rhs.max_lat);
|
||||
}
|
||||
};
|
||||
|
||||
BOOST_AUTO_TEST_CASE(tile_to_bbox_test)
|
||||
{
|
||||
R equal;
|
||||
RectangleInt2D expected(-180000000,0,85051128,0);
|
||||
BOOST_CHECK_EQUAL(true, equal(expected, TileToBBOX(1,0,0)));
|
||||
|
||||
expected = RectangleInt2D(13051757,13095703,52402418,52375599);
|
||||
BOOST_CHECK_EQUAL(true, equal(expected, TileToBBOX(13,4393,2691)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
Loading…
Reference in New Issue
Block a user