Preliminary integration of the tile plugin
This commit is contained in:
		
							parent
							
								
									41c9f94e5f
								
							
						
					
					
						commit
						f7b7dbf51a
					
				
							
								
								
									
										28
									
								
								include/engine/api/tile_parameters.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								include/engine/api/tile_parameters.hpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | |||||||
|  | #ifndef ENGINE_API_TILE_PARAMETERS_HPP | ||||||
|  | #define ENGINE_API_TILE_PARAMETERS_HPP | ||||||
|  | 
 | ||||||
|  | namespace osrm | ||||||
|  | { | ||||||
|  | namespace engine | ||||||
|  | { | ||||||
|  | namespace api | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | struct TileParameters final | ||||||
|  | { | ||||||
|  |     unsigned x; | ||||||
|  |     unsigned y; | ||||||
|  |     unsigned z; | ||||||
|  | 
 | ||||||
|  |     // FIXME check if x and y work with z
 | ||||||
|  |     bool IsValid() | ||||||
|  |     { | ||||||
|  |         return z < 20; | ||||||
|  |     }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
| @ -31,6 +31,7 @@ struct TableParameters; | |||||||
| struct NearestParameters; | struct NearestParameters; | ||||||
| struct TripParameters; | struct TripParameters; | ||||||
| struct MatchParameters; | struct MatchParameters; | ||||||
|  | struct TileParameters; | ||||||
| } | } | ||||||
| namespace plugins | namespace plugins | ||||||
| { | { | ||||||
| @ -39,6 +40,7 @@ class TablePlugin; | |||||||
| class NearestPlugin; | class NearestPlugin; | ||||||
| class TripPlugin; | class TripPlugin; | ||||||
| class MatchPlugin; | class MatchPlugin; | ||||||
|  | class TilePlugin; | ||||||
| } | } | ||||||
| // End fwd decls
 | // End fwd decls
 | ||||||
| 
 | 
 | ||||||
| @ -66,6 +68,7 @@ class Engine final | |||||||
|     Status Nearest(const api::NearestParameters ¶meters, util::json::Object &result); |     Status Nearest(const api::NearestParameters ¶meters, util::json::Object &result); | ||||||
|     Status Trip(const api::TripParameters ¶meters, util::json::Object &result); |     Status Trip(const api::TripParameters ¶meters, util::json::Object &result); | ||||||
|     Status Match(const api::MatchParameters ¶meters, util::json::Object &result); |     Status Match(const api::MatchParameters ¶meters, util::json::Object &result); | ||||||
|  |     Status Tile(const api::TileParameters ¶meters, std::string &result); | ||||||
| 
 | 
 | ||||||
|   private: |   private: | ||||||
|     std::unique_ptr<EngineLock> lock; |     std::unique_ptr<EngineLock> lock; | ||||||
| @ -75,6 +78,7 @@ class Engine final | |||||||
|     std::unique_ptr<plugins::NearestPlugin> nearest_plugin; |     std::unique_ptr<plugins::NearestPlugin> nearest_plugin; | ||||||
|     std::unique_ptr<plugins::TripPlugin> trip_plugin; |     std::unique_ptr<plugins::TripPlugin> trip_plugin; | ||||||
|     std::unique_ptr<plugins::MatchPlugin> match_plugin; |     std::unique_ptr<plugins::MatchPlugin> match_plugin; | ||||||
|  |     std::unique_ptr<plugins::TilePlugin> tile_plugin; | ||||||
| 
 | 
 | ||||||
|     std::unique_ptr<datafacade::BaseDataFacade> query_data_facade; |     std::unique_ptr<datafacade::BaseDataFacade> query_data_facade; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -2,17 +2,9 @@ | |||||||
| #define TILEPLUGIN_HPP | #define TILEPLUGIN_HPP | ||||||
| 
 | 
 | ||||||
| #include "engine/plugins/plugin_base.hpp" | #include "engine/plugins/plugin_base.hpp" | ||||||
| #include "osrm/json_container.hpp" | #include "engine/api/tile_parameters.hpp" | ||||||
| 
 |  | ||||||
| #include <protozero/varint.hpp> |  | ||||||
| #include <protozero/pbf_writer.hpp> |  | ||||||
| 
 | 
 | ||||||
| #include <string> | #include <string> | ||||||
| #include <vector> |  | ||||||
| #include <utility> |  | ||||||
| 
 |  | ||||||
| #include <cmath> |  | ||||||
| #include <cstdint> |  | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  * This plugin generates Mapbox Vector tiles that show the internal |  * This plugin generates Mapbox Vector tiles that show the internal | ||||||
| @ -29,405 +21,12 @@ namespace engine | |||||||
| namespace plugins | namespace plugins | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
| // from mapnik/well_known_srs.hpp
 | class TilePlugin final : public BasePlugin | ||||||
| const constexpr double EARTH_RADIUS = 6378137.0; |  | ||||||
| const constexpr double EARTH_DIAMETER = EARTH_RADIUS * 2.0; |  | ||||||
| const constexpr double EARTH_CIRCUMFERENCE = EARTH_DIAMETER * M_PI; |  | ||||||
| const constexpr double MAXEXTENT = EARTH_CIRCUMFERENCE / 2.0; |  | ||||||
| const constexpr double M_PI_by2 = M_PI / 2.0; |  | ||||||
| const constexpr double D2R = M_PI / 180.0; |  | ||||||
| const constexpr double R2D = 180.0 / M_PI; |  | ||||||
| const constexpr double M_PIby360 = M_PI / 360.0; |  | ||||||
| const constexpr double MAXEXTENTby180 = MAXEXTENT / 180.0; |  | ||||||
| const double MAX_LATITUDE = R2D * (2.0 * std::atan(std::exp(180.0 * D2R)) - M_PI_by2); |  | ||||||
| // ^ math functions are not constexpr since they have side-effects (setting errno) :(
 |  | ||||||
| 
 |  | ||||||
| // from mapnik-vector-tile
 |  | ||||||
| namespace detail_pbf |  | ||||||
| { |  | ||||||
| 
 |  | ||||||
| inline unsigned encode_length(const unsigned len) { return (len << 3u) | 2u; } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Converts a regular WSG84 lon/lat pair into
 |  | ||||||
| // a mercator coordinate
 |  | ||||||
| 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; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // This is the global default tile size for all Mapbox Vector Tiles
 |  | ||||||
| const constexpr double tile_size_ = 256.0; |  | ||||||
| 
 |  | ||||||
| //
 |  | ||||||
| inline void from_pixels(const double shift, double &x, double &y) |  | ||||||
| { |  | ||||||
|     const double b = shift / 2.0; |  | ||||||
|     x = (x - b) / (shift / 360.0); |  | ||||||
|     const double g = (y - b) / -(shift / (2 * M_PI)); |  | ||||||
|     y = R2D * (2.0 * std::atan(std::exp(g)) - M_PI_by2); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Converts a WMS tile coordinate (z,x,y) into a mercator bounding box
 |  | ||||||
| inline void xyz2mercator( |  | ||||||
|     const int x, const int y, const 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_; |  | ||||||
|     const 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); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Converts a WMS tile coordinate (z,x,y) into a wsg84 bounding box
 |  | ||||||
| inline void xyz2wsg84( |  | ||||||
|     const int x, const int y, const 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_; |  | ||||||
|     const double shift = std::pow(2.0, z) * tile_size_; |  | ||||||
|     from_pixels(shift, minx, miny); |  | ||||||
|     from_pixels(shift, maxx, maxy); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // emulates mapbox::box2d, just a simple container for
 |  | ||||||
| // a box
 |  | ||||||
| struct bbox final |  | ||||||
| { |  | ||||||
|     bbox(const double _minx, const double _miny, const double _maxx, const double _maxy) |  | ||||||
|         : minx(_minx), miny(_miny), maxx(_maxx), maxy(_maxy) |  | ||||||
|     { |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     double width() const { return maxx - minx; } |  | ||||||
|     double height() const { return maxy - miny; } |  | ||||||
| 
 |  | ||||||
|     const double minx; |  | ||||||
|     const double miny; |  | ||||||
|     const double maxx; |  | ||||||
|     const double maxy; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| // Simple container class for WSG84 coordinates
 |  | ||||||
| struct point_type_d final |  | ||||||
| { |  | ||||||
|     point_type_d(double _x, double _y) : x(_x), y(_y) {} |  | ||||||
| 
 |  | ||||||
|     const double x; |  | ||||||
|     const double y; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| // Simple container for integer coordinates (i.e. pixel coords)
 |  | ||||||
| struct point_type_i final |  | ||||||
| { |  | ||||||
|     point_type_i(std::int64_t _x, std::int64_t _y) : x(_x), y(_y) {} |  | ||||||
| 
 |  | ||||||
|     const std::int64_t x; |  | ||||||
|     const std::int64_t y; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| using line_type = std::vector<point_type_i>; |  | ||||||
| using line_typed = std::vector<point_type_d>; |  | ||||||
| 
 |  | ||||||
| // from mapnik-vector-tile
 |  | ||||||
| // Encodes a linestring using protobuf zigzag encoding
 |  | ||||||
| inline bool encode_linestring(line_type line, |  | ||||||
|                               protozero::packed_field_uint32 &geometry, |  | ||||||
|                               std::int32_t &start_x, |  | ||||||
|                               std::int32_t &start_y) |  | ||||||
| { |  | ||||||
|     const std::size_t line_size = line.size(); |  | ||||||
|     // line_size -= detail_pbf::repeated_point_count(line);
 |  | ||||||
|     if (line_size < 2) |  | ||||||
|     { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     const unsigned line_to_length = static_cast<const 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) |  | ||||||
|     { |  | ||||||
|         const std::int32_t dx = pt->x - start_x; |  | ||||||
|         const std::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: |   public: | ||||||
|     explicit TilePlugin(DataFacadeT *facade) : facade(facade), descriptor_string("tile") {} |     TilePlugin(datafacade::BaseDataFacade &facade) : BasePlugin(facade) {} | ||||||
| 
 | 
 | ||||||
|     const std::string GetDescriptor() const override final { return descriptor_string; } |     Status HandleRequest(const api::TileParameters ¶meters, std::string &pbf_buffer); | ||||||
| 
 |  | ||||||
|     Status HandleRequest(const RouteParameters &route_parameters, |  | ||||||
|                          util::json::Object &json_result) override final |  | ||||||
|     { |  | ||||||
| 
 |  | ||||||
|         // Vector tiles are 4096 virtual pixels on each side
 |  | ||||||
|         const double tile_extent = 4096.0; |  | ||||||
|         double min_lon, min_lat, max_lon, max_lat; |  | ||||||
| 
 |  | ||||||
|         // Convert the z,x,y mercator tile coordinates into WSG84 lon/lat values
 |  | ||||||
|         xyz2wsg84(route_parameters.x, route_parameters.y, route_parameters.z, min_lon, min_lat, |  | ||||||
|                   max_lon, max_lat); |  | ||||||
| 
 |  | ||||||
|         FixedPointCoordinate southwest{static_cast<std::int32_t>(min_lat * COORDINATE_PRECISION), |  | ||||||
|                                        static_cast<std::int32_t>(min_lon * COORDINATE_PRECISION)}; |  | ||||||
|         FixedPointCoordinate northeast{static_cast<std::int32_t>(max_lat * COORDINATE_PRECISION), |  | ||||||
|                                        static_cast<std::int32_t>(max_lon * COORDINATE_PRECISION)}; |  | ||||||
| 
 |  | ||||||
|         // Fetch all the segments that are in our bounding box.
 |  | ||||||
|         // This hits the OSRM StaticRTree
 |  | ||||||
|         const auto edges = facade->GetEdgesInBox(southwest, northeast); |  | ||||||
| 
 |  | ||||||
|         // TODO: extract speed values for compressed and uncompressed geometries
 |  | ||||||
| 
 |  | ||||||
|         // Convert tile coordinates into mercator coordinates
 |  | ||||||
|         xyz2mercator(route_parameters.x, route_parameters.y, route_parameters.z, min_lon, min_lat, |  | ||||||
|                      max_lon, max_lat); |  | ||||||
|         const bbox tile_bbox{min_lon, min_lat, max_lon, max_lat}; |  | ||||||
| 
 |  | ||||||
|         // Protobuf serialized blocks when objects go out of scope, hence
 |  | ||||||
|         // the extra scoping below.
 |  | ||||||
|         std::string buffer; |  | ||||||
|         protozero::pbf_writer tile_writer(buffer); |  | ||||||
|         { |  | ||||||
|             // Add a layer object to the PBF stream.  3=='layer' from the vector tile spec (2.1)
 |  | ||||||
|             protozero::pbf_writer layer_writer(tile_writer, 3); |  | ||||||
|             // TODO: don't write a layer if there are no features
 |  | ||||||
|             // Field 15 is the "version field, and it's a uint32
 |  | ||||||
|             layer_writer.add_uint32(15, 2); // version
 |  | ||||||
|             // Field 1 is the "layer name" field, it's a string
 |  | ||||||
|             layer_writer.add_string(1, "speeds"); // name
 |  | ||||||
|             // Field 5 is the tile extent.  It's a uint32 and should be set to 4096
 |  | ||||||
|             // for normal vector tiles.
 |  | ||||||
|             layer_writer.add_uint32(5, 4096); // extent
 |  | ||||||
| 
 |  | ||||||
|             // Begin the layer features block
 |  | ||||||
|             { |  | ||||||
|                 // Each feature gets a unique id, starting at 1
 |  | ||||||
|                 unsigned id = 1; |  | ||||||
|                 for (const auto &edge : edges) |  | ||||||
|                 { |  | ||||||
|                     // Get coordinates for start/end nodes of segmet (NodeIDs u and v)
 |  | ||||||
|                     const auto a = facade->GetCoordinateOfNode(edge.u); |  | ||||||
|                     const auto b = facade->GetCoordinateOfNode(edge.v); |  | ||||||
|                     // Calculate the length in meters, using the same calculation used to set the
 |  | ||||||
|                     // weight, so we can back-calculate the speed value that was set.
 |  | ||||||
|                     const double length = osrm::util::coordinate_calculation::greatCircleDistance( |  | ||||||
|                         a.lat, a.lon, b.lat, b.lon); |  | ||||||
| 
 |  | ||||||
|                     int forward_weight = 0; |  | ||||||
|                     int reverse_weight = 0; |  | ||||||
| 
 |  | ||||||
|                     if (edge.forward_packed_geometry_id != SPECIAL_EDGEID) { |  | ||||||
|                         std::vector<EdgeWeight> forward_weight_vector; |  | ||||||
|                         facade->GetUncompressedWeights(edge.forward_packed_geometry_id, |  | ||||||
|                                                           forward_weight_vector); |  | ||||||
|                         forward_weight = forward_weight_vector[edge.fwd_segment_position]; |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     if (edge.reverse_packed_geometry_id != SPECIAL_EDGEID) { |  | ||||||
|                         std::vector<EdgeWeight> reverse_weight_vector; |  | ||||||
|                         facade->GetUncompressedWeights(edge.reverse_packed_geometry_id, |  | ||||||
|                                                           reverse_weight_vector); |  | ||||||
| 
 |  | ||||||
|                         BOOST_ASSERT(edge.fwd_segment_position < reverse_weight_vector.size()); |  | ||||||
| 
 |  | ||||||
|                         reverse_weight = reverse_weight_vector[reverse_weight_vector.size() - |  | ||||||
|                                                                edge.fwd_segment_position - 1]; |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     // If this is a valid forward edge, go ahead and add it to the tile
 |  | ||||||
|                     if (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); |  | ||||||
| 
 |  | ||||||
|                         // Calculate the speed for this line
 |  | ||||||
|                         std::uint32_t speed = static_cast<std::uint32_t>( |  | ||||||
|                             round(length / forward_weight * 10 * 3.6)); |  | ||||||
| 
 |  | ||||||
|                         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 lon/lat to tile coordinates
 |  | ||||||
|                             const auto px = std::round(((px_merc - tile_bbox.minx) * tile_extent / |  | ||||||
|                                                         16.0 / tile_bbox.width()) * |  | ||||||
|                                                        tile_extent / 256.0); |  | ||||||
|                             const auto py = std::round(((tile_bbox.maxy - py_merc) * tile_extent / |  | ||||||
|                                                         16.0 / tile_bbox.height()) * |  | ||||||
|                                                        tile_extent / 256.0); |  | ||||||
|                             tile_line.emplace_back(px, py); |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         // Here, we save the two attributes for our feature: the speed and the
 |  | ||||||
|                         // is_small
 |  | ||||||
|                         // boolean.  We onl serve up speeds from 0-139, so all we do is save the
 |  | ||||||
|                         // first
 |  | ||||||
|                         protozero::pbf_writer feature_writer(layer_writer, 2); |  | ||||||
|                         // Field 3 is the "geometry type" field.  Value 2 is "line"
 |  | ||||||
|                         feature_writer.add_enum(3, 2); // geometry type
 |  | ||||||
|                         // Field 1 for the feature is the "id" field.
 |  | ||||||
|                         feature_writer.add_uint64(1, id++); // id
 |  | ||||||
|                         { |  | ||||||
|                             // When adding attributes to a feature, we have to write
 |  | ||||||
|                             // pairs of numbers.  The first value is the index in the
 |  | ||||||
|                             // keys array (written later), and the second value is the
 |  | ||||||
|                             // index into the "values" array (also written later).  We're
 |  | ||||||
|                             // not writing the actual speed or bool value here, we're saving
 |  | ||||||
|                             // an index into the "values" array.  This means many features
 |  | ||||||
|                             // can share the same value data, leading to smaller tiles.
 |  | ||||||
|                             protozero::packed_field_uint32 field(feature_writer, 2); |  | ||||||
| 
 |  | ||||||
|                             field.add_element(0); // "speed" tag key offset
 |  | ||||||
|                             field.add_element( |  | ||||||
|                                 std::min(speed, 127u)); // save the speed value, capped at 127
 |  | ||||||
|                             field.add_element(1);       // "is_small" tag key offset
 |  | ||||||
|                             field.add_element(edge.component.is_tiny ? 0 : 1); // is_small feature
 |  | ||||||
|                         } |  | ||||||
|                         { |  | ||||||
|                             // Encode the geometry for the feature
 |  | ||||||
|                             protozero::packed_field_uint32 geometry(feature_writer, 4); |  | ||||||
|                             encode_linestring(tile_line, geometry, start_x, start_y); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     // Repeat the above for the coordinates reversed and using the `reverse`
 |  | ||||||
|                     // properties
 |  | ||||||
|                     if (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); |  | ||||||
| 
 |  | ||||||
|                         const auto speed = static_cast<const std::uint32_t>( |  | ||||||
|                             round(length / reverse_weight * 10 * 3.6)); |  | ||||||
| 
 |  | ||||||
|                         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
 |  | ||||||
|                             const auto px = std::round(((px_merc - tile_bbox.minx) * tile_extent / |  | ||||||
|                                                         16.0 / tile_bbox.width()) * |  | ||||||
|                                                        tile_extent / 256.0); |  | ||||||
|                             const auto py = std::round(((tile_bbox.maxy - py_merc) * tile_extent / |  | ||||||
|                                                         16.0 / tile_bbox.height()) * |  | ||||||
|                                                        tile_extent / 256.0); |  | ||||||
|                             tile_line.emplace_back(px, py); |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         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( |  | ||||||
|                                 std::min(speed, 127u)); // save the speed value, capped at 127
 |  | ||||||
|                             field.add_element(1);       // "is_small" tag key offset
 |  | ||||||
|                             field.add_element(edge.component.is_tiny ? 0 : 1); // is_small feature
 |  | ||||||
|                         } |  | ||||||
|                         { |  | ||||||
|                             protozero::packed_field_uint32 geometry(feature_writer, 4); |  | ||||||
|                             encode_linestring(tile_line, geometry, start_x, start_y); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             // Field id 3 is the "keys" attribute
 |  | ||||||
|             // We need two "key" fields, these are referred to with 0 and 1 (their array indexes)
 |  | ||||||
|             // earlier
 |  | ||||||
|             layer_writer.add_string(3, "speed"); |  | ||||||
|             layer_writer.add_string(3, "is_small"); |  | ||||||
| 
 |  | ||||||
|             // Now, we write out the possible speed value arrays and possible is_tiny
 |  | ||||||
|             // values.  Field type 4 is the "values" field.  It's a variable type field,
 |  | ||||||
|             // so requires a two-step write (create the field, then write its value)
 |  | ||||||
|             for (std::size_t i = 0; i < 128; i++) |  | ||||||
|             { |  | ||||||
|                 { |  | ||||||
|                     // Writing field type 4 == variant type
 |  | ||||||
|                     protozero::pbf_writer values_writer(layer_writer, 4); |  | ||||||
|                     // Attribute value 5 == uin64 type
 |  | ||||||
|                     values_writer.add_uint64(5, i); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             { |  | ||||||
|                 protozero::pbf_writer values_writer(layer_writer, 4); |  | ||||||
|                 // Attribute value 7 == bool type
 |  | ||||||
|                 values_writer.add_bool(7, true); |  | ||||||
|             } |  | ||||||
|             { |  | ||||||
|                 protozero::pbf_writer values_writer(layer_writer, 4); |  | ||||||
|                 // Attribute value 7 == bool type
 |  | ||||||
|                 values_writer.add_bool(7, false); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Encode the PBF result as a special Buffer object on the response.
 |  | ||||||
|         // This will allow downstream consumers to handle this type differently
 |  | ||||||
|         // to the String type.
 |  | ||||||
|         json_result.values["pbf"] = osrm::util::json::Buffer(buffer); |  | ||||||
| 
 |  | ||||||
|         return Status::Ok; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|   private: |  | ||||||
|     DataFacadeT *const facade; |  | ||||||
|     const std::string descriptor_string; |  | ||||||
| }; | }; | ||||||
| } | } | ||||||
| } | } | ||||||
|  | |||||||
| @ -9,7 +9,7 @@ | |||||||
| //#include "engine/plugins/timestamp.hpp"
 | //#include "engine/plugins/timestamp.hpp"
 | ||||||
| #include "engine/plugins/trip.hpp" | #include "engine/plugins/trip.hpp" | ||||||
| #include "engine/plugins/viaroute.hpp" | #include "engine/plugins/viaroute.hpp" | ||||||
| //#include "engine/plugins/tile.hpp"
 | #include "engine/plugins/tile.hpp" | ||||||
| #include "engine/plugins/match.hpp" | #include "engine/plugins/match.hpp" | ||||||
| 
 | 
 | ||||||
| #include "engine/datafacade/datafacade_base.hpp" | #include "engine/datafacade/datafacade_base.hpp" | ||||||
| @ -87,16 +87,16 @@ namespace | |||||||
| { | { | ||||||
| // Abstracted away the query locking into a template function
 | // Abstracted away the query locking into a template function
 | ||||||
| // Works the same for every plugin.
 | // Works the same for every plugin.
 | ||||||
| template <typename ParameterT, typename PluginT> | template <typename ParameterT, typename PluginT, typename ResultT> | ||||||
| osrm::engine::Status RunQuery(const std::unique_ptr<osrm::engine::Engine::EngineLock> &lock, | osrm::engine::Status RunQuery(const std::unique_ptr<osrm::engine::Engine::EngineLock> &lock, | ||||||
|                               osrm::engine::datafacade::BaseDataFacade &facade, |                               osrm::engine::datafacade::BaseDataFacade &facade, | ||||||
|                               const ParameterT ¶meters, |                               const ParameterT ¶meters, | ||||||
|                               PluginT &plugin, |                               PluginT &plugin, | ||||||
|                               osrm::util::json::Object &json_result) |                               ResultT &result) | ||||||
| { | { | ||||||
|     if (!lock) |     if (!lock) | ||||||
|     { |     { | ||||||
|         return plugin.HandleRequest(parameters, json_result); |         return plugin.HandleRequest(parameters, result); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     BOOST_ASSERT(lock); |     BOOST_ASSERT(lock); | ||||||
| @ -108,7 +108,7 @@ osrm::engine::Status RunQuery(const std::unique_ptr<osrm::engine::Engine::Engine | |||||||
|     // things while the query is running
 |     // things while the query is running
 | ||||||
|     boost::shared_lock<boost::shared_mutex> data_lock{shared_facade.data_mutex}; |     boost::shared_lock<boost::shared_mutex> data_lock{shared_facade.data_mutex}; | ||||||
| 
 | 
 | ||||||
|     osrm::engine::Status status = plugin.HandleRequest(parameters, json_result); |     osrm::engine::Status status = plugin.HandleRequest(parameters, result); | ||||||
| 
 | 
 | ||||||
|     lock->DecreaseQueryCount(); |     lock->DecreaseQueryCount(); | ||||||
|     return status; |     return status; | ||||||
| @ -148,6 +148,7 @@ Engine::Engine(EngineConfig &config) | |||||||
|     nearest_plugin = create<NearestPlugin>(*query_data_facade); |     nearest_plugin = create<NearestPlugin>(*query_data_facade); | ||||||
|     trip_plugin = create<TripPlugin>(*query_data_facade, config.max_locations_trip); |     trip_plugin = create<TripPlugin>(*query_data_facade, config.max_locations_trip); | ||||||
|     match_plugin = create<MatchPlugin>(*query_data_facade, config.max_locations_map_matching); |     match_plugin = create<MatchPlugin>(*query_data_facade, config.max_locations_map_matching); | ||||||
|  |     tile_plugin = create<TilePlugin>(*query_data_facade); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // make sure we deallocate the unique ptr at a position where we know the size of the plugins
 | // make sure we deallocate the unique ptr at a position where we know the size of the plugins
 | ||||||
| @ -180,5 +181,10 @@ Status Engine::Match(const api::MatchParameters ¶ms, util::json::Object &res | |||||||
|     return RunQuery(lock, *query_data_facade, params, *match_plugin, result); |     return RunQuery(lock, *query_data_facade, params, *match_plugin, result); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | Status Engine::Tile(const api::TileParameters ¶ms, std::string &result) | ||||||
|  | { | ||||||
|  |     return RunQuery(lock, *query_data_facade, params, *tile_plugin, result); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // engine ns
 | } // engine ns
 | ||||||
| } // osrm ns
 | } // osrm ns
 | ||||||
|  | |||||||
							
								
								
									
										393
									
								
								src/engine/plugins/tile.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										393
									
								
								src/engine/plugins/tile.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,393 @@ | |||||||
|  | #include "engine/plugins/plugin_base.hpp" | ||||||
|  | #include "engine/plugins/tile.hpp" | ||||||
|  | 
 | ||||||
|  | #include <protozero/varint.hpp> | ||||||
|  | #include <protozero/pbf_writer.hpp> | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  | #include <utility> | ||||||
|  | 
 | ||||||
|  | #include <cmath> | ||||||
|  | #include <cstdint> | ||||||
|  | 
 | ||||||
|  | namespace osrm | ||||||
|  | { | ||||||
|  | namespace engine | ||||||
|  | { | ||||||
|  | namespace plugins | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | // from mapnik/well_known_srs.hpp
 | ||||||
|  | const constexpr double EARTH_RADIUS = 6378137.0; | ||||||
|  | const constexpr double EARTH_DIAMETER = EARTH_RADIUS * 2.0; | ||||||
|  | const constexpr double EARTH_CIRCUMFERENCE = EARTH_DIAMETER * M_PI; | ||||||
|  | const constexpr double MAXEXTENT = EARTH_CIRCUMFERENCE / 2.0; | ||||||
|  | const constexpr double M_PI_by2 = M_PI / 2.0; | ||||||
|  | const constexpr double D2R = M_PI / 180.0; | ||||||
|  | const constexpr double R2D = 180.0 / M_PI; | ||||||
|  | const constexpr double M_PIby360 = M_PI / 360.0; | ||||||
|  | const constexpr double MAXEXTENTby180 = MAXEXTENT / 180.0; | ||||||
|  | const double MAX_LATITUDE = R2D * (2.0 * std::atan(std::exp(180.0 * D2R)) - M_PI_by2); | ||||||
|  | // ^ math functions are not constexpr since they have side-effects (setting errno) :(
 | ||||||
|  | 
 | ||||||
|  | // from mapnik-vector-tile
 | ||||||
|  | namespace detail_pbf | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | inline unsigned encode_length(const unsigned len) { return (len << 3u) | 2u; } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Converts a regular WSG84 lon/lat pair into
 | ||||||
|  | // a mercator coordinate
 | ||||||
|  | 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; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // This is the global default tile size for all Mapbox Vector Tiles
 | ||||||
|  | const constexpr double tile_size_ = 256.0; | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | inline void from_pixels(const double shift, double &x, double &y) | ||||||
|  | { | ||||||
|  |     const double b = shift / 2.0; | ||||||
|  |     x = (x - b) / (shift / 360.0); | ||||||
|  |     const double g = (y - b) / -(shift / (2 * M_PI)); | ||||||
|  |     y = R2D * (2.0 * std::atan(std::exp(g)) - M_PI_by2); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Converts a WMS tile coordinate (z,x,y) into a mercator bounding box
 | ||||||
|  | inline void xyz2mercator( | ||||||
|  |     const int x, const int y, const 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_; | ||||||
|  |     const 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); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Converts a WMS tile coordinate (z,x,y) into a wsg84 bounding box
 | ||||||
|  | inline void xyz2wsg84( | ||||||
|  |     const int x, const int y, const 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_; | ||||||
|  |     const double shift = std::pow(2.0, z) * tile_size_; | ||||||
|  |     from_pixels(shift, minx, miny); | ||||||
|  |     from_pixels(shift, maxx, maxy); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // emulates mapbox::box2d, just a simple container for
 | ||||||
|  | // a box
 | ||||||
|  | struct bbox final | ||||||
|  | { | ||||||
|  |     bbox(const double _minx, const double _miny, const double _maxx, const double _maxy) | ||||||
|  |         : minx(_minx), miny(_miny), maxx(_maxx), maxy(_maxy) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     double width() const { return maxx - minx; } | ||||||
|  |     double height() const { return maxy - miny; } | ||||||
|  | 
 | ||||||
|  |     const double minx; | ||||||
|  |     const double miny; | ||||||
|  |     const double maxx; | ||||||
|  |     const double maxy; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Simple container class for WSG84 coordinates
 | ||||||
|  | struct point_type_d final | ||||||
|  | { | ||||||
|  |     point_type_d(double _x, double _y) : x(_x), y(_y) {} | ||||||
|  | 
 | ||||||
|  |     const double x; | ||||||
|  |     const double y; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Simple container for integer coordinates (i.e. pixel coords)
 | ||||||
|  | struct point_type_i final | ||||||
|  | { | ||||||
|  |     point_type_i(std::int64_t _x, std::int64_t _y) : x(_x), y(_y) {} | ||||||
|  | 
 | ||||||
|  |     const std::int64_t x; | ||||||
|  |     const std::int64_t y; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | using line_type = std::vector<point_type_i>; | ||||||
|  | using line_typed = std::vector<point_type_d>; | ||||||
|  | 
 | ||||||
|  | // from mapnik-vector-tile
 | ||||||
|  | // Encodes a linestring using protobuf zigzag encoding
 | ||||||
|  | inline bool encode_linestring(line_type line, | ||||||
|  |                               protozero::packed_field_uint32 &geometry, | ||||||
|  |                               std::int32_t &start_x, | ||||||
|  |                               std::int32_t &start_y) | ||||||
|  | { | ||||||
|  |     const std::size_t line_size = line.size(); | ||||||
|  |     if (line_size < 2) | ||||||
|  |     { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const unsigned line_to_length = static_cast<const 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) | ||||||
|  |     { | ||||||
|  |         const std::int32_t dx = pt->x - start_x; | ||||||
|  |         const std::int32_t dy = pt->y - start_y; | ||||||
|  |         geometry.add_element(protozero::encode_zigzag32(dx)); | ||||||
|  |         geometry.add_element(protozero::encode_zigzag32(dy)); | ||||||
|  |         start_x = pt->x; | ||||||
|  |         start_y = pt->y; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Status TilePlugin::HandleRequest(const api::TileParameters ¶meters, std::string &pbf_buffer) | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  |     // Vector tiles are 4096 virtual pixels on each side
 | ||||||
|  |     const double tile_extent = 4096.0; | ||||||
|  |     double min_lon, min_lat, max_lon, max_lat; | ||||||
|  | 
 | ||||||
|  |     // Convert the z,x,y mercator tile coordinates into WSG84 lon/lat values
 | ||||||
|  |     xyz2wsg84(parameters.x, parameters.y, parameters.z, min_lon, min_lat, max_lon, max_lat); | ||||||
|  | 
 | ||||||
|  |     util::Coordinate southwest{util::FloatLongitude(min_lon), util::FloatLatitude(min_lat)}; | ||||||
|  |     util::Coordinate northeast{util::FloatLongitude(max_lon), util::FloatLatitude(max_lat)}; | ||||||
|  | 
 | ||||||
|  |     // Fetch all the segments that are in our bounding box.
 | ||||||
|  |     // This hits the OSRM StaticRTree
 | ||||||
|  |     const auto edges = facade.GetEdgesInBox(southwest, northeast); | ||||||
|  | 
 | ||||||
|  |     // TODO: extract speed values for compressed and uncompressed geometries
 | ||||||
|  | 
 | ||||||
|  |     // Convert tile coordinates into mercator coordinates
 | ||||||
|  |     xyz2mercator(parameters.x, parameters.y, parameters.z, min_lon, min_lat, max_lon, max_lat); | ||||||
|  |     const bbox tile_bbox{min_lon, min_lat, max_lon, max_lat}; | ||||||
|  | 
 | ||||||
|  |     // Protobuf serialized blocks when objects go out of scope, hence
 | ||||||
|  |     // the extra scoping below.
 | ||||||
|  |     protozero::pbf_writer tile_writer{pbf_buffer}; | ||||||
|  |     { | ||||||
|  |         // Add a layer object to the PBF stream.  3=='layer' from the vector tile spec (2.1)
 | ||||||
|  |         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
 | ||||||
|  |         // Field 1 is the "layer name" field, it's a string
 | ||||||
|  |         layer_writer.add_string(1, "speeds"); // name
 | ||||||
|  |         // Field 5 is the tile extent.  It's a uint32 and should be set to 4096
 | ||||||
|  |         // for normal vector tiles.
 | ||||||
|  |         layer_writer.add_uint32(5, 4096); // extent
 | ||||||
|  | 
 | ||||||
|  |         // Begin the layer features block
 | ||||||
|  |         { | ||||||
|  |             // Each feature gets a unique id, starting at 1
 | ||||||
|  |             unsigned id = 1; | ||||||
|  |             for (const auto &edge : edges) | ||||||
|  |             { | ||||||
|  |                 // Get coordinates for start/end nodes of segmet (NodeIDs u and v)
 | ||||||
|  |                 const auto a = facade.GetCoordinateOfNode(edge.u); | ||||||
|  |                 const auto b = facade.GetCoordinateOfNode(edge.v); | ||||||
|  |                 // Calculate the length in meters
 | ||||||
|  |                 const double length = osrm::util::coordinate_calculation::haversineDistance(a, b); | ||||||
|  | 
 | ||||||
|  |                 int forward_weight = 0; | ||||||
|  |                 int reverse_weight = 0; | ||||||
|  | 
 | ||||||
|  |                 if (edge.forward_packed_geometry_id != SPECIAL_EDGEID) | ||||||
|  |                 { | ||||||
|  |                     std::vector<EdgeWeight> forward_weight_vector; | ||||||
|  |                     facade.GetUncompressedWeights(edge.forward_packed_geometry_id, | ||||||
|  |                                                   forward_weight_vector); | ||||||
|  |                     forward_weight = forward_weight_vector[edge.fwd_segment_position]; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (edge.reverse_packed_geometry_id != SPECIAL_EDGEID) | ||||||
|  |                 { | ||||||
|  |                     std::vector<EdgeWeight> reverse_weight_vector; | ||||||
|  |                     facade.GetUncompressedWeights(edge.reverse_packed_geometry_id, | ||||||
|  |                                                   reverse_weight_vector); | ||||||
|  | 
 | ||||||
|  |                     BOOST_ASSERT(edge.fwd_segment_position < reverse_weight_vector.size()); | ||||||
|  | 
 | ||||||
|  |                     reverse_weight = reverse_weight_vector[reverse_weight_vector.size() - | ||||||
|  |                                                            edge.fwd_segment_position - 1]; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 // If this is a valid forward edge, go ahead and add it to the tile
 | ||||||
|  |                 if (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(static_cast<double>(util::toFloating(a.lon)), | ||||||
|  |                                           static_cast<double>(util::toFloating(a.lat))); | ||||||
|  |                     geo_line.emplace_back(static_cast<double>(util::toFloating(b.lon)), | ||||||
|  |                                           static_cast<double>(util::toFloating(b.lat))); | ||||||
|  | 
 | ||||||
|  |                     // Calculate the speed for this line
 | ||||||
|  |                     std::uint32_t speed = | ||||||
|  |                         static_cast<std::uint32_t>(round(length / forward_weight * 10 * 3.6)); | ||||||
|  | 
 | ||||||
|  |                     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 lon/lat to tile coordinates
 | ||||||
|  |                         const auto px = std::round( | ||||||
|  |                             ((px_merc - tile_bbox.minx) * tile_extent / 16.0 / tile_bbox.width()) * | ||||||
|  |                             tile_extent / 256.0); | ||||||
|  |                         const auto py = std::round( | ||||||
|  |                             ((tile_bbox.maxy - py_merc) * tile_extent / 16.0 / tile_bbox.height()) * | ||||||
|  |                             tile_extent / 256.0); | ||||||
|  |                         tile_line.emplace_back(px, py); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     // Here, we save the two attributes for our feature: the speed and the
 | ||||||
|  |                     // is_small
 | ||||||
|  |                     // boolean.  We onl serve up speeds from 0-139, so all we do is save the
 | ||||||
|  |                     // first
 | ||||||
|  |                     protozero::pbf_writer feature_writer(layer_writer, 2); | ||||||
|  |                     // Field 3 is the "geometry type" field.  Value 2 is "line"
 | ||||||
|  |                     feature_writer.add_enum(3, 2); // geometry type
 | ||||||
|  |                     // Field 1 for the feature is the "id" field.
 | ||||||
|  |                     feature_writer.add_uint64(1, id++); // id
 | ||||||
|  |                     { | ||||||
|  |                         // When adding attributes to a feature, we have to write
 | ||||||
|  |                         // pairs of numbers.  The first value is the index in the
 | ||||||
|  |                         // keys array (written later), and the second value is the
 | ||||||
|  |                         // index into the "values" array (also written later).  We're
 | ||||||
|  |                         // not writing the actual speed or bool value here, we're saving
 | ||||||
|  |                         // an index into the "values" array.  This means many features
 | ||||||
|  |                         // can share the same value data, leading to smaller tiles.
 | ||||||
|  |                         protozero::packed_field_uint32 field(feature_writer, 2); | ||||||
|  | 
 | ||||||
|  |                         field.add_element(0); // "speed" tag key offset
 | ||||||
|  |                         field.add_element( | ||||||
|  |                             std::min(speed, 127u)); // save the speed value, capped at 127
 | ||||||
|  |                         field.add_element(1);       // "is_small" tag key offset
 | ||||||
|  |                         field.add_element(edge.component.is_tiny ? 0 : 1); // is_small feature
 | ||||||
|  |                     } | ||||||
|  |                     { | ||||||
|  |                         // Encode the geometry for the feature
 | ||||||
|  |                         protozero::packed_field_uint32 geometry(feature_writer, 4); | ||||||
|  |                         encode_linestring(tile_line, geometry, start_x, start_y); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 // Repeat the above for the coordinates reversed and using the `reverse`
 | ||||||
|  |                 // properties
 | ||||||
|  |                 if (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(static_cast<double>(util::toFloating(b.lon)), | ||||||
|  |                                           static_cast<double>(util::toFloating(b.lat))); | ||||||
|  |                     geo_line.emplace_back(static_cast<double>(util::toFloating(a.lon)), | ||||||
|  |                                           static_cast<double>(util::toFloating(a.lat))); | ||||||
|  | 
 | ||||||
|  |                     const auto speed = | ||||||
|  |                         static_cast<const std::uint32_t>(round(length / reverse_weight * 10 * 3.6)); | ||||||
|  | 
 | ||||||
|  |                     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
 | ||||||
|  |                         const auto px = std::round( | ||||||
|  |                             ((px_merc - tile_bbox.minx) * tile_extent / 16.0 / tile_bbox.width()) * | ||||||
|  |                             tile_extent / 256.0); | ||||||
|  |                         const auto py = std::round( | ||||||
|  |                             ((tile_bbox.maxy - py_merc) * tile_extent / 16.0 / tile_bbox.height()) * | ||||||
|  |                             tile_extent / 256.0); | ||||||
|  |                         tile_line.emplace_back(px, py); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     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( | ||||||
|  |                             std::min(speed, 127u)); // save the speed value, capped at 127
 | ||||||
|  |                         field.add_element(1);       // "is_small" tag key offset
 | ||||||
|  |                         field.add_element(edge.component.is_tiny ? 0 : 1); // is_small feature
 | ||||||
|  |                     } | ||||||
|  |                     { | ||||||
|  |                         protozero::packed_field_uint32 geometry(feature_writer, 4); | ||||||
|  |                         encode_linestring(tile_line, geometry, start_x, start_y); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Field id 3 is the "keys" attribute
 | ||||||
|  |         // We need two "key" fields, these are referred to with 0 and 1 (their array indexes)
 | ||||||
|  |         // earlier
 | ||||||
|  |         layer_writer.add_string(3, "speed"); | ||||||
|  |         layer_writer.add_string(3, "is_small"); | ||||||
|  | 
 | ||||||
|  |         // Now, we write out the possible speed value arrays and possible is_tiny
 | ||||||
|  |         // values.  Field type 4 is the "values" field.  It's a variable type field,
 | ||||||
|  |         // so requires a two-step write (create the field, then write its value)
 | ||||||
|  |         for (std::size_t i = 0; i < 128; i++) | ||||||
|  |         { | ||||||
|  |             { | ||||||
|  |                 // Writing field type 4 == variant type
 | ||||||
|  |                 protozero::pbf_writer values_writer(layer_writer, 4); | ||||||
|  |                 // Attribute value 5 == uin64 type
 | ||||||
|  |                 values_writer.add_uint64(5, i); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         { | ||||||
|  |             protozero::pbf_writer values_writer(layer_writer, 4); | ||||||
|  |             // Attribute value 7 == bool type
 | ||||||
|  |             values_writer.add_bool(7, true); | ||||||
|  |         } | ||||||
|  |         { | ||||||
|  |             protozero::pbf_writer values_writer(layer_writer, 4); | ||||||
|  |             // Attribute value 7 == bool type
 | ||||||
|  |             values_writer.add_bool(7, false); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return Status::Ok; | ||||||
|  | } | ||||||
|  | } | ||||||
|  | } | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user