From acab77f4f8e50132a0eb14b327f04b088244eb11 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Fri, 16 May 2014 15:52:26 +0200 Subject: [PATCH 01/36] add simple isValid() function to PhantomNodes --- DataStructures/PhantomNodes.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/DataStructures/PhantomNodes.h b/DataStructures/PhantomNodes.h index a037dbba89e..280e8f6aab4 100644 --- a/DataStructures/PhantomNodes.h +++ b/DataStructures/PhantomNodes.h @@ -115,6 +115,12 @@ struct PhantomNode ); } + bool isValid() const + { + return location.isValid() && + (name_id != std::numeric_limits::max()); + } + bool operator==(const PhantomNode & other) const { return location == other.location; From a80815d57a5b751ace40a6b8b8dd4366a421ba04 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Fri, 16 May 2014 16:09:40 +0200 Subject: [PATCH 02/36] implements output generation through a dedicated JSON container: - JSON syntax is not scattered over several files, but one place - Reduces code duplication - breaking changes: - new property in json(p) formatted response: "found_alternative": True/False - returned filenames now response.js(on) or route.gpx - /hello plugin returns JSON now --- Algorithms/PolylineCompressor.cpp | 87 ++--- Algorithms/PolylineCompressor.h | 14 +- DataStructures/JSONContainer.h | 237 +++++++++++++ DataStructures/SearchEngine.h | 4 +- Descriptors/BaseDescriptor.h | 2 +- Descriptors/DescriptionFactory.cpp | 29 +- Descriptors/DescriptionFactory.h | 24 +- Descriptors/GPXDescriptor.h | 73 ++-- Descriptors/JSONDescriptor.h | 395 +++++++++------------ Include/osrm/Reply.h | 2 +- Library/OSRM_impl.cpp | 2 + Plugins/DistanceTablePlugin.h | 153 ++++++++ Plugins/HelloWorldPlugin.h | 93 +++-- Plugins/LocatePlugin.h | 70 +--- Plugins/NearestPlugin.h | 78 +--- Plugins/TimestampPlugin.h | 47 +-- Plugins/ViaRoutePlugin.h | 82 +---- RoutingAlgorithms/AlternativePathRouting.h | 1 + RoutingAlgorithms/ManyToManyRouting.h | 246 +++++++++++++ RoutingAlgorithms/ShortestPathRouting.h | 1 + Server/APIGrammar.h | 2 +- Server/Connection.cpp | 19 +- Server/Connection.h | 2 +- Server/Http/Reply.cpp | 19 +- Server/RequestHandler.cpp | 46 ++- Server/ServerFactory.h | 6 +- Tools/simpleclient.cpp | 6 +- Util/StringUtil.h | 35 +- 28 files changed, 1096 insertions(+), 679 deletions(-) create mode 100644 DataStructures/JSONContainer.h create mode 100644 Plugins/DistanceTablePlugin.h create mode 100644 RoutingAlgorithms/ManyToManyRouting.h diff --git a/Algorithms/PolylineCompressor.cpp b/Algorithms/PolylineCompressor.cpp index 76d93823f86..cc8ec9f3feb 100644 --- a/Algorithms/PolylineCompressor.cpp +++ b/Algorithms/PolylineCompressor.cpp @@ -26,6 +26,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "PolylineCompressor.h" +#include "../Util/StringUtil.h" void PolylineCompressor::encodeVectorSignedNumber(std::vector &numbers, std::string &output) const @@ -39,9 +40,9 @@ void PolylineCompressor::encodeVectorSignedNumber(std::vector &numbers, std numbers[i] = ~(numbers[i]); } } - for (unsigned i = 0; i < end; ++i) + for (const int number: numbers) { - encodeNumber(numbers[i], output); + encodeNumber(number, output); } } @@ -66,37 +67,36 @@ void PolylineCompressor::encodeNumber(int number_to_encode, std::string &output) } } -void PolylineCompressor::printEncodedString(const std::vector &polyline, - std::string &output) const +JSON::String PolylineCompressor::printEncodedString(const std::vector &polyline) const { + std::string output; std::vector delta_numbers; - output += "\""; if (!polyline.empty()) { FixedPointCoordinate last_coordinate = polyline[0].location; delta_numbers.emplace_back(last_coordinate.lat); delta_numbers.emplace_back(last_coordinate.lon); - for (unsigned i = 1; i < polyline.size(); ++i) + for (const auto & segment : polyline) { - if (polyline[i].necessary) + if (segment.necessary) { - int lat_diff = polyline[i].location.lat - last_coordinate.lat; - int lon_diff = polyline[i].location.lon - last_coordinate.lon; + int lat_diff = segment.location.lat - last_coordinate.lat; + int lon_diff = segment.location.lon - last_coordinate.lon; delta_numbers.emplace_back(lat_diff); delta_numbers.emplace_back(lon_diff); - last_coordinate = polyline[i].location; + last_coordinate = segment.location; } } encodeVectorSignedNumber(delta_numbers, output); } - output += "\""; + JSON::String return_value(output); + return return_value; } -void PolylineCompressor::printEncodedString(const std::vector &polyline, - std::string &output) const +JSON::String PolylineCompressor::printEncodedString(const std::vector &polyline) const { + std::string output; std::vector delta_numbers(2 * polyline.size()); - output += "\""; if (!polyline.empty()) { delta_numbers[0] = polyline[0].lat; @@ -110,53 +110,40 @@ void PolylineCompressor::printEncodedString(const std::vector &polyline, - std::string &output) const + +JSON::Array PolylineCompressor::printUnencodedString(const std::vector &polyline) const { - output += "["; - std::string tmp; - for (unsigned i = 0; i < polyline.size(); i++) + JSON::Array json_geometry_array; + for( const auto & coordinate : polyline) { - FixedPointCoordinate::convertInternalLatLonToString(polyline[i].lat, tmp); - output += "["; + std::string tmp, output; + FixedPointCoordinate::convertInternalLatLonToString(coordinate.lat, tmp); + output += (tmp + ","); + FixedPointCoordinate::convertInternalLatLonToString(coordinate.lon, tmp); output += tmp; - FixedPointCoordinate::convertInternalLatLonToString(polyline[i].lon, tmp); - output += ", "; - output += tmp; - output += "]"; - if (i < polyline.size() - 1) - { - output += ","; - } + json_geometry_array.values.push_back(output); } - output += "]"; + return json_geometry_array; } -void PolylineCompressor::printUnencodedString(const std::vector &polyline, - std::string &output) const +JSON::Array PolylineCompressor::printUnencodedString(const std::vector &polyline) const { - output += "["; - std::string tmp; - for (unsigned i = 0; i < polyline.size(); i++) + JSON::Array json_geometry_array; + for( const auto & segment : polyline) { - if (!polyline[i].necessary) - { - continue; - } - FixedPointCoordinate::convertInternalLatLonToString(polyline[i].location.lat, tmp); - output += "["; - output += tmp; - FixedPointCoordinate::convertInternalLatLonToString(polyline[i].location.lon, tmp); - output += ", "; - output += tmp; - output += "]"; - if (i < polyline.size() - 1) + if (segment.necessary) { - output += ","; + std::string tmp, output; + FixedPointCoordinate::convertInternalLatLonToString(segment.location.lat, tmp); + output += (tmp + ","); + FixedPointCoordinate::convertInternalLatLonToString(segment.location.lon, tmp); + output += tmp; + json_geometry_array.values.push_back(output); } } - output += "]"; + return json_geometry_array; } diff --git a/Algorithms/PolylineCompressor.h b/Algorithms/PolylineCompressor.h index 2860e2e6575..7a5fd8ba1c6 100644 --- a/Algorithms/PolylineCompressor.h +++ b/Algorithms/PolylineCompressor.h @@ -28,8 +28,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef POLYLINECOMPRESSOR_H_ #define POLYLINECOMPRESSOR_H_ +#include "../DataStructures/JSONContainer.h" #include "../DataStructures/SegmentInformation.h" -#include "../Util/StringUtil.h" #include #include @@ -42,17 +42,13 @@ class PolylineCompressor void encodeNumber(int number_to_encode, std::string &output) const; public: - void printEncodedString(const std::vector &polyline, - std::string &output) const; + JSON::String printEncodedString(const std::vector &polyline) const; - void printEncodedString(const std::vector &polyline, - std::string &output) const; + JSON::String printEncodedString(const std::vector &polyline) const; - void printUnencodedString(const std::vector &polyline, - std::string &output) const; + JSON::Array printUnencodedString(const std::vector &polyline) const; - void printUnencodedString(const std::vector &polyline, - std::string &output) const; + JSON::Array printUnencodedString(const std::vector &polyline) const; }; #endif /* POLYLINECOMPRESSOR_H_ */ diff --git a/DataStructures/JSONContainer.h b/DataStructures/JSONContainer.h new file mode 100644 index 00000000000..47dc34b279e --- /dev/null +++ b/DataStructures/JSONContainer.h @@ -0,0 +1,237 @@ +/* + +Copyright (c) 2013, Project OSRM, Dennis Luxen, others +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +// based on https://svn.apache.org/repos/asf/mesos/tags/release-0.9.0-incubating-RC0/src/common/json.hpp + +#ifndef JSON_CONTAINER_H +#define JSON_CONTAINER_H + +#include "../Util/StringUtil.h" + +#include + +#include +#include +#include +#include + +namespace JSON +{ + +struct String; +struct Number; +struct Object; +struct Array; +struct True; +struct False; +struct Null; + +typedef boost::variant, + boost::recursive_wrapper, + boost::recursive_wrapper, + boost::recursive_wrapper, + boost::recursive_wrapper, + boost::recursive_wrapper, + boost::recursive_wrapper > Value; + +struct String +{ + String() {} + String(const char *value) : value(value) {} + String(const std::string &value) : value(value) {} + std::string value; +}; + +struct Number +{ + Number() {} + Number(double value) : value(value) {} + double value; +}; + +struct Object +{ + std::unordered_map values; +}; + +struct Array +{ + std::vector values; +}; + +struct True +{ +}; + +struct False +{ +}; + +struct Null +{ +}; + +struct Renderer : boost::static_visitor<> +{ + Renderer(std::ostream &_out) : out(_out) {} + + void operator()(const String &string) const { out << "\"" << string.value << "\""; } + + void operator()(const Number &number) const + { + out.precision(10); + out << number.value; + } + + void operator()(const Object &object) const + { + out << "{"; + auto iterator = object.values.begin(); + while (iterator != object.values.end()) + { + out << "\"" << (*iterator).first << "\":"; + boost::apply_visitor(Renderer(out), (*iterator).second); + if (++iterator != object.values.end()) + { + out << ","; + } + } + out << "}"; + } + + void operator()(const Array &array) const + { + out << "["; + std::vector::const_iterator iterator; + iterator = array.values.begin(); + while (iterator != array.values.end()) + { + boost::apply_visitor(Renderer(out), *iterator); + if (++iterator != array.values.end()) + { + out << ","; + } + } + out << "]"; + } + + void operator()(const True &) const { out << "true"; } + + void operator()(const False &) const { out << "false"; } + + void operator()(const Null &) const { out << "null"; } + + private: + std::ostream &out; +}; + +struct ArrayRenderer : boost::static_visitor<> +{ + ArrayRenderer(std::vector &_out) : out(_out) {} + + void operator()(const String &string) const { + out.push_back('\"'); + out.insert(out.end(), string.value.begin(), string.value.end()); + out.push_back('\"'); + } + + void operator()(const Number &number) const + { + const std::string number_string = FixedDoubleToString(number.value); + out.insert(out.end(), number_string.begin(), number_string.end()); + } + + void operator()(const Object &object) const + { + out.push_back('{'); + auto iterator = object.values.begin(); + while (iterator != object.values.end()) + { + out.push_back('\"'); + out.insert(out.end(), (*iterator).first.begin(), (*iterator).first.end()); + out.push_back('\"'); + out.push_back(':'); + + boost::apply_visitor(ArrayRenderer(out), (*iterator).second); + if (++iterator != object.values.end()) + { + out.push_back(','); + } + } + out.push_back('}'); + } + + void operator()(const Array &array) const + { + out.push_back('['); + std::vector::const_iterator iterator; + iterator = array.values.begin(); + while (iterator != array.values.end()) + { + boost::apply_visitor(ArrayRenderer(out), *iterator); + if (++iterator != array.values.end()) + { + out.push_back(','); + } + } + out.push_back(']'); + } + + void operator()(const True &) const { + const std::string temp("true"); + out.insert(out.end(), temp.begin(), temp.end()); + } + + void operator()(const False &) const { + const std::string temp("false"); + out.insert(out.end(), temp.begin(), temp.end()); + } + + void operator()(const Null &) const { + const std::string temp("null"); + out.insert(out.end(), temp.begin(), temp.end()); + } + + private: + std::vector &out; +}; + +inline void render(std::ostream &out, const Object &object) +{ + Value value = object; + boost::apply_visitor(Renderer(out), value); +} + +inline void render(std::vector &out, const Object &object) +{ + Value value = object; + boost::apply_visitor(ArrayRenderer(out), value); +} + +} // namespace JSON + +#endif // JSON_CONTAINER_H diff --git a/DataStructures/SearchEngine.h b/DataStructures/SearchEngine.h index 84c87e93f78..6f937370754 100644 --- a/DataStructures/SearchEngine.h +++ b/DataStructures/SearchEngine.h @@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "SearchEngineData.h" #include "../RoutingAlgorithms/AlternativePathRouting.h" +#include "../RoutingAlgorithms/ManyToManyRouting.h" #include "../RoutingAlgorithms/ShortestPathRouting.h" template class SearchEngine @@ -41,10 +42,11 @@ template class SearchEngine public: ShortestPathRouting shortest_path; AlternativeRouting alternative_path; + ManyToManyRouting distance_table; explicit SearchEngine(DataFacadeT *facade) : facade(facade), shortest_path(facade, engine_working_data), - alternative_path(facade, engine_working_data) + alternative_path(facade, engine_working_data), distance_table(facade, engine_working_data) { } diff --git a/Descriptors/BaseDescriptor.h b/Descriptors/BaseDescriptor.h index 11c5393e02e..60d28635594 100644 --- a/Descriptors/BaseDescriptor.h +++ b/Descriptors/BaseDescriptor.h @@ -51,7 +51,7 @@ struct DescriptorConfig template class BaseDescriptor { public: - BaseDescriptor() {} + BaseDescriptor() {} //TODO: initialize facade here. // Maybe someone can explain the pure virtual destructor thing to me (dennis) virtual ~BaseDescriptor() {} virtual void Run(const RawRouteData &raw_route, diff --git a/Descriptors/DescriptionFactory.cpp b/Descriptors/DescriptionFactory.cpp index d56b65416d7..7d6fc037bd9 100644 --- a/Descriptors/DescriptionFactory.cpp +++ b/Descriptors/DescriptionFactory.cpp @@ -94,38 +94,23 @@ void DescriptionFactory::AppendSegment(const FixedPointCoordinate &coordinate, } } -void DescriptionFactory::AppendEncodedPolylineString(const bool return_encoded, - std::vector &output) +JSON::Value DescriptionFactory::AppendEncodedPolylineString(const bool return_encoded) { - std::string temp; if (return_encoded) { - polyline_compressor.printEncodedString(path_description, temp); + return polyline_compressor.printEncodedString(path_description); } - else - { - polyline_compressor.printUnencodedString(path_description, temp); - } - output.emplace_back(temp); -} - -void DescriptionFactory::AppendEncodedPolylineString(std::vector &output) const -{ - std::string temp; - polyline_compressor.printEncodedString(path_description, temp); - output.emplace_back(temp); + return polyline_compressor.printUnencodedString(path_description); } -void DescriptionFactory::AppendUnencodedPolylineString(std::vector &output) const +JSON::Value DescriptionFactory::AppendUnencodedPolylineString() const { - std::string temp; - polyline_compressor.printUnencodedString(path_description, temp); - output.emplace_back(temp); + return polyline_compressor.printUnencodedString(path_description); } void DescriptionFactory::BuildRouteSummary(const double distance, const unsigned time) { - summary.startName = start_phantom.name_id; - summary.destName = target_phantom.name_id; + summary.source_name_id = start_phantom.name_id; + summary.target_name_id = target_phantom.name_id; summary.BuildDurationAndLengthStrings(distance, time); } diff --git a/Descriptors/DescriptionFactory.h b/Descriptors/DescriptionFactory.h index 710b7f73f39..082ce675b59 100644 --- a/Descriptors/DescriptionFactory.h +++ b/Descriptors/DescriptionFactory.h @@ -57,18 +57,17 @@ class DescriptionFactory public: struct RouteSummary { - std::string lengthString; - std::string durationString; - unsigned startName; - unsigned destName; - RouteSummary() : lengthString("0"), durationString("0"), startName(0), destName(0) {} + unsigned distance; + EdgeWeight duration; + unsigned source_name_id; + unsigned target_name_id; + RouteSummary() : distance(0), duration(0), source_name_id(0), target_name_id(0) {} - void BuildDurationAndLengthStrings(const double distance, const unsigned time) + void BuildDurationAndLengthStrings(const double raw_distance, const unsigned raw_duration) { // compute distance/duration for route summary - intToString(round(distance), lengthString); - int travel_time = round(time / 10.); - intToString(std::max(travel_time, 1), durationString); + distance = round(raw_distance); + duration = round(raw_duration / 10.); } } summary; @@ -79,13 +78,12 @@ class DescriptionFactory DescriptionFactory(); virtual ~DescriptionFactory(); double GetBearing(const FixedPointCoordinate &C, const FixedPointCoordinate &B) const; - void AppendEncodedPolylineString(std::vector &output) const; - void AppendUnencodedPolylineString(std::vector &output) const; + JSON::Value AppendUnencodedPolylineString() const; void AppendSegment(const FixedPointCoordinate &coordinate, const PathData &data); void BuildRouteSummary(const double distance, const unsigned time); void SetStartSegment(const PhantomNode &start_phantom); void SetEndSegment(const PhantomNode &start_phantom); - void AppendEncodedPolylineString(const bool return_encoded, std::vector &output); + JSON::Value AppendEncodedPolylineString(const bool return_encoded); template void Run(const DataFacadeT *facade, const unsigned zoomLevel) { @@ -198,7 +196,7 @@ class DescriptionFactory { if (path_description[i].necessary) { - double angle = + const double angle = GetBearing(path_description[i].location, path_description[i + 1].location); path_description[i].bearing = angle * 10; } diff --git a/Descriptors/GPXDescriptor.h b/Descriptors/GPXDescriptor.h index e7a0c0cebda..027cb831371 100644 --- a/Descriptors/GPXDescriptor.h +++ b/Descriptors/GPXDescriptor.h @@ -36,7 +36,24 @@ template class GPXDescriptor : public BaseDescriptor & output) + { + const std::string route_point_head = ""; + + std::string tmp; + + FixedPointCoordinate::convertInternalLatLonToString(coordinate.lat, tmp); + output.insert(output.end(), route_point_head.begin(), route_point_head.end()); + output.insert(output.end(), tmp.begin(), tmp.end()); + output.push_back('\"'); + + FixedPointCoordinate::convertInternalLatLonToString(coordinate.lon, tmp); + output.insert(output.end(), route_point_middle.begin(), route_point_middle.end()); + output.insert(output.end(), tmp.begin(), tmp.end()); + output.insert(output.end(), route_point_tail.begin(), route_point_tail.end()); + } public: void SetConfig(const DescriptorConfig &c) { config = c; } @@ -47,51 +64,37 @@ template class GPXDescriptor : public BaseDescriptor"); - reply.content.emplace_back(""); - reply.content.emplace_back("Data (c)" - " OpenStreetMap contributors (ODbL)" - ""); - reply.content.emplace_back(""); - bool found_route = (raw_route.shortest_path_length != INVALID_EDGE_WEIGHT) && - (!raw_route.unpacked_path_segments.front().empty()); + std::string header("" + "" + "Data (c)" + " OpenStreetMap contributors (ODbL)" + "" + ""); + reply.content.insert(reply.content.end(), header.begin(), header.end()); + const bool found_route = (raw_route.shortest_path_length != INVALID_EDGE_WEIGHT) && + (!raw_route.unpacked_path_segments.front().empty()); if (found_route) { - FixedPointCoordinate::convertInternalLatLonToString( - phantom_node_list.source_phantom.location.lat, tmp); - reply.content.emplace_back(""); + AddRoutePoint(phantom_node_list.source_phantom.location, reply.content); for (const std::vector &path_data_vector : raw_route.unpacked_path_segments) { for (const PathData &path_data : path_data_vector) { - FixedPointCoordinate current_coordinate = + const FixedPointCoordinate current_coordinate = facade->GetCoordinateOfNode(path_data.node); - - FixedPointCoordinate::convertInternalLatLonToString(current_coordinate.lat, - tmp); - reply.content.emplace_back(""); + AddRoutePoint(current_coordinate, reply.content); } } - // Add the via point or the end coordinate - FixedPointCoordinate::convertInternalLatLonToString( - phantom_node_list.target_phantom.location.lat, tmp); - reply.content.emplace_back(""); + AddRoutePoint(phantom_node_list.target_phantom.location, reply.content); + } - reply.content.emplace_back(""); + std::string footer(""); + reply.content.insert(reply.content.end(), footer.begin(), footer.end()); } }; #endif // GPX_DESCRIPTOR_H diff --git a/Descriptors/JSONDescriptor.h b/Descriptors/JSONDescriptor.h index 30e73bfb8ac..d35ccc16734 100644 --- a/Descriptors/JSONDescriptor.h +++ b/Descriptors/JSONDescriptor.h @@ -31,10 +31,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "BaseDescriptor.h" #include "DescriptionFactory.h" #include "../Algorithms/ObjectToBase64.h" +#include "../DataStructures/JSONContainer.h" #include "../DataStructures/SegmentInformation.h" #include "../DataStructures/TurnInstructions.h" #include "../Util/Azimuth.h" #include "../Util/StringUtil.h" +#include "../Util/TimingUtil.h" #include @@ -45,7 +47,7 @@ template class JSONDescriptor : public BaseDescriptor class JSONDescriptor : public BaseDescriptor class JSONDescriptor : public BaseDescriptor class JSONDescriptor : public BaseDescriptorGetEscapedNameForNameID(description_factory.summary.source_name_id); + json_route_summary.values["end_point"] = facade->GetEscapedNameForNameID(description_factory.summary.target_name_id); + json_result.values["route_summary"] = json_route_summary; + + // Get Names for both routes + RouteNames route_names; + JSON::Array json_route_names; + GetRouteNames(shortest_path_segments, alternative_path_segments, facade, route_names); + json_route_names.values.push_back(route_names.shortest_path_name_1); + json_route_names.values.push_back(route_names.shortest_path_name_2); + json_result.values["route_name"] = json_route_names; + + BOOST_ASSERT(!raw_route.segment_end_coordinates.empty()); + + JSON::Array json_via_points_array; + JSON::Array json_first_coordinate; + json_first_coordinate.values.push_back(raw_route.segment_end_coordinates.front().source_phantom.location.lat/COORDINATE_PRECISION); + json_first_coordinate.values.push_back(raw_route.segment_end_coordinates.front().source_phantom.location.lon/COORDINATE_PRECISION); + json_via_points_array.values.push_back(json_first_coordinate); + for (const PhantomNodes &nodes : raw_route.segment_end_coordinates) + { + std::string tmp; + JSON::Array json_coordinate; + json_coordinate.values.push_back(nodes.target_phantom.location.lat/COORDINATE_PRECISION); + json_coordinate.values.push_back(nodes.target_phantom.location.lon/COORDINATE_PRECISION); + json_via_points_array.values.push_back(json_coordinate); + } + json_result.values["via_points"] = json_via_points_array; - reply.content.emplace_back("\"route_summary\":"); - reply.content.emplace_back("{"); - reply.content.emplace_back("\"total_distance\":"); - reply.content.emplace_back(description_factory.summary.lengthString); - reply.content.emplace_back("," - "\"total_time\":"); - reply.content.emplace_back(description_factory.summary.durationString); - reply.content.emplace_back("," - "\"start_point\":\""); - reply.content.emplace_back( - facade->GetEscapedNameForNameID(description_factory.summary.startName)); - reply.content.emplace_back("\"," - "\"end_point\":\""); - reply.content.emplace_back( - facade->GetEscapedNameForNameID(description_factory.summary.destName)); - reply.content.emplace_back("\""); - reply.content.emplace_back("}"); - reply.content.emplace_back(","); + JSON::Array json_via_indices_array; + json_via_indices_array.values.insert(json_via_indices_array.values.end(), shortest_leg_end_indices.begin(), shortest_leg_end_indices.end()); + json_result.values["via_indices"] = json_via_indices_array; // only one alternative route is computed at this time, so this is hardcoded - if (raw_route.alternative_path_length != INVALID_EDGE_WEIGHT) + if (INVALID_EDGE_WEIGHT != raw_route.alternative_path_length) { - alternate_descriptionFactory.SetStartSegment(phantom_nodes.source_phantom); + json_result.values["found_alternative"] = JSON::True(); + alternate_description_factory.SetStartSegment(phantom_nodes.source_phantom); // Get all the coordinates for the computed route for (const PathData &path_data : raw_route.unpacked_alternative) { current = facade->GetCoordinateOfNode(path_data.node); - alternate_descriptionFactory.AppendSegment(current, path_data); + alternate_description_factory.AppendSegment(current, path_data); } - } - alternate_descriptionFactory.Run(facade, config.zoom_level); + alternate_description_factory.Run(facade, config.zoom_level); - // //give an array of alternative routes - reply.content.emplace_back("\"alternative_geometries\": ["); - if (config.geometry && INVALID_EDGE_WEIGHT != raw_route.alternative_path_length) - { - // Generate the linestrings for each alternative - alternate_descriptionFactory.AppendEncodedPolylineString(config.encode_geometry, - reply.content); - } - reply.content.emplace_back("],"); - reply.content.emplace_back("\"alternative_instructions\":["); - if (INVALID_EDGE_WEIGHT != raw_route.alternative_path_length) - { - reply.content.emplace_back("["); - // Generate instructions for each alternative + if (config.geometry) + { + JSON::Value alternate_geometry_string = alternate_description_factory.AppendEncodedPolylineString(config.encode_geometry); + JSON::Array json_alternate_geometries_array; + json_alternate_geometries_array.values.push_back(alternate_geometry_string); + json_result.values["alternative_geometries"] = json_alternate_geometries_array; + } + // Generate instructions for each alternative (simulated here) + JSON::Array json_alt_instructions; + JSON::Array json_current_alt_instructions; if (config.instructions) { - BuildTextualDescription(alternate_descriptionFactory, - reply, + BuildTextualDescription(alternate_description_factory, + json_current_alt_instructions, raw_route.alternative_path_length, facade, alternative_path_segments); + json_alt_instructions.values.push_back(json_current_alt_instructions); + json_result.values["alternative_instructions"] = json_alt_instructions; } - reply.content.emplace_back("]"); + alternate_description_factory.BuildRouteSummary( + alternate_description_factory.entireLength, raw_route.alternative_path_length); + + JSON::Object json_alternate_route_summary; + JSON::Array json_alternate_route_summary_array; + json_alternate_route_summary.values["total_distance"] = alternate_description_factory.summary.distance; + json_alternate_route_summary.values["total_time"] = alternate_description_factory.summary.duration; + json_alternate_route_summary.values["start_point"] = facade->GetEscapedNameForNameID(alternate_description_factory.summary.source_name_id); + json_alternate_route_summary.values["end_point"] = facade->GetEscapedNameForNameID(alternate_description_factory.summary.target_name_id); + json_alternate_route_summary_array.values.push_back(json_alternate_route_summary); + json_result.values["alternative_summaries"] = json_alternate_route_summary_array; + + JSON::Array json_altenative_indices_array; + json_altenative_indices_array.values.push_back(0); + json_altenative_indices_array.values.push_back(alternate_description_factory.path_description.size()); + json_result.values["alternative_indices"] = json_altenative_indices_array; + + JSON::Array json_alternate_names_array; + JSON::Array json_alternate_names; + json_alternate_names.values.push_back(route_names.alternative_path_name_1); + json_alternate_names.values.push_back(route_names.alternative_path_name_2); + json_alternate_names_array.values.push_back(json_alternate_names); + json_result.values["alternative_names"] = json_alternate_names_array; + + } else { + json_result.values["found_alternative"] = JSON::False(); } - reply.content.emplace_back("],"); - reply.content.emplace_back("\"alternative_summaries\":["); - if (INVALID_EDGE_WEIGHT != raw_route.alternative_path_length) - { - // Generate route summary (length, duration) for each alternative - alternate_descriptionFactory.BuildRouteSummary( - alternate_descriptionFactory.entireLength, raw_route.alternative_path_length); - reply.content.emplace_back("{"); - reply.content.emplace_back("\"total_distance\":"); - reply.content.emplace_back(alternate_descriptionFactory.summary.lengthString); - reply.content.emplace_back("," - "\"total_time\":"); - reply.content.emplace_back(alternate_descriptionFactory.summary.durationString); - reply.content.emplace_back("," - "\"start_point\":\""); - reply.content.emplace_back( - facade->GetEscapedNameForNameID(description_factory.summary.startName)); - reply.content.emplace_back("\"," - "\"end_point\":\""); - reply.content.emplace_back( - facade->GetEscapedNameForNameID(description_factory.summary.destName)); - reply.content.emplace_back("\""); - reply.content.emplace_back("}"); - } - reply.content.emplace_back("],"); - - // //Get Names for both routes - RouteNames routeNames; - GetRouteNames(shortest_path_segments, alternative_path_segments, facade, routeNames); - - reply.content.emplace_back("\"route_name\":[\""); - reply.content.emplace_back(routeNames.shortest_path_name_1); - reply.content.emplace_back("\",\""); - reply.content.emplace_back(routeNames.shortest_path_name_2); - reply.content.emplace_back("\"]," - "\"alternative_names\":["); - reply.content.emplace_back("[\""); - reply.content.emplace_back(routeNames.alternative_path_name_1); - reply.content.emplace_back("\",\""); - reply.content.emplace_back(routeNames.alternative_path_name_2); - reply.content.emplace_back("\"]"); - reply.content.emplace_back("],"); - // list all viapoints so that the client may display it - reply.content.emplace_back("\"via_points\":["); - - BOOST_ASSERT(!raw_route.segment_end_coordinates.empty()); - - std::string tmp; - FixedPointCoordinate::convertInternalReversedCoordinateToString( - raw_route.segment_end_coordinates.front().source_phantom.location, tmp); - reply.content.emplace_back("["); - reply.content.emplace_back(tmp); - reply.content.emplace_back("]"); - - for (const PhantomNodes &nodes : raw_route.segment_end_coordinates) - { - tmp.clear(); - FixedPointCoordinate::convertInternalReversedCoordinateToString( - nodes.target_phantom.location, tmp); - reply.content.emplace_back(",["); - reply.content.emplace_back(tmp); - reply.content.emplace_back("]"); - } - - reply.content.emplace_back("],"); - reply.content.emplace_back("\"via_indices\":["); - for (const unsigned index : shortest_leg_end_indices) - { - tmp.clear(); - intToString(index, tmp); - reply.content.emplace_back(tmp); - if (index != shortest_leg_end_indices.back()) - { - reply.content.emplace_back(","); - } - } - reply.content.emplace_back("],\"alternative_indices\":["); - if (INVALID_EDGE_WEIGHT != raw_route.alternative_path_length) - { - reply.content.emplace_back("0,"); - tmp.clear(); - intToString(alternate_descriptionFactory.path_description.size(), tmp); - reply.content.emplace_back(tmp); - } - - reply.content.emplace_back("],"); - reply.content.emplace_back("\"hint_data\": {"); - reply.content.emplace_back("\"checksum\":"); - intToString(raw_route.check_sum, tmp); - reply.content.emplace_back(tmp); - reply.content.emplace_back(", \"locations\": ["); + JSON::Object json_hint_object; + json_hint_object.values["checksum"] = raw_route.check_sum; + JSON::Array json_location_hint_array; std::string hint; for (unsigned i = 0; i < raw_route.segment_end_coordinates.size(); ++i) { - reply.content.emplace_back("\""); EncodeObjectToBase64(raw_route.segment_end_coordinates[i].source_phantom, hint); - reply.content.emplace_back(hint); - reply.content.emplace_back("\", "); + json_location_hint_array.values.push_back(hint); } EncodeObjectToBase64(raw_route.segment_end_coordinates.back().target_phantom, hint); - reply.content.emplace_back("\""); - reply.content.emplace_back(hint); - reply.content.emplace_back("\"]"); - reply.content.emplace_back("}}"); + json_location_hint_array.values.push_back(hint); + json_hint_object.values["locations"] = json_location_hint_array; + json_result.values["hint_data"] = json_hint_object; + + // render the content to the output array + TIMER_START(route_render); + JSON::render(reply.content, json_result); + TIMER_STOP(route_render); + JSON::render(SimpleLogger().Write(), json_result); + SimpleLogger().Write(logDEBUG) << "rendering took: " << TIMER_MSEC(route_render); } + // TODO: break into function object with several functions, i.e. simplify // construct routes names void GetRouteNames(std::vector &shortest_path_segments, std::vector &alternative_path_segments, const DataFacadeT *facade, - RouteNames &routeNames) + RouteNames &route_names) { Segment shortest_segment_1, shortest_segment_2; - Segment alternativeSegment1, alternative_segment_2; + Segment alternative_segment_1, alternative_segment_2; auto length_comperator = [](Segment a, Segment b) { return a.length < b.length; }; @@ -351,28 +305,28 @@ template class JSONDescriptor : public BaseDescriptor shortestDifference(shortest_path_segments.size()); - std::vector alternativeDifference(alternative_path_segments.size()); + std::vector shortest_path_set_difference(shortest_path_segments.size()); + std::vector alternative_path_set_difference(alternative_path_segments.size()); std::set_difference(shortest_path_segments.begin(), shortest_path_segments.end(), alternative_path_segments.begin(), alternative_path_segments.end(), - shortestDifference.begin(), + shortest_path_set_difference.begin(), length_comperator); - int size_of_difference = shortestDifference.size(); + int size_of_difference = shortest_path_set_difference.size(); if (size_of_difference) { int i = 0; while (i < size_of_difference && - shortestDifference[i].name_id == shortest_path_segments[0].name_id) + shortest_path_set_difference[i].name_id == shortest_path_segments[0].name_id) { ++i; } if (i < size_of_difference) { - shortest_segment_2 = shortestDifference[i]; + shortest_segment_2 = shortest_path_set_difference[i]; } } @@ -380,50 +334,51 @@ template class JSONDescriptor : public BaseDescriptor shortest_segment_2.position) + { std::swap(shortest_segment_1, shortest_segment_2); - - if (alternativeSegment1.position > alternative_segment_2.position) - std::swap(alternativeSegment1, alternative_segment_2); - - routeNames.shortest_path_name_1 = + } + if (alternative_segment_1.position > alternative_segment_2.position) + { + std::swap(alternative_segment_1, alternative_segment_2); + } + route_names.shortest_path_name_1 = facade->GetEscapedNameForNameID(shortest_segment_1.name_id); - routeNames.shortest_path_name_2 = + route_names.shortest_path_name_2 = facade->GetEscapedNameForNameID(shortest_segment_2.name_id); - routeNames.alternative_path_name_1 = - facade->GetEscapedNameForNameID(alternativeSegment1.name_id); - routeNames.alternative_path_name_2 = + route_names.alternative_path_name_1 = + facade->GetEscapedNameForNameID(alternative_segment_1.name_id); + route_names.alternative_path_name_2 = facade->GetEscapedNameForNameID(alternative_segment_2.name_id); } } // TODO: reorder parameters inline void BuildTextualDescription(DescriptionFactory &description_factory, - http::Reply &reply, + JSON::Array & json_instruction_array, const int route_length, const DataFacadeT *facade, std::vector &route_segments_list) { // Segment information has following format: - //["instruction","streetname",length,position,time,"length","earth_direction",azimuth] - // Example: ["Turn left","High Street",200,4,10,"200m","NE",22.5] + //["instruction id","streetname",length,position,time,"length","earth_direction",azimuth] unsigned necessary_segments_running_index = 0; round_about.leave_at_exit = 0; round_about.name_id = 0; @@ -432,6 +387,7 @@ template class JSONDescriptor : public BaseDescriptor class JSONDescriptor : public BaseDescriptorGetEscapedNameForNameID(segment.name_id)); - reply.content.emplace_back("\","); - intToString(segment.length, temp_dist); - reply.content.emplace_back(temp_dist); - reply.content.emplace_back(","); - intToString(necessary_segments_running_index, temp_length); - reply.content.emplace_back(temp_length); - reply.content.emplace_back(","); - intToString(round(segment.duration / 10.), temp_duration); - reply.content.emplace_back(temp_duration); - reply.content.emplace_back(",\""); - intToString(segment.length, temp_length); - reply.content.emplace_back(temp_length); - reply.content.emplace_back("m\",\""); + json_instruction_row.values.push_back(facade->GetEscapedNameForNameID(segment.name_id)); + json_instruction_row.values.push_back(std::round(segment.length)); + json_instruction_row.values.push_back(necessary_segments_running_index); + json_instruction_row.values.push_back(round(segment.duration / 10)); + json_instruction_row.values.push_back(IntToString(segment.length)+"m"); int bearing_value = round(segment.bearing / 10.); - reply.content.emplace_back(Azimuth::Get(bearing_value)); - reply.content.emplace_back("\","); - intToString(bearing_value, temp_bearing); - reply.content.emplace_back(temp_bearing); - reply.content.emplace_back("]"); + json_instruction_row.values.push_back(Azimuth::Get(bearing_value)); + json_instruction_row.values.push_back(bearing_value); route_segments_list.emplace_back( - Segment(segment.name_id, segment.length, route_segments_list.size())); + segment.name_id, segment.length, route_segments_list.size()); + json_instruction_array.values.push_back(json_instruction_row); } } else if (TurnInstruction::StayOnRoundAbout == current_instruction) @@ -498,25 +439,21 @@ template class JSONDescriptor : public BaseDescriptor headers; std::vector toBuffers(); std::vector HeaderstoBuffers(); - std::vector content; + std::vector content; static Reply StockReply(status_type status); void setSize(const unsigned size); void SetUncompressedSize(); diff --git a/Library/OSRM_impl.cpp b/Library/OSRM_impl.cpp index 2df13d6ae03..1ca4ccdd54b 100644 --- a/Library/OSRM_impl.cpp +++ b/Library/OSRM_impl.cpp @@ -28,6 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "OSRM_impl.h" #include "OSRM.h" +#include "../Plugins/DistanceTablePlugin.h" #include "../Plugins/HelloWorldPlugin.h" #include "../Plugins/LocatePlugin.h" #include "../Plugins/NearestPlugin.h" @@ -55,6 +56,7 @@ OSRM_impl::OSRM_impl(const ServerPaths &server_paths, const bool use_shared_memo } // The following plugins handle all requests. + RegisterPlugin(new DistanceTablePlugin>(query_data_facade)); RegisterPlugin(new HelloWorldPlugin()); RegisterPlugin(new LocatePlugin>(query_data_facade)); RegisterPlugin(new NearestPlugin>(query_data_facade)); diff --git a/Plugins/DistanceTablePlugin.h b/Plugins/DistanceTablePlugin.h new file mode 100644 index 00000000000..7dc88b72be2 --- /dev/null +++ b/Plugins/DistanceTablePlugin.h @@ -0,0 +1,153 @@ +/* + +Copyright (c) 2014, Project OSRM, Dennis Luxen, others +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef DISTANCE_TABLE_PLUGIN_H +#define DISTANCE_TABLE_PLUGIN_H + +#include "BasePlugin.h" + +#include "../Algorithms/ObjectToBase64.h" +#include "../DataStructures/JSONContainer.h" +#include "../DataStructures/QueryEdge.h" +#include "../DataStructures/SearchEngine.h" +#include "../Descriptors/BaseDescriptor.h" +#include "../Descriptors/GPXDescriptor.h" +#include "../Descriptors/JSONDescriptor.h" +#include "../Util/SimpleLogger.h" +#include "../Util/StringUtil.h" +#include "../Util/TimingUtil.h" + +#include + +#include +#include +#include +#include +#include + +template class DistanceTablePlugin : public BasePlugin +{ + private: + std::shared_ptr> search_engine_ptr; + + public: + explicit DistanceTablePlugin(DataFacadeT *facade) : descriptor_string("table"), facade(facade) + { + search_engine_ptr = std::make_shared>(facade); + } + + virtual ~DistanceTablePlugin() {} + + const std::string GetDescriptor() const { return descriptor_string; } + + void HandleRequest(const RouteParameters &route_parameters, http::Reply &reply) + { + SimpleLogger().Write() << "running DT plugin"; + + // check number of parameters + if (2 > route_parameters.coordinates.size()) + { + reply = http::Reply::StockReply(http::Reply::badRequest); + return; + } + + RawRouteData raw_route; + raw_route.check_sum = facade->GetCheckSum(); + + if (std::any_of(begin(route_parameters.coordinates), + end(route_parameters.coordinates), + [&](FixedPointCoordinate coordinate) + { return !coordinate.isValid(); })) + { + SimpleLogger().Write() << "invalid coordinate"; + + reply = http::Reply::StockReply(http::Reply::badRequest); + return; + } + + for (const FixedPointCoordinate &coordinate : route_parameters.coordinates) + { + raw_route.raw_via_node_coordinates.emplace_back(coordinate); + SimpleLogger().Write() << "adding coordinate " << coordinate; + } + + std::vector phantom_node_vector(raw_route.raw_via_node_coordinates.size()); + const bool checksum_OK = (route_parameters.check_sum == raw_route.check_sum); + + unsigned max_locations = std::min(25, max_locations); + for (unsigned i = 0; i < max_locations; ++i) + { + if (checksum_OK && i < route_parameters.hints.size() && + !route_parameters.hints[i].empty()) + { + DecodeObjectFromBase64(route_parameters.hints[i], phantom_node_vector[i]); + if (phantom_node_vector[i].isValid(facade->GetNumberOfNodes())) + { + SimpleLogger().Write() << "decoded phantom node for " << phantom_node_vector[i].location; + continue; + } + } + SimpleLogger().Write() << "looking up coordinate in tree"; + facade->FindPhantomNodeForCoordinate(raw_route.raw_via_node_coordinates[i], + phantom_node_vector[i], + route_parameters.zoom_level); + BOOST_ASSERT(phantom_node_vector[i].isValid(facade->GetNumberOfNodes())); + } + + TIMER_START(distance_table); + std::shared_ptr> result_table = search_engine_ptr->distance_table(phantom_node_vector); + TIMER_STOP(distance_table); + SimpleLogger().Write() << "table computation took " << TIMER_MSEC(distance_table) << "ms"; + + if (!result_table) + { + SimpleLogger().Write() << "computation failed"; + reply = http::Reply::StockReply(http::Reply::badRequest); + return; + } + SimpleLogger().Write() << "computation successful"; + JSON::Object json_object; + JSON::Array json_array; + const unsigned number_of_locations = phantom_node_vector.size(); + for(unsigned row = 0; row < number_of_locations; ++row) + { + JSON::Array json_row; + auto row_begin_iterator = result_table->begin() + (row*number_of_locations); + auto row_end_iterator = result_table->begin() + ((row+1)*number_of_locations); + json_row.values.insert(json_row.values.end(), row_begin_iterator, row_end_iterator); + json_array.values.push_back(json_row); + } + json_object.values["distance_table"] = json_array; + JSON::render(reply.content, json_object); + } + + private: + std::string descriptor_string; + DataFacadeT *facade; +}; + +#endif // DISTANCE_TABLE_PLUGIN_H diff --git a/Plugins/HelloWorldPlugin.h b/Plugins/HelloWorldPlugin.h index 448bd272ff2..301dbff9f70 100644 --- a/Plugins/HelloWorldPlugin.h +++ b/Plugins/HelloWorldPlugin.h @@ -25,10 +25,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef HELLOWORLDPLUGIN_H_ -#define HELLOWORLDPLUGIN_H_ +#ifndef HELLO_WORLD_PLUGIN_H +#define HELLO_WORLD_PLUGIN_H #include "BasePlugin.h" +#include "../DataStructures/JSONContainer.h" #include "../Util/StringUtil.h" #include @@ -46,70 +47,58 @@ class HelloWorldPlugin : public BasePlugin void HandleRequest(const RouteParameters &routeParameters, http::Reply &reply) { reply.status = http::Reply::ok; - reply.content.emplace_back("Hello World Demonstration " - "Document

Hello, World!

"); - reply.content.emplace_back("
");
-        reply.content.emplace_back("zoom level: ");
-        intToString(routeParameters.zoom_level, temp_string);
-        reply.content.emplace_back(temp_string);
-        reply.content.emplace_back("\nchecksum: ");
-        intToString(routeParameters.check_sum, temp_string);
-        reply.content.emplace_back(temp_string);
-        reply.content.emplace_back("\ninstructions: ");
-        reply.content.emplace_back((routeParameters.print_instructions ? "yes" : "no"));
-        reply.content.emplace_back(temp_string);
-        reply.content.emplace_back("\ngeometry: ");
-        reply.content.emplace_back((routeParameters.geometry ? "yes" : "no"));
-        reply.content.emplace_back("\ncompression: ");
-        reply.content.emplace_back((routeParameters.compression ? "yes" : "no"));
-        reply.content.emplace_back("\noutput format: ");
-        reply.content.emplace_back(routeParameters.output_format);
-        reply.content.emplace_back("\njson parameter: ");
-        reply.content.emplace_back(routeParameters.jsonp_parameter);
-        reply.content.emplace_back("\nlanguage: ");
-        reply.content.emplace_back(routeParameters.language);
-        reply.content.emplace_back("\nNumber of locations: ");
-        intToString(routeParameters.coordinates.size(), temp_string);
-        reply.content.emplace_back(temp_string);
-        reply.content.emplace_back("\n");
 
+        JSON::Object json_result;
+        std::string temp_string;
+        json_result.values["title"] = "Hello World";
+
+        temp_string = IntToString(routeParameters.zoom_level);
+        json_result.values["zoom_level"] = temp_string;
+
+        temp_string = IntToString(routeParameters.check_sum);
+        json_result.values["check_sum"] = temp_string;
+        json_result.values["instructions"] = (routeParameters.print_instructions ? "yes" : "no");
+        json_result.values["geometry"] = (routeParameters.geometry ? "yes" : "no");
+        json_result.values["compression"] = (routeParameters.compression ? "yes" : "no");
+        json_result.values["output_format"] = (!routeParameters.output_format.empty() ? "yes" : "no");
+
+        json_result.values["jsonp_parameter"] = (!routeParameters.jsonp_parameter.empty() ? "yes" : "no");
+        json_result.values["language"] = (!routeParameters.language.empty() ? "yes" : "no");
+
+        temp_string = IntToString(routeParameters.coordinates.size());
+        json_result.values["location_count"] = temp_string;
+
+        JSON::Array json_locations;
         unsigned counter = 0;
         for (const FixedPointCoordinate &coordinate : routeParameters.coordinates)
         {
-            reply.content.emplace_back("  [");
-            intToString(counter, temp_string);
-            reply.content.emplace_back(temp_string);
-            reply.content.emplace_back("] ");
-            doubleToString(coordinate.lat / COORDINATE_PRECISION, temp_string);
-            reply.content.emplace_back(temp_string);
-            reply.content.emplace_back(",");
-            doubleToString(coordinate.lon / COORDINATE_PRECISION, temp_string);
-            reply.content.emplace_back(temp_string);
-            reply.content.emplace_back("\n");
+            JSON::Object json_location;
+            JSON::Array json_coordinates;
+
+            json_coordinates.values.push_back(coordinate.lat / COORDINATE_PRECISION);
+            json_coordinates.values.push_back(coordinate.lon / COORDINATE_PRECISION);
+            json_location.values[IntToString(counter)] = json_coordinates;
+            json_locations.values.push_back(json_location);
             ++counter;
         }
+        json_result.values["locations"] = json_locations;
+        json_result.values["hint_count"] = routeParameters.hints.size();
 
-        reply.content.emplace_back("Number of hints: ");
-        intToString(routeParameters.hints.size(), temp_string);
-        reply.content.emplace_back(temp_string);
-        reply.content.emplace_back("\n");
-
+        JSON::Array json_hints;
         counter = 0;
-        for (const std::string ¤t_string : routeParameters.hints)
+        for (const std::string ¤t_hint : routeParameters.hints)
         {
-            reply.content.emplace_back("  [");
-            intToString(counter, temp_string);
-            reply.content.emplace_back(temp_string);
-            reply.content.emplace_back("] ");
-            reply.content.emplace_back(current_string);
-            reply.content.emplace_back("\n");
+            // JSON::String json_hint_string = current_hint;
+            json_hints.values.push_back(current_hint);
             ++counter;
         }
-        reply.content.emplace_back("
"); + json_result.values["hints"] = json_hints; + + JSON::render(reply.content, json_result); } private: std::string descriptor_string; }; -#endif /* HELLOWORLDPLUGIN_H_ */ +#endif // HELLO_WORLD_PLUGIN_H diff --git a/Plugins/LocatePlugin.h b/Plugins/LocatePlugin.h index 8ff2a459d21..a5e89c684c6 100644 --- a/Plugins/LocatePlugin.h +++ b/Plugins/LocatePlugin.h @@ -29,10 +29,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define LOCATE_PLUGIN_H #include "BasePlugin.h" +#include "../DataStructures/JSONContainer.h" #include "../Util/StringUtil.h" -// locates the nearest node in the road network for a given coordinate. +#include +// locates the nearest node in the road network for a given coordinate. template class LocatePlugin : public BasePlugin { public: @@ -42,70 +44,36 @@ template class LocatePlugin : public BasePlugin void HandleRequest(const RouteParameters &route_parameters, http::Reply &reply) { // check number of parameters - if (route_parameters.coordinates.empty() || !route_parameters.coordinates.front().isValid()) + if (route_parameters.coordinates.empty()) { reply = http::Reply::StockReply(http::Reply::badRequest); return; } - - // query to helpdesk - FixedPointCoordinate result; - std::string tmp; - // json - - if (!route_parameters.jsonp_parameter.empty()) + // check if queried location is sane + if (!route_parameters.coordinates.front().isValid()) { - reply.content.emplace_back(route_parameters.jsonp_parameter); - reply.content.emplace_back("("); + reply = http::Reply::StockReply(http::Reply::badRequest); + return; } - reply.status = http::Reply::ok; - reply.content.emplace_back("{"); + + JSON::Object json_result; + FixedPointCoordinate result; if (!facade->LocateClosestEndPointForCoordinate(route_parameters.coordinates.front(), result)) { - reply.content.emplace_back("\"status\":207,"); - reply.content.emplace_back("\"mapped_coordinate\":[]"); + json_result.values["status"] = 207; } else { - // Write coordinate to stream reply.status = http::Reply::ok; - reply.content.emplace_back("\"status\":0,"); - reply.content.emplace_back("\"mapped_coordinate\":"); - FixedPointCoordinate::convertInternalLatLonToString(result.lat, tmp); - reply.content.emplace_back("["); - reply.content.emplace_back(tmp); - FixedPointCoordinate::convertInternalLatLonToString(result.lon, tmp); - reply.content.emplace_back(","); - reply.content.emplace_back(tmp); - reply.content.emplace_back("]"); + json_result.values["status"] = 0; + JSON::Array json_coordinate; + json_coordinate.values.push_back(result.lat/COORDINATE_PRECISION); + json_coordinate.values.push_back(result.lon/COORDINATE_PRECISION); + json_result.values["mapped_coordinate"] = json_coordinate; } - reply.content.emplace_back("}"); - reply.headers.resize(3); - if (!route_parameters.jsonp_parameter.empty()) - { - reply.content.emplace_back(")"); - reply.headers[1].name = "Content-Type"; - reply.headers[1].value = "text/javascript"; - reply.headers[2].name = "Content-Disposition"; - reply.headers[2].value = "attachment; filename=\"location.js\""; - } - else - { - reply.headers[1].name = "Content-Type"; - reply.headers[1].value = "application/x-javascript"; - reply.headers[2].name = "Content-Disposition"; - reply.headers[2].value = "attachment; filename=\"location.json\""; - } - reply.headers[0].name = "Content-Length"; - unsigned content_length = 0; - for (const std::string &snippet : reply.content) - { - content_length += snippet.length(); - } - intToString(content_length, tmp); - reply.headers[0].value = tmp; - return; + + JSON::render(reply.content, json_result); } private: diff --git a/Plugins/NearestPlugin.h b/Plugins/NearestPlugin.h index 82ded9c2ffd..8dafc2208e3 100644 --- a/Plugins/NearestPlugin.h +++ b/Plugins/NearestPlugin.h @@ -29,10 +29,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define NEAREST_PLUGIN_H #include "BasePlugin.h" +#include "../DataStructures/JSONContainer.h" #include "../DataStructures/PhantomNodes.h" #include "../Util/StringUtil.h" -#include +#include /* * This Plugin locates the nearest point on a street in the road network for a given coordinate. @@ -43,8 +44,6 @@ template class NearestPlugin : public BasePlugin public: explicit NearestPlugin(DataFacadeT *facade) : facade(facade), descriptor_string("nearest") { - descriptor_table.emplace("", 0); // default descriptor - descriptor_table.emplace("json", 1); } const std::string GetDescriptor() const { return descriptor_string; } @@ -57,81 +56,40 @@ template class NearestPlugin : public BasePlugin reply = http::Reply::StockReply(http::Reply::badRequest); return; } + // check if queried location is sane if (!route_parameters.coordinates.front().isValid()) { reply = http::Reply::StockReply(http::Reply::badRequest); return; } - PhantomNode result; + PhantomNode phantom_node; facade->FindPhantomNodeForCoordinate( - route_parameters.coordinates.front(), result, route_parameters.zoom_level); + route_parameters.coordinates.front(), phantom_node, route_parameters.zoom_level); - // json - - if (!route_parameters.jsonp_parameter.empty()) - { - reply.content.emplace_back(route_parameters.jsonp_parameter); - reply.content.emplace_back("("); - } - - reply.status = http::Reply::ok; - reply.content.emplace_back("{\"status\":"); - if (SPECIAL_NODEID != result.forward_node_id) + JSON::Object json_result; + if (!phantom_node.isValid()) { - reply.content.emplace_back("0,"); + json_result.values["status"] = 207; } else { - reply.content.emplace_back("207,"); + reply.status = http::Reply::ok; + json_result.values["status"] = 0; + JSON::Array json_coordinate; + json_coordinate.values.push_back(phantom_node.location.lat/COORDINATE_PRECISION); + json_coordinate.values.push_back(phantom_node.location.lon/COORDINATE_PRECISION); + json_result.values["mapped_coordinate"] = json_coordinate; + std::string temp_string; + facade->GetName(phantom_node.name_id, temp_string); + json_result.values["name"] = temp_string; } - reply.content.emplace_back("\"mapped_coordinate\":["); - std::string temp_string; - if (SPECIAL_NODEID != result.forward_node_id) - { - FixedPointCoordinate::convertInternalLatLonToString(result.location.lat, temp_string); - reply.content.emplace_back(temp_string); - FixedPointCoordinate::convertInternalLatLonToString(result.location.lon, temp_string); - reply.content.emplace_back(","); - reply.content.emplace_back(temp_string); - } - reply.content.emplace_back("],\"name\":\""); - if (SPECIAL_NODEID != result.forward_node_id) - { - facade->GetName(result.name_id, temp_string); - reply.content.emplace_back(temp_string); - } - reply.content.emplace_back("\"}"); - reply.headers.resize(3); - if (!route_parameters.jsonp_parameter.empty()) - { - reply.content.emplace_back(")"); - reply.headers[1].name = "Content-Type"; - reply.headers[1].value = "text/javascript"; - reply.headers[2].name = "Content-Disposition"; - reply.headers[2].value = "attachment; filename=\"location.js\""; - } - else - { - reply.headers[1].name = "Content-Type"; - reply.headers[1].value = "application/x-javascript"; - reply.headers[2].name = "Content-Disposition"; - reply.headers[2].value = "attachment; filename=\"location.json\""; - } - reply.headers[0].name = "Content-Length"; - unsigned content_length = 0; - for (const std::string &snippet : reply.content) - { - content_length += snippet.length(); - } - intToString(content_length, temp_string); - reply.headers[0].value = temp_string; + JSON::render(reply.content, json_result); } private: DataFacadeT *facade; - std::unordered_map descriptor_table; std::string descriptor_string; }; diff --git a/Plugins/TimestampPlugin.h b/Plugins/TimestampPlugin.h index 3fb290ee003..63f63b2db07 100644 --- a/Plugins/TimestampPlugin.h +++ b/Plugins/TimestampPlugin.h @@ -28,8 +28,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef TIMESTAMP_PLUGIN_H #define TIMESTAMP_PLUGIN_H +#include "../DataStructures/JSONContainer.h" #include "BasePlugin.h" +#include + template class TimestampPlugin : public BasePlugin { public: @@ -40,46 +43,12 @@ template class TimestampPlugin : public BasePlugin const std::string GetDescriptor() const { return descriptor_string; } void HandleRequest(const RouteParameters &route_parameters, http::Reply &reply) { - std::string tmp; - - // json - if (!route_parameters.jsonp_parameter.empty()) - { - reply.content.emplace_back(route_parameters.jsonp_parameter); - reply.content.emplace_back("("); - } - reply.status = http::Reply::ok; - reply.content.emplace_back("{"); - reply.content.emplace_back("\"status\":"); - reply.content.emplace_back("0,"); - reply.content.emplace_back("\"timestamp\":\""); - reply.content.emplace_back(facade->GetTimestamp()); - reply.content.emplace_back("\""); - reply.content.emplace_back("}"); - reply.headers.resize(3); - if (!route_parameters.jsonp_parameter.empty()) - { - reply.content.emplace_back(")"); - reply.headers[1].name = "Content-Type"; - reply.headers[1].value = "text/javascript"; - reply.headers[2].name = "Content-Disposition"; - reply.headers[2].value = "attachment; filename=\"timestamp.js\""; - } - else - { - reply.headers[1].name = "Content-Type"; - reply.headers[1].value = "application/x-javascript"; - reply.headers[2].name = "Content-Disposition"; - reply.headers[2].value = "attachment; filename=\"timestamp.json\""; - } - unsigned content_length = 0; - for (const std::string &snippet : reply.content) - { - content_length += snippet.length(); - } - intToString(content_length, tmp); - reply.headers[0].value = tmp; + JSON::Object json_result; + json_result.values["status"] = 0; + const std::string timestamp = facade->GetTimestamp(); + json_result.values["timestamp"] = timestamp; + JSON::render(reply.content, json_result); } private: diff --git a/Plugins/ViaRoutePlugin.h b/Plugins/ViaRoutePlugin.h index 817fa64a2b7..9c0b6f2e8cd 100644 --- a/Plugins/ViaRoutePlugin.h +++ b/Plugins/ViaRoutePlugin.h @@ -25,12 +25,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef VIAROUTEPLUGIN_H_ -#define VIAROUTEPLUGIN_H_ +#ifndef VIA_ROUTE_PLUGIN_H +#define VIA_ROUTE_PLUGIN_H #include "BasePlugin.h" #include "../Algorithms/ObjectToBase64.h" + #include "../DataStructures/QueryEdge.h" #include "../DataStructures/SearchEngine.h" #include "../Descriptors/BaseDescriptor.h" @@ -60,6 +61,7 @@ template class ViaRoutePlugin : public BasePlugin descriptor_table.emplace("json", 0); descriptor_table.emplace("gpx", 1); + // descriptor_table.emplace("geojson", 2); } virtual ~ViaRoutePlugin() {} @@ -119,7 +121,9 @@ template class ViaRoutePlugin : public BasePlugin raw_route.segment_end_coordinates.emplace_back(current_phantom_node_pair); } - if ((route_parameters.alternate_route) && (1 == raw_route.segment_end_coordinates.size())) + const bool is_alternate_requested = route_parameters.alternate_route; + const bool is_only_one_segment = (1 == raw_route.segment_end_coordinates.size()); + if (is_alternate_requested && is_only_one_segment) { search_engine_ptr->alternative_path(raw_route.segment_end_coordinates.front(), raw_route); @@ -135,12 +139,6 @@ template class ViaRoutePlugin : public BasePlugin } reply.status = http::Reply::ok; - if (!route_parameters.jsonp_parameter.empty()) - { - reply.content.emplace_back(route_parameters.jsonp_parameter); - reply.content.emplace_back("("); - } - DescriptorConfig descriptor_config; auto iter = descriptor_table.find(route_parameters.output_format); @@ -160,6 +158,9 @@ template class ViaRoutePlugin : public BasePlugin case 1: descriptor = std::make_shared>(); break; + // case 2: + // descriptor = std::make_shared>(); + // break; default: descriptor = std::make_shared>(); break; @@ -170,67 +171,6 @@ template class ViaRoutePlugin : public BasePlugin phantom_nodes.target_phantom = raw_route.segment_end_coordinates.back().target_phantom; descriptor->SetConfig(descriptor_config); descriptor->Run(raw_route, phantom_nodes, facade, reply); - - if (!route_parameters.jsonp_parameter.empty()) - { - reply.content.emplace_back(")\n"); - } - reply.headers.resize(3); - reply.headers[0].name = "Content-Length"; - - unsigned content_length = 0; - for (const std::string &snippet : reply.content) - { - content_length += snippet.length(); - } - std::string tmp_string; - intToString(content_length, tmp_string); - reply.headers[0].value = tmp_string; - - switch (descriptor_type) - { - case 0: - if (!route_parameters.jsonp_parameter.empty()) - { - reply.headers[1].name = "Content-Type"; - reply.headers[1].value = "text/javascript"; - reply.headers[2].name = "Content-Disposition"; - reply.headers[2].value = "attachment; filename=\"route.js\""; - } - else - { - reply.headers[1].name = "Content-Type"; - reply.headers[1].value = "application/x-javascript"; - reply.headers[2].name = "Content-Disposition"; - reply.headers[2].value = "attachment; filename=\"route.json\""; - } - - break; - case 1: - reply.headers[1].name = "Content-Type"; - reply.headers[1].value = "application/gpx+xml; charset=UTF-8"; - reply.headers[2].name = "Content-Disposition"; - reply.headers[2].value = "attachment; filename=\"route.gpx\""; - - break; - default: - if (!route_parameters.jsonp_parameter.empty()) - { - reply.headers[1].name = "Content-Type"; - reply.headers[1].value = "text/javascript"; - reply.headers[2].name = "Content-Disposition"; - reply.headers[2].value = "attachment; filename=\"route.js\""; - } - else - { - reply.headers[1].name = "Content-Type"; - reply.headers[1].value = "application/x-javascript"; - reply.headers[2].name = "Content-Disposition"; - reply.headers[2].value = "attachment; filename=\"route.json\""; - } - break; - } - return; } private: @@ -238,4 +178,4 @@ template class ViaRoutePlugin : public BasePlugin DataFacadeT *facade; }; -#endif /* VIAROUTEPLUGIN_H_ */ +#endif // VIA_ROUTE_PLUGIN_H diff --git a/RoutingAlgorithms/AlternativePathRouting.h b/RoutingAlgorithms/AlternativePathRouting.h index 8d75ff68275..f70cb5547e1 100644 --- a/RoutingAlgorithms/AlternativePathRouting.h +++ b/RoutingAlgorithms/AlternativePathRouting.h @@ -78,6 +78,7 @@ template class AlternativeRouting : private BasicRoutingInte { if (phantom_node_pair.PhantomNodesHaveEqualLocation()) { + BOOST_ASSERT(false); return; } diff --git a/RoutingAlgorithms/ManyToManyRouting.h b/RoutingAlgorithms/ManyToManyRouting.h new file mode 100644 index 00000000000..6f142e0ec3a --- /dev/null +++ b/RoutingAlgorithms/ManyToManyRouting.h @@ -0,0 +1,246 @@ +/* + +Copyright (c) 2014, Project OSRM, Dennis Luxen, others +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef MANY_TO_MANY_ROUTING_H +#define MANY_TO_MANY_ROUTING_H + +#include "BasicRoutingInterface.h" +#include "../DataStructures/SearchEngineData.h" +#include "../typedefs.h" + +#include + +#include +#include +#include +#include + +template class ManyToManyRouting : public BasicRoutingInterface +{ + typedef BasicRoutingInterface super; + typedef SearchEngineData::QueryHeap QueryHeap; + SearchEngineData &engine_working_data; + + struct NodeBucket + { + unsigned target_id; // essentially a row in the distance matrix + EdgeWeight distance; + NodeBucket(const unsigned target_id, const EdgeWeight distance) + : target_id(target_id), distance(distance) + { + } + }; + typedef std::unordered_map> SearchSpaceWithBuckets; + + public: + ManyToManyRouting(DataFacadeT *facade, SearchEngineData &engine_working_data) + : super(facade), engine_working_data(engine_working_data) + { + } + + ~ManyToManyRouting() {} + + std::shared_ptr> + operator()(const std::vector &phantom_nodes_vector) const + { + const unsigned number_of_locations = phantom_nodes_vector.size(); + std::shared_ptr> result_table = std::make_shared>( + number_of_locations * number_of_locations, std::numeric_limits::max()); + + engine_working_data.InitializeOrClearFirstThreadLocalStorage( + super::facade->GetNumberOfNodes()); + + QueryHeap &query_heap = *(engine_working_data.forwardHeap); + + SearchSpaceWithBuckets search_space_with_buckets; + + unsigned target_id = 0; + for (const PhantomNode &phantom_node : phantom_nodes_vector) + { + query_heap.Clear(); + // insert target(s) at distance 0 + if (SPECIAL_NODEID != phantom_node.forward_node_id) + { + query_heap.Insert(phantom_node.forward_node_id, phantom_node.GetForwardWeightPlusOffset(), phantom_node.forward_node_id); + } + if (SPECIAL_NODEID != phantom_node.reverse_node_id) + { + query_heap.Insert(phantom_node.reverse_node_id, phantom_node.GetReverseWeightPlusOffset(), phantom_node.reverse_node_id); + } + + // explore search space + while (!query_heap.Empty()) + { + BackwardRoutingStep(target_id, query_heap, search_space_with_buckets); + } + ++target_id; + } + + // for each source do forward search + unsigned source_id = 0; + for (const PhantomNode &phantom_node : phantom_nodes_vector) + { + query_heap.Clear(); + // insert sources at distance 0 + if (SPECIAL_NODEID != phantom_node.forward_node_id) + { + query_heap.Insert(phantom_node.forward_node_id, -phantom_node.GetForwardWeightPlusOffset(), phantom_node.forward_node_id); + } + if (SPECIAL_NODEID != phantom_node.reverse_node_id) + { + query_heap.Insert(phantom_node.reverse_node_id, -phantom_node.GetReverseWeightPlusOffset(), phantom_node.reverse_node_id); + } + + // explore search space + while (!query_heap.Empty()) + { + ForwardRoutingStep(source_id, + number_of_locations, + query_heap, + search_space_with_buckets, + result_table); + } + + ++source_id; + } + BOOST_ASSERT(source_id == target_id); + return result_table; + } + + void ForwardRoutingStep(const unsigned source_id, + const unsigned number_of_locations, + QueryHeap &query_heap, + const SearchSpaceWithBuckets &search_space_with_buckets, + std::shared_ptr> result_table) const + { + const NodeID node = query_heap.DeleteMin(); + const int source_distance = query_heap.GetKey(node); + + // check if each encountered node has an entry + const auto bucket_iterator = search_space_with_buckets.find(node); + // iterate bucket if there exists one + if (bucket_iterator != search_space_with_buckets.end()) + { + const std::vector &bucket_list = bucket_iterator->second; + for (const NodeBucket ¤t_bucket : bucket_list) + { + // get target id from bucket entry + const unsigned target_id = current_bucket.target_id; + const unsigned target_distance = current_bucket.distance; + const EdgeWeight current_distance = + (*result_table)[source_id * number_of_locations + target_id]; + // check if new distance is better + if ((source_distance + target_distance) < current_distance) + { + (*result_table)[source_id * number_of_locations + target_id] = + (source_distance + target_distance); + } + } + } + if (StallAtNode(node, source_distance, query_heap)) + { + return; + } + RelaxOutgoingEdges(node, source_distance, query_heap); + } + + void BackwardRoutingStep(const unsigned target_id, + QueryHeap &query_heap, + SearchSpaceWithBuckets &search_space_with_buckets) const + { + const NodeID node = query_heap.DeleteMin(); + const int target_distance = query_heap.GetKey(node); + + // store settled nodes in search space bucket + search_space_with_buckets[node].emplace_back(target_id, target_distance); + + if (StallAtNode(node, target_distance, query_heap)) + { + return; + } + + RelaxOutgoingEdges(node, target_distance, query_heap); + } + + template + inline void + RelaxOutgoingEdges(const NodeID node, const EdgeWeight distance, QueryHeap &query_heap) const + { + for (auto edge : super::facade->GetAdjacentEdgeRange(node)) + { + const auto &data = super::facade->GetEdgeData(edge); + const bool direction_flag = (forward_direction ? data.forward : data.backward); + if (direction_flag) + { + const NodeID to = super::facade->GetTarget(edge); + const int edge_weight = data.distance; + + BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); + const int to_distance = distance + edge_weight; + + // New Node discovered -> Add to Heap + Node Info Storage + if (!query_heap.WasInserted(to)) + { + query_heap.Insert(to, to_distance, node); + } + // Found a shorter Path -> Update distance + else if (to_distance < query_heap.GetKey(to)) + { + // new parent + query_heap.GetData(to).parent = node; + query_heap.DecreaseKey(to, to_distance); + } + } + } + } + + // Stalling + template + inline bool StallAtNode(const NodeID node, const EdgeWeight distance, QueryHeap &query_heap) const + { + for (auto edge : super::facade->GetAdjacentEdgeRange(node)) + { + const auto &data = super::facade->GetEdgeData(edge); + const bool reverse_flag = ((!forward_direction) ? data.forward : data.backward); + if (reverse_flag) + { + const NodeID to = super::facade->GetTarget(edge); + const int edge_weight = data.distance; + BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); + if (query_heap.WasInserted(to)) + { + if (query_heap.GetKey(to) + edge_weight < distance) + { + return true; + } + } + } + } + return false; + } +}; +#endif diff --git a/RoutingAlgorithms/ShortestPathRouting.h b/RoutingAlgorithms/ShortestPathRouting.h index 6a94e3da7dd..819e39d89b7 100644 --- a/RoutingAlgorithms/ShortestPathRouting.h +++ b/RoutingAlgorithms/ShortestPathRouting.h @@ -55,6 +55,7 @@ template class ShortestPathRouting : public BasicRoutingInte { if (phantom_node_pair.AtLeastOnePhantomNodeIsInvalid()) { + BOOST_ASSERT(false); return; } } diff --git a/Server/APIGrammar.h b/Server/APIGrammar.h index 8f343d973c7..4c339d05817 100644 --- a/Server/APIGrammar.h +++ b/Server/APIGrammar.h @@ -45,7 +45,7 @@ struct APIGrammar : qi::grammar zoom = (-qi::lit('&')) >> qi::lit('z') >> '=' >> qi::short_[boost::bind(&HandlerT::setZoomLevel, handler, ::_1)]; output = (-qi::lit('&')) >> qi::lit("output") >> '=' >> string[boost::bind(&HandlerT::setOutputFormat, handler, ::_1)]; jsonp = (-qi::lit('&')) >> qi::lit("jsonp") >> '=' >> stringwithPercent[boost::bind(&HandlerT::setJSONpParameter, handler, ::_1)]; - checksum = (-qi::lit('&')) >> qi::lit("checksum") >> '=' >> qi::int_[boost::bind(&HandlerT::setChecksum, handler, ::_1)]; + checksum = (-qi::lit('&')) >> qi::lit("checksum") >> '=' >> qi::uint_[boost::bind(&HandlerT::setChecksum, handler, ::_1)]; instruction = (-qi::lit('&')) >> qi::lit("instructions") >> '=' >> qi::bool_[boost::bind(&HandlerT::setInstructionFlag, handler, ::_1)]; geometry = (-qi::lit('&')) >> qi::lit("geometry") >> '=' >> qi::bool_[boost::bind(&HandlerT::setGeometryFlag, handler, ::_1)]; cmp = (-qi::lit('&')) >> qi::lit("compression") >> '=' >> qi::bool_[boost::bind(&HandlerT::setCompressionFlag, handler, ::_1)]; diff --git a/Server/Connection.cpp b/Server/Connection.cpp index 29f46ebcf75..09a8b4c2b4b 100644 --- a/Server/Connection.cpp +++ b/Server/Connection.cpp @@ -81,6 +81,7 @@ void Connection::handle_read(const boost::system::error_code &e, std::size_t byt Header compression_header; std::vector compressed_output; std::vector output_buffer; + switch (compression_type) { case deflateRFC1951: @@ -158,7 +159,7 @@ void Connection::handle_write(const boost::system::error_code &e) } } -void Connection::compressBufferCollection(std::vector uncompressed_data, +void Connection::compressBufferCollection(std::vector uncompressed_data, CompressionType compression_type, std::vector &compressed_data) { @@ -171,16 +172,10 @@ void Connection::compressBufferCollection(std::vector uncompressed_ } BOOST_ASSERT(compressed_data.empty()); - boost::iostreams::filtering_ostream compressing_stream; - - compressing_stream.push(boost::iostreams::gzip_compressor(compression_parameters)); - compressing_stream.push(boost::iostreams::back_inserter(compressed_data)); - - for (const std::string &line : uncompressed_data) - { - compressing_stream << line; - } - - compressing_stream.reset(); + boost::iostreams::filtering_ostream gzip_stream; + gzip_stream.push(boost::iostreams::gzip_compressor(compression_parameters)); + gzip_stream.push(boost::iostreams::back_inserter(compressed_data)); + gzip_stream.write(&uncompressed_data[0], uncompressed_data.size()); + boost::iostreams::close(gzip_stream); } } diff --git a/Server/Connection.h b/Server/Connection.h index 122d4fe4a79..2b814582021 100644 --- a/Server/Connection.h +++ b/Server/Connection.h @@ -89,7 +89,7 @@ class Connection : public std::enable_shared_from_this /// Handle completion of a write operation. void handle_write(const boost::system::error_code &e); - void compressBufferCollection(std::vector uncompressed_data, + void compressBufferCollection(std::vector uncompressed_data, CompressionType compression_type, std::vector &compressed_data); diff --git a/Server/Http/Reply.cpp b/Server/Http/Reply.cpp index b2b1b836dee..da429ce34cf 100644 --- a/Server/Http/Reply.cpp +++ b/Server/Http/Reply.cpp @@ -38,7 +38,7 @@ void Reply::setSize(const unsigned size) { if ("Content-Length" == h.name) { - intToString(size, h.value); + h.value = IntToString(size); } } } @@ -47,10 +47,7 @@ void Reply::setSize(const unsigned size) void Reply::SetUncompressedSize() { unsigned uncompressed_size = 0; - for (const std::string ¤t_line : content) - { - uncompressed_size += current_line.size(); - } + uncompressed_size = content.size(); setSize(uncompressed_size); } @@ -66,10 +63,7 @@ std::vector Reply::toBuffers() buffers.push_back(boost::asio::buffer(crlf)); } buffers.push_back(boost::asio::buffer(crlf)); - for (const std::string &line : content) - { - buffers.push_back(boost::asio::buffer(line)); - } + buffers.push_back(boost::asio::buffer(content)); return buffers; } @@ -94,14 +88,15 @@ Reply Reply::StockReply(Reply::status_type status) Reply rep; rep.status = status; rep.content.clear(); - rep.content.push_back(rep.ToString(status)); + + const std::string status_string = rep.ToString(status); + rep.content.insert(rep.content.end(), status_string.begin(), status_string.end()); rep.headers.resize(3); rep.headers[0].name = "Access-Control-Allow-Origin"; rep.headers[0].value = "*"; rep.headers[1].name = "Content-Length"; - std::string size_string; - intToString(rep.content.size(), size_string); + std::string size_string = IntToString(rep.content.size()); rep.headers[1].value = size_string; rep.headers[2].name = "Content-Type"; diff --git a/Server/RequestHandler.cpp b/Server/RequestHandler.cpp index 181032ec41e..cc7ed2273fe 100644 --- a/Server/RequestHandler.cpp +++ b/Server/RequestHandler.cpp @@ -29,6 +29,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "RequestHandler.h" #include "Http/Request.h" +#include "../DataStructures/JSONContainer.h" #include "../Library/OSRM.h" #include "../Util/SimpleLogger.h" #include "../Util/StringUtil.h" @@ -85,18 +86,51 @@ void RequestHandler::handle_request(const http::Request &req, http::Reply &reply reply = http::Reply::StockReply(http::Reply::badRequest); reply.content.clear(); const int position = std::distance(request.begin(), it); - reply.content.push_back( - "{\"status\":400,\"status_message\":\"Query string malformed close to position "); - std::string tmp_position_string; - intToString(position, tmp_position_string); - reply.content.push_back(tmp_position_string); - reply.content.push_back("\"}"); + JSON::Object json_result; + json_result.values["status"] = 400; + std::string tmp_position_string = IntToString(position); + std::string message = ("Query string malformed close to position " + IntToString(position)); + json_result.values["status_message"] = message; + JSON::render(reply.content, json_result); } else { // parsing done, lets call the right plugin to handle the request BOOST_ASSERT_MSG(routing_machine != nullptr, "pointer not init'ed"); + + if (!route_parameters.jsonp_parameter.empty()) + { + const std::string json_p = (route_parameters.jsonp_parameter + "("); + reply.content.insert(reply.content.end(), json_p.begin(), json_p.end()); + } routing_machine->RunQuery(route_parameters, reply); + + // set headers, still ugly and should be reworked + reply.headers.resize(3); + if ("gpx" == route_parameters.output_format) + { + reply.headers[1].name = "Content-Type"; + reply.headers[1].value = "application/gpx+xml; charset=UTF-8"; + reply.headers[2].name = "Content-Disposition"; + reply.headers[2].value = "attachment; filename=\"route.gpx\""; + } + else if (!route_parameters.jsonp_parameter.empty()) + { + reply.content.push_back(')'); + reply.headers[1].name = "Content-Type"; + reply.headers[1].value = "text/javascript"; + reply.headers[2].name = "Content-Disposition"; + reply.headers[2].value = "attachment; filename=\"response.js\""; + } + else + { + reply.headers[1].name = "Content-Type"; + reply.headers[1].value = "application/x-javascript"; + reply.headers[2].name = "Content-Disposition"; + reply.headers[2].value = "attachment; filename=\"response.json\""; + } + reply.headers[0].name = "Content-Length"; + reply.headers[0].value = IntToString(reply.content.size()); return; } } diff --git a/Server/ServerFactory.h b/Server/ServerFactory.h index a61ba2aade3..bc153633e46 100644 --- a/Server/ServerFactory.h +++ b/Server/ServerFactory.h @@ -41,12 +41,8 @@ struct ServerFactory ServerFactory(const ServerFactory &) = delete; static Server *CreateServer(std::string &ip_address, int ip_port, int threads) { - SimpleLogger().Write() << "http 1.1 compression handled by zlib version " << zlibVersion(); - - std::string port_stream; - intToString(ip_port, port_stream); - + std::string port_stream = IntToString(ip_port); return new Server(ip_address, port_stream, std::min(omp_get_num_procs(), threads)); } }; diff --git a/Tools/simpleclient.cpp b/Tools/simpleclient.cpp index 1b76211caa4..68d4b1d6b7b 100644 --- a/Tools/simpleclient.cpp +++ b/Tools/simpleclient.cpp @@ -110,10 +110,10 @@ int main(int argc, const char *argv[]) // attention: super-inefficient hack below: std::stringstream my_stream; - for (const std::string &line : osrm_reply.content) + for (const auto &element : osrm_reply.content) { - std::cout << line; - my_stream << line; + std::cout << element; + my_stream << element; } std::cout << std::endl; diff --git a/Util/StringUtil.h b/Util/StringUtil.h index 87df95c57b9..14b713a236d 100644 --- a/Util/StringUtil.h +++ b/Util/StringUtil.h @@ -80,11 +80,12 @@ auto as_integer(Enumeration const value) return static_cast::type>(value); } -static inline void intToString(const int value, std::string &output) +static inline std::string IntToString(const int value) { - output.clear(); + std::string output; std::back_insert_iterator sink(output); boost::spirit::karma::generate(sink, boost::spirit::karma::int_, value); + return output; } static inline void int64ToString(const int64_t value, std::string &output) @@ -168,11 +169,35 @@ static inline double StringToDouble(const char *p) return r; } -static inline void doubleToString(const double value, std::string &output) +template +struct scientific_policy : boost::spirit::karma::real_policies { - output.clear(); + // we want the numbers always to be in fixed format + static int floatfield(T n) { return boost::spirit::karma::real_policies::fmtflags::fixed; } + static unsigned int precision(T) { return 6; } +}; +typedef +boost::spirit::karma::real_generator > +science_type; + +static inline std::string FixedDoubleToString(const double value) +{ + std::string output; + std::back_insert_iterator sink(output); + boost::spirit::karma::generate(sink, science_type(), value); + if (output.size() >= 2 && output[output.size()-2] == '.' && output[output.size()-1] == '0') + { + output.resize(output.size()-2); + } + return output; +} + +static inline std::string DoubleToString(const double value) +{ + std::string output; std::back_insert_iterator sink(output); - boost::spirit::karma::generate(sink, boost::spirit::karma::double_, value); + boost::spirit::karma::generate(sink, value); + return output; } static inline void doubleToStringWithTwoDigitsBehindComma(const double value, std::string &output) From 2d498cb88bff144327510932da9770fe231bafd6 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Fri, 16 May 2014 16:13:25 +0200 Subject: [PATCH 03/36] adapt JSON parsing in tests to allow for omitted fields --- features/support/route.rb | 68 +++++++++++++++++---------------- features/testbot/status.feature | 4 +- 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/features/support/route.rb b/features/support/route.rb index f32c616006d..2f930751e3b 100644 --- a/features/support/route.rb +++ b/features/support/route.rb @@ -93,10 +93,12 @@ def route_status response end def extract_instruction_list instructions, index, postfix=nil - instructions.reject { |r| r[0].to_s=="#{DESTINATION_REACHED}" }. - map { |r| r[index] }. - map { |r| (r=="" || r==nil) ? '""' : "#{r}#{postfix}" }. - join(',') + if instructions + instructions.reject { |r| r[0].to_s=="#{DESTINATION_REACHED}" }. + map { |r| r[index] }. + map { |r| (r=="" || r==nil) ? '""' : "#{r}#{postfix}" }. + join(',') + end end def way_list instructions @@ -112,33 +114,35 @@ def bearing_list instructions end def turn_list instructions - types = { - 0 => :none, - 1 => :straight, - 2 => :slight_right, - 3 => :right, - 4 => :sharp_right, - 5 => :u_turn, - 6 => :sharp_left, - 7 => :left, - 8 => :slight_left, - 9 => :via, - 10 => :head, - 11 => :enter_roundabout, - 12 => :leave_roundabout, - 13 => :stay_roundabout, - 14 => :start_end_of_street, - 15 => :destination, - 16 => :enter_contraflow, - 17 => :leave_contraflow - } - # replace instructions codes with strings - # "11-3" (enter roundabout and leave a 3rd exit) gets converted to "enter_roundabout-3" - instructions.map do |r| - r[0].to_s.gsub(/^\d*/) do |match| - types[match.to_i].to_s - end - end.join(',') + if instructions + types = { + 0 => :none, + 1 => :straight, + 2 => :slight_right, + 3 => :right, + 4 => :sharp_right, + 5 => :u_turn, + 6 => :sharp_left, + 7 => :left, + 8 => :slight_left, + 9 => :via, + 10 => :head, + 11 => :enter_roundabout, + 12 => :leave_roundabout, + 13 => :stay_roundabout, + 14 => :start_end_of_street, + 15 => :destination, + 16 => :enter_contraflow, + 17 => :leave_contraflow + } + # replace instructions codes with strings + # "11-3" (enter roundabout and leave a 3rd exit) gets converted to "enter_roundabout-3" + instructions.map do |r| + r[0].to_s.gsub(/^\d*/) do |match| + types[match.to_i].to_s + end + end.join(',') + end end def mode_list instructions @@ -151,4 +155,4 @@ def time_list instructions def distance_list instructions extract_instruction_list instructions, 2, "m" -end \ No newline at end of file +end diff --git a/features/testbot/status.feature b/features/testbot/status.feature index 061b614a39e..4fa15ebb3c8 100644 --- a/features/testbot/status.feature +++ b/features/testbot/status.feature @@ -27,7 +27,7 @@ Feature: Status messages | nodes | | ab | | cd | - + When I route I should get | from | to | route | status | message | | a | b | ab | 0 | Found route between points | @@ -64,4 +64,4 @@ Feature: Status messages | viaroute/loc=1,1&loc=1,1 | 400 | Query string malformed close to position 9 | | viaroute/loc=1,1&loc=1,1,1 | 400 | Query string malformed close to position 9 | | viaroute/loc=1,1&loc=x | 400 | Query string malformed close to position 9 | - | viaroute/loc=1,1&loc=x,y | 400 | Query string malformed close to position 9 | \ No newline at end of file + | viaroute/loc=1,1&loc=x,y | 400 | Query string malformed close to position 9 | From d2f19353ed5cb37e8e51ee6d4c834d406d6ed6ee Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Fri, 16 May 2014 16:18:52 +0200 Subject: [PATCH 04/36] remove some debug output --- Descriptors/JSONDescriptor.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/Descriptors/JSONDescriptor.h b/Descriptors/JSONDescriptor.h index d35ccc16734..646d21d6d97 100644 --- a/Descriptors/JSONDescriptor.h +++ b/Descriptors/JSONDescriptor.h @@ -120,8 +120,6 @@ template class JSONDescriptor : public BaseDescriptorGetEscapedNameForNameID(phantom_nodes.source_phantom.name_id); @@ -276,7 +274,6 @@ template class JSONDescriptor : public BaseDescriptor Date: Fri, 16 May 2014 16:40:23 +0200 Subject: [PATCH 05/36] make comparsion explicitly unsigned --- Plugins/DistanceTablePlugin.h | 2 +- Plugins/ViaRoutePlugin.h | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Plugins/DistanceTablePlugin.h b/Plugins/DistanceTablePlugin.h index 7dc88b72be2..0517f6b61a5 100644 --- a/Plugins/DistanceTablePlugin.h +++ b/Plugins/DistanceTablePlugin.h @@ -98,7 +98,7 @@ template class DistanceTablePlugin : public BasePlugin std::vector phantom_node_vector(raw_route.raw_via_node_coordinates.size()); const bool checksum_OK = (route_parameters.check_sum == raw_route.check_sum); - unsigned max_locations = std::min(25, max_locations); + unsigned max_locations = std::min(25u, max_locations); for (unsigned i = 0; i < max_locations; ++i) { if (checksum_OK && i < route_parameters.hints.size() && diff --git a/Plugins/ViaRoutePlugin.h b/Plugins/ViaRoutePlugin.h index 9c0b6f2e8cd..d8ac8b44894 100644 --- a/Plugins/ViaRoutePlugin.h +++ b/Plugins/ViaRoutePlugin.h @@ -39,6 +39,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "../Descriptors/JSONDescriptor.h" #include "../Util/SimpleLogger.h" #include "../Util/StringUtil.h" +#include "../Util/TimingUtil.h" #include @@ -123,6 +124,7 @@ template class ViaRoutePlugin : public BasePlugin const bool is_alternate_requested = route_parameters.alternate_route; const bool is_only_one_segment = (1 == raw_route.segment_end_coordinates.size()); + TIMER_START(routing); if (is_alternate_requested && is_only_one_segment) { search_engine_ptr->alternative_path(raw_route.segment_end_coordinates.front(), @@ -132,6 +134,8 @@ template class ViaRoutePlugin : public BasePlugin { search_engine_ptr->shortest_path(raw_route.segment_end_coordinates, raw_route); } + TIMER_STOP(routing); + SimpleLogger().Write() << "routing took " << TIMER_MSEC(routing) << "ms"; if (INVALID_EDGE_WEIGHT == raw_route.shortest_path_length) { @@ -170,7 +174,10 @@ template class ViaRoutePlugin : public BasePlugin phantom_nodes.source_phantom = raw_route.segment_end_coordinates.front().source_phantom; phantom_nodes.target_phantom = raw_route.segment_end_coordinates.back().target_phantom; descriptor->SetConfig(descriptor_config); + TIMER_START(descriptor); descriptor->Run(raw_route, phantom_nodes, facade, reply); + TIMER_STOP(descriptor); + SimpleLogger().Write() << "descriptor took " << TIMER_MSEC(descriptor) << "ms"; } private: From f4c23f3259db1e5091bc414aec0d94032bb684f5 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Fri, 16 May 2014 16:47:10 +0200 Subject: [PATCH 06/36] fix comparison --- Plugins/DistanceTablePlugin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/DistanceTablePlugin.h b/Plugins/DistanceTablePlugin.h index 0517f6b61a5..2114f6d85cd 100644 --- a/Plugins/DistanceTablePlugin.h +++ b/Plugins/DistanceTablePlugin.h @@ -98,7 +98,7 @@ template class DistanceTablePlugin : public BasePlugin std::vector phantom_node_vector(raw_route.raw_via_node_coordinates.size()); const bool checksum_OK = (route_parameters.check_sum == raw_route.check_sum); - unsigned max_locations = std::min(25u, max_locations); + unsigned max_locations = std::min(25u, raw_route.raw_via_node_coordinates.size()); for (unsigned i = 0; i < max_locations; ++i) { if (checksum_OK && i < route_parameters.hints.size() && From a69b3535a540f2a6678000768ff545afd2f42e95 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Fri, 16 May 2014 16:49:10 +0200 Subject: [PATCH 07/36] fix typo --- Plugins/DistanceTablePlugin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/DistanceTablePlugin.h b/Plugins/DistanceTablePlugin.h index 2114f6d85cd..c2f8ab19e82 100644 --- a/Plugins/DistanceTablePlugin.h +++ b/Plugins/DistanceTablePlugin.h @@ -98,7 +98,7 @@ template class DistanceTablePlugin : public BasePlugin std::vector phantom_node_vector(raw_route.raw_via_node_coordinates.size()); const bool checksum_OK = (route_parameters.check_sum == raw_route.check_sum); - unsigned max_locations = std::min(25u, raw_route.raw_via_node_coordinates.size()); + unsigned max_locations = std::min((std::size_t)25, raw_route.raw_via_node_coordinates.size()); for (unsigned i = 0; i < max_locations; ++i) { if (checksum_OK && i < route_parameters.hints.size() && From da5eec1c5f00c286a49148c1a70a0a4e1d7067d2 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Sun, 18 May 2014 12:19:20 +0200 Subject: [PATCH 08/36] refactor route name extraction into its own class, fix name extraction --- Algorithms/ExtractRouteNames.h | 143 +++++++++++++++++++++++++++++++++ Descriptors/JSONDescriptor.h | 125 ++++------------------------ 2 files changed, 158 insertions(+), 110 deletions(-) create mode 100644 Algorithms/ExtractRouteNames.h diff --git a/Algorithms/ExtractRouteNames.h b/Algorithms/ExtractRouteNames.h new file mode 100644 index 00000000000..2727074c0ce --- /dev/null +++ b/Algorithms/ExtractRouteNames.h @@ -0,0 +1,143 @@ +/* + +Copyright (c) 2013, Project OSRM, Dennis Luxen, others +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef EXTRACT_ROUTE_NAMES_H +#define EXTRACT_ROUTE_NAMES_H + +#include + +#include +#include +#include + +struct RouteNames +{ + std::string shortest_path_name_1; + std::string shortest_path_name_2; + std::string alternative_path_name_1; + std::string alternative_path_name_2; +}; + +template struct ExtractRouteNames +{ + // TODO: break into function object with several functions, i.e. simplify + // construct routes names + RouteNames operator()(std::vector &shortest_path_segments, + std::vector &alternative_path_segments, + const DataFacadeT *facade) + { + RouteNames route_names; + + SegmentT shortest_segment_1, shortest_segment_2; + SegmentT alternative_segment_1, alternative_segment_2; + + auto length_comperator = [](SegmentT a, SegmentT b) + { return a.length > b.length; }; + auto name_id_comperator = [](SegmentT a, SegmentT b) + { return a.name_id < b.name_id; }; + + if (shortest_path_segments.empty()) + { + return route_names; + } + + std::sort(shortest_path_segments.begin(), shortest_path_segments.end(), length_comperator); + shortest_segment_1 = shortest_path_segments[0]; + if (!alternative_path_segments.empty()) + { + std::sort(alternative_path_segments.begin(), + alternative_path_segments.end(), + length_comperator); + alternative_segment_1 = alternative_path_segments[0]; + } + std::vector shortest_path_set_difference(shortest_path_segments.size()); + std::vector alternative_path_set_difference(alternative_path_segments.size()); + std::set_difference(shortest_path_segments.begin(), + shortest_path_segments.end(), + alternative_path_segments.begin(), + alternative_path_segments.end(), + shortest_path_set_difference.begin(), + name_id_comperator); + int size_of_difference = shortest_path_set_difference.size(); + if (size_of_difference) + { + int i = 0; + while (i < size_of_difference && + shortest_path_set_difference[i].name_id == shortest_path_segments[0].name_id) + { + ++i; + } + if (i < size_of_difference) + { + shortest_segment_2 = shortest_path_set_difference[i]; + } + } + + std::set_difference(alternative_path_segments.begin(), + alternative_path_segments.end(), + shortest_path_segments.begin(), + shortest_path_segments.end(), + alternative_path_set_difference.begin(), + name_id_comperator); + size_of_difference = alternative_path_set_difference.size(); + if (size_of_difference) + { + int i = 0; + while (i < size_of_difference && + alternative_path_set_difference[i].name_id == + alternative_path_segments[0].name_id) + { + ++i; + } + if (i < size_of_difference) + { + alternative_segment_2 = alternative_path_set_difference[i]; + } + } + if (shortest_segment_1.position > shortest_segment_2.position) + { + std::swap(shortest_segment_1, shortest_segment_2); + } + if (alternative_segment_1.position > alternative_segment_2.position) + { + std::swap(alternative_segment_1, alternative_segment_2); + } + route_names.shortest_path_name_1 = + facade->GetEscapedNameForNameID(shortest_segment_1.name_id); + route_names.shortest_path_name_2 = + facade->GetEscapedNameForNameID(shortest_segment_2.name_id); + + route_names.alternative_path_name_1 = + facade->GetEscapedNameForNameID(alternative_segment_1.name_id); + route_names.alternative_path_name_2 = + facade->GetEscapedNameForNameID(alternative_segment_2.name_id); + + return route_names; + } +}; + +#endif // EXTRACT_ROUTE_NAMES_H diff --git a/Descriptors/JSONDescriptor.h b/Descriptors/JSONDescriptor.h index 646d21d6d97..6127e5a8269 100644 --- a/Descriptors/JSONDescriptor.h +++ b/Descriptors/JSONDescriptor.h @@ -31,6 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "BaseDescriptor.h" #include "DescriptionFactory.h" #include "../Algorithms/ObjectToBase64.h" +#include "../Algorithms/ExtractRouteNames.h" #include "../DataStructures/JSONContainer.h" #include "../DataStructures/SegmentInformation.h" #include "../DataStructures/TurnInstructions.h" @@ -46,8 +47,7 @@ template class JSONDescriptor : public BaseDescriptor class JSONDescriptor : public BaseDescriptor shortest_path_segments, alternative_path_segments; std::vector shortest_leg_end_indices, alternative_leg_end_indices; + ExtractRouteNames GenerateRouteNames; - struct RouteNames - { - std::string shortest_path_name_1; - std::string shortest_path_name_2; - std::string alternative_path_name_1; - std::string alternative_path_name_2; - }; public: JSONDescriptor() : facade(nullptr), entered_restricted_area_count(0) @@ -166,14 +160,6 @@ template class JSONDescriptor : public BaseDescriptorGetEscapedNameForNameID(description_factory.summary.target_name_id); json_result.values["route_summary"] = json_route_summary; - // Get Names for both routes - RouteNames route_names; - JSON::Array json_route_names; - GetRouteNames(shortest_path_segments, alternative_path_segments, facade, route_names); - json_route_names.values.push_back(route_names.shortest_path_name_1); - json_route_names.values.push_back(route_names.shortest_path_name_2); - json_result.values["route_name"] = json_route_names; - BOOST_ASSERT(!raw_route.segment_end_coordinates.empty()); JSON::Array json_via_points_array; @@ -244,16 +230,25 @@ template class JSONDescriptor : public BaseDescriptor class JSONDescriptor : public BaseDescriptor &shortest_path_segments, - std::vector &alternative_path_segments, - const DataFacadeT *facade, - RouteNames &route_names) - { - Segment shortest_segment_1, shortest_segment_2; - Segment alternative_segment_1, alternative_segment_2; - - auto length_comperator = [](Segment a, Segment b) - { return a.length < b.length; }; - auto name_id_comperator = [](Segment a, Segment b) - { return a.name_id < b.name_id; }; - - if (!shortest_path_segments.empty()) - { - std::sort( - shortest_path_segments.begin(), shortest_path_segments.end(), length_comperator); - shortest_segment_1 = shortest_path_segments[0]; - if (!alternative_path_segments.empty()) - { - std::sort(alternative_path_segments.begin(), - alternative_path_segments.end(), - length_comperator); - alternative_segment_1 = alternative_path_segments[0]; - } - std::vector shortest_path_set_difference(shortest_path_segments.size()); - std::vector alternative_path_set_difference(alternative_path_segments.size()); - std::set_difference(shortest_path_segments.begin(), - shortest_path_segments.end(), - alternative_path_segments.begin(), - alternative_path_segments.end(), - shortest_path_set_difference.begin(), - length_comperator); - int size_of_difference = shortest_path_set_difference.size(); - if (size_of_difference) - { - int i = 0; - while (i < size_of_difference && - shortest_path_set_difference[i].name_id == shortest_path_segments[0].name_id) - { - ++i; - } - if (i < size_of_difference) - { - shortest_segment_2 = shortest_path_set_difference[i]; - } - } - - std::set_difference(alternative_path_segments.begin(), - alternative_path_segments.end(), - shortest_path_segments.begin(), - shortest_path_segments.end(), - alternative_path_set_difference.begin(), - name_id_comperator); - size_of_difference = alternative_path_set_difference.size(); - if (size_of_difference) - { - int i = 0; - while (i < size_of_difference && - alternative_path_set_difference[i].name_id == alternative_path_segments[0].name_id) - { - ++i; - } - if (i < size_of_difference) - { - alternative_segment_2 = alternative_path_set_difference[i]; - } - } - if (shortest_segment_1.position > shortest_segment_2.position) - { - std::swap(shortest_segment_1, shortest_segment_2); - } - if (alternative_segment_1.position > alternative_segment_2.position) - { - std::swap(alternative_segment_1, alternative_segment_2); - } - route_names.shortest_path_name_1 = - facade->GetEscapedNameForNameID(shortest_segment_1.name_id); - route_names.shortest_path_name_2 = - facade->GetEscapedNameForNameID(shortest_segment_2.name_id); - - route_names.alternative_path_name_1 = - facade->GetEscapedNameForNameID(alternative_segment_1.name_id); - route_names.alternative_path_name_2 = - facade->GetEscapedNameForNameID(alternative_segment_2.name_id); - } - } - // TODO: reorder parameters inline void BuildTextualDescription(DescriptionFactory &description_factory, JSON::Array & json_instruction_array, From ef206eb4d9473642bd322c44bf4aa8611ecdc3ed Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Sun, 18 May 2014 12:27:31 +0200 Subject: [PATCH 09/36] clean up code a bit --- Algorithms/ExtractRouteNames.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Algorithms/ExtractRouteNames.h b/Algorithms/ExtractRouteNames.h index 2727074c0ce..dee7a1a8645 100644 --- a/Algorithms/ExtractRouteNames.h +++ b/Algorithms/ExtractRouteNames.h @@ -42,10 +42,9 @@ struct RouteNames std::string alternative_path_name_2; }; +// construct routes names template struct ExtractRouteNames { - // TODO: break into function object with several functions, i.e. simplify - // construct routes names RouteNames operator()(std::vector &shortest_path_segments, std::vector &alternative_path_segments, const DataFacadeT *facade) From a47467f29bb1462f28c408786820c6b033c092f0 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Sun, 18 May 2014 12:37:53 +0200 Subject: [PATCH 10/36] store facade ptr in c'tor, save a param in sub-sequent function calls --- DataStructures/Coordinate.cpp | 89 +++++++++++++++++++++++++++++++++++ Descriptors/BaseDescriptor.h | 3 +- Descriptors/GPXDescriptor.h | 4 +- Descriptors/JSONDescriptor.h | 8 +--- Plugins/ViaRoutePlugin.h | 6 +-- 5 files changed, 97 insertions(+), 13 deletions(-) diff --git a/DataStructures/Coordinate.cpp b/DataStructures/Coordinate.cpp index 3ee0176a358..8383b6abc57 100644 --- a/DataStructures/Coordinate.cpp +++ b/DataStructures/Coordinate.cpp @@ -329,3 +329,92 @@ void FixedPointCoordinate::Output(std::ostream &out) const { out << "(" << lat / COORDINATE_PRECISION << "," << lon / COORDINATE_PRECISION << ")"; } + + +// double PointSegmentDistanceSquared( double px, double py, +// double p1x, double p1y, +// double p2x, double p2y, +// double& t, +// double& qx, double& qy) +// { +// static const double kMinSegmentLenSquared = 0.00000001; // adjust to suit. If you use float, you'll probably want something like 0.000001f +// static const double kEpsilon = 1.0E-14; // adjust to suit. If you use floats, you'll probably want something like 1E-7f +// double dx = p2x - p1x; +// double dy = p2y - p1y; +// double dp1x = px - p1x; +// double dp1y = py - p1y; +// const double segLenSquared = (dx * dx) + (dy * dy); +// if (segLenSquared >= -kMinSegmentLenSquared && segLenSquared <= kMinSegmentLenSquared) +// { +// // segment is a point. +// qx = p1x; +// qy = p1y; +// t = 0.0; +// return ((dp1x * dp1x) + (dp1y * dp1y)); +// } +// else +// { +// // Project a line from p to the segment [p1,p2]. By considering the line +// // extending the segment, parameterized as p1 + (t * (p2 - p1)), +// // we find projection of point p onto the line. +// // It falls where t = [(p - p1) . (p2 - p1)] / |p2 - p1|^2 +// t = ((dp1x * dx) + (dp1y * dy)) / segLenSquared; +// if (t < kEpsilon) +// { +// // intersects at or to the "left" of first segment vertex (p1x, p1y). If t is approximately 0.0, then +// // intersection is at p1. If t is less than that, then there is no intersection (i.e. p is not within +// // the 'bounds' of the segment) +// if (t > -kEpsilon) +// { +// // intersects at 1st segment vertex +// t = 0.0; +// } +// // set our 'intersection' point to p1. +// qx = p1x; +// qy = p1y; +// // Note: If you wanted the ACTUAL intersection point of where the projected lines would intersect if +// // we were doing PointLineDistanceSquared, then qx would be (p1x + (t * dx)) and qy would be (p1y + (t * dy)). +// } +// else if (t > (1.0 - kEpsilon)) +// { +// // intersects at or to the "right" of second segment vertex (p2x, p2y). If t is approximately 1.0, then +// // intersection is at p2. If t is greater than that, then there is no intersection (i.e. p is not within +// // the 'bounds' of the segment) +// if (t < (1.0 + kEpsilon)) +// { +// // intersects at 2nd segment vertex +// t = 1.0; +// } +// // set our 'intersection' point to p2. +// qx = p2x; +// qy = p2y; +// // Note: If you wanted the ACTUAL intersection point of where the projected lines would intersect if +// // we were doing PointLineDistanceSquared, then qx would be (p1x + (t * dx)) and qy would be (p1y + (t * dy)). +// } +// else +// { +// // The projection of the point to the point on the segment that is perpendicular succeeded and the point +// // is 'within' the bounds of the segment. Set the intersection point as that projected point. +// qx = p1x + (t * dx); +// qy = p1y + (t * dy); +// } +// // return the squared distance from p to the intersection point. Note that we return the squared distance +// // as an optimization because many times you just need to compare relative distances and the squared values +// // works fine for that. If you want the ACTUAL distance, just take the square root of this value. +// double dpqx = px - qx; +// double dpqy = py - qy; +// return ((dpqx * dpqx) + (dpqy * dpqy)); +// } +// } + +// public float DistanceOfPointToLine2(PointF p1, PointF p2, PointF p) +// { +// // (y1-y2)x + (x2-x1)y + (x1y2-x2y1) +// //d(P,L) = -------------------------------- +// // sqrt( (x2-x1)pow2 + (y2-y1)pow2 ) + +// double ch = (p1.Y - p2.Y) * p.X + (p2.X - p1.X) * p.Y + (p1.X * p2.Y - p2.X * p1.Y); +// double del = Math.Sqrt(Math.Pow(p2.X - p1.X, 2) + Math.Pow(p2.Y - p1.Y, 2)); +// double d = ch / del; +// return (float)d; +// } diff --git a/Descriptors/BaseDescriptor.h b/Descriptors/BaseDescriptor.h index 60d28635594..5177ebed002 100644 --- a/Descriptors/BaseDescriptor.h +++ b/Descriptors/BaseDescriptor.h @@ -51,12 +51,11 @@ struct DescriptorConfig template class BaseDescriptor { public: - BaseDescriptor() {} //TODO: initialize facade here. + BaseDescriptor() {} // Maybe someone can explain the pure virtual destructor thing to me (dennis) virtual ~BaseDescriptor() {} virtual void Run(const RawRouteData &raw_route, const PhantomNodes &phantom_nodes, - DataFacadeT *facade, http::Reply &reply) = 0; virtual void SetConfig(const DescriptorConfig &config) = 0; }; diff --git a/Descriptors/GPXDescriptor.h b/Descriptors/GPXDescriptor.h index 027cb831371..020e0a3caba 100644 --- a/Descriptors/GPXDescriptor.h +++ b/Descriptors/GPXDescriptor.h @@ -35,6 +35,7 @@ template class GPXDescriptor : public BaseDescriptor & output) { @@ -56,12 +57,13 @@ template class GPXDescriptor : public BaseDescriptor" diff --git a/Descriptors/JSONDescriptor.h b/Descriptors/JSONDescriptor.h index 6127e5a8269..46b85aba28a 100644 --- a/Descriptors/JSONDescriptor.h +++ b/Descriptors/JSONDescriptor.h @@ -72,7 +72,7 @@ template class JSONDescriptor : public BaseDescriptor class JSONDescriptor : public BaseDescriptor class JSONDescriptor : public BaseDescriptor class JSONDescriptor : public BaseDescriptor class JSONDescriptor : public BaseDescriptor &route_segments_list) { // Segment information has following format: diff --git a/Plugins/ViaRoutePlugin.h b/Plugins/ViaRoutePlugin.h index d8ac8b44894..89bbb2e443d 100644 --- a/Plugins/ViaRoutePlugin.h +++ b/Plugins/ViaRoutePlugin.h @@ -160,13 +160,13 @@ template class ViaRoutePlugin : public BasePlugin // descriptor = std::make_shared>(); // break; case 1: - descriptor = std::make_shared>(); + descriptor = std::make_shared>(facade); break; // case 2: // descriptor = std::make_shared>(); // break; default: - descriptor = std::make_shared>(); + descriptor = std::make_shared>(facade); break; } @@ -175,7 +175,7 @@ template class ViaRoutePlugin : public BasePlugin phantom_nodes.target_phantom = raw_route.segment_end_coordinates.back().target_phantom; descriptor->SetConfig(descriptor_config); TIMER_START(descriptor); - descriptor->Run(raw_route, phantom_nodes, facade, reply); + descriptor->Run(raw_route, phantom_nodes, reply); TIMER_STOP(descriptor); SimpleLogger().Write() << "descriptor took " << TIMER_MSEC(descriptor) << "ms"; } From 8983c0f92718cae19232037edfa01fd5dd3a83c7 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Sun, 18 May 2014 13:06:00 +0200 Subject: [PATCH 11/36] move GetBearing(.) function into FixedPointCoordinate --- DataStructures/Coordinate.cpp | 893 +++++++++++++++-------------- Descriptors/DescriptionFactory.cpp | 33 -- Descriptors/DescriptionFactory.h | 4 +- Descriptors/JSONDescriptor.h | 713 ++++++++++++----------- Include/osrm/Coordinate.h | 7 + 5 files changed, 837 insertions(+), 813 deletions(-) diff --git a/DataStructures/Coordinate.cpp b/DataStructures/Coordinate.cpp index 8383b6abc57..05ab8c2049a 100644 --- a/DataStructures/Coordinate.cpp +++ b/DataStructures/Coordinate.cpp @@ -1,420 +1,473 @@ -/* - -Copyright (c) 2013, Project OSRM, Dennis Luxen, others -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this list -of conditions and the following disclaimer. -Redistributions in binary form must reproduce the above copyright notice, this -list of conditions and the following disclaimer in the documentation and/or -other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include -#include "../Util/MercatorUtil.h" -#include "../Util/SimpleLogger.h" -#include "../Util/StringUtil.h" - -#include - -#ifndef NDEBUG -#include -#endif -#include -#include - -FixedPointCoordinate::FixedPointCoordinate() - : lat(std::numeric_limits::min()), lon(std::numeric_limits::min()) -{ -} - -FixedPointCoordinate::FixedPointCoordinate(int lat, int lon) : lat(lat), lon(lon) -{ -#ifndef NDEBUG - if (0 != (std::abs(lat) >> 30)) - { - std::bitset<32> y(lat); - SimpleLogger().Write(logDEBUG) << "broken lat: " << lat << ", bits: " << y; - } - if (0 != (std::abs(lon) >> 30)) - { - std::bitset<32> x(lon); - SimpleLogger().Write(logDEBUG) << "broken lon: " << lon << ", bits: " << x; - } -#endif -} - -void FixedPointCoordinate::Reset() -{ - lat = std::numeric_limits::min(); - lon = std::numeric_limits::min(); -} -bool FixedPointCoordinate::isSet() const -{ - return (std::numeric_limits::min() != lat) && (std::numeric_limits::min() != lon); -} -bool FixedPointCoordinate::isValid() const -{ - if (lat > 90 * COORDINATE_PRECISION || lat < -90 * COORDINATE_PRECISION || - lon > 180 * COORDINATE_PRECISION || lon < -180 * COORDINATE_PRECISION) - { - return false; - } - return true; -} -bool FixedPointCoordinate::operator==(const FixedPointCoordinate &other) const -{ - return lat == other.lat && lon == other.lon; -} - -double FixedPointCoordinate::ApproximateDistance(const int lat1, - const int lon1, - const int lat2, - const int lon2) -{ - BOOST_ASSERT(lat1 != std::numeric_limits::min()); - BOOST_ASSERT(lon1 != std::numeric_limits::min()); - BOOST_ASSERT(lat2 != std::numeric_limits::min()); - BOOST_ASSERT(lon2 != std::numeric_limits::min()); - double RAD = 0.017453292519943295769236907684886; - double lt1 = lat1 / COORDINATE_PRECISION; - double ln1 = lon1 / COORDINATE_PRECISION; - double lt2 = lat2 / COORDINATE_PRECISION; - double ln2 = lon2 / COORDINATE_PRECISION; - double dlat1 = lt1 * (RAD); - - double dlong1 = ln1 * (RAD); - double dlat2 = lt2 * (RAD); - double dlong2 = ln2 * (RAD); - - double dLong = dlong1 - dlong2; - double dLat = dlat1 - dlat2; - - double aHarv = pow(sin(dLat / 2.0), 2.0) + cos(dlat1) * cos(dlat2) * pow(sin(dLong / 2.), 2); - double cHarv = 2. * atan2(sqrt(aHarv), sqrt(1.0 - aHarv)); - // earth radius varies between 6,356.750-6,378.135 km (3,949.901-3,963.189mi) - // The IUGG value for the equatorial radius is 6378.137 km (3963.19 miles) - const double earth = 6372797.560856; - return earth * cHarv; -} - -double FixedPointCoordinate::ApproximateDistance(const FixedPointCoordinate &c1, - const FixedPointCoordinate &c2) -{ - return ApproximateDistance(c1.lat, c1.lon, c2.lat, c2.lon); -} - -double FixedPointCoordinate::ApproximateEuclideanDistance(const FixedPointCoordinate &c1, - const FixedPointCoordinate &c2) -{ - return ApproximateEuclideanDistance(c1.lat, c1.lon, c2.lat, c2.lon); -} - -double FixedPointCoordinate::ApproximateEuclideanDistance(const int lat1, - const int lon1, - const int lat2, - const int lon2) -{ - BOOST_ASSERT(lat1 != std::numeric_limits::min()); - BOOST_ASSERT(lon1 != std::numeric_limits::min()); - BOOST_ASSERT(lat2 != std::numeric_limits::min()); - BOOST_ASSERT(lon2 != std::numeric_limits::min()); - - const double RAD = 0.017453292519943295769236907684886; - const double float_lat1 = (lat1 / COORDINATE_PRECISION) * RAD; - const double float_lon1 = (lon1 / COORDINATE_PRECISION) * RAD; - const double float_lat2 = (lat2 / COORDINATE_PRECISION) * RAD; - const double float_lon2 = (lon2 / COORDINATE_PRECISION) * RAD; - - const double x = (float_lon2 - float_lon1) * cos((float_lat1 + float_lat2) / 2.); - const double y = (float_lat2 - float_lat1); - const double earth_radius = 6372797.560856; - return sqrt(x * x + y * y) * earth_radius; -} - -// Yuck! Code duplication. This function is also in EgdeBasedNode.h -double FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordinate &point, - const FixedPointCoordinate &segA, - const FixedPointCoordinate &segB) -{ - const double x = lat2y(point.lat / COORDINATE_PRECISION); - const double y = point.lon / COORDINATE_PRECISION; - const double a = lat2y(segA.lat / COORDINATE_PRECISION); - const double b = segA.lon / COORDINATE_PRECISION; - const double c = lat2y(segB.lat / COORDINATE_PRECISION); - const double d = segB.lon / COORDINATE_PRECISION; - double p, q, nY; - if (std::abs(a - c) > std::numeric_limits::epsilon()) - { - const double m = (d - b) / (c - a); // slope - // Projection of (x,y) on line joining (a,b) and (c,d) - p = ((x + (m * y)) + (m * m * a - m * b)) / (1. + m * m); - q = b + m * (p - a); - } - else - { - p = c; - q = y; - } - nY = (d * p - c * q) / (a * d - b * c); - - // discretize the result to coordinate precision. it's a hack! - if (std::abs(nY) < (1. / COORDINATE_PRECISION)) - { - nY = 0.; - } - - double r = (p - nY * a) / c; - if (std::isnan(r)) - { - r = ((segB.lat == point.lat) && (segB.lon == point.lon)) ? 1. : 0.; - } - else if (std::abs(r) <= std::numeric_limits::epsilon()) - { - r = 0.; - } - else if (std::abs(r - 1.) <= std::numeric_limits::epsilon()) - { - r = 1.; - } - FixedPointCoordinate nearest_location; - BOOST_ASSERT(!std::isnan(r)); - if (r <= 0.) - { // point is "left" of edge - nearest_location.lat = segA.lat; - nearest_location.lon = segA.lon; - } - else if (r >= 1.) - { // point is "right" of edge - nearest_location.lat = segB.lat; - nearest_location.lon = segB.lon; - } - else - { // point lies in between - nearest_location.lat = y2lat(p) * COORDINATE_PRECISION; - nearest_location.lon = q * COORDINATE_PRECISION; - } - BOOST_ASSERT(nearest_location.isValid()); - const double approximated_distance = - FixedPointCoordinate::ApproximateDistance(point, nearest_location); - BOOST_ASSERT(0. <= approximated_distance); - return approximated_distance; -} - -double FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordinate &coord_a, - const FixedPointCoordinate &coord_b, - const FixedPointCoordinate &query_location, - FixedPointCoordinate &nearest_location, - double &r) -{ - BOOST_ASSERT(query_location.isValid()); - - const double x = lat2y(query_location.lat / COORDINATE_PRECISION); - const double y = query_location.lon / COORDINATE_PRECISION; - const double a = lat2y(coord_a.lat / COORDINATE_PRECISION); - const double b = coord_a.lon / COORDINATE_PRECISION; - const double c = lat2y(coord_b.lat / COORDINATE_PRECISION); - const double d = coord_b.lon / COORDINATE_PRECISION; - double p, q /*,mX*/, nY; - if (std::abs(a - c) > std::numeric_limits::epsilon()) - { - const double m = (d - b) / (c - a); // slope - // Projection of (x,y) on line joining (a,b) and (c,d) - p = ((x + (m * y)) + (m * m * a - m * b)) / (1. + m * m); - q = b + m * (p - a); - } - else - { - p = c; - q = y; - } - nY = (d * p - c * q) / (a * d - b * c); - - // discretize the result to coordinate precision. it's a hack! - if (std::abs(nY) < (1. / COORDINATE_PRECISION)) - { - nY = 0.; - } - - r = (p - nY * a) / c; // These values are actually n/m+n and m/m+n , we need - // not calculate the explicit values of m an n as we - // are just interested in the ratio - if (std::isnan(r)) - { - r = ((coord_b.lat == query_location.lat) && (coord_b.lon == query_location.lon)) ? 1. : 0.; - } - else if (std::abs(r) <= std::numeric_limits::epsilon()) - { - r = 0.; - } - else if (std::abs(r - 1.) <= std::numeric_limits::epsilon()) - { - r = 1.; - } - BOOST_ASSERT(!std::isnan(r)); - if (r <= 0.) - { - nearest_location.lat = coord_a.lat; - nearest_location.lon = coord_a.lon; - } - else if (r >= 1.) - { - nearest_location.lat = coord_b.lat; - nearest_location.lon = coord_b.lon; - } - else - { - // point lies in between - nearest_location.lat = y2lat(p) * COORDINATE_PRECISION; - nearest_location.lon = q * COORDINATE_PRECISION; - } - BOOST_ASSERT(nearest_location.isValid()); - - // TODO: Replace with euclidean approximation when k-NN search is done - // const double approximated_distance = FixedPointCoordinate::ApproximateEuclideanDistance( - const double approximated_distance = - FixedPointCoordinate::ApproximateDistance(query_location, nearest_location); - BOOST_ASSERT(0. <= approximated_distance); - return approximated_distance; -} - -void FixedPointCoordinate::convertInternalLatLonToString(const int value, std::string &output) -{ - char buffer[12]; - buffer[11] = 0; // zero termination - output = printInt<11, 6>(buffer, value); -} - -void FixedPointCoordinate::convertInternalCoordinateToString(const FixedPointCoordinate &coord, - std::string &output) -{ - std::string tmp; - tmp.reserve(23); - convertInternalLatLonToString(coord.lon, tmp); - output = tmp; - output += ","; - convertInternalLatLonToString(coord.lat, tmp); - output += tmp; -} - -void -FixedPointCoordinate::convertInternalReversedCoordinateToString(const FixedPointCoordinate &coord, - std::string &output) -{ - std::string tmp; - tmp.reserve(23); - convertInternalLatLonToString(coord.lat, tmp); - output = tmp; - output += ","; - convertInternalLatLonToString(coord.lon, tmp); - output += tmp; -} - -void FixedPointCoordinate::Output(std::ostream &out) const -{ - out << "(" << lat / COORDINATE_PRECISION << "," << lon / COORDINATE_PRECISION << ")"; -} - - -// double PointSegmentDistanceSquared( double px, double py, -// double p1x, double p1y, -// double p2x, double p2y, -// double& t, -// double& qx, double& qy) -// { -// static const double kMinSegmentLenSquared = 0.00000001; // adjust to suit. If you use float, you'll probably want something like 0.000001f -// static const double kEpsilon = 1.0E-14; // adjust to suit. If you use floats, you'll probably want something like 1E-7f -// double dx = p2x - p1x; -// double dy = p2y - p1y; -// double dp1x = px - p1x; -// double dp1y = py - p1y; -// const double segLenSquared = (dx * dx) + (dy * dy); -// if (segLenSquared >= -kMinSegmentLenSquared && segLenSquared <= kMinSegmentLenSquared) -// { -// // segment is a point. -// qx = p1x; -// qy = p1y; -// t = 0.0; -// return ((dp1x * dp1x) + (dp1y * dp1y)); -// } -// else -// { -// // Project a line from p to the segment [p1,p2]. By considering the line -// // extending the segment, parameterized as p1 + (t * (p2 - p1)), -// // we find projection of point p onto the line. -// // It falls where t = [(p - p1) . (p2 - p1)] / |p2 - p1|^2 -// t = ((dp1x * dx) + (dp1y * dy)) / segLenSquared; -// if (t < kEpsilon) -// { -// // intersects at or to the "left" of first segment vertex (p1x, p1y). If t is approximately 0.0, then -// // intersection is at p1. If t is less than that, then there is no intersection (i.e. p is not within -// // the 'bounds' of the segment) -// if (t > -kEpsilon) -// { -// // intersects at 1st segment vertex -// t = 0.0; -// } -// // set our 'intersection' point to p1. -// qx = p1x; -// qy = p1y; -// // Note: If you wanted the ACTUAL intersection point of where the projected lines would intersect if -// // we were doing PointLineDistanceSquared, then qx would be (p1x + (t * dx)) and qy would be (p1y + (t * dy)). -// } -// else if (t > (1.0 - kEpsilon)) -// { -// // intersects at or to the "right" of second segment vertex (p2x, p2y). If t is approximately 1.0, then -// // intersection is at p2. If t is greater than that, then there is no intersection (i.e. p is not within -// // the 'bounds' of the segment) -// if (t < (1.0 + kEpsilon)) -// { -// // intersects at 2nd segment vertex -// t = 1.0; -// } -// // set our 'intersection' point to p2. -// qx = p2x; -// qy = p2y; -// // Note: If you wanted the ACTUAL intersection point of where the projected lines would intersect if -// // we were doing PointLineDistanceSquared, then qx would be (p1x + (t * dx)) and qy would be (p1y + (t * dy)). -// } -// else -// { -// // The projection of the point to the point on the segment that is perpendicular succeeded and the point -// // is 'within' the bounds of the segment. Set the intersection point as that projected point. -// qx = p1x + (t * dx); -// qy = p1y + (t * dy); -// } -// // return the squared distance from p to the intersection point. Note that we return the squared distance -// // as an optimization because many times you just need to compare relative distances and the squared values -// // works fine for that. If you want the ACTUAL distance, just take the square root of this value. -// double dpqx = px - qx; -// double dpqy = py - qy; -// return ((dpqx * dpqx) + (dpqy * dpqy)); -// } -// } - -// public float DistanceOfPointToLine2(PointF p1, PointF p2, PointF p) -// { -// // (y1-y2)x + (x2-x1)y + (x1y2-x2y1) -// //d(P,L) = -------------------------------- -// // sqrt( (x2-x1)pow2 + (y2-y1)pow2 ) - -// double ch = (p1.Y - p2.Y) * p.X + (p2.X - p1.X) * p.Y + (p1.X * p2.Y - p2.X * p1.Y); -// double del = Math.Sqrt(Math.Pow(p2.X - p1.X, 2) + Math.Pow(p2.Y - p1.Y, 2)); -// double d = ch / del; -// return (float)d; -// } +/* + +Copyright (c) 2013, Project OSRM, Dennis Luxen, others +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include "../Util/MercatorUtil.h" +#include "../Util/SimpleLogger.h" +#include "../Util/StringUtil.h" + +#include + +#ifndef NDEBUG +#include +#endif +#include +#include + +FixedPointCoordinate::FixedPointCoordinate() + : lat(std::numeric_limits::min()), lon(std::numeric_limits::min()) +{ +} + +FixedPointCoordinate::FixedPointCoordinate(int lat, int lon) : lat(lat), lon(lon) +{ +#ifndef NDEBUG + if (0 != (std::abs(lat) >> 30)) + { + std::bitset<32> y(lat); + SimpleLogger().Write(logDEBUG) << "broken lat: " << lat << ", bits: " << y; + } + if (0 != (std::abs(lon) >> 30)) + { + std::bitset<32> x(lon); + SimpleLogger().Write(logDEBUG) << "broken lon: " << lon << ", bits: " << x; + } +#endif +} + +void FixedPointCoordinate::Reset() +{ + lat = std::numeric_limits::min(); + lon = std::numeric_limits::min(); +} +bool FixedPointCoordinate::isSet() const +{ + return (std::numeric_limits::min() != lat) && (std::numeric_limits::min() != lon); +} +bool FixedPointCoordinate::isValid() const +{ + if (lat > 90 * COORDINATE_PRECISION || lat < -90 * COORDINATE_PRECISION || + lon > 180 * COORDINATE_PRECISION || lon < -180 * COORDINATE_PRECISION) + { + return false; + } + return true; +} +bool FixedPointCoordinate::operator==(const FixedPointCoordinate &other) const +{ + return lat == other.lat && lon == other.lon; +} + +double FixedPointCoordinate::ApproximateDistance(const int lat1, + const int lon1, + const int lat2, + const int lon2) +{ + BOOST_ASSERT(lat1 != std::numeric_limits::min()); + BOOST_ASSERT(lon1 != std::numeric_limits::min()); + BOOST_ASSERT(lat2 != std::numeric_limits::min()); + BOOST_ASSERT(lon2 != std::numeric_limits::min()); + double RAD = 0.017453292519943295769236907684886; + double lt1 = lat1 / COORDINATE_PRECISION; + double ln1 = lon1 / COORDINATE_PRECISION; + double lt2 = lat2 / COORDINATE_PRECISION; + double ln2 = lon2 / COORDINATE_PRECISION; + double dlat1 = lt1 * (RAD); + + double dlong1 = ln1 * (RAD); + double dlat2 = lt2 * (RAD); + double dlong2 = ln2 * (RAD); + + double dLong = dlong1 - dlong2; + double dLat = dlat1 - dlat2; + + double aHarv = pow(sin(dLat / 2.0), 2.0) + cos(dlat1) * cos(dlat2) * pow(sin(dLong / 2.), 2); + double cHarv = 2. * atan2(sqrt(aHarv), sqrt(1.0 - aHarv)); + // earth radius varies between 6,356.750-6,378.135 km (3,949.901-3,963.189mi) + // The IUGG value for the equatorial radius is 6378.137 km (3963.19 miles) + const double earth = 6372797.560856; + return earth * cHarv; +} + +double FixedPointCoordinate::ApproximateDistance(const FixedPointCoordinate &c1, + const FixedPointCoordinate &c2) +{ + return ApproximateDistance(c1.lat, c1.lon, c2.lat, c2.lon); +} + +double FixedPointCoordinate::ApproximateEuclideanDistance(const FixedPointCoordinate &c1, + const FixedPointCoordinate &c2) +{ + return ApproximateEuclideanDistance(c1.lat, c1.lon, c2.lat, c2.lon); +} + +double FixedPointCoordinate::ApproximateEuclideanDistance(const int lat1, + const int lon1, + const int lat2, + const int lon2) +{ + BOOST_ASSERT(lat1 != std::numeric_limits::min()); + BOOST_ASSERT(lon1 != std::numeric_limits::min()); + BOOST_ASSERT(lat2 != std::numeric_limits::min()); + BOOST_ASSERT(lon2 != std::numeric_limits::min()); + + const double RAD = 0.017453292519943295769236907684886; + const double float_lat1 = (lat1 / COORDINATE_PRECISION) * RAD; + const double float_lon1 = (lon1 / COORDINATE_PRECISION) * RAD; + const double float_lat2 = (lat2 / COORDINATE_PRECISION) * RAD; + const double float_lon2 = (lon2 / COORDINATE_PRECISION) * RAD; + + const double x = (float_lon2 - float_lon1) * cos((float_lat1 + float_lat2) / 2.); + const double y = (float_lat2 - float_lat1); + const double earth_radius = 6372797.560856; + return sqrt(x * x + y * y) * earth_radius; +} + +// Yuck! Code duplication. This function is also in EgdeBasedNode.h +double FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordinate &point, + const FixedPointCoordinate &segA, + const FixedPointCoordinate &segB) +{ + const double x = lat2y(point.lat / COORDINATE_PRECISION); + const double y = point.lon / COORDINATE_PRECISION; + const double a = lat2y(segA.lat / COORDINATE_PRECISION); + const double b = segA.lon / COORDINATE_PRECISION; + const double c = lat2y(segB.lat / COORDINATE_PRECISION); + const double d = segB.lon / COORDINATE_PRECISION; + double p, q, nY; + if (std::abs(a - c) > std::numeric_limits::epsilon()) + { + const double m = (d - b) / (c - a); // slope + // Projection of (x,y) on line joining (a,b) and (c,d) + p = ((x + (m * y)) + (m * m * a - m * b)) / (1. + m * m); + q = b + m * (p - a); + } + else + { + p = c; + q = y; + } + nY = (d * p - c * q) / (a * d - b * c); + + // discretize the result to coordinate precision. it's a hack! + if (std::abs(nY) < (1. / COORDINATE_PRECISION)) + { + nY = 0.; + } + + double r = (p - nY * a) / c; + if (std::isnan(r)) + { + r = ((segB.lat == point.lat) && (segB.lon == point.lon)) ? 1. : 0.; + } + else if (std::abs(r) <= std::numeric_limits::epsilon()) + { + r = 0.; + } + else if (std::abs(r - 1.) <= std::numeric_limits::epsilon()) + { + r = 1.; + } + FixedPointCoordinate nearest_location; + BOOST_ASSERT(!std::isnan(r)); + if (r <= 0.) + { // point is "left" of edge + nearest_location.lat = segA.lat; + nearest_location.lon = segA.lon; + } + else if (r >= 1.) + { // point is "right" of edge + nearest_location.lat = segB.lat; + nearest_location.lon = segB.lon; + } + else + { // point lies in between + nearest_location.lat = y2lat(p) * COORDINATE_PRECISION; + nearest_location.lon = q * COORDINATE_PRECISION; + } + BOOST_ASSERT(nearest_location.isValid()); + const double approximated_distance = + FixedPointCoordinate::ApproximateDistance(point, nearest_location); + BOOST_ASSERT(0. <= approximated_distance); + return approximated_distance; +} + +double FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordinate &coord_a, + const FixedPointCoordinate &coord_b, + const FixedPointCoordinate &query_location, + FixedPointCoordinate &nearest_location, + double &r) +{ + BOOST_ASSERT(query_location.isValid()); + + const double x = lat2y(query_location.lat / COORDINATE_PRECISION); + const double y = query_location.lon / COORDINATE_PRECISION; + const double a = lat2y(coord_a.lat / COORDINATE_PRECISION); + const double b = coord_a.lon / COORDINATE_PRECISION; + const double c = lat2y(coord_b.lat / COORDINATE_PRECISION); + const double d = coord_b.lon / COORDINATE_PRECISION; + double p, q /*,mX*/, nY; + if (std::abs(a - c) > std::numeric_limits::epsilon()) + { + const double m = (d - b) / (c - a); // slope + // Projection of (x,y) on line joining (a,b) and (c,d) + p = ((x + (m * y)) + (m * m * a - m * b)) / (1. + m * m); + q = b + m * (p - a); + } + else + { + p = c; + q = y; + } + nY = (d * p - c * q) / (a * d - b * c); + + // discretize the result to coordinate precision. it's a hack! + if (std::abs(nY) < (1. / COORDINATE_PRECISION)) + { + nY = 0.; + } + + r = (p - nY * a) / c; // These values are actually n/m+n and m/m+n , we need + // not calculate the explicit values of m an n as we + // are just interested in the ratio + if (std::isnan(r)) + { + r = ((coord_b.lat == query_location.lat) && (coord_b.lon == query_location.lon)) ? 1. : 0.; + } + else if (std::abs(r) <= std::numeric_limits::epsilon()) + { + r = 0.; + } + else if (std::abs(r - 1.) <= std::numeric_limits::epsilon()) + { + r = 1.; + } + BOOST_ASSERT(!std::isnan(r)); + if (r <= 0.) + { + nearest_location.lat = coord_a.lat; + nearest_location.lon = coord_a.lon; + } + else if (r >= 1.) + { + nearest_location.lat = coord_b.lat; + nearest_location.lon = coord_b.lon; + } + else + { + // point lies in between + nearest_location.lat = y2lat(p) * COORDINATE_PRECISION; + nearest_location.lon = q * COORDINATE_PRECISION; + } + BOOST_ASSERT(nearest_location.isValid()); + + // TODO: Replace with euclidean approximation when k-NN search is done + // const double approximated_distance = FixedPointCoordinate::ApproximateEuclideanDistance( + const double approximated_distance = + FixedPointCoordinate::ApproximateDistance(query_location, nearest_location); + BOOST_ASSERT(0. <= approximated_distance); + return approximated_distance; +} + +void FixedPointCoordinate::convertInternalLatLonToString(const int value, std::string &output) +{ + char buffer[12]; + buffer[11] = 0; // zero termination + output = printInt<11, 6>(buffer, value); +} + +void FixedPointCoordinate::convertInternalCoordinateToString(const FixedPointCoordinate &coord, + std::string &output) +{ + std::string tmp; + tmp.reserve(23); + convertInternalLatLonToString(coord.lon, tmp); + output = tmp; + output += ","; + convertInternalLatLonToString(coord.lat, tmp); + output += tmp; +} + +void +FixedPointCoordinate::convertInternalReversedCoordinateToString(const FixedPointCoordinate &coord, + std::string &output) +{ + std::string tmp; + tmp.reserve(23); + convertInternalLatLonToString(coord.lat, tmp); + output = tmp; + output += ","; + convertInternalLatLonToString(coord.lon, tmp); + output += tmp; +} + +void FixedPointCoordinate::Output(std::ostream &out) const +{ + out << "(" << lat / COORDINATE_PRECISION << "," << lon / COORDINATE_PRECISION << ")"; +} + +double FixedPointCoordinate::GetBearing(const FixedPointCoordinate &A, const FixedPointCoordinate &B) +{ + double delta_long = DegreeToRadian(B.lon / COORDINATE_PRECISION - A.lon / COORDINATE_PRECISION); + + const double lat1 = DegreeToRadian(A.lat / COORDINATE_PRECISION); + const double lat2 = DegreeToRadian(B.lat / COORDINATE_PRECISION); + + const double y = sin(delta_long) * cos(lat2); + const double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(delta_long); + double result = RadianToDegree(atan2(y, x)); + while (result < 0.) + { + result += 360.; + } + + while (result >= 360.) + { + result -= 360.; + } + return result; +} + +double FixedPointCoordinate::GetBearing(const FixedPointCoordinate &other) const +{ + double delta_long = DegreeToRadian(lon / COORDINATE_PRECISION - other.lon / COORDINATE_PRECISION); + + const double lat1 = DegreeToRadian(other.lat / COORDINATE_PRECISION); + const double lat2 = DegreeToRadian(lat / COORDINATE_PRECISION); + + const double y = sin(delta_long) * cos(lat2); + const double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(delta_long); + double result = RadianToDegree(atan2(y, x)); + while (result < 0.) + { + result += 360.; + } + + while (result >= 360.) + { + result -= 360.; + } + return result; +} + +double FixedPointCoordinate::DegreeToRadian(const double degree) +{ + return degree * (M_PI / 180.); +} + +double FixedPointCoordinate::RadianToDegree(const double radian) +{ + return radian * (180. / M_PI); +} + +// double PointSegmentDistanceSquared( double px, double py, +// double p1x, double p1y, +// double p2x, double p2y, +// double& t, +// double& qx, double& qy) +// { +// static const double kMinSegmentLenSquared = 0.00000001; // adjust to suit. If you use float, you'll probably want something like 0.000001f +// static const double kEpsilon = 1.0E-14; // adjust to suit. If you use floats, you'll probably want something like 1E-7f +// double dx = p2x - p1x; +// double dy = p2y - p1y; +// double dp1x = px - p1x; +// double dp1y = py - p1y; +// const double segLenSquared = (dx * dx) + (dy * dy); +// if (segLenSquared >= -kMinSegmentLenSquared && segLenSquared <= kMinSegmentLenSquared) +// { +// // segment is a point. +// qx = p1x; +// qy = p1y; +// t = 0.0; +// return ((dp1x * dp1x) + (dp1y * dp1y)); +// } +// else +// { +// // Project a line from p to the segment [p1,p2]. By considering the line +// // extending the segment, parameterized as p1 + (t * (p2 - p1)), +// // we find projection of point p onto the line. +// // It falls where t = [(p - p1) . (p2 - p1)] / |p2 - p1|^2 +// t = ((dp1x * dx) + (dp1y * dy)) / segLenSquared; +// if (t < kEpsilon) +// { +// // intersects at or to the "left" of first segment vertex (p1x, p1y). If t is approximately 0.0, then +// // intersection is at p1. If t is less than that, then there is no intersection (i.e. p is not within +// // the 'bounds' of the segment) +// if (t > -kEpsilon) +// { +// // intersects at 1st segment vertex +// t = 0.0; +// } +// // set our 'intersection' point to p1. +// qx = p1x; +// qy = p1y; +// // Note: If you wanted the ACTUAL intersection point of where the projected lines would intersect if +// // we were doing PointLineDistanceSquared, then qx would be (p1x + (t * dx)) and qy would be (p1y + (t * dy)). +// } +// else if (t > (1.0 - kEpsilon)) +// { +// // intersects at or to the "right" of second segment vertex (p2x, p2y). If t is approximately 1.0, then +// // intersection is at p2. If t is greater than that, then there is no intersection (i.e. p is not within +// // the 'bounds' of the segment) +// if (t < (1.0 + kEpsilon)) +// { +// // intersects at 2nd segment vertex +// t = 1.0; +// } +// // set our 'intersection' point to p2. +// qx = p2x; +// qy = p2y; +// // Note: If you wanted the ACTUAL intersection point of where the projected lines would intersect if +// // we were doing PointLineDistanceSquared, then qx would be (p1x + (t * dx)) and qy would be (p1y + (t * dy)). +// } +// else +// { +// // The projection of the point to the point on the segment that is perpendicular succeeded and the point +// // is 'within' the bounds of the segment. Set the intersection point as that projected point. +// qx = p1x + (t * dx); +// qy = p1y + (t * dy); +// } +// // return the squared distance from p to the intersection point. Note that we return the squared distance +// // as an optimization because many times you just need to compare relative distances and the squared values +// // works fine for that. If you want the ACTUAL distance, just take the square root of this value. +// double dpqx = px - qx; +// double dpqy = py - qy; +// return ((dpqx * dpqx) + (dpqy * dpqy)); +// } +// } + +// public float DistanceOfPointToLine2(PointF p1, PointF p2, PointF p) +// { +// // (y1-y2)x + (x2-x1)y + (x1y2-x2y1) +// //d(P,L) = -------------------------------- +// // sqrt( (x2-x1)pow2 + (y2-y1)pow2 ) + +// double ch = (p1.Y - p2.Y) * p.X + (p2.X - p1.X) * p.Y + (p1.X * p2.Y - p2.X * p1.Y); +// double del = Math.Sqrt(Math.Pow(p2.X - p1.X, 2) + Math.Pow(p2.Y - p1.Y, 2)); +// double d = ch / del; +// return (float)d; +// } diff --git a/Descriptors/DescriptionFactory.cpp b/Descriptors/DescriptionFactory.cpp index 7d6fc037bd9..0d95ddeda7e 100644 --- a/Descriptors/DescriptionFactory.cpp +++ b/Descriptors/DescriptionFactory.cpp @@ -31,39 +31,6 @@ DescriptionFactory::DescriptionFactory() : entireLength(0) {} DescriptionFactory::~DescriptionFactory() {} -inline double DescriptionFactory::DegreeToRadian(const double degree) const -{ - return degree * (M_PI / 180.); -} - -inline double DescriptionFactory::RadianToDegree(const double radian) const -{ - return radian * (180. / M_PI); -} - -double DescriptionFactory::GetBearing(const FixedPointCoordinate &A, const FixedPointCoordinate &B) - const -{ - double delta_long = DegreeToRadian(B.lon / COORDINATE_PRECISION - A.lon / COORDINATE_PRECISION); - - const double lat1 = DegreeToRadian(A.lat / COORDINATE_PRECISION); - const double lat2 = DegreeToRadian(B.lat / COORDINATE_PRECISION); - - const double y = sin(delta_long) * cos(lat2); - const double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(delta_long); - double result = RadianToDegree(atan2(y, x)); - while (result < 0.) - { - result += 360.; - } - - while (result >= 360.) - { - result -= 360.; - } - return result; -} - void DescriptionFactory::SetStartSegment(const PhantomNode &source) { start_phantom = source; diff --git a/Descriptors/DescriptionFactory.h b/Descriptors/DescriptionFactory.h index 082ce675b59..85c3a3c29dc 100644 --- a/Descriptors/DescriptionFactory.h +++ b/Descriptors/DescriptionFactory.h @@ -77,7 +77,6 @@ class DescriptionFactory std::vector path_description; DescriptionFactory(); virtual ~DescriptionFactory(); - double GetBearing(const FixedPointCoordinate &C, const FixedPointCoordinate &B) const; JSON::Value AppendUnencodedPolylineString() const; void AppendSegment(const FixedPointCoordinate &coordinate, const PathData &data); void BuildRouteSummary(const double distance, const unsigned time); @@ -196,8 +195,7 @@ class DescriptionFactory { if (path_description[i].necessary) { - const double angle = - GetBearing(path_description[i].location, path_description[i + 1].location); + const double angle = path_description[i].location.GetBearing(path_description[i + 1].location); path_description[i].bearing = angle * 10; } } diff --git a/Descriptors/JSONDescriptor.h b/Descriptors/JSONDescriptor.h index 46b85aba28a..7be9c9a73f3 100644 --- a/Descriptors/JSONDescriptor.h +++ b/Descriptors/JSONDescriptor.h @@ -1,357 +1,356 @@ -/* - -Copyright (c) 2013, Project OSRM, Dennis Luxen, others -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this list -of conditions and the following disclaimer. -Redistributions in binary form must reproduce the above copyright notice, this -list of conditions and the following disclaimer in the documentation and/or -other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef JSON_DESCRIPTOR_H_ -#define JSON_DESCRIPTOR_H_ - -#include "BaseDescriptor.h" -#include "DescriptionFactory.h" -#include "../Algorithms/ObjectToBase64.h" -#include "../Algorithms/ExtractRouteNames.h" -#include "../DataStructures/JSONContainer.h" -#include "../DataStructures/SegmentInformation.h" -#include "../DataStructures/TurnInstructions.h" -#include "../Util/Azimuth.h" -#include "../Util/StringUtil.h" -#include "../Util/TimingUtil.h" - -#include - -template class JSONDescriptor : public BaseDescriptor -{ - private: - // TODO: initalize in c'tor - DataFacadeT *facade; - DescriptorConfig config; - DescriptionFactory description_factory, alternate_description_factory; - FixedPointCoordinate current; - unsigned entered_restricted_area_count; - struct RoundAbout - { - RoundAbout() : start_index(INT_MAX), name_id(INT_MAX), leave_at_exit(INT_MAX) {} - int start_index; - int name_id; - int leave_at_exit; - } round_about; - - struct Segment - { - Segment() : name_id(-1), length(-1), position(-1) {} - Segment(int n, int l, int p) : name_id(n), length(l), position(p) {} - int name_id; - int length; - int position; - }; - std::vector shortest_path_segments, alternative_path_segments; - std::vector shortest_leg_end_indices, alternative_leg_end_indices; - ExtractRouteNames GenerateRouteNames; - - - public: - JSONDescriptor(DataFacadeT *facade) : facade(facade), entered_restricted_area_count(0) - { - shortest_leg_end_indices.emplace_back(0); - alternative_leg_end_indices.emplace_back(0); - } - - void SetConfig(const DescriptorConfig &c) { config = c; } - - unsigned DescribeLeg(const std::vector route_leg, const PhantomNodes &leg_phantoms) - { - unsigned added_element_count = 0; - // Get all the coordinates for the computed route - FixedPointCoordinate current_coordinate; - for (const PathData &path_data : route_leg) - { - current_coordinate = facade->GetCoordinateOfNode(path_data.node); - description_factory.AppendSegment(current_coordinate, path_data); - ++added_element_count; - } - ++added_element_count; - BOOST_ASSERT((route_leg.size() + 1) == added_element_count); - return added_element_count; - } - - void Run(const RawRouteData &raw_route, - const PhantomNodes &phantom_nodes, - http::Reply &reply) - { - JSON::Object json_result; - - if (INVALID_EDGE_WEIGHT == raw_route.shortest_path_length) - { - // We do not need to do much, if there is no route ;-) - json_result.values["status"] = 207; - json_result.values["status_message"] = "Cannot find route between points"; - JSON::render(reply.content, json_result); - return; - } - - // check if first segment is non-zero - std::string road_name = - facade->GetEscapedNameForNameID(phantom_nodes.source_phantom.name_id); - - BOOST_ASSERT(raw_route.unpacked_path_segments.size() == - raw_route.segment_end_coordinates.size()); - - description_factory.SetStartSegment(phantom_nodes.source_phantom); - json_result.values["status"] = 0; - json_result.values["status_message"] = "Found route between points"; - - // for each unpacked segment add the leg to the description - for (unsigned i = 0; i < raw_route.unpacked_path_segments.size(); ++i) - { - const int added_segments = DescribeLeg(raw_route.unpacked_path_segments[i], - raw_route.segment_end_coordinates[i]); - BOOST_ASSERT(0 < added_segments); - shortest_leg_end_indices.emplace_back(added_segments + shortest_leg_end_indices.back()); - } - description_factory.SetEndSegment(phantom_nodes.target_phantom); - description_factory.Run(facade, config.zoom_level); - - if (config.geometry) - { - JSON::Value route_geometry = description_factory.AppendEncodedPolylineString(config.encode_geometry); - json_result.values["route_geometry"] = route_geometry; - } - if (config.instructions) - { - JSON::Array json_route_instructions; - BuildTextualDescription(description_factory, - json_route_instructions, - raw_route.shortest_path_length, - shortest_path_segments); - json_result.values["route_instructions"] = json_route_instructions; - } - description_factory.BuildRouteSummary(description_factory.entireLength, - raw_route.shortest_path_length); - JSON::Object json_route_summary; - json_route_summary.values["total_distance"] = description_factory.summary.distance; - json_route_summary.values["total_time"] = description_factory.summary.duration; - json_route_summary.values["start_point"] = facade->GetEscapedNameForNameID(description_factory.summary.source_name_id); - json_route_summary.values["end_point"] = facade->GetEscapedNameForNameID(description_factory.summary.target_name_id); - json_result.values["route_summary"] = json_route_summary; - - BOOST_ASSERT(!raw_route.segment_end_coordinates.empty()); - - JSON::Array json_via_points_array; - JSON::Array json_first_coordinate; - json_first_coordinate.values.push_back(raw_route.segment_end_coordinates.front().source_phantom.location.lat/COORDINATE_PRECISION); - json_first_coordinate.values.push_back(raw_route.segment_end_coordinates.front().source_phantom.location.lon/COORDINATE_PRECISION); - json_via_points_array.values.push_back(json_first_coordinate); - for (const PhantomNodes &nodes : raw_route.segment_end_coordinates) - { - std::string tmp; - JSON::Array json_coordinate; - json_coordinate.values.push_back(nodes.target_phantom.location.lat/COORDINATE_PRECISION); - json_coordinate.values.push_back(nodes.target_phantom.location.lon/COORDINATE_PRECISION); - json_via_points_array.values.push_back(json_coordinate); - } - json_result.values["via_points"] = json_via_points_array; - - JSON::Array json_via_indices_array; - json_via_indices_array.values.insert(json_via_indices_array.values.end(), shortest_leg_end_indices.begin(), shortest_leg_end_indices.end()); - json_result.values["via_indices"] = json_via_indices_array; - - // only one alternative route is computed at this time, so this is hardcoded - if (INVALID_EDGE_WEIGHT != raw_route.alternative_path_length) - { - json_result.values["found_alternative"] = JSON::True(); - alternate_description_factory.SetStartSegment(phantom_nodes.source_phantom); - // Get all the coordinates for the computed route - for (const PathData &path_data : raw_route.unpacked_alternative) - { - current = facade->GetCoordinateOfNode(path_data.node); - alternate_description_factory.AppendSegment(current, path_data); - } - alternate_description_factory.Run(facade, config.zoom_level); - - if (config.geometry) - { - JSON::Value alternate_geometry_string = alternate_description_factory.AppendEncodedPolylineString(config.encode_geometry); - JSON::Array json_alternate_geometries_array; - json_alternate_geometries_array.values.push_back(alternate_geometry_string); - json_result.values["alternative_geometries"] = json_alternate_geometries_array; - } - // Generate instructions for each alternative (simulated here) - JSON::Array json_alt_instructions; - JSON::Array json_current_alt_instructions; - if (config.instructions) - { - BuildTextualDescription(alternate_description_factory, - json_current_alt_instructions, - raw_route.alternative_path_length, - alternative_path_segments); - json_alt_instructions.values.push_back(json_current_alt_instructions); - json_result.values["alternative_instructions"] = json_alt_instructions; - } - alternate_description_factory.BuildRouteSummary( - alternate_description_factory.entireLength, raw_route.alternative_path_length); - - JSON::Object json_alternate_route_summary; - JSON::Array json_alternate_route_summary_array; - json_alternate_route_summary.values["total_distance"] = alternate_description_factory.summary.distance; - json_alternate_route_summary.values["total_time"] = alternate_description_factory.summary.duration; - json_alternate_route_summary.values["start_point"] = facade->GetEscapedNameForNameID(alternate_description_factory.summary.source_name_id); - json_alternate_route_summary.values["end_point"] = facade->GetEscapedNameForNameID(alternate_description_factory.summary.target_name_id); - json_alternate_route_summary_array.values.push_back(json_alternate_route_summary); - json_result.values["alternative_summaries"] = json_alternate_route_summary_array; - - JSON::Array json_altenative_indices_array; - json_altenative_indices_array.values.push_back(0); - json_altenative_indices_array.values.push_back(alternate_description_factory.path_description.size()); - json_result.values["alternative_indices"] = json_altenative_indices_array; - } else { - json_result.values["found_alternative"] = JSON::False(); - } - - // Get Names for both routes - RouteNames route_names = GenerateRouteNames(shortest_path_segments, alternative_path_segments, facade); - JSON::Array json_route_names; - json_route_names.values.push_back(route_names.shortest_path_name_1); - json_route_names.values.push_back(route_names.shortest_path_name_2); - json_result.values["route_name"] = json_route_names; - - if (INVALID_EDGE_WEIGHT != raw_route.alternative_path_length) - { - JSON::Array json_alternate_names_array; - JSON::Array json_alternate_names; - json_alternate_names.values.push_back(route_names.alternative_path_name_1); - json_alternate_names.values.push_back(route_names.alternative_path_name_2); - json_alternate_names_array.values.push_back(json_alternate_names); - json_result.values["alternative_names"] = json_alternate_names_array; - } - - JSON::Object json_hint_object; - json_hint_object.values["checksum"] = raw_route.check_sum; - JSON::Array json_location_hint_array; - std::string hint; - for (unsigned i = 0; i < raw_route.segment_end_coordinates.size(); ++i) - { - EncodeObjectToBase64(raw_route.segment_end_coordinates[i].source_phantom, hint); - json_location_hint_array.values.push_back(hint); - } - EncodeObjectToBase64(raw_route.segment_end_coordinates.back().target_phantom, hint); - json_location_hint_array.values.push_back(hint); - json_hint_object.values["locations"] = json_location_hint_array; - json_result.values["hint_data"] = json_hint_object; - - // render the content to the output array - TIMER_START(route_render); - JSON::render(reply.content, json_result); - TIMER_STOP(route_render); - SimpleLogger().Write(logDEBUG) << "rendering took: " << TIMER_MSEC(route_render); - } - - // TODO: reorder parameters - inline void BuildTextualDescription(DescriptionFactory &description_factory, - JSON::Array & json_instruction_array, - const int route_length, - std::vector &route_segments_list) - { - // Segment information has following format: - //["instruction id","streetname",length,position,time,"length","earth_direction",azimuth] - unsigned necessary_segments_running_index = 0; - round_about.leave_at_exit = 0; - round_about.name_id = 0; - std::string temp_dist, temp_length, temp_duration, temp_bearing, temp_instruction; - - // Fetch data from Factory and generate a string from it. - for (const SegmentInformation &segment : description_factory.path_description) - { - JSON::Array json_instruction_row; - TurnInstruction current_instruction = segment.turn_instruction; - entered_restricted_area_count += (current_instruction != segment.turn_instruction); - if (TurnInstructionsClass::TurnIsNecessary(current_instruction)) - { - if (TurnInstruction::EnterRoundAbout == current_instruction) - { - round_about.name_id = segment.name_id; - round_about.start_index = necessary_segments_running_index; - } - else - { - std::string current_turn_instruction; - if (TurnInstruction::LeaveRoundAbout == current_instruction) - { - temp_instruction = IntToString(as_integer(TurnInstruction::EnterRoundAbout)); - current_turn_instruction += temp_instruction; - current_turn_instruction += "-"; - temp_instruction = IntToString(round_about.leave_at_exit + 1); - current_turn_instruction += temp_instruction; - round_about.leave_at_exit = 0; - } - else - { - temp_instruction = IntToString(as_integer(current_instruction)); - current_turn_instruction += temp_instruction; - } - json_instruction_row.values.push_back(current_turn_instruction); - - json_instruction_row.values.push_back(facade->GetEscapedNameForNameID(segment.name_id)); - json_instruction_row.values.push_back(std::round(segment.length)); - json_instruction_row.values.push_back(necessary_segments_running_index); - json_instruction_row.values.push_back(round(segment.duration / 10)); - json_instruction_row.values.push_back(IntToString(segment.length)+"m"); - int bearing_value = round(segment.bearing / 10.); - json_instruction_row.values.push_back(Azimuth::Get(bearing_value)); - json_instruction_row.values.push_back(bearing_value); - - route_segments_list.emplace_back( - segment.name_id, segment.length, route_segments_list.size()); - json_instruction_array.values.push_back(json_instruction_row); - } - } - else if (TurnInstruction::StayOnRoundAbout == current_instruction) - { - ++round_about.leave_at_exit; - } - if (segment.necessary) - { - ++necessary_segments_running_index; - } - } - - //TODO: check if this in an invariant - if (INVALID_EDGE_WEIGHT != route_length) - { - JSON::Array json_last_instruction_row; - temp_instruction = IntToString(as_integer(TurnInstruction::ReachedYourDestination)); - json_last_instruction_row.values.push_back(temp_instruction); - json_last_instruction_row.values.push_back(""); - json_last_instruction_row.values.push_back(0); - json_last_instruction_row.values.push_back(necessary_segments_running_index - 1); - json_last_instruction_row.values.push_back(0); - json_last_instruction_row.values.push_back("0m"); - json_last_instruction_row.values.push_back(Azimuth::Get(0.0)); - json_last_instruction_row.values.push_back(0.); - json_instruction_array.values.push_back(json_last_instruction_row); - } - } -}; - -#endif /* JSON_DESCRIPTOR_H_ */ +/* + +Copyright (c) 2013, Project OSRM, Dennis Luxen, others +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef JSON_DESCRIPTOR_H_ +#define JSON_DESCRIPTOR_H_ + +#include "BaseDescriptor.h" +#include "DescriptionFactory.h" +#include "../Algorithms/ObjectToBase64.h" +#include "../Algorithms/ExtractRouteNames.h" +#include "../DataStructures/JSONContainer.h" +#include "../DataStructures/SegmentInformation.h" +#include "../DataStructures/TurnInstructions.h" +#include "../Util/Azimuth.h" +#include "../Util/StringUtil.h" +#include "../Util/TimingUtil.h" + +#include + +template class JSONDescriptor : public BaseDescriptor +{ + private: + DataFacadeT *facade; + DescriptorConfig config; + DescriptionFactory description_factory, alternate_description_factory; + FixedPointCoordinate current; + unsigned entered_restricted_area_count; + struct RoundAbout + { + RoundAbout() : start_index(INT_MAX), name_id(INT_MAX), leave_at_exit(INT_MAX) {} + int start_index; + int name_id; + int leave_at_exit; + } round_about; + + struct Segment + { + Segment() : name_id(-1), length(-1), position(-1) {} + Segment(int n, int l, int p) : name_id(n), length(l), position(p) {} + int name_id; + int length; + int position; + }; + std::vector shortest_path_segments, alternative_path_segments; + std::vector shortest_leg_end_indices, alternative_leg_end_indices; + ExtractRouteNames GenerateRouteNames; + + + public: + JSONDescriptor(DataFacadeT *facade) : facade(facade), entered_restricted_area_count(0) + { + shortest_leg_end_indices.emplace_back(0); + alternative_leg_end_indices.emplace_back(0); + } + + void SetConfig(const DescriptorConfig &c) { config = c; } + + unsigned DescribeLeg(const std::vector route_leg, const PhantomNodes &leg_phantoms) + { + unsigned added_element_count = 0; + // Get all the coordinates for the computed route + FixedPointCoordinate current_coordinate; + for (const PathData &path_data : route_leg) + { + current_coordinate = facade->GetCoordinateOfNode(path_data.node); + description_factory.AppendSegment(current_coordinate, path_data); + ++added_element_count; + } + ++added_element_count; + BOOST_ASSERT((route_leg.size() + 1) == added_element_count); + return added_element_count; + } + + void Run(const RawRouteData &raw_route, + const PhantomNodes &phantom_nodes, + http::Reply &reply) + { + JSON::Object json_result; + + if (INVALID_EDGE_WEIGHT == raw_route.shortest_path_length) + { + // We do not need to do much, if there is no route ;-) + json_result.values["status"] = 207; + json_result.values["status_message"] = "Cannot find route between points"; + JSON::render(reply.content, json_result); + return; + } + + // check if first segment is non-zero + std::string road_name = + facade->GetEscapedNameForNameID(phantom_nodes.source_phantom.name_id); + + BOOST_ASSERT(raw_route.unpacked_path_segments.size() == + raw_route.segment_end_coordinates.size()); + + description_factory.SetStartSegment(phantom_nodes.source_phantom); + json_result.values["status"] = 0; + json_result.values["status_message"] = "Found route between points"; + + // for each unpacked segment add the leg to the description + for (unsigned i = 0; i < raw_route.unpacked_path_segments.size(); ++i) + { + const int added_segments = DescribeLeg(raw_route.unpacked_path_segments[i], + raw_route.segment_end_coordinates[i]); + BOOST_ASSERT(0 < added_segments); + shortest_leg_end_indices.emplace_back(added_segments + shortest_leg_end_indices.back()); + } + description_factory.SetEndSegment(phantom_nodes.target_phantom); + description_factory.Run(facade, config.zoom_level); + + if (config.geometry) + { + JSON::Value route_geometry = description_factory.AppendEncodedPolylineString(config.encode_geometry); + json_result.values["route_geometry"] = route_geometry; + } + if (config.instructions) + { + JSON::Array json_route_instructions; + BuildTextualDescription(description_factory, + json_route_instructions, + raw_route.shortest_path_length, + shortest_path_segments); + json_result.values["route_instructions"] = json_route_instructions; + } + description_factory.BuildRouteSummary(description_factory.entireLength, + raw_route.shortest_path_length); + JSON::Object json_route_summary; + json_route_summary.values["total_distance"] = description_factory.summary.distance; + json_route_summary.values["total_time"] = description_factory.summary.duration; + json_route_summary.values["start_point"] = facade->GetEscapedNameForNameID(description_factory.summary.source_name_id); + json_route_summary.values["end_point"] = facade->GetEscapedNameForNameID(description_factory.summary.target_name_id); + json_result.values["route_summary"] = json_route_summary; + + BOOST_ASSERT(!raw_route.segment_end_coordinates.empty()); + + JSON::Array json_via_points_array; + JSON::Array json_first_coordinate; + json_first_coordinate.values.push_back(raw_route.segment_end_coordinates.front().source_phantom.location.lat/COORDINATE_PRECISION); + json_first_coordinate.values.push_back(raw_route.segment_end_coordinates.front().source_phantom.location.lon/COORDINATE_PRECISION); + json_via_points_array.values.push_back(json_first_coordinate); + for (const PhantomNodes &nodes : raw_route.segment_end_coordinates) + { + std::string tmp; + JSON::Array json_coordinate; + json_coordinate.values.push_back(nodes.target_phantom.location.lat/COORDINATE_PRECISION); + json_coordinate.values.push_back(nodes.target_phantom.location.lon/COORDINATE_PRECISION); + json_via_points_array.values.push_back(json_coordinate); + } + json_result.values["via_points"] = json_via_points_array; + + JSON::Array json_via_indices_array; + json_via_indices_array.values.insert(json_via_indices_array.values.end(), shortest_leg_end_indices.begin(), shortest_leg_end_indices.end()); + json_result.values["via_indices"] = json_via_indices_array; + + // only one alternative route is computed at this time, so this is hardcoded + if (INVALID_EDGE_WEIGHT != raw_route.alternative_path_length) + { + json_result.values["found_alternative"] = JSON::True(); + alternate_description_factory.SetStartSegment(phantom_nodes.source_phantom); + // Get all the coordinates for the computed route + for (const PathData &path_data : raw_route.unpacked_alternative) + { + current = facade->GetCoordinateOfNode(path_data.node); + alternate_description_factory.AppendSegment(current, path_data); + } + alternate_description_factory.Run(facade, config.zoom_level); + + if (config.geometry) + { + JSON::Value alternate_geometry_string = alternate_description_factory.AppendEncodedPolylineString(config.encode_geometry); + JSON::Array json_alternate_geometries_array; + json_alternate_geometries_array.values.push_back(alternate_geometry_string); + json_result.values["alternative_geometries"] = json_alternate_geometries_array; + } + // Generate instructions for each alternative (simulated here) + JSON::Array json_alt_instructions; + JSON::Array json_current_alt_instructions; + if (config.instructions) + { + BuildTextualDescription(alternate_description_factory, + json_current_alt_instructions, + raw_route.alternative_path_length, + alternative_path_segments); + json_alt_instructions.values.push_back(json_current_alt_instructions); + json_result.values["alternative_instructions"] = json_alt_instructions; + } + alternate_description_factory.BuildRouteSummary( + alternate_description_factory.entireLength, raw_route.alternative_path_length); + + JSON::Object json_alternate_route_summary; + JSON::Array json_alternate_route_summary_array; + json_alternate_route_summary.values["total_distance"] = alternate_description_factory.summary.distance; + json_alternate_route_summary.values["total_time"] = alternate_description_factory.summary.duration; + json_alternate_route_summary.values["start_point"] = facade->GetEscapedNameForNameID(alternate_description_factory.summary.source_name_id); + json_alternate_route_summary.values["end_point"] = facade->GetEscapedNameForNameID(alternate_description_factory.summary.target_name_id); + json_alternate_route_summary_array.values.push_back(json_alternate_route_summary); + json_result.values["alternative_summaries"] = json_alternate_route_summary_array; + + JSON::Array json_altenative_indices_array; + json_altenative_indices_array.values.push_back(0); + json_altenative_indices_array.values.push_back(alternate_description_factory.path_description.size()); + json_result.values["alternative_indices"] = json_altenative_indices_array; + } else { + json_result.values["found_alternative"] = JSON::False(); + } + + // Get Names for both routes + RouteNames route_names = GenerateRouteNames(shortest_path_segments, alternative_path_segments, facade); + JSON::Array json_route_names; + json_route_names.values.push_back(route_names.shortest_path_name_1); + json_route_names.values.push_back(route_names.shortest_path_name_2); + json_result.values["route_name"] = json_route_names; + + if (INVALID_EDGE_WEIGHT != raw_route.alternative_path_length) + { + JSON::Array json_alternate_names_array; + JSON::Array json_alternate_names; + json_alternate_names.values.push_back(route_names.alternative_path_name_1); + json_alternate_names.values.push_back(route_names.alternative_path_name_2); + json_alternate_names_array.values.push_back(json_alternate_names); + json_result.values["alternative_names"] = json_alternate_names_array; + } + + JSON::Object json_hint_object; + json_hint_object.values["checksum"] = raw_route.check_sum; + JSON::Array json_location_hint_array; + std::string hint; + for (unsigned i = 0; i < raw_route.segment_end_coordinates.size(); ++i) + { + EncodeObjectToBase64(raw_route.segment_end_coordinates[i].source_phantom, hint); + json_location_hint_array.values.push_back(hint); + } + EncodeObjectToBase64(raw_route.segment_end_coordinates.back().target_phantom, hint); + json_location_hint_array.values.push_back(hint); + json_hint_object.values["locations"] = json_location_hint_array; + json_result.values["hint_data"] = json_hint_object; + + // render the content to the output array + TIMER_START(route_render); + JSON::render(reply.content, json_result); + TIMER_STOP(route_render); + SimpleLogger().Write(logDEBUG) << "rendering took: " << TIMER_MSEC(route_render); + } + + // TODO: reorder parameters + inline void BuildTextualDescription(DescriptionFactory &description_factory, + JSON::Array & json_instruction_array, + const int route_length, + std::vector &route_segments_list) + { + // Segment information has following format: + //["instruction id","streetname",length,position,time,"length","earth_direction",azimuth] + unsigned necessary_segments_running_index = 0; + round_about.leave_at_exit = 0; + round_about.name_id = 0; + std::string temp_dist, temp_length, temp_duration, temp_bearing, temp_instruction; + + // Fetch data from Factory and generate a string from it. + for (const SegmentInformation &segment : description_factory.path_description) + { + JSON::Array json_instruction_row; + TurnInstruction current_instruction = segment.turn_instruction; + entered_restricted_area_count += (current_instruction != segment.turn_instruction); + if (TurnInstructionsClass::TurnIsNecessary(current_instruction)) + { + if (TurnInstruction::EnterRoundAbout == current_instruction) + { + round_about.name_id = segment.name_id; + round_about.start_index = necessary_segments_running_index; + } + else + { + std::string current_turn_instruction; + if (TurnInstruction::LeaveRoundAbout == current_instruction) + { + temp_instruction = IntToString(as_integer(TurnInstruction::EnterRoundAbout)); + current_turn_instruction += temp_instruction; + current_turn_instruction += "-"; + temp_instruction = IntToString(round_about.leave_at_exit + 1); + current_turn_instruction += temp_instruction; + round_about.leave_at_exit = 0; + } + else + { + temp_instruction = IntToString(as_integer(current_instruction)); + current_turn_instruction += temp_instruction; + } + json_instruction_row.values.push_back(current_turn_instruction); + + json_instruction_row.values.push_back(facade->GetEscapedNameForNameID(segment.name_id)); + json_instruction_row.values.push_back(std::round(segment.length)); + json_instruction_row.values.push_back(necessary_segments_running_index); + json_instruction_row.values.push_back(round(segment.duration / 10)); + json_instruction_row.values.push_back(IntToString(segment.length)+"m"); + int bearing_value = round(segment.bearing / 10.); + json_instruction_row.values.push_back(Azimuth::Get(bearing_value)); + json_instruction_row.values.push_back(bearing_value); + + route_segments_list.emplace_back( + segment.name_id, segment.length, route_segments_list.size()); + json_instruction_array.values.push_back(json_instruction_row); + } + } + else if (TurnInstruction::StayOnRoundAbout == current_instruction) + { + ++round_about.leave_at_exit; + } + if (segment.necessary) + { + ++necessary_segments_running_index; + } + } + + //TODO: check if this in an invariant + if (INVALID_EDGE_WEIGHT != route_length) + { + JSON::Array json_last_instruction_row; + temp_instruction = IntToString(as_integer(TurnInstruction::ReachedYourDestination)); + json_last_instruction_row.values.push_back(temp_instruction); + json_last_instruction_row.values.push_back(""); + json_last_instruction_row.values.push_back(0); + json_last_instruction_row.values.push_back(necessary_segments_running_index - 1); + json_last_instruction_row.values.push_back(0); + json_last_instruction_row.values.push_back("0m"); + json_last_instruction_row.values.push_back(Azimuth::Get(0.0)); + json_last_instruction_row.values.push_back(0.); + json_instruction_array.values.push_back(json_last_instruction_row); + } + } +}; + +#endif /* JSON_DESCRIPTOR_H_ */ diff --git a/Include/osrm/Coordinate.h b/Include/osrm/Coordinate.h index 18d37366bcc..d36cd05bc3f 100644 --- a/Include/osrm/Coordinate.h +++ b/Include/osrm/Coordinate.h @@ -74,7 +74,14 @@ struct FixedPointCoordinate FixedPointCoordinate &nearest_location, double &r); + static double GetBearing(const FixedPointCoordinate &A, const FixedPointCoordinate &B); + + double GetBearing(const FixedPointCoordinate &other) const; + void Output(std::ostream &out) const; + + static double DegreeToRadian(const double degree); + static double RadianToDegree(const double radian); }; inline std::ostream &operator<<(std::ostream &o, FixedPointCoordinate const &c) From c970cd13ccab7f70f976fb1dfbfa91c92eb39bea Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Sun, 18 May 2014 13:12:10 +0200 Subject: [PATCH 12/36] flip bearings by 180 --- Descriptors/DescriptionFactory.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Descriptors/DescriptionFactory.h b/Descriptors/DescriptionFactory.h index 85c3a3c29dc..6562d0c0796 100644 --- a/Descriptors/DescriptionFactory.h +++ b/Descriptors/DescriptionFactory.h @@ -195,7 +195,7 @@ class DescriptionFactory { if (path_description[i].necessary) { - const double angle = path_description[i].location.GetBearing(path_description[i + 1].location); + const double angle = path_description[i+1].location.GetBearing(path_description[i].location); path_description[i].bearing = angle * 10; } } From bc951de2a5269779af15330d8126e73e27d4a35f Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Sun, 18 May 2014 13:45:59 +0200 Subject: [PATCH 13/36] use trig functions from std namespace --- DataStructures/Coordinate.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/DataStructures/Coordinate.cpp b/DataStructures/Coordinate.cpp index 05ab8c2049a..b4d06cacfca 100644 --- a/DataStructures/Coordinate.cpp +++ b/DataStructures/Coordinate.cpp @@ -359,9 +359,10 @@ double FixedPointCoordinate::GetBearing(const FixedPointCoordinate &other) const const double lat1 = DegreeToRadian(other.lat / COORDINATE_PRECISION); const double lat2 = DegreeToRadian(lat / COORDINATE_PRECISION); - const double y = sin(delta_long) * cos(lat2); - const double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(delta_long); - double result = RadianToDegree(atan2(y, x)); + const double y = std::sin(delta_long) * std::cos(lat2); + const double x = std::cos(lat1) * std::sin(lat2) - std::sin(lat1) * std::cos(lat2) * std::cos(delta_long); + double result = RadianToDegree(std::atan2(y, x)); + while (result < 0.) { result += 360.; From 8fe09c85b6f068809ae454a47a2329bea21b80b4 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Sun, 18 May 2014 14:00:42 +0200 Subject: [PATCH 14/36] move atan2 lookup into trig header --- DataStructures/Coordinate.cpp | 5 +- Util/ComputeAngle.h | 756 +------------------------------- Util/TrigonometryTables.h | 789 ++++++++++++++++++++++++++++++++++ 3 files changed, 793 insertions(+), 757 deletions(-) create mode 100644 Util/TrigonometryTables.h diff --git a/DataStructures/Coordinate.cpp b/DataStructures/Coordinate.cpp index b4d06cacfca..61afb4c2060 100644 --- a/DataStructures/Coordinate.cpp +++ b/DataStructures/Coordinate.cpp @@ -29,6 +29,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "../Util/MercatorUtil.h" #include "../Util/SimpleLogger.h" #include "../Util/StringUtil.h" +#include "../Util/TrigonometryTables.h" #include @@ -339,7 +340,7 @@ double FixedPointCoordinate::GetBearing(const FixedPointCoordinate &A, const Fix const double y = sin(delta_long) * cos(lat2); const double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(delta_long); - double result = RadianToDegree(atan2(y, x)); + double result = RadianToDegree(atan2_lookup(y, x)); while (result < 0.) { result += 360.; @@ -361,7 +362,7 @@ double FixedPointCoordinate::GetBearing(const FixedPointCoordinate &other) const const double y = std::sin(delta_long) * std::cos(lat2); const double x = std::cos(lat1) * std::sin(lat2) - std::sin(lat1) * std::cos(lat2) * std::cos(delta_long); - double result = RadianToDegree(std::atan2(y, x)); + double result = RadianToDegree(atan2_lookup(y, x)); while (result < 0.) { diff --git a/Util/ComputeAngle.h b/Util/ComputeAngle.h index 89a7348e63d..02f678a0759 100644 --- a/Util/ComputeAngle.h +++ b/Util/ComputeAngle.h @@ -28,767 +28,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef COMPUTE_ANGLE_H #define COMPUTE_ANGLE_H +#include "TrigonometryTables.h" #include "../Util/MercatorUtil.h" #include #include #include -constexpr unsigned short atan_table[4096] = { -0x0000, 0x0014, 0x0028, 0x003d, 0x0051, 0x0065, -0x007a, 0x008e, 0x00a3, 0x00b7, 0x00cb, 0x00e0, -0x00f4, 0x0108, 0x011d, 0x0131, 0x0146, 0x015a, -0x016e, 0x0183, 0x0197, 0x01ab, 0x01c0, 0x01d4, -0x01e9, 0x01fd, 0x0211, 0x0226, 0x023a, 0x024e, -0x0263, 0x0277, 0x028c, 0x02a0, 0x02b4, 0x02c9, -0x02dd, 0x02f1, 0x0306, 0x031a, 0x032f, 0x0343, -0x0357, 0x036c, 0x0380, 0x0394, 0x03a9, 0x03bd, -0x03d2, 0x03e6, 0x03fa, 0x040f, 0x0423, 0x0437, -0x044c, 0x0460, 0x0475, 0x0489, 0x049d, 0x04b2, -0x04c6, 0x04da, 0x04ef, 0x0503, 0x0517, 0x052c, -0x0540, 0x0555, 0x0569, 0x057d, 0x0592, 0x05a6, -0x05ba, 0x05cf, 0x05e3, 0x05f8, 0x060c, 0x0620, -0x0635, 0x0649, 0x065d, 0x0672, 0x0686, 0x069b, -0x06af, 0x06c3, 0x06d8, 0x06ec, 0x0700, 0x0715, -0x0729, 0x073d, 0x0752, 0x0766, 0x077b, 0x078f, -0x07a3, 0x07b8, 0x07cc, 0x07e0, 0x07f5, 0x0809, -0x081d, 0x0832, 0x0846, 0x085b, 0x086f, 0x0883, -0x0898, 0x08ac, 0x08c0, 0x08d5, 0x08e9, 0x08fd, -0x0912, 0x0926, 0x093b, 0x094f, 0x0963, 0x0978, -0x098c, 0x09a0, 0x09b5, 0x09c9, 0x09dd, 0x09f2, -0x0a06, 0x0a1a, 0x0a2f, 0x0a43, 0x0a58, 0x0a6c, -0x0a80, 0x0a95, 0x0aa9, 0x0abd, 0x0ad2, 0x0ae6, -0x0afa, 0x0b0f, 0x0b23, 0x0b37, 0x0b4c, 0x0b60, -0x0b75, 0x0b89, 0x0b9d, 0x0bb2, 0x0bc6, 0x0bda, -0x0bef, 0x0c03, 0x0c17, 0x0c2c, 0x0c40, 0x0c54, -0x0c69, 0x0c7d, 0x0c91, 0x0ca6, 0x0cba, 0x0cce, -0x0ce3, 0x0cf7, 0x0d0b, 0x0d20, 0x0d34, 0x0d48, -0x0d5d, 0x0d71, 0x0d86, 0x0d9a, 0x0dae, 0x0dc3, -0x0dd7, 0x0deb, 0x0e00, 0x0e14, 0x0e28, 0x0e3d, -0x0e51, 0x0e65, 0x0e7a, 0x0e8e, 0x0ea2, 0x0eb7, -0x0ecb, 0x0edf, 0x0ef4, 0x0f08, 0x0f1c, 0x0f31, -0x0f45, 0x0f59, 0x0f6e, 0x0f82, 0x0f96, 0x0fab, -0x0fbf, 0x0fd3, 0x0fe8, 0x0ffc, 0x1010, 0x1025, -0x1039, 0x104d, 0x1062, 0x1076, 0x108a, 0x109e, -0x10b3, 0x10c7, 0x10db, 0x10f0, 0x1104, 0x1118, -0x112d, 0x1141, 0x1155, 0x116a, 0x117e, 0x1192, -0x11a7, 0x11bb, 0x11cf, 0x11e4, 0x11f8, 0x120c, -0x1221, 0x1235, 0x1249, 0x125d, 0x1272, 0x1286, -0x129a, 0x12af, 0x12c3, 0x12d7, 0x12ec, 0x1300, -0x1314, 0x1329, 0x133d, 0x1351, 0x1365, 0x137a, -0x138e, 0x13a2, 0x13b7, 0x13cb, 0x13df, 0x13f4, -0x1408, 0x141c, 0x1431, 0x1445, 0x1459, 0x146d, -0x1482, 0x1496, 0x14aa, 0x14bf, 0x14d3, 0x14e7, -0x14fb, 0x1510, 0x1524, 0x1538, 0x154d, 0x1561, -0x1575, 0x1589, 0x159e, 0x15b2, 0x15c6, 0x15db, -0x15ef, 0x1603, 0x1617, 0x162c, 0x1640, 0x1654, -0x1669, 0x167d, 0x1691, 0x16a5, 0x16ba, 0x16ce, -0x16e2, 0x16f7, 0x170b, 0x171f, 0x1733, 0x1748, -0x175c, 0x1770, 0x1784, 0x1799, 0x17ad, 0x17c1, -0x17d6, 0x17ea, 0x17fe, 0x1812, 0x1827, 0x183b, -0x184f, 0x1863, 0x1878, 0x188c, 0x18a0, 0x18b4, -0x18c9, 0x18dd, 0x18f1, 0x1905, 0x191a, 0x192e, -0x1942, 0x1957, 0x196b, 0x197f, 0x1993, 0x19a8, -0x19bc, 0x19d0, 0x19e4, 0x19f9, 0x1a0d, 0x1a21, -0x1a35, 0x1a49, 0x1a5e, 0x1a72, 0x1a86, 0x1a9a, -0x1aaf, 0x1ac3, 0x1ad7, 0x1aeb, 0x1b00, 0x1b14, -0x1b28, 0x1b3c, 0x1b51, 0x1b65, 0x1b79, 0x1b8d, -0x1ba2, 0x1bb6, 0x1bca, 0x1bde, 0x1bf2, 0x1c07, -0x1c1b, 0x1c2f, 0x1c43, 0x1c58, 0x1c6c, 0x1c80, -0x1c94, 0x1ca8, 0x1cbd, 0x1cd1, 0x1ce5, 0x1cf9, -0x1d0e, 0x1d22, 0x1d36, 0x1d4a, 0x1d5e, 0x1d73, -0x1d87, 0x1d9b, 0x1daf, 0x1dc3, 0x1dd8, 0x1dec, -0x1e00, 0x1e14, 0x1e28, 0x1e3d, 0x1e51, 0x1e65, -0x1e79, 0x1e8d, 0x1ea2, 0x1eb6, 0x1eca, 0x1ede, -0x1ef2, 0x1f07, 0x1f1b, 0x1f2f, 0x1f43, 0x1f57, -0x1f6c, 0x1f80, 0x1f94, 0x1fa8, 0x1fbc, 0x1fd1, -0x1fe5, 0x1ff9, 0x200d, 0x2021, 0x2035, 0x204a, -0x205e, 0x2072, 0x2086, 0x209a, 0x20ae, 0x20c3, -0x20d7, 0x20eb, 0x20ff, 0x2113, 0x2127, 0x213c, -0x2150, 0x2164, 0x2178, 0x218c, 0x21a0, 0x21b5, -0x21c9, 0x21dd, 0x21f1, 0x2205, 0x2219, 0x222e, -0x2242, 0x2256, 0x226a, 0x227e, 0x2292, 0x22a6, -0x22bb, 0x22cf, 0x22e3, 0x22f7, 0x230b, 0x231f, -0x2333, 0x2348, 0x235c, 0x2370, 0x2384, 0x2398, -0x23ac, 0x23c0, 0x23d5, 0x23e9, 0x23fd, 0x2411, -0x2425, 0x2439, 0x244d, 0x2461, 0x2476, 0x248a, -0x249e, 0x24b2, 0x24c6, 0x24da, 0x24ee, 0x2502, -0x2517, 0x252b, 0x253f, 0x2553, 0x2567, 0x257b, -0x258f, 0x25a3, 0x25b7, 0x25cb, 0x25e0, 0x25f4, -0x2608, 0x261c, 0x2630, 0x2644, 0x2658, 0x266c, -0x2680, 0x2694, 0x26a9, 0x26bd, 0x26d1, 0x26e5, -0x26f9, 0x270d, 0x2721, 0x2735, 0x2749, 0x275d, -0x2771, 0x2785, 0x279a, 0x27ae, 0x27c2, 0x27d6, -0x27ea, 0x27fe, 0x2812, 0x2826, 0x283a, 0x284e, -0x2862, 0x2876, 0x288a, 0x289e, 0x28b3, 0x28c7, -0x28db, 0x28ef, 0x2903, 0x2917, 0x292b, 0x293f, -0x2953, 0x2967, 0x297b, 0x298f, 0x29a3, 0x29b7, -0x29cb, 0x29df, 0x29f3, 0x2a07, 0x2a1b, 0x2a2f, -0x2a43, 0x2a58, 0x2a6c, 0x2a80, 0x2a94, 0x2aa8, -0x2abc, 0x2ad0, 0x2ae4, 0x2af8, 0x2b0c, 0x2b20, -0x2b34, 0x2b48, 0x2b5c, 0x2b70, 0x2b84, 0x2b98, -0x2bac, 0x2bc0, 0x2bd4, 0x2be8, 0x2bfc, 0x2c10, -0x2c24, 0x2c38, 0x2c4c, 0x2c60, 0x2c74, 0x2c88, -0x2c9c, 0x2cb0, 0x2cc4, 0x2cd8, 0x2cec, 0x2d00, -0x2d14, 0x2d28, 0x2d3c, 0x2d50, 0x2d64, 0x2d78, -0x2d8c, 0x2da0, 0x2db4, 0x2dc8, 0x2ddc, 0x2df0, -0x2e04, 0x2e18, 0x2e2c, 0x2e40, 0x2e54, 0x2e68, -0x2e7c, 0x2e90, 0x2ea3, 0x2eb7, 0x2ecb, 0x2edf, -0x2ef3, 0x2f07, 0x2f1b, 0x2f2f, 0x2f43, 0x2f57, -0x2f6b, 0x2f7f, 0x2f93, 0x2fa7, 0x2fbb, 0x2fcf, -0x2fe3, 0x2ff7, 0x300b, 0x301e, 0x3032, 0x3046, -0x305a, 0x306e, 0x3082, 0x3096, 0x30aa, 0x30be, -0x30d2, 0x30e6, 0x30fa, 0x310e, 0x3122, 0x3135, -0x3149, 0x315d, 0x3171, 0x3185, 0x3199, 0x31ad, -0x31c1, 0x31d5, 0x31e9, 0x31fd, 0x3210, 0x3224, -0x3238, 0x324c, 0x3260, 0x3274, 0x3288, 0x329c, -0x32b0, 0x32c3, 0x32d7, 0x32eb, 0x32ff, 0x3313, -0x3327, 0x333b, 0x334f, 0x3363, 0x3376, 0x338a, -0x339e, 0x33b2, 0x33c6, 0x33da, 0x33ee, 0x3401, -0x3415, 0x3429, 0x343d, 0x3451, 0x3465, 0x3479, -0x348c, 0x34a0, 0x34b4, 0x34c8, 0x34dc, 0x34f0, -0x3504, 0x3517, 0x352b, 0x353f, 0x3553, 0x3567, -0x357b, 0x358e, 0x35a2, 0x35b6, 0x35ca, 0x35de, -0x35f2, 0x3605, 0x3619, 0x362d, 0x3641, 0x3655, -0x3668, 0x367c, 0x3690, 0x36a4, 0x36b8, 0x36cb, -0x36df, 0x36f3, 0x3707, 0x371b, 0x372f, 0x3742, -0x3756, 0x376a, 0x377e, 0x3791, 0x37a5, 0x37b9, -0x37cd, 0x37e1, 0x37f4, 0x3808, 0x381c, 0x3830, -0x3844, 0x3857, 0x386b, 0x387f, 0x3893, 0x38a6, -0x38ba, 0x38ce, 0x38e2, 0x38f5, 0x3909, 0x391d, -0x3931, 0x3944, 0x3958, 0x396c, 0x3980, 0x3993, -0x39a7, 0x39bb, 0x39cf, 0x39e2, 0x39f6, 0x3a0a, -0x3a1e, 0x3a31, 0x3a45, 0x3a59, 0x3a6d, 0x3a80, -0x3a94, 0x3aa8, 0x3abb, 0x3acf, 0x3ae3, 0x3af7, -0x3b0a, 0x3b1e, 0x3b32, 0x3b45, 0x3b59, 0x3b6d, -0x3b81, 0x3b94, 0x3ba8, 0x3bbc, 0x3bcf, 0x3be3, -0x3bf7, 0x3c0b, 0x3c1e, 0x3c32, 0x3c46, 0x3c59, -0x3c6d, 0x3c81, 0x3c94, 0x3ca8, 0x3cbc, 0x3ccf, -0x3ce3, 0x3cf7, 0x3d0a, 0x3d1e, 0x3d32, 0x3d45, -0x3d59, 0x3d6d, 0x3d80, 0x3d94, 0x3da8, 0x3dbb, -0x3dcf, 0x3de3, 0x3df6, 0x3e0a, 0x3e1e, 0x3e31, -0x3e45, 0x3e59, 0x3e6c, 0x3e80, 0x3e93, 0x3ea7, -0x3ebb, 0x3ece, 0x3ee2, 0x3ef6, 0x3f09, 0x3f1d, -0x3f30, 0x3f44, 0x3f58, 0x3f6b, 0x3f7f, 0x3f93, -0x3fa6, 0x3fba, 0x3fcd, 0x3fe1, 0x3ff5, 0x4008, -0x401c, 0x402f, 0x4043, 0x4057, 0x406a, 0x407e, -0x4091, 0x40a5, 0x40b8, 0x40cc, 0x40e0, 0x40f3, -0x4107, 0x411a, 0x412e, 0x4142, 0x4155, 0x4169, -0x417c, 0x4190, 0x41a3, 0x41b7, 0x41ca, 0x41de, -0x41f2, 0x4205, 0x4219, 0x422c, 0x4240, 0x4253, -0x4267, 0x427a, 0x428e, 0x42a1, 0x42b5, 0x42c9, -0x42dc, 0x42f0, 0x4303, 0x4317, 0x432a, 0x433e, -0x4351, 0x4365, 0x4378, 0x438c, 0x439f, 0x43b3, -0x43c6, 0x43da, 0x43ed, 0x4401, 0x4414, 0x4428, -0x443b, 0x444f, 0x4462, 0x4476, 0x4489, 0x449d, -0x44b0, 0x44c4, 0x44d7, 0x44eb, 0x44fe, 0x4512, -0x4525, 0x4539, 0x454c, 0x4560, 0x4573, 0x4586, -0x459a, 0x45ad, 0x45c1, 0x45d4, 0x45e8, 0x45fb, -0x460f, 0x4622, 0x4636, 0x4649, 0x465c, 0x4670, -0x4683, 0x4697, 0x46aa, 0x46be, 0x46d1, 0x46e5, -0x46f8, 0x470b, 0x471f, 0x4732, 0x4746, 0x4759, -0x476c, 0x4780, 0x4793, 0x47a7, 0x47ba, 0x47cd, -0x47e1, 0x47f4, 0x4808, 0x481b, 0x482e, 0x4842, -0x4855, 0x4869, 0x487c, 0x488f, 0x48a3, 0x48b6, -0x48ca, 0x48dd, 0x48f0, 0x4904, 0x4917, 0x492a, -0x493e, 0x4951, 0x4965, 0x4978, 0x498b, 0x499f, -0x49b2, 0x49c5, 0x49d9, 0x49ec, 0x49ff, 0x4a13, -0x4a26, 0x4a39, 0x4a4d, 0x4a60, 0x4a73, 0x4a87, -0x4a9a, 0x4aad, 0x4ac1, 0x4ad4, 0x4ae7, 0x4afb, -0x4b0e, 0x4b21, 0x4b35, 0x4b48, 0x4b5b, 0x4b6f, -0x4b82, 0x4b95, 0x4ba8, 0x4bbc, 0x4bcf, 0x4be2, -0x4bf6, 0x4c09, 0x4c1c, 0x4c2f, 0x4c43, 0x4c56, -0x4c69, 0x4c7d, 0x4c90, 0x4ca3, 0x4cb6, 0x4cca, -0x4cdd, 0x4cf0, 0x4d03, 0x4d17, 0x4d2a, 0x4d3d, -0x4d50, 0x4d64, 0x4d77, 0x4d8a, 0x4d9d, 0x4db1, -0x4dc4, 0x4dd7, 0x4dea, 0x4dfe, 0x4e11, 0x4e24, -0x4e37, 0x4e4b, 0x4e5e, 0x4e71, 0x4e84, 0x4e97, -0x4eab, 0x4ebe, 0x4ed1, 0x4ee4, 0x4ef7, 0x4f0b, -0x4f1e, 0x4f31, 0x4f44, 0x4f57, 0x4f6b, 0x4f7e, -0x4f91, 0x4fa4, 0x4fb7, 0x4fcb, 0x4fde, 0x4ff1, -0x5004, 0x5017, 0x502a, 0x503e, 0x5051, 0x5064, -0x5077, 0x508a, 0x509d, 0x50b1, 0x50c4, 0x50d7, -0x50ea, 0x50fd, 0x5110, 0x5123, 0x5137, 0x514a, -0x515d, 0x5170, 0x5183, 0x5196, 0x51a9, 0x51bc, -0x51d0, 0x51e3, 0x51f6, 0x5209, 0x521c, 0x522f, -0x5242, 0x5255, 0x5268, 0x527c, 0x528f, 0x52a2, -0x52b5, 0x52c8, 0x52db, 0x52ee, 0x5301, 0x5314, -0x5327, 0x533a, 0x534e, 0x5361, 0x5374, 0x5387, -0x539a, 0x53ad, 0x53c0, 0x53d3, 0x53e6, 0x53f9, -0x540c, 0x541f, 0x5432, 0x5445, 0x5458, 0x546b, -0x547e, 0x5491, 0x54a5, 0x54b8, 0x54cb, 0x54de, -0x54f1, 0x5504, 0x5517, 0x552a, 0x553d, 0x5550, -0x5563, 0x5576, 0x5589, 0x559c, 0x55af, 0x55c2, -0x55d5, 0x55e8, 0x55fb, 0x560e, 0x5621, 0x5634, -0x5647, 0x565a, 0x566d, 0x5680, 0x5693, 0x56a6, -0x56b9, 0x56cb, 0x56de, 0x56f1, 0x5704, 0x5717, -0x572a, 0x573d, 0x5750, 0x5763, 0x5776, 0x5789, -0x579c, 0x57af, 0x57c2, 0x57d5, 0x57e8, 0x57fb, -0x580e, 0x5820, 0x5833, 0x5846, 0x5859, 0x586c, -0x587f, 0x5892, 0x58a5, 0x58b8, 0x58cb, 0x58de, -0x58f0, 0x5903, 0x5916, 0x5929, 0x593c, 0x594f, -0x5962, 0x5975, 0x5988, 0x599a, 0x59ad, 0x59c0, -0x59d3, 0x59e6, 0x59f9, 0x5a0c, 0x5a1f, 0x5a31, -0x5a44, 0x5a57, 0x5a6a, 0x5a7d, 0x5a90, 0x5aa2, -0x5ab5, 0x5ac8, 0x5adb, 0x5aee, 0x5b01, 0x5b13, -0x5b26, 0x5b39, 0x5b4c, 0x5b5f, 0x5b72, 0x5b84, -0x5b97, 0x5baa, 0x5bbd, 0x5bd0, 0x5be2, 0x5bf5, -0x5c08, 0x5c1b, 0x5c2e, 0x5c40, 0x5c53, 0x5c66, -0x5c79, 0x5c8c, 0x5c9e, 0x5cb1, 0x5cc4, 0x5cd7, -0x5ce9, 0x5cfc, 0x5d0f, 0x5d22, 0x5d34, 0x5d47, -0x5d5a, 0x5d6d, 0x5d7f, 0x5d92, 0x5da5, 0x5db8, -0x5dca, 0x5ddd, 0x5df0, 0x5e03, 0x5e15, 0x5e28, -0x5e3b, 0x5e4d, 0x5e60, 0x5e73, 0x5e86, 0x5e98, -0x5eab, 0x5ebe, 0x5ed0, 0x5ee3, 0x5ef6, 0x5f09, -0x5f1b, 0x5f2e, 0x5f41, 0x5f53, 0x5f66, 0x5f79, -0x5f8b, 0x5f9e, 0x5fb1, 0x5fc3, 0x5fd6, 0x5fe9, -0x5ffb, 0x600e, 0x6021, 0x6033, 0x6046, 0x6059, -0x606b, 0x607e, 0x6091, 0x60a3, 0x60b6, 0x60c8, -0x60db, 0x60ee, 0x6100, 0x6113, 0x6126, 0x6138, -0x614b, 0x615d, 0x6170, 0x6183, 0x6195, 0x61a8, -0x61ba, 0x61cd, 0x61e0, 0x61f2, 0x6205, 0x6217, -0x622a, 0x623d, 0x624f, 0x6262, 0x6274, 0x6287, -0x6299, 0x62ac, 0x62bf, 0x62d1, 0x62e4, 0x62f6, -0x6309, 0x631b, 0x632e, 0x6340, 0x6353, 0x6366, -0x6378, 0x638b, 0x639d, 0x63b0, 0x63c2, 0x63d5, -0x63e7, 0x63fa, 0x640c, 0x641f, 0x6431, 0x6444, -0x6456, 0x6469, 0x647b, 0x648e, 0x64a0, 0x64b3, -0x64c5, 0x64d8, 0x64ea, 0x64fd, 0x650f, 0x6522, -0x6534, 0x6547, 0x6559, 0x656c, 0x657e, 0x6591, -0x65a3, 0x65b5, 0x65c8, 0x65da, 0x65ed, 0x65ff, -0x6612, 0x6624, 0x6637, 0x6649, 0x665b, 0x666e, -0x6680, 0x6693, 0x66a5, 0x66b8, 0x66ca, 0x66dc, -0x66ef, 0x6701, 0x6714, 0x6726, 0x6738, 0x674b, -0x675d, 0x6770, 0x6782, 0x6794, 0x67a7, 0x67b9, -0x67cc, 0x67de, 0x67f0, 0x6803, 0x6815, 0x6827, -0x683a, 0x684c, 0x685e, 0x6871, 0x6883, 0x6896, -0x68a8, 0x68ba, 0x68cd, 0x68df, 0x68f1, 0x6904, -0x6916, 0x6928, 0x693b, 0x694d, 0x695f, 0x6972, -0x6984, 0x6996, 0x69a8, 0x69bb, 0x69cd, 0x69df, -0x69f2, 0x6a04, 0x6a16, 0x6a29, 0x6a3b, 0x6a4d, -0x6a5f, 0x6a72, 0x6a84, 0x6a96, 0x6aa9, 0x6abb, -0x6acd, 0x6adf, 0x6af2, 0x6b04, 0x6b16, 0x6b28, -0x6b3b, 0x6b4d, 0x6b5f, 0x6b71, 0x6b84, 0x6b96, -0x6ba8, 0x6bba, 0x6bcd, 0x6bdf, 0x6bf1, 0x6c03, -0x6c15, 0x6c28, 0x6c3a, 0x6c4c, 0x6c5e, 0x6c70, -0x6c83, 0x6c95, 0x6ca7, 0x6cb9, 0x6ccb, 0x6cde, -0x6cf0, 0x6d02, 0x6d14, 0x6d26, 0x6d39, 0x6d4b, -0x6d5d, 0x6d6f, 0x6d81, 0x6d93, 0x6da6, 0x6db8, -0x6dca, 0x6ddc, 0x6dee, 0x6e00, 0x6e12, 0x6e25, -0x6e37, 0x6e49, 0x6e5b, 0x6e6d, 0x6e7f, 0x6e91, -0x6ea3, 0x6eb6, 0x6ec8, 0x6eda, 0x6eec, 0x6efe, -0x6f10, 0x6f22, 0x6f34, 0x6f46, 0x6f58, 0x6f6b, -0x6f7d, 0x6f8f, 0x6fa1, 0x6fb3, 0x6fc5, 0x6fd7, -0x6fe9, 0x6ffb, 0x700d, 0x701f, 0x7031, 0x7043, -0x7055, 0x7068, 0x707a, 0x708c, 0x709e, 0x70b0, -0x70c2, 0x70d4, 0x70e6, 0x70f8, 0x710a, 0x711c, -0x712e, 0x7140, 0x7152, 0x7164, 0x7176, 0x7188, -0x719a, 0x71ac, 0x71be, 0x71d0, 0x71e2, 0x71f4, -0x7206, 0x7218, 0x722a, 0x723c, 0x724e, 0x7260, -0x7272, 0x7284, 0x7296, 0x72a8, 0x72ba, 0x72cc, -0x72dd, 0x72ef, 0x7301, 0x7313, 0x7325, 0x7337, -0x7349, 0x735b, 0x736d, 0x737f, 0x7391, 0x73a3, -0x73b5, 0x73c7, 0x73d8, 0x73ea, 0x73fc, 0x740e, -0x7420, 0x7432, 0x7444, 0x7456, 0x7468, 0x747a, -0x748b, 0x749d, 0x74af, 0x74c1, 0x74d3, 0x74e5, -0x74f7, 0x7509, 0x751a, 0x752c, 0x753e, 0x7550, -0x7562, 0x7574, 0x7585, 0x7597, 0x75a9, 0x75bb, -0x75cd, 0x75df, 0x75f0, 0x7602, 0x7614, 0x7626, -0x7638, 0x764a, 0x765b, 0x766d, 0x767f, 0x7691, -0x76a3, 0x76b4, 0x76c6, 0x76d8, 0x76ea, 0x76fb, -0x770d, 0x771f, 0x7731, 0x7743, 0x7754, 0x7766, -0x7778, 0x778a, 0x779b, 0x77ad, 0x77bf, 0x77d1, -0x77e2, 0x77f4, 0x7806, 0x7818, 0x7829, 0x783b, -0x784d, 0x785e, 0x7870, 0x7882, 0x7894, 0x78a5, -0x78b7, 0x78c9, 0x78da, 0x78ec, 0x78fe, 0x7910, -0x7921, 0x7933, 0x7945, 0x7956, 0x7968, 0x797a, -0x798b, 0x799d, 0x79af, 0x79c0, 0x79d2, 0x79e4, -0x79f5, 0x7a07, 0x7a19, 0x7a2a, 0x7a3c, 0x7a4e, -0x7a5f, 0x7a71, 0x7a82, 0x7a94, 0x7aa6, 0x7ab7, -0x7ac9, 0x7adb, 0x7aec, 0x7afe, 0x7b0f, 0x7b21, -0x7b33, 0x7b44, 0x7b56, 0x7b67, 0x7b79, 0x7b8b, -0x7b9c, 0x7bae, 0x7bbf, 0x7bd1, 0x7be2, 0x7bf4, -0x7c06, 0x7c17, 0x7c29, 0x7c3a, 0x7c4c, 0x7c5d, -0x7c6f, 0x7c81, 0x7c92, 0x7ca4, 0x7cb5, 0x7cc7, -0x7cd8, 0x7cea, 0x7cfb, 0x7d0d, 0x7d1e, 0x7d30, -0x7d41, 0x7d53, 0x7d64, 0x7d76, 0x7d87, 0x7d99, -0x7daa, 0x7dbc, 0x7dcd, 0x7ddf, 0x7df0, 0x7e02, -0x7e13, 0x7e25, 0x7e36, 0x7e48, 0x7e59, 0x7e6b, -0x7e7c, 0x7e8e, 0x7e9f, 0x7eb0, 0x7ec2, 0x7ed3, -0x7ee5, 0x7ef6, 0x7f08, 0x7f19, 0x7f2b, 0x7f3c, -0x7f4d, 0x7f5f, 0x7f70, 0x7f82, 0x7f93, 0x7fa4, -0x7fb6, 0x7fc7, 0x7fd9, 0x7fea, 0x7ffb, 0x800d, -0x801e, 0x8030, 0x8041, 0x8052, 0x8064, 0x8075, -0x8086, 0x8098, 0x80a9, 0x80bb, 0x80cc, 0x80dd, -0x80ef, 0x8100, 0x8111, 0x8123, 0x8134, 0x8145, -0x8157, 0x8168, 0x8179, 0x818b, 0x819c, 0x81ad, -0x81bf, 0x81d0, 0x81e1, 0x81f3, 0x8204, 0x8215, -0x8226, 0x8238, 0x8249, 0x825a, 0x826c, 0x827d, -0x828e, 0x829f, 0x82b1, 0x82c2, 0x82d3, 0x82e5, -0x82f6, 0x8307, 0x8318, 0x832a, 0x833b, 0x834c, -0x835d, 0x836f, 0x8380, 0x8391, 0x83a2, 0x83b3, -0x83c5, 0x83d6, 0x83e7, 0x83f8, 0x840a, 0x841b, -0x842c, 0x843d, 0x844e, 0x8460, 0x8471, 0x8482, -0x8493, 0x84a4, 0x84b6, 0x84c7, 0x84d8, 0x84e9, -0x84fa, 0x850b, 0x851d, 0x852e, 0x853f, 0x8550, -0x8561, 0x8572, 0x8584, 0x8595, 0x85a6, 0x85b7, -0x85c8, 0x85d9, 0x85ea, 0x85fb, 0x860d, 0x861e, -0x862f, 0x8640, 0x8651, 0x8662, 0x8673, 0x8684, -0x8695, 0x86a7, 0x86b8, 0x86c9, 0x86da, 0x86eb, -0x86fc, 0x870d, 0x871e, 0x872f, 0x8740, 0x8751, -0x8762, 0x8773, 0x8784, 0x8796, 0x87a7, 0x87b8, -0x87c9, 0x87da, 0x87eb, 0x87fc, 0x880d, 0x881e, -0x882f, 0x8840, 0x8851, 0x8862, 0x8873, 0x8884, -0x8895, 0x88a6, 0x88b7, 0x88c8, 0x88d9, 0x88ea, -0x88fb, 0x890c, 0x891d, 0x892e, 0x893f, 0x8950, -0x8961, 0x8972, 0x8983, 0x8994, 0x89a5, 0x89b6, -0x89c6, 0x89d7, 0x89e8, 0x89f9, 0x8a0a, 0x8a1b, -0x8a2c, 0x8a3d, 0x8a4e, 0x8a5f, 0x8a70, 0x8a81, -0x8a92, 0x8aa3, 0x8ab3, 0x8ac4, 0x8ad5, 0x8ae6, -0x8af7, 0x8b08, 0x8b19, 0x8b2a, 0x8b3b, 0x8b4b, -0x8b5c, 0x8b6d, 0x8b7e, 0x8b8f, 0x8ba0, 0x8bb1, -0x8bc1, 0x8bd2, 0x8be3, 0x8bf4, 0x8c05, 0x8c16, -0x8c27, 0x8c37, 0x8c48, 0x8c59, 0x8c6a, 0x8c7b, -0x8c8c, 0x8c9c, 0x8cad, 0x8cbe, 0x8ccf, 0x8ce0, -0x8cf0, 0x8d01, 0x8d12, 0x8d23, 0x8d34, 0x8d44, -0x8d55, 0x8d66, 0x8d77, 0x8d87, 0x8d98, 0x8da9, -0x8dba, 0x8dca, 0x8ddb, 0x8dec, 0x8dfd, 0x8e0d, -0x8e1e, 0x8e2f, 0x8e40, 0x8e50, 0x8e61, 0x8e72, -0x8e83, 0x8e93, 0x8ea4, 0x8eb5, 0x8ec5, 0x8ed6, -0x8ee7, 0x8ef8, 0x8f08, 0x8f19, 0x8f2a, 0x8f3a, -0x8f4b, 0x8f5c, 0x8f6c, 0x8f7d, 0x8f8e, 0x8f9e, -0x8faf, 0x8fc0, 0x8fd0, 0x8fe1, 0x8ff2, 0x9002, -0x9013, 0x9024, 0x9034, 0x9045, 0x9056, 0x9066, -0x9077, 0x9088, 0x9098, 0x90a9, 0x90b9, 0x90ca, -0x90db, 0x90eb, 0x90fc, 0x910c, 0x911d, 0x912e, -0x913e, 0x914f, 0x915f, 0x9170, 0x9181, 0x9191, -0x91a2, 0x91b2, 0x91c3, 0x91d3, 0x91e4, 0x91f5, -0x9205, 0x9216, 0x9226, 0x9237, 0x9247, 0x9258, -0x9268, 0x9279, 0x9289, 0x929a, 0x92aa, 0x92bb, -0x92cc, 0x92dc, 0x92ed, 0x92fd, 0x930e, 0x931e, -0x932f, 0x933f, 0x9350, 0x9360, 0x9370, 0x9381, -0x9391, 0x93a2, 0x93b2, 0x93c3, 0x93d3, 0x93e4, -0x93f4, 0x9405, 0x9415, 0x9426, 0x9436, 0x9447, -0x9457, 0x9467, 0x9478, 0x9488, 0x9499, 0x94a9, -0x94ba, 0x94ca, 0x94da, 0x94eb, 0x94fb, 0x950c, -0x951c, 0x952c, 0x953d, 0x954d, 0x955e, 0x956e, -0x957e, 0x958f, 0x959f, 0x95af, 0x95c0, 0x95d0, -0x95e1, 0x95f1, 0x9601, 0x9612, 0x9622, 0x9632, -0x9643, 0x9653, 0x9663, 0x9674, 0x9684, 0x9694, -0x96a5, 0x96b5, 0x96c5, 0x96d6, 0x96e6, 0x96f6, -0x9707, 0x9717, 0x9727, 0x9738, 0x9748, 0x9758, -0x9768, 0x9779, 0x9789, 0x9799, 0x97aa, 0x97ba, -0x97ca, 0x97da, 0x97eb, 0x97fb, 0x980b, 0x981b, -0x982c, 0x983c, 0x984c, 0x985c, 0x986d, 0x987d, -0x988d, 0x989d, 0x98ad, 0x98be, 0x98ce, 0x98de, -0x98ee, 0x98ff, 0x990f, 0x991f, 0x992f, 0x993f, -0x9950, 0x9960, 0x9970, 0x9980, 0x9990, 0x99a0, -0x99b1, 0x99c1, 0x99d1, 0x99e1, 0x99f1, 0x9a01, -0x9a12, 0x9a22, 0x9a32, 0x9a42, 0x9a52, 0x9a62, -0x9a72, 0x9a83, 0x9a93, 0x9aa3, 0x9ab3, 0x9ac3, -0x9ad3, 0x9ae3, 0x9af3, 0x9b04, 0x9b14, 0x9b24, -0x9b34, 0x9b44, 0x9b54, 0x9b64, 0x9b74, 0x9b84, -0x9b94, 0x9ba4, 0x9bb5, 0x9bc5, 0x9bd5, 0x9be5, -0x9bf5, 0x9c05, 0x9c15, 0x9c25, 0x9c35, 0x9c45, -0x9c55, 0x9c65, 0x9c75, 0x9c85, 0x9c95, 0x9ca5, -0x9cb5, 0x9cc5, 0x9cd5, 0x9ce5, 0x9cf5, 0x9d05, -0x9d15, 0x9d25, 0x9d35, 0x9d45, 0x9d55, 0x9d65, -0x9d75, 0x9d85, 0x9d95, 0x9da5, 0x9db5, 0x9dc5, -0x9dd5, 0x9de5, 0x9df5, 0x9e05, 0x9e15, 0x9e25, -0x9e35, 0x9e45, 0x9e55, 0x9e65, 0x9e74, 0x9e84, -0x9e94, 0x9ea4, 0x9eb4, 0x9ec4, 0x9ed4, 0x9ee4, -0x9ef4, 0x9f04, 0x9f14, 0x9f23, 0x9f33, 0x9f43, -0x9f53, 0x9f63, 0x9f73, 0x9f83, 0x9f93, 0x9fa3, -0x9fb2, 0x9fc2, 0x9fd2, 0x9fe2, 0x9ff2, 0xa002, -0xa012, 0xa021, 0xa031, 0xa041, 0xa051, 0xa061, -0xa071, 0xa080, 0xa090, 0xa0a0, 0xa0b0, 0xa0c0, -0xa0cf, 0xa0df, 0xa0ef, 0xa0ff, 0xa10f, 0xa11e, -0xa12e, 0xa13e, 0xa14e, 0xa15e, 0xa16d, 0xa17d, -0xa18d, 0xa19d, 0xa1ac, 0xa1bc, 0xa1cc, 0xa1dc, -0xa1eb, 0xa1fb, 0xa20b, 0xa21b, 0xa22a, 0xa23a, -0xa24a, 0xa25a, 0xa269, 0xa279, 0xa289, 0xa298, -0xa2a8, 0xa2b8, 0xa2c8, 0xa2d7, 0xa2e7, 0xa2f7, -0xa306, 0xa316, 0xa326, 0xa335, 0xa345, 0xa355, -0xa364, 0xa374, 0xa384, 0xa393, 0xa3a3, 0xa3b3, -0xa3c2, 0xa3d2, 0xa3e2, 0xa3f1, 0xa401, 0xa411, -0xa420, 0xa430, 0xa440, 0xa44f, 0xa45f, 0xa46e, -0xa47e, 0xa48e, 0xa49d, 0xa4ad, 0xa4bc, 0xa4cc, -0xa4dc, 0xa4eb, 0xa4fb, 0xa50a, 0xa51a, 0xa52a, -0xa539, 0xa549, 0xa558, 0xa568, 0xa577, 0xa587, -0xa597, 0xa5a6, 0xa5b6, 0xa5c5, 0xa5d5, 0xa5e4, -0xa5f4, 0xa603, 0xa613, 0xa622, 0xa632, 0xa641, -0xa651, 0xa660, 0xa670, 0xa67f, 0xa68f, 0xa69e, -0xa6ae, 0xa6bd, 0xa6cd, 0xa6dc, 0xa6ec, 0xa6fb, -0xa70b, 0xa71a, 0xa72a, 0xa739, 0xa749, 0xa758, -0xa768, 0xa777, 0xa787, 0xa796, 0xa7a5, 0xa7b5, -0xa7c4, 0xa7d4, 0xa7e3, 0xa7f3, 0xa802, 0xa812, -0xa821, 0xa830, 0xa840, 0xa84f, 0xa85f, 0xa86e, -0xa87d, 0xa88d, 0xa89c, 0xa8ac, 0xa8bb, 0xa8ca, -0xa8da, 0xa8e9, 0xa8f8, 0xa908, 0xa917, 0xa927, -0xa936, 0xa945, 0xa955, 0xa964, 0xa973, 0xa983, -0xa992, 0xa9a1, 0xa9b1, 0xa9c0, 0xa9cf, 0xa9df, -0xa9ee, 0xa9fd, 0xaa0d, 0xaa1c, 0xaa2b, 0xaa3b, -0xaa4a, 0xaa59, 0xaa69, 0xaa78, 0xaa87, 0xaa96, -0xaaa6, 0xaab5, 0xaac4, 0xaad4, 0xaae3, 0xaaf2, -0xab01, 0xab11, 0xab20, 0xab2f, 0xab3e, 0xab4e, -0xab5d, 0xab6c, 0xab7b, 0xab8b, 0xab9a, 0xaba9, -0xabb8, 0xabc7, 0xabd7, 0xabe6, 0xabf5, 0xac04, -0xac14, 0xac23, 0xac32, 0xac41, 0xac50, 0xac60, -0xac6f, 0xac7e, 0xac8d, 0xac9c, 0xacab, 0xacbb, -0xacca, 0xacd9, 0xace8, 0xacf7, 0xad06, 0xad16, -0xad25, 0xad34, 0xad43, 0xad52, 0xad61, 0xad70, -0xad80, 0xad8f, 0xad9e, 0xadad, 0xadbc, 0xadcb, -0xadda, 0xade9, 0xadf8, 0xae08, 0xae17, 0xae26, -0xae35, 0xae44, 0xae53, 0xae62, 0xae71, 0xae80, -0xae8f, 0xae9e, 0xaead, 0xaebd, 0xaecc, 0xaedb, -0xaeea, 0xaef9, 0xaf08, 0xaf17, 0xaf26, 0xaf35, -0xaf44, 0xaf53, 0xaf62, 0xaf71, 0xaf80, 0xaf8f, -0xaf9e, 0xafad, 0xafbc, 0xafcb, 0xafda, 0xafe9, -0xaff8, 0xb007, 0xb016, 0xb025, 0xb034, 0xb043, -0xb052, 0xb061, 0xb070, 0xb07f, 0xb08e, 0xb09d, -0xb0ac, 0xb0bb, 0xb0ca, 0xb0d9, 0xb0e8, 0xb0f6, -0xb105, 0xb114, 0xb123, 0xb132, 0xb141, 0xb150, -0xb15f, 0xb16e, 0xb17d, 0xb18c, 0xb19b, 0xb1aa, -0xb1b8, 0xb1c7, 0xb1d6, 0xb1e5, 0xb1f4, 0xb203, -0xb212, 0xb221, 0xb22f, 0xb23e, 0xb24d, 0xb25c, -0xb26b, 0xb27a, 0xb289, 0xb297, 0xb2a6, 0xb2b5, -0xb2c4, 0xb2d3, 0xb2e2, 0xb2f1, 0xb2ff, 0xb30e, -0xb31d, 0xb32c, 0xb33b, 0xb349, 0xb358, 0xb367, -0xb376, 0xb385, 0xb393, 0xb3a2, 0xb3b1, 0xb3c0, -0xb3cf, 0xb3dd, 0xb3ec, 0xb3fb, 0xb40a, 0xb418, -0xb427, 0xb436, 0xb445, 0xb453, 0xb462, 0xb471, -0xb480, 0xb48e, 0xb49d, 0xb4ac, 0xb4bb, 0xb4c9, -0xb4d8, 0xb4e7, 0xb4f6, 0xb504, 0xb513, 0xb522, -0xb530, 0xb53f, 0xb54e, 0xb55c, 0xb56b, 0xb57a, -0xb588, 0xb597, 0xb5a6, 0xb5b5, 0xb5c3, 0xb5d2, -0xb5e1, 0xb5ef, 0xb5fe, 0xb60d, 0xb61b, 0xb62a, -0xb638, 0xb647, 0xb656, 0xb664, 0xb673, 0xb682, -0xb690, 0xb69f, 0xb6ae, 0xb6bc, 0xb6cb, 0xb6d9, -0xb6e8, 0xb6f7, 0xb705, 0xb714, 0xb722, 0xb731, -0xb740, 0xb74e, 0xb75d, 0xb76b, 0xb77a, 0xb788, -0xb797, 0xb7a6, 0xb7b4, 0xb7c3, 0xb7d1, 0xb7e0, -0xb7ee, 0xb7fd, 0xb80b, 0xb81a, 0xb829, 0xb837, -0xb846, 0xb854, 0xb863, 0xb871, 0xb880, 0xb88e, -0xb89d, 0xb8ab, 0xb8ba, 0xb8c8, 0xb8d7, 0xb8e5, -0xb8f4, 0xb902, 0xb911, 0xb91f, 0xb92e, 0xb93c, -0xb94b, 0xb959, 0xb968, 0xb976, 0xb984, 0xb993, -0xb9a1, 0xb9b0, 0xb9be, 0xb9cd, 0xb9db, 0xb9ea, -0xb9f8, 0xba06, 0xba15, 0xba23, 0xba32, 0xba40, -0xba4f, 0xba5d, 0xba6b, 0xba7a, 0xba88, 0xba97, -0xbaa5, 0xbab3, 0xbac2, 0xbad0, 0xbade, 0xbaed, -0xbafb, 0xbb0a, 0xbb18, 0xbb26, 0xbb35, 0xbb43, -0xbb51, 0xbb60, 0xbb6e, 0xbb7c, 0xbb8b, 0xbb99, -0xbba8, 0xbbb6, 0xbbc4, 0xbbd3, 0xbbe1, 0xbbef, -0xbbfd, 0xbc0c, 0xbc1a, 0xbc28, 0xbc37, 0xbc45, -0xbc53, 0xbc62, 0xbc70, 0xbc7e, 0xbc8c, 0xbc9b, -0xbca9, 0xbcb7, 0xbcc6, 0xbcd4, 0xbce2, 0xbcf0, -0xbcff, 0xbd0d, 0xbd1b, 0xbd29, 0xbd38, 0xbd46, -0xbd54, 0xbd62, 0xbd71, 0xbd7f, 0xbd8d, 0xbd9b, -0xbdaa, 0xbdb8, 0xbdc6, 0xbdd4, 0xbde2, 0xbdf1, -0xbdff, 0xbe0d, 0xbe1b, 0xbe29, 0xbe38, 0xbe46, -0xbe54, 0xbe62, 0xbe70, 0xbe7f, 0xbe8d, 0xbe9b, -0xbea9, 0xbeb7, 0xbec5, 0xbed4, 0xbee2, 0xbef0, -0xbefe, 0xbf0c, 0xbf1a, 0xbf28, 0xbf37, 0xbf45, -0xbf53, 0xbf61, 0xbf6f, 0xbf7d, 0xbf8b, 0xbf99, -0xbfa7, 0xbfb6, 0xbfc4, 0xbfd2, 0xbfe0, 0xbfee, -0xbffc, 0xc00a, 0xc018, 0xc026, 0xc034, 0xc042, -0xc051, 0xc05f, 0xc06d, 0xc07b, 0xc089, 0xc097, -0xc0a5, 0xc0b3, 0xc0c1, 0xc0cf, 0xc0dd, 0xc0eb, -0xc0f9, 0xc107, 0xc115, 0xc123, 0xc131, 0xc13f, -0xc14d, 0xc15b, 0xc169, 0xc177, 0xc185, 0xc193, -0xc1a1, 0xc1af, 0xc1bd, 0xc1cb, 0xc1d9, 0xc1e7, -0xc1f5, 0xc203, 0xc211, 0xc21f, 0xc22d, 0xc23b, -0xc249, 0xc257, 0xc265, 0xc273, 0xc281, 0xc28f, -0xc29d, 0xc2ab, 0xc2b8, 0xc2c6, 0xc2d4, 0xc2e2, -0xc2f0, 0xc2fe, 0xc30c, 0xc31a, 0xc328, 0xc336, -0xc344, 0xc352, 0xc35f, 0xc36d, 0xc37b, 0xc389, -0xc397, 0xc3a5, 0xc3b3, 0xc3c1, 0xc3ce, 0xc3dc, -0xc3ea, 0xc3f8, 0xc406, 0xc414, 0xc422, 0xc42f, -0xc43d, 0xc44b, 0xc459, 0xc467, 0xc475, 0xc482, -0xc490, 0xc49e, 0xc4ac, 0xc4ba, 0xc4c7, 0xc4d5, -0xc4e3, 0xc4f1, 0xc4ff, 0xc50d, 0xc51a, 0xc528, -0xc536, 0xc544, 0xc551, 0xc55f, 0xc56d, 0xc57b, -0xc589, 0xc596, 0xc5a4, 0xc5b2, 0xc5c0, 0xc5cd, -0xc5db, 0xc5e9, 0xc5f7, 0xc604, 0xc612, 0xc620, -0xc62d, 0xc63b, 0xc649, 0xc657, 0xc664, 0xc672, -0xc680, 0xc68d, 0xc69b, 0xc6a9, 0xc6b7, 0xc6c4, -0xc6d2, 0xc6e0, 0xc6ed, 0xc6fb, 0xc709, 0xc716, -0xc724, 0xc732, 0xc73f, 0xc74d, 0xc75b, 0xc768, -0xc776, 0xc784, 0xc791, 0xc79f, 0xc7ad, 0xc7ba, -0xc7c8, 0xc7d6, 0xc7e3, 0xc7f1, 0xc7fe, 0xc80c, -0xc81a, 0xc827, 0xc835, 0xc842, 0xc850, 0xc85e, -0xc86b, 0xc879, 0xc886, 0xc894, 0xc8a2, 0xc8af, -0xc8bd, 0xc8ca, 0xc8d8, 0xc8e5, 0xc8f3, 0xc901, -0xc90e, 0xc91c, 0xc929, 0xc937, 0xc944, 0xc952, -0xc95f, 0xc96d, 0xc97b, 0xc988, 0xc996, 0xc9a3, -0xc9b1, 0xc9be, 0xc9cc, 0xc9d9, 0xc9e7, 0xc9f4, -0xca02, 0xca0f, 0xca1d, 0xca2a, 0xca38, 0xca45, -0xca53, 0xca60, 0xca6e, 0xca7b, 0xca89, 0xca96, -0xcaa4, 0xcab1, 0xcabe, 0xcacc, 0xcad9, 0xcae7, -0xcaf4, 0xcb02, 0xcb0f, 0xcb1d, 0xcb2a, 0xcb37, -0xcb45, 0xcb52, 0xcb60, 0xcb6d, 0xcb7b, 0xcb88, -0xcb95, 0xcba3, 0xcbb0, 0xcbbe, 0xcbcb, 0xcbd8, -0xcbe6, 0xcbf3, 0xcc01, 0xcc0e, 0xcc1b, 0xcc29, -0xcc36, 0xcc43, 0xcc51, 0xcc5e, 0xcc6c, 0xcc79, -0xcc86, 0xcc94, 0xcca1, 0xccae, 0xccbc, 0xccc9, -0xccd6, 0xcce4, 0xccf1, 0xccfe, 0xcd0c, 0xcd19, -0xcd26, 0xcd34, 0xcd41, 0xcd4e, 0xcd5b, 0xcd69, -0xcd76, 0xcd83, 0xcd91, 0xcd9e, 0xcdab, 0xcdb9, -0xcdc6, 0xcdd3, 0xcde0, 0xcdee, 0xcdfb, 0xce08, -0xce15, 0xce23, 0xce30, 0xce3d, 0xce4a, 0xce58, -0xce65, 0xce72, 0xce7f, 0xce8d, 0xce9a, 0xcea7, -0xceb4, 0xcec2, 0xcecf, 0xcedc, 0xcee9, 0xcef6, -0xcf04, 0xcf11, 0xcf1e, 0xcf2b, 0xcf38, 0xcf46, -0xcf53, 0xcf60, 0xcf6d, 0xcf7a, 0xcf87, 0xcf95, -0xcfa2, 0xcfaf, 0xcfbc, 0xcfc9, 0xcfd6, 0xcfe4, -0xcff1, 0xcffe, 0xd00b, 0xd018, 0xd025, 0xd032, -0xd040, 0xd04d, 0xd05a, 0xd067, 0xd074, 0xd081, -0xd08e, 0xd09b, 0xd0a9, 0xd0b6, 0xd0c3, 0xd0d0, -0xd0dd, 0xd0ea, 0xd0f7, 0xd104, 0xd111, 0xd11e, -0xd12b, 0xd139, 0xd146, 0xd153, 0xd160, 0xd16d, -0xd17a, 0xd187, 0xd194, 0xd1a1, 0xd1ae, 0xd1bb, -0xd1c8, 0xd1d5, 0xd1e2, 0xd1ef, 0xd1fc, 0xd209, -0xd216, 0xd223, 0xd230, 0xd23d, 0xd24a, 0xd257, -0xd264, 0xd271, 0xd27e, 0xd28b, 0xd298, 0xd2a5, -0xd2b2, 0xd2bf, 0xd2cc, 0xd2d9, 0xd2e6, 0xd2f3, -0xd300, 0xd30d, 0xd31a, 0xd327, 0xd334, 0xd341, -0xd34e, 0xd35b, 0xd368, 0xd375, 0xd382, 0xd38f, -0xd39c, 0xd3a8, 0xd3b5, 0xd3c2, 0xd3cf, 0xd3dc, -0xd3e9, 0xd3f6, 0xd403, 0xd410, 0xd41d, 0xd42a, -0xd436, 0xd443, 0xd450, 0xd45d, 0xd46a, 0xd477, -0xd484, 0xd491, 0xd49e, 0xd4aa, 0xd4b7, 0xd4c4, -0xd4d1, 0xd4de, 0xd4eb, 0xd4f8, 0xd504, 0xd511, -0xd51e, 0xd52b, 0xd538, 0xd545, 0xd551, 0xd55e, -0xd56b, 0xd578, 0xd585, 0xd591, 0xd59e, 0xd5ab, -0xd5b8, 0xd5c5, 0xd5d1, 0xd5de, 0xd5eb, 0xd5f8, -0xd605, 0xd611, 0xd61e, 0xd62b, 0xd638, 0xd645, -0xd651, 0xd65e, 0xd66b, 0xd678, 0xd684, 0xd691, -0xd69e, 0xd6ab, 0xd6b7, 0xd6c4, 0xd6d1, 0xd6de, -0xd6ea, 0xd6f7, 0xd704, 0xd710, 0xd71d, 0xd72a, -0xd737, 0xd743, 0xd750, 0xd75d, 0xd769, 0xd776, -0xd783, 0xd78f, 0xd79c, 0xd7a9, 0xd7b6, 0xd7c2, -0xd7cf, 0xd7dc, 0xd7e8, 0xd7f5, 0xd802, 0xd80e, -0xd81b, 0xd828, 0xd834, 0xd841, 0xd84e, 0xd85a, -0xd867, 0xd873, 0xd880, 0xd88d, 0xd899, 0xd8a6, -0xd8b3, 0xd8bf, 0xd8cc, 0xd8d8, 0xd8e5, 0xd8f2, -0xd8fe, 0xd90b, 0xd917, 0xd924, 0xd931, 0xd93d, -0xd94a, 0xd956, 0xd963, 0xd970, 0xd97c, 0xd989, -0xd995, 0xd9a2, 0xd9ae, 0xd9bb, 0xd9c8, 0xd9d4, -0xd9e1, 0xd9ed, 0xd9fa, 0xda06, 0xda13, 0xda1f, -0xda2c, 0xda38, 0xda45, 0xda51, 0xda5e, 0xda6a, -0xda77, 0xda84, 0xda90, 0xda9d, 0xdaa9, 0xdab6, -0xdac2, 0xdacf, 0xdadb, 0xdae7, 0xdaf4, 0xdb00, -0xdb0d, 0xdb19, 0xdb26, 0xdb32, 0xdb3f, 0xdb4b, -0xdb58, 0xdb64, 0xdb71, 0xdb7d, 0xdb8a, 0xdb96, -0xdba2, 0xdbaf, 0xdbbb, 0xdbc8, 0xdbd4, 0xdbe1, -0xdbed, 0xdbf9, 0xdc06, 0xdc12, 0xdc1f, 0xdc2b, -0xdc38, 0xdc44, 0xdc50, 0xdc5d, 0xdc69, 0xdc76, -0xdc82, 0xdc8e, 0xdc9b, 0xdca7, 0xdcb3, 0xdcc0, -0xdccc, 0xdcd9, 0xdce5, 0xdcf1, 0xdcfe, 0xdd0a, -0xdd16, 0xdd23, 0xdd2f, 0xdd3b, 0xdd48, 0xdd54, -0xdd60, 0xdd6d, 0xdd79, 0xdd85, 0xdd92, 0xdd9e, -0xddaa, 0xddb7, 0xddc3, 0xddcf, 0xdddc, 0xdde8, -0xddf4, 0xde01, 0xde0d, 0xde19, 0xde25, 0xde32, -0xde3e, 0xde4a, 0xde57, 0xde63, 0xde6f, 0xde7b, -0xde88, 0xde94, 0xdea0, 0xdeac, 0xdeb9, 0xdec5, -0xded1, 0xdedd, 0xdeea, 0xdef6, 0xdf02, 0xdf0e, -0xdf1b, 0xdf27, 0xdf33, 0xdf3f, 0xdf4c, 0xdf58, -0xdf64, 0xdf70, 0xdf7c, 0xdf89, 0xdf95, 0xdfa1, -0xdfad, 0xdfb9, 0xdfc6, 0xdfd2, 0xdfde, 0xdfea, -0xdff6, 0xe003, 0xe00f, 0xe01b, 0xe027, 0xe033, -0xe03f, 0xe04c, 0xe058, 0xe064, 0xe070, 0xe07c, -0xe088, 0xe094, 0xe0a1, 0xe0ad, 0xe0b9, 0xe0c5, -0xe0d1, 0xe0dd, 0xe0e9, 0xe0f5, 0xe102, 0xe10e, -0xe11a, 0xe126, 0xe132, 0xe13e, 0xe14a, 0xe156, -0xe162, 0xe16e, 0xe17b, 0xe187, 0xe193, 0xe19f, -0xe1ab, 0xe1b7, 0xe1c3, 0xe1cf, 0xe1db, 0xe1e7, -0xe1f3, 0xe1ff, 0xe20b, 0xe217, 0xe223, 0xe22f, -0xe23c, 0xe248, 0xe254, 0xe260, 0xe26c, 0xe278, -0xe284, 0xe290, 0xe29c, 0xe2a8, 0xe2b4, 0xe2c0, -0xe2cc, 0xe2d8, 0xe2e4, 0xe2f0, 0xe2fc, 0xe308, -0xe314, 0xe320, 0xe32c, 0xe338, 0xe344, 0xe350, -0xe35c, 0xe368, 0xe374, 0xe380, 0xe38b, 0xe397, -0xe3a3, 0xe3af, 0xe3bb, 0xe3c7, 0xe3d3, 0xe3df, -0xe3eb, 0xe3f7, 0xe403, 0xe40f, 0xe41b, 0xe427, -0xe433, 0xe43f, 0xe44a, 0xe456, 0xe462, 0xe46e, -0xe47a, 0xe486, 0xe492, 0xe49e, 0xe4aa, 0xe4b6, -0xe4c1, 0xe4cd, 0xe4d9, 0xe4e5, 0xe4f1, 0xe4fd, -0xe509, 0xe515, 0xe520, 0xe52c, 0xe538, 0xe544, -0xe550, 0xe55c, 0xe567, 0xe573, 0xe57f, 0xe58b, -0xe597, 0xe5a3, 0xe5af, 0xe5ba, 0xe5c6, 0xe5d2, -0xe5de, 0xe5ea, 0xe5f5, 0xe601, 0xe60d, 0xe619, -0xe625, 0xe630, 0xe63c, 0xe648, 0xe654, 0xe660, -0xe66b, 0xe677, 0xe683, 0xe68f, 0xe69a, 0xe6a6, -0xe6b2, 0xe6be, 0xe6ca, 0xe6d5, 0xe6e1, 0xe6ed, -0xe6f9, 0xe704, 0xe710, 0xe71c, 0xe727, 0xe733, -0xe73f, 0xe74b, 0xe756, 0xe762, 0xe76e, 0xe77a, -0xe785, 0xe791, 0xe79d, 0xe7a8, 0xe7b4, 0xe7c0, -0xe7cb, 0xe7d7, 0xe7e3, 0xe7ef, 0xe7fa, 0xe806, -0xe812, 0xe81d, 0xe829, 0xe835, 0xe840, 0xe84c, -0xe858, 0xe863, 0xe86f, 0xe87b, 0xe886, 0xe892, -0xe89e, 0xe8a9, 0xe8b5, 0xe8c0, 0xe8cc, 0xe8d8, -0xe8e3, 0xe8ef, 0xe8fb, 0xe906, 0xe912, 0xe91d, -0xe929, 0xe935, 0xe940, 0xe94c, 0xe958, 0xe963, -0xe96f, 0xe97a, 0xe986, 0xe991, 0xe99d, 0xe9a9, -0xe9b4, 0xe9c0, 0xe9cb, 0xe9d7, 0xe9e3, 0xe9ee, -0xe9fa, 0xea05, 0xea11, 0xea1c, 0xea28, 0xea33, -0xea3f, 0xea4a, 0xea56, 0xea62, 0xea6d, 0xea79, -0xea84, 0xea90, 0xea9b, 0xeaa7, 0xeab2, 0xeabe, -0xeac9, 0xead5, 0xeae0, 0xeaec, 0xeaf7, 0xeb03, -0xeb0e, 0xeb1a, 0xeb25, 0xeb31, 0xeb3c, 0xeb48, -0xeb53, 0xeb5f, 0xeb6a, 0xeb76, 0xeb81, 0xeb8d, -0xeb98, 0xeba3, 0xebaf, 0xebba, 0xebc6, 0xebd1, -0xebdd, 0xebe8, 0xebf4, 0xebff, 0xec0a, 0xec16, -0xec21, 0xec2d, 0xec38, 0xec44, 0xec4f, 0xec5a, -0xec66, 0xec71, 0xec7d, 0xec88, 0xec93, 0xec9f, -0xecaa, 0xecb6, 0xecc1, 0xeccc, 0xecd8, 0xece3, -0xecef, 0xecfa, 0xed05, 0xed11, 0xed1c, 0xed27, -0xed33, 0xed3e, 0xed4a, 0xed55, 0xed60, 0xed6c, -0xed77, 0xed82, 0xed8e, 0xed99, 0xeda4, 0xedb0, -0xedbb, 0xedc6, 0xedd2, 0xeddd, 0xede8, 0xedf4, -0xedff, 0xee0a, 0xee15, 0xee21, 0xee2c, 0xee37, -0xee43, 0xee4e, 0xee59, 0xee65, 0xee70, 0xee7b, -0xee86, 0xee92, 0xee9d, 0xeea8, 0xeeb3, 0xeebf, -0xeeca, 0xeed5, 0xeee1, 0xeeec, 0xeef7, 0xef02, -0xef0e, 0xef19, 0xef24, 0xef2f, 0xef3a, 0xef46, -0xef51, 0xef5c, 0xef67, 0xef73, 0xef7e, 0xef89, -0xef94, 0xef9f, 0xefab, 0xefb6, 0xefc1, 0xefcc, -0xefd7, 0xefe3, 0xefee, 0xeff9, 0xf004, 0xf00f, -0xf01b, 0xf026, 0xf031, 0xf03c, 0xf047, 0xf052, -0xf05e, 0xf069, 0xf074, 0xf07f, 0xf08a, 0xf095, -0xf0a1, 0xf0ac, 0xf0b7, 0xf0c2, 0xf0cd, 0xf0d8, -0xf0e3, 0xf0ef, 0xf0fa, 0xf105, 0xf110, 0xf11b, -0xf126, 0xf131, 0xf13c, 0xf147, 0xf153, 0xf15e, -0xf169, 0xf174, 0xf17f, 0xf18a, 0xf195, 0xf1a0, -0xf1ab, 0xf1b6, 0xf1c2, 0xf1cd, 0xf1d8, 0xf1e3, -0xf1ee, 0xf1f9, 0xf204, 0xf20f, 0xf21a, 0xf225, -0xf230, 0xf23b, 0xf246, 0xf251, 0xf25c, 0xf267, -0xf272, 0xf27d, 0xf288, 0xf293, 0xf29f, 0xf2aa, -0xf2b5, 0xf2c0, 0xf2cb, 0xf2d6, 0xf2e1, 0xf2ec, -0xf2f7, 0xf302, 0xf30d, 0xf318, 0xf323, 0xf32e, -0xf339, 0xf344, 0xf34f, 0xf35a, 0xf364, 0xf36f, -0xf37a, 0xf385, 0xf390, 0xf39b, 0xf3a6, 0xf3b1, -0xf3bc, 0xf3c7, 0xf3d2, 0xf3dd, 0xf3e8, 0xf3f3, -0xf3fe, 0xf409, 0xf414, 0xf41f, 0xf42a, 0xf435, -0xf43f, 0xf44a, 0xf455, 0xf460, 0xf46b, 0xf476, -0xf481, 0xf48c, 0xf497, 0xf4a2, 0xf4ad, 0xf4b7, -0xf4c2, 0xf4cd, 0xf4d8, 0xf4e3, 0xf4ee, 0xf4f9, -0xf504, 0xf50f, 0xf519, 0xf524, 0xf52f, 0xf53a, -0xf545, 0xf550, 0xf55b, 0xf565, 0xf570, 0xf57b, -0xf586, 0xf591, 0xf59c, 0xf5a6, 0xf5b1, 0xf5bc, -0xf5c7, 0xf5d2, 0xf5dd, 0xf5e7, 0xf5f2, 0xf5fd, -0xf608, 0xf613, 0xf61d, 0xf628, 0xf633, 0xf63e, -0xf649, 0xf653, 0xf65e, 0xf669, 0xf674, 0xf67f, -0xf689, 0xf694, 0xf69f, 0xf6aa, 0xf6b4, 0xf6bf, -0xf6ca, 0xf6d5, 0xf6e0, 0xf6ea, 0xf6f5, 0xf700, -0xf70b, 0xf715, 0xf720, 0xf72b, 0xf736, 0xf740, -0xf74b, 0xf756, 0xf760, 0xf76b, 0xf776, 0xf781, -0xf78b, 0xf796, 0xf7a1, 0xf7ab, 0xf7b6, 0xf7c1, -0xf7cc, 0xf7d6, 0xf7e1, 0xf7ec, 0xf7f6, 0xf801, -0xf80c, 0xf816, 0xf821, 0xf82c, 0xf836, 0xf841, -0xf84c, 0xf856, 0xf861, 0xf86c, 0xf876, 0xf881, -0xf88c, 0xf896, 0xf8a1, 0xf8ac, 0xf8b6, 0xf8c1, -0xf8cc, 0xf8d6, 0xf8e1, 0xf8ec, 0xf8f6, 0xf901, -0xf90b, 0xf916, 0xf921, 0xf92b, 0xf936, 0xf941, -0xf94b, 0xf956, 0xf960, 0xf96b, 0xf976, 0xf980, -0xf98b, 0xf995, 0xf9a0, 0xf9aa, 0xf9b5, 0xf9c0, -0xf9ca, 0xf9d5, 0xf9df, 0xf9ea, 0xf9f4, 0xf9ff, -0xfa0a, 0xfa14, 0xfa1f, 0xfa29, 0xfa34, 0xfa3e, -0xfa49, 0xfa53, 0xfa5e, 0xfa69, 0xfa73, 0xfa7e, -0xfa88, 0xfa93, 0xfa9d, 0xfaa8, 0xfab2, 0xfabd, -0xfac7, 0xfad2, 0xfadc, 0xfae7, 0xfaf1, 0xfafc, -0xfb06, 0xfb11, 0xfb1b, 0xfb26, 0xfb30, 0xfb3b, -0xfb45, 0xfb50, 0xfb5a, 0xfb65, 0xfb6f, 0xfb7a, -0xfb84, 0xfb8f, 0xfb99, 0xfba4, 0xfbae, 0xfbb8, -0xfbc3, 0xfbcd, 0xfbd8, 0xfbe2, 0xfbed, 0xfbf7, -0xfc02, 0xfc0c, 0xfc16, 0xfc21, 0xfc2b, 0xfc36, -0xfc40, 0xfc4b, 0xfc55, 0xfc5f, 0xfc6a, 0xfc74, -0xfc7f, 0xfc89, 0xfc93, 0xfc9e, 0xfca8, 0xfcb3, -0xfcbd, 0xfcc7, 0xfcd2, 0xfcdc, 0xfce7, 0xfcf1, -0xfcfb, 0xfd06, 0xfd10, 0xfd1a, 0xfd25, 0xfd2f, -0xfd3a, 0xfd44, 0xfd4e, 0xfd59, 0xfd63, 0xfd6d, -0xfd78, 0xfd82, 0xfd8c, 0xfd97, 0xfda1, 0xfdab, -0xfdb6, 0xfdc0, 0xfdca, 0xfdd5, 0xfddf, 0xfde9, -0xfdf4, 0xfdfe, 0xfe08, 0xfe13, 0xfe1d, 0xfe27, -0xfe32, 0xfe3c, 0xfe46, 0xfe50, 0xfe5b, 0xfe65, -0xfe6f, 0xfe7a, 0xfe84, 0xfe8e, 0xfe98, 0xfea3, -0xfead, 0xfeb7, 0xfec1, 0xfecc, 0xfed6, 0xfee0, -0xfeeb, 0xfef5, 0xfeff, 0xff09, 0xff14, 0xff1e, -0xff28, 0xff32, 0xff3c, 0xff47, 0xff51, 0xff5b, -0xff65, 0xff70, 0xff7a, 0xff84, 0xff8e, 0xff98, -0xffa3, 0xffad, 0xffb7, 0xffc1, 0xffcc, 0xffd6, -0xffe0, 0xffea, 0xfff4, 0xffff -}; - -// max value is pi/4 -constexpr double SCALING_FACTOR = 4. / M_PI * 0xFFFF; - -inline double atan2_lookup(double y, double x) -{ - if (std::abs(x) < std::numeric_limits::epsilon()) - { - if (y >= 0.) - { - return M_PI / 2.; - } - else - { - return -M_PI / 2.; - } - } - - unsigned octant = 0; - - if (x < 0.) - { - octant = 1; - x = -x; - } - if (y < 0.) - { - octant |= 2; - y = -y; - } - - double t = y / x; - if (t > 1.0) - { - octant |= 4; - t = 1.0 / t; - } - - double angle = atan_table[(unsigned)(t * 4095)] / SCALING_FACTOR; - - switch (octant) - { - case 0: - break; - case 1: - angle = M_PI - angle; - break; - case 2: - angle = -angle; - break; - case 3: - angle = -M_PI + angle; - break; - case 4: - angle = M_PI / 2.0 - angle; - break; - case 5: - angle = M_PI / 2.0 + angle; - break; - case 6: - angle = -M_PI / 2.0 + angle; - break; - case 7: - angle = -M_PI / 2.0 - angle; - break; - } - - return angle; -} - /* Get angle of line segment (A,C)->(C,B), atan2 magic, formerly cosine theorem*/ template inline static double GetAngleBetweenThreeFixedPointCoordinates(const CoordinateT &A, diff --git a/Util/TrigonometryTables.h b/Util/TrigonometryTables.h new file mode 100644 index 00000000000..665a397f84d --- /dev/null +++ b/Util/TrigonometryTables.h @@ -0,0 +1,789 @@ +/* + +Copyright (c) 2013, Project OSRM, Dennis Luxen, others +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TRIGONOMETRY_TABLES_H +#define TRIGONOMETRY_TABLES_H + +#include + +#include + +constexpr unsigned short atan_table[4096] = { +0x0000, 0x0014, 0x0028, 0x003d, 0x0051, 0x0065, +0x007a, 0x008e, 0x00a3, 0x00b7, 0x00cb, 0x00e0, +0x00f4, 0x0108, 0x011d, 0x0131, 0x0146, 0x015a, +0x016e, 0x0183, 0x0197, 0x01ab, 0x01c0, 0x01d4, +0x01e9, 0x01fd, 0x0211, 0x0226, 0x023a, 0x024e, +0x0263, 0x0277, 0x028c, 0x02a0, 0x02b4, 0x02c9, +0x02dd, 0x02f1, 0x0306, 0x031a, 0x032f, 0x0343, +0x0357, 0x036c, 0x0380, 0x0394, 0x03a9, 0x03bd, +0x03d2, 0x03e6, 0x03fa, 0x040f, 0x0423, 0x0437, +0x044c, 0x0460, 0x0475, 0x0489, 0x049d, 0x04b2, +0x04c6, 0x04da, 0x04ef, 0x0503, 0x0517, 0x052c, +0x0540, 0x0555, 0x0569, 0x057d, 0x0592, 0x05a6, +0x05ba, 0x05cf, 0x05e3, 0x05f8, 0x060c, 0x0620, +0x0635, 0x0649, 0x065d, 0x0672, 0x0686, 0x069b, +0x06af, 0x06c3, 0x06d8, 0x06ec, 0x0700, 0x0715, +0x0729, 0x073d, 0x0752, 0x0766, 0x077b, 0x078f, +0x07a3, 0x07b8, 0x07cc, 0x07e0, 0x07f5, 0x0809, +0x081d, 0x0832, 0x0846, 0x085b, 0x086f, 0x0883, +0x0898, 0x08ac, 0x08c0, 0x08d5, 0x08e9, 0x08fd, +0x0912, 0x0926, 0x093b, 0x094f, 0x0963, 0x0978, +0x098c, 0x09a0, 0x09b5, 0x09c9, 0x09dd, 0x09f2, +0x0a06, 0x0a1a, 0x0a2f, 0x0a43, 0x0a58, 0x0a6c, +0x0a80, 0x0a95, 0x0aa9, 0x0abd, 0x0ad2, 0x0ae6, +0x0afa, 0x0b0f, 0x0b23, 0x0b37, 0x0b4c, 0x0b60, +0x0b75, 0x0b89, 0x0b9d, 0x0bb2, 0x0bc6, 0x0bda, +0x0bef, 0x0c03, 0x0c17, 0x0c2c, 0x0c40, 0x0c54, +0x0c69, 0x0c7d, 0x0c91, 0x0ca6, 0x0cba, 0x0cce, +0x0ce3, 0x0cf7, 0x0d0b, 0x0d20, 0x0d34, 0x0d48, +0x0d5d, 0x0d71, 0x0d86, 0x0d9a, 0x0dae, 0x0dc3, +0x0dd7, 0x0deb, 0x0e00, 0x0e14, 0x0e28, 0x0e3d, +0x0e51, 0x0e65, 0x0e7a, 0x0e8e, 0x0ea2, 0x0eb7, +0x0ecb, 0x0edf, 0x0ef4, 0x0f08, 0x0f1c, 0x0f31, +0x0f45, 0x0f59, 0x0f6e, 0x0f82, 0x0f96, 0x0fab, +0x0fbf, 0x0fd3, 0x0fe8, 0x0ffc, 0x1010, 0x1025, +0x1039, 0x104d, 0x1062, 0x1076, 0x108a, 0x109e, +0x10b3, 0x10c7, 0x10db, 0x10f0, 0x1104, 0x1118, +0x112d, 0x1141, 0x1155, 0x116a, 0x117e, 0x1192, +0x11a7, 0x11bb, 0x11cf, 0x11e4, 0x11f8, 0x120c, +0x1221, 0x1235, 0x1249, 0x125d, 0x1272, 0x1286, +0x129a, 0x12af, 0x12c3, 0x12d7, 0x12ec, 0x1300, +0x1314, 0x1329, 0x133d, 0x1351, 0x1365, 0x137a, +0x138e, 0x13a2, 0x13b7, 0x13cb, 0x13df, 0x13f4, +0x1408, 0x141c, 0x1431, 0x1445, 0x1459, 0x146d, +0x1482, 0x1496, 0x14aa, 0x14bf, 0x14d3, 0x14e7, +0x14fb, 0x1510, 0x1524, 0x1538, 0x154d, 0x1561, +0x1575, 0x1589, 0x159e, 0x15b2, 0x15c6, 0x15db, +0x15ef, 0x1603, 0x1617, 0x162c, 0x1640, 0x1654, +0x1669, 0x167d, 0x1691, 0x16a5, 0x16ba, 0x16ce, +0x16e2, 0x16f7, 0x170b, 0x171f, 0x1733, 0x1748, +0x175c, 0x1770, 0x1784, 0x1799, 0x17ad, 0x17c1, +0x17d6, 0x17ea, 0x17fe, 0x1812, 0x1827, 0x183b, +0x184f, 0x1863, 0x1878, 0x188c, 0x18a0, 0x18b4, +0x18c9, 0x18dd, 0x18f1, 0x1905, 0x191a, 0x192e, +0x1942, 0x1957, 0x196b, 0x197f, 0x1993, 0x19a8, +0x19bc, 0x19d0, 0x19e4, 0x19f9, 0x1a0d, 0x1a21, +0x1a35, 0x1a49, 0x1a5e, 0x1a72, 0x1a86, 0x1a9a, +0x1aaf, 0x1ac3, 0x1ad7, 0x1aeb, 0x1b00, 0x1b14, +0x1b28, 0x1b3c, 0x1b51, 0x1b65, 0x1b79, 0x1b8d, +0x1ba2, 0x1bb6, 0x1bca, 0x1bde, 0x1bf2, 0x1c07, +0x1c1b, 0x1c2f, 0x1c43, 0x1c58, 0x1c6c, 0x1c80, +0x1c94, 0x1ca8, 0x1cbd, 0x1cd1, 0x1ce5, 0x1cf9, +0x1d0e, 0x1d22, 0x1d36, 0x1d4a, 0x1d5e, 0x1d73, +0x1d87, 0x1d9b, 0x1daf, 0x1dc3, 0x1dd8, 0x1dec, +0x1e00, 0x1e14, 0x1e28, 0x1e3d, 0x1e51, 0x1e65, +0x1e79, 0x1e8d, 0x1ea2, 0x1eb6, 0x1eca, 0x1ede, +0x1ef2, 0x1f07, 0x1f1b, 0x1f2f, 0x1f43, 0x1f57, +0x1f6c, 0x1f80, 0x1f94, 0x1fa8, 0x1fbc, 0x1fd1, +0x1fe5, 0x1ff9, 0x200d, 0x2021, 0x2035, 0x204a, +0x205e, 0x2072, 0x2086, 0x209a, 0x20ae, 0x20c3, +0x20d7, 0x20eb, 0x20ff, 0x2113, 0x2127, 0x213c, +0x2150, 0x2164, 0x2178, 0x218c, 0x21a0, 0x21b5, +0x21c9, 0x21dd, 0x21f1, 0x2205, 0x2219, 0x222e, +0x2242, 0x2256, 0x226a, 0x227e, 0x2292, 0x22a6, +0x22bb, 0x22cf, 0x22e3, 0x22f7, 0x230b, 0x231f, +0x2333, 0x2348, 0x235c, 0x2370, 0x2384, 0x2398, +0x23ac, 0x23c0, 0x23d5, 0x23e9, 0x23fd, 0x2411, +0x2425, 0x2439, 0x244d, 0x2461, 0x2476, 0x248a, +0x249e, 0x24b2, 0x24c6, 0x24da, 0x24ee, 0x2502, +0x2517, 0x252b, 0x253f, 0x2553, 0x2567, 0x257b, +0x258f, 0x25a3, 0x25b7, 0x25cb, 0x25e0, 0x25f4, +0x2608, 0x261c, 0x2630, 0x2644, 0x2658, 0x266c, +0x2680, 0x2694, 0x26a9, 0x26bd, 0x26d1, 0x26e5, +0x26f9, 0x270d, 0x2721, 0x2735, 0x2749, 0x275d, +0x2771, 0x2785, 0x279a, 0x27ae, 0x27c2, 0x27d6, +0x27ea, 0x27fe, 0x2812, 0x2826, 0x283a, 0x284e, +0x2862, 0x2876, 0x288a, 0x289e, 0x28b3, 0x28c7, +0x28db, 0x28ef, 0x2903, 0x2917, 0x292b, 0x293f, +0x2953, 0x2967, 0x297b, 0x298f, 0x29a3, 0x29b7, +0x29cb, 0x29df, 0x29f3, 0x2a07, 0x2a1b, 0x2a2f, +0x2a43, 0x2a58, 0x2a6c, 0x2a80, 0x2a94, 0x2aa8, +0x2abc, 0x2ad0, 0x2ae4, 0x2af8, 0x2b0c, 0x2b20, +0x2b34, 0x2b48, 0x2b5c, 0x2b70, 0x2b84, 0x2b98, +0x2bac, 0x2bc0, 0x2bd4, 0x2be8, 0x2bfc, 0x2c10, +0x2c24, 0x2c38, 0x2c4c, 0x2c60, 0x2c74, 0x2c88, +0x2c9c, 0x2cb0, 0x2cc4, 0x2cd8, 0x2cec, 0x2d00, +0x2d14, 0x2d28, 0x2d3c, 0x2d50, 0x2d64, 0x2d78, +0x2d8c, 0x2da0, 0x2db4, 0x2dc8, 0x2ddc, 0x2df0, +0x2e04, 0x2e18, 0x2e2c, 0x2e40, 0x2e54, 0x2e68, +0x2e7c, 0x2e90, 0x2ea3, 0x2eb7, 0x2ecb, 0x2edf, +0x2ef3, 0x2f07, 0x2f1b, 0x2f2f, 0x2f43, 0x2f57, +0x2f6b, 0x2f7f, 0x2f93, 0x2fa7, 0x2fbb, 0x2fcf, +0x2fe3, 0x2ff7, 0x300b, 0x301e, 0x3032, 0x3046, +0x305a, 0x306e, 0x3082, 0x3096, 0x30aa, 0x30be, +0x30d2, 0x30e6, 0x30fa, 0x310e, 0x3122, 0x3135, +0x3149, 0x315d, 0x3171, 0x3185, 0x3199, 0x31ad, +0x31c1, 0x31d5, 0x31e9, 0x31fd, 0x3210, 0x3224, +0x3238, 0x324c, 0x3260, 0x3274, 0x3288, 0x329c, +0x32b0, 0x32c3, 0x32d7, 0x32eb, 0x32ff, 0x3313, +0x3327, 0x333b, 0x334f, 0x3363, 0x3376, 0x338a, +0x339e, 0x33b2, 0x33c6, 0x33da, 0x33ee, 0x3401, +0x3415, 0x3429, 0x343d, 0x3451, 0x3465, 0x3479, +0x348c, 0x34a0, 0x34b4, 0x34c8, 0x34dc, 0x34f0, +0x3504, 0x3517, 0x352b, 0x353f, 0x3553, 0x3567, +0x357b, 0x358e, 0x35a2, 0x35b6, 0x35ca, 0x35de, +0x35f2, 0x3605, 0x3619, 0x362d, 0x3641, 0x3655, +0x3668, 0x367c, 0x3690, 0x36a4, 0x36b8, 0x36cb, +0x36df, 0x36f3, 0x3707, 0x371b, 0x372f, 0x3742, +0x3756, 0x376a, 0x377e, 0x3791, 0x37a5, 0x37b9, +0x37cd, 0x37e1, 0x37f4, 0x3808, 0x381c, 0x3830, +0x3844, 0x3857, 0x386b, 0x387f, 0x3893, 0x38a6, +0x38ba, 0x38ce, 0x38e2, 0x38f5, 0x3909, 0x391d, +0x3931, 0x3944, 0x3958, 0x396c, 0x3980, 0x3993, +0x39a7, 0x39bb, 0x39cf, 0x39e2, 0x39f6, 0x3a0a, +0x3a1e, 0x3a31, 0x3a45, 0x3a59, 0x3a6d, 0x3a80, +0x3a94, 0x3aa8, 0x3abb, 0x3acf, 0x3ae3, 0x3af7, +0x3b0a, 0x3b1e, 0x3b32, 0x3b45, 0x3b59, 0x3b6d, +0x3b81, 0x3b94, 0x3ba8, 0x3bbc, 0x3bcf, 0x3be3, +0x3bf7, 0x3c0b, 0x3c1e, 0x3c32, 0x3c46, 0x3c59, +0x3c6d, 0x3c81, 0x3c94, 0x3ca8, 0x3cbc, 0x3ccf, +0x3ce3, 0x3cf7, 0x3d0a, 0x3d1e, 0x3d32, 0x3d45, +0x3d59, 0x3d6d, 0x3d80, 0x3d94, 0x3da8, 0x3dbb, +0x3dcf, 0x3de3, 0x3df6, 0x3e0a, 0x3e1e, 0x3e31, +0x3e45, 0x3e59, 0x3e6c, 0x3e80, 0x3e93, 0x3ea7, +0x3ebb, 0x3ece, 0x3ee2, 0x3ef6, 0x3f09, 0x3f1d, +0x3f30, 0x3f44, 0x3f58, 0x3f6b, 0x3f7f, 0x3f93, +0x3fa6, 0x3fba, 0x3fcd, 0x3fe1, 0x3ff5, 0x4008, +0x401c, 0x402f, 0x4043, 0x4057, 0x406a, 0x407e, +0x4091, 0x40a5, 0x40b8, 0x40cc, 0x40e0, 0x40f3, +0x4107, 0x411a, 0x412e, 0x4142, 0x4155, 0x4169, +0x417c, 0x4190, 0x41a3, 0x41b7, 0x41ca, 0x41de, +0x41f2, 0x4205, 0x4219, 0x422c, 0x4240, 0x4253, +0x4267, 0x427a, 0x428e, 0x42a1, 0x42b5, 0x42c9, +0x42dc, 0x42f0, 0x4303, 0x4317, 0x432a, 0x433e, +0x4351, 0x4365, 0x4378, 0x438c, 0x439f, 0x43b3, +0x43c6, 0x43da, 0x43ed, 0x4401, 0x4414, 0x4428, +0x443b, 0x444f, 0x4462, 0x4476, 0x4489, 0x449d, +0x44b0, 0x44c4, 0x44d7, 0x44eb, 0x44fe, 0x4512, +0x4525, 0x4539, 0x454c, 0x4560, 0x4573, 0x4586, +0x459a, 0x45ad, 0x45c1, 0x45d4, 0x45e8, 0x45fb, +0x460f, 0x4622, 0x4636, 0x4649, 0x465c, 0x4670, +0x4683, 0x4697, 0x46aa, 0x46be, 0x46d1, 0x46e5, +0x46f8, 0x470b, 0x471f, 0x4732, 0x4746, 0x4759, +0x476c, 0x4780, 0x4793, 0x47a7, 0x47ba, 0x47cd, +0x47e1, 0x47f4, 0x4808, 0x481b, 0x482e, 0x4842, +0x4855, 0x4869, 0x487c, 0x488f, 0x48a3, 0x48b6, +0x48ca, 0x48dd, 0x48f0, 0x4904, 0x4917, 0x492a, +0x493e, 0x4951, 0x4965, 0x4978, 0x498b, 0x499f, +0x49b2, 0x49c5, 0x49d9, 0x49ec, 0x49ff, 0x4a13, +0x4a26, 0x4a39, 0x4a4d, 0x4a60, 0x4a73, 0x4a87, +0x4a9a, 0x4aad, 0x4ac1, 0x4ad4, 0x4ae7, 0x4afb, +0x4b0e, 0x4b21, 0x4b35, 0x4b48, 0x4b5b, 0x4b6f, +0x4b82, 0x4b95, 0x4ba8, 0x4bbc, 0x4bcf, 0x4be2, +0x4bf6, 0x4c09, 0x4c1c, 0x4c2f, 0x4c43, 0x4c56, +0x4c69, 0x4c7d, 0x4c90, 0x4ca3, 0x4cb6, 0x4cca, +0x4cdd, 0x4cf0, 0x4d03, 0x4d17, 0x4d2a, 0x4d3d, +0x4d50, 0x4d64, 0x4d77, 0x4d8a, 0x4d9d, 0x4db1, +0x4dc4, 0x4dd7, 0x4dea, 0x4dfe, 0x4e11, 0x4e24, +0x4e37, 0x4e4b, 0x4e5e, 0x4e71, 0x4e84, 0x4e97, +0x4eab, 0x4ebe, 0x4ed1, 0x4ee4, 0x4ef7, 0x4f0b, +0x4f1e, 0x4f31, 0x4f44, 0x4f57, 0x4f6b, 0x4f7e, +0x4f91, 0x4fa4, 0x4fb7, 0x4fcb, 0x4fde, 0x4ff1, +0x5004, 0x5017, 0x502a, 0x503e, 0x5051, 0x5064, +0x5077, 0x508a, 0x509d, 0x50b1, 0x50c4, 0x50d7, +0x50ea, 0x50fd, 0x5110, 0x5123, 0x5137, 0x514a, +0x515d, 0x5170, 0x5183, 0x5196, 0x51a9, 0x51bc, +0x51d0, 0x51e3, 0x51f6, 0x5209, 0x521c, 0x522f, +0x5242, 0x5255, 0x5268, 0x527c, 0x528f, 0x52a2, +0x52b5, 0x52c8, 0x52db, 0x52ee, 0x5301, 0x5314, +0x5327, 0x533a, 0x534e, 0x5361, 0x5374, 0x5387, +0x539a, 0x53ad, 0x53c0, 0x53d3, 0x53e6, 0x53f9, +0x540c, 0x541f, 0x5432, 0x5445, 0x5458, 0x546b, +0x547e, 0x5491, 0x54a5, 0x54b8, 0x54cb, 0x54de, +0x54f1, 0x5504, 0x5517, 0x552a, 0x553d, 0x5550, +0x5563, 0x5576, 0x5589, 0x559c, 0x55af, 0x55c2, +0x55d5, 0x55e8, 0x55fb, 0x560e, 0x5621, 0x5634, +0x5647, 0x565a, 0x566d, 0x5680, 0x5693, 0x56a6, +0x56b9, 0x56cb, 0x56de, 0x56f1, 0x5704, 0x5717, +0x572a, 0x573d, 0x5750, 0x5763, 0x5776, 0x5789, +0x579c, 0x57af, 0x57c2, 0x57d5, 0x57e8, 0x57fb, +0x580e, 0x5820, 0x5833, 0x5846, 0x5859, 0x586c, +0x587f, 0x5892, 0x58a5, 0x58b8, 0x58cb, 0x58de, +0x58f0, 0x5903, 0x5916, 0x5929, 0x593c, 0x594f, +0x5962, 0x5975, 0x5988, 0x599a, 0x59ad, 0x59c0, +0x59d3, 0x59e6, 0x59f9, 0x5a0c, 0x5a1f, 0x5a31, +0x5a44, 0x5a57, 0x5a6a, 0x5a7d, 0x5a90, 0x5aa2, +0x5ab5, 0x5ac8, 0x5adb, 0x5aee, 0x5b01, 0x5b13, +0x5b26, 0x5b39, 0x5b4c, 0x5b5f, 0x5b72, 0x5b84, +0x5b97, 0x5baa, 0x5bbd, 0x5bd0, 0x5be2, 0x5bf5, +0x5c08, 0x5c1b, 0x5c2e, 0x5c40, 0x5c53, 0x5c66, +0x5c79, 0x5c8c, 0x5c9e, 0x5cb1, 0x5cc4, 0x5cd7, +0x5ce9, 0x5cfc, 0x5d0f, 0x5d22, 0x5d34, 0x5d47, +0x5d5a, 0x5d6d, 0x5d7f, 0x5d92, 0x5da5, 0x5db8, +0x5dca, 0x5ddd, 0x5df0, 0x5e03, 0x5e15, 0x5e28, +0x5e3b, 0x5e4d, 0x5e60, 0x5e73, 0x5e86, 0x5e98, +0x5eab, 0x5ebe, 0x5ed0, 0x5ee3, 0x5ef6, 0x5f09, +0x5f1b, 0x5f2e, 0x5f41, 0x5f53, 0x5f66, 0x5f79, +0x5f8b, 0x5f9e, 0x5fb1, 0x5fc3, 0x5fd6, 0x5fe9, +0x5ffb, 0x600e, 0x6021, 0x6033, 0x6046, 0x6059, +0x606b, 0x607e, 0x6091, 0x60a3, 0x60b6, 0x60c8, +0x60db, 0x60ee, 0x6100, 0x6113, 0x6126, 0x6138, +0x614b, 0x615d, 0x6170, 0x6183, 0x6195, 0x61a8, +0x61ba, 0x61cd, 0x61e0, 0x61f2, 0x6205, 0x6217, +0x622a, 0x623d, 0x624f, 0x6262, 0x6274, 0x6287, +0x6299, 0x62ac, 0x62bf, 0x62d1, 0x62e4, 0x62f6, +0x6309, 0x631b, 0x632e, 0x6340, 0x6353, 0x6366, +0x6378, 0x638b, 0x639d, 0x63b0, 0x63c2, 0x63d5, +0x63e7, 0x63fa, 0x640c, 0x641f, 0x6431, 0x6444, +0x6456, 0x6469, 0x647b, 0x648e, 0x64a0, 0x64b3, +0x64c5, 0x64d8, 0x64ea, 0x64fd, 0x650f, 0x6522, +0x6534, 0x6547, 0x6559, 0x656c, 0x657e, 0x6591, +0x65a3, 0x65b5, 0x65c8, 0x65da, 0x65ed, 0x65ff, +0x6612, 0x6624, 0x6637, 0x6649, 0x665b, 0x666e, +0x6680, 0x6693, 0x66a5, 0x66b8, 0x66ca, 0x66dc, +0x66ef, 0x6701, 0x6714, 0x6726, 0x6738, 0x674b, +0x675d, 0x6770, 0x6782, 0x6794, 0x67a7, 0x67b9, +0x67cc, 0x67de, 0x67f0, 0x6803, 0x6815, 0x6827, +0x683a, 0x684c, 0x685e, 0x6871, 0x6883, 0x6896, +0x68a8, 0x68ba, 0x68cd, 0x68df, 0x68f1, 0x6904, +0x6916, 0x6928, 0x693b, 0x694d, 0x695f, 0x6972, +0x6984, 0x6996, 0x69a8, 0x69bb, 0x69cd, 0x69df, +0x69f2, 0x6a04, 0x6a16, 0x6a29, 0x6a3b, 0x6a4d, +0x6a5f, 0x6a72, 0x6a84, 0x6a96, 0x6aa9, 0x6abb, +0x6acd, 0x6adf, 0x6af2, 0x6b04, 0x6b16, 0x6b28, +0x6b3b, 0x6b4d, 0x6b5f, 0x6b71, 0x6b84, 0x6b96, +0x6ba8, 0x6bba, 0x6bcd, 0x6bdf, 0x6bf1, 0x6c03, +0x6c15, 0x6c28, 0x6c3a, 0x6c4c, 0x6c5e, 0x6c70, +0x6c83, 0x6c95, 0x6ca7, 0x6cb9, 0x6ccb, 0x6cde, +0x6cf0, 0x6d02, 0x6d14, 0x6d26, 0x6d39, 0x6d4b, +0x6d5d, 0x6d6f, 0x6d81, 0x6d93, 0x6da6, 0x6db8, +0x6dca, 0x6ddc, 0x6dee, 0x6e00, 0x6e12, 0x6e25, +0x6e37, 0x6e49, 0x6e5b, 0x6e6d, 0x6e7f, 0x6e91, +0x6ea3, 0x6eb6, 0x6ec8, 0x6eda, 0x6eec, 0x6efe, +0x6f10, 0x6f22, 0x6f34, 0x6f46, 0x6f58, 0x6f6b, +0x6f7d, 0x6f8f, 0x6fa1, 0x6fb3, 0x6fc5, 0x6fd7, +0x6fe9, 0x6ffb, 0x700d, 0x701f, 0x7031, 0x7043, +0x7055, 0x7068, 0x707a, 0x708c, 0x709e, 0x70b0, +0x70c2, 0x70d4, 0x70e6, 0x70f8, 0x710a, 0x711c, +0x712e, 0x7140, 0x7152, 0x7164, 0x7176, 0x7188, +0x719a, 0x71ac, 0x71be, 0x71d0, 0x71e2, 0x71f4, +0x7206, 0x7218, 0x722a, 0x723c, 0x724e, 0x7260, +0x7272, 0x7284, 0x7296, 0x72a8, 0x72ba, 0x72cc, +0x72dd, 0x72ef, 0x7301, 0x7313, 0x7325, 0x7337, +0x7349, 0x735b, 0x736d, 0x737f, 0x7391, 0x73a3, +0x73b5, 0x73c7, 0x73d8, 0x73ea, 0x73fc, 0x740e, +0x7420, 0x7432, 0x7444, 0x7456, 0x7468, 0x747a, +0x748b, 0x749d, 0x74af, 0x74c1, 0x74d3, 0x74e5, +0x74f7, 0x7509, 0x751a, 0x752c, 0x753e, 0x7550, +0x7562, 0x7574, 0x7585, 0x7597, 0x75a9, 0x75bb, +0x75cd, 0x75df, 0x75f0, 0x7602, 0x7614, 0x7626, +0x7638, 0x764a, 0x765b, 0x766d, 0x767f, 0x7691, +0x76a3, 0x76b4, 0x76c6, 0x76d8, 0x76ea, 0x76fb, +0x770d, 0x771f, 0x7731, 0x7743, 0x7754, 0x7766, +0x7778, 0x778a, 0x779b, 0x77ad, 0x77bf, 0x77d1, +0x77e2, 0x77f4, 0x7806, 0x7818, 0x7829, 0x783b, +0x784d, 0x785e, 0x7870, 0x7882, 0x7894, 0x78a5, +0x78b7, 0x78c9, 0x78da, 0x78ec, 0x78fe, 0x7910, +0x7921, 0x7933, 0x7945, 0x7956, 0x7968, 0x797a, +0x798b, 0x799d, 0x79af, 0x79c0, 0x79d2, 0x79e4, +0x79f5, 0x7a07, 0x7a19, 0x7a2a, 0x7a3c, 0x7a4e, +0x7a5f, 0x7a71, 0x7a82, 0x7a94, 0x7aa6, 0x7ab7, +0x7ac9, 0x7adb, 0x7aec, 0x7afe, 0x7b0f, 0x7b21, +0x7b33, 0x7b44, 0x7b56, 0x7b67, 0x7b79, 0x7b8b, +0x7b9c, 0x7bae, 0x7bbf, 0x7bd1, 0x7be2, 0x7bf4, +0x7c06, 0x7c17, 0x7c29, 0x7c3a, 0x7c4c, 0x7c5d, +0x7c6f, 0x7c81, 0x7c92, 0x7ca4, 0x7cb5, 0x7cc7, +0x7cd8, 0x7cea, 0x7cfb, 0x7d0d, 0x7d1e, 0x7d30, +0x7d41, 0x7d53, 0x7d64, 0x7d76, 0x7d87, 0x7d99, +0x7daa, 0x7dbc, 0x7dcd, 0x7ddf, 0x7df0, 0x7e02, +0x7e13, 0x7e25, 0x7e36, 0x7e48, 0x7e59, 0x7e6b, +0x7e7c, 0x7e8e, 0x7e9f, 0x7eb0, 0x7ec2, 0x7ed3, +0x7ee5, 0x7ef6, 0x7f08, 0x7f19, 0x7f2b, 0x7f3c, +0x7f4d, 0x7f5f, 0x7f70, 0x7f82, 0x7f93, 0x7fa4, +0x7fb6, 0x7fc7, 0x7fd9, 0x7fea, 0x7ffb, 0x800d, +0x801e, 0x8030, 0x8041, 0x8052, 0x8064, 0x8075, +0x8086, 0x8098, 0x80a9, 0x80bb, 0x80cc, 0x80dd, +0x80ef, 0x8100, 0x8111, 0x8123, 0x8134, 0x8145, +0x8157, 0x8168, 0x8179, 0x818b, 0x819c, 0x81ad, +0x81bf, 0x81d0, 0x81e1, 0x81f3, 0x8204, 0x8215, +0x8226, 0x8238, 0x8249, 0x825a, 0x826c, 0x827d, +0x828e, 0x829f, 0x82b1, 0x82c2, 0x82d3, 0x82e5, +0x82f6, 0x8307, 0x8318, 0x832a, 0x833b, 0x834c, +0x835d, 0x836f, 0x8380, 0x8391, 0x83a2, 0x83b3, +0x83c5, 0x83d6, 0x83e7, 0x83f8, 0x840a, 0x841b, +0x842c, 0x843d, 0x844e, 0x8460, 0x8471, 0x8482, +0x8493, 0x84a4, 0x84b6, 0x84c7, 0x84d8, 0x84e9, +0x84fa, 0x850b, 0x851d, 0x852e, 0x853f, 0x8550, +0x8561, 0x8572, 0x8584, 0x8595, 0x85a6, 0x85b7, +0x85c8, 0x85d9, 0x85ea, 0x85fb, 0x860d, 0x861e, +0x862f, 0x8640, 0x8651, 0x8662, 0x8673, 0x8684, +0x8695, 0x86a7, 0x86b8, 0x86c9, 0x86da, 0x86eb, +0x86fc, 0x870d, 0x871e, 0x872f, 0x8740, 0x8751, +0x8762, 0x8773, 0x8784, 0x8796, 0x87a7, 0x87b8, +0x87c9, 0x87da, 0x87eb, 0x87fc, 0x880d, 0x881e, +0x882f, 0x8840, 0x8851, 0x8862, 0x8873, 0x8884, +0x8895, 0x88a6, 0x88b7, 0x88c8, 0x88d9, 0x88ea, +0x88fb, 0x890c, 0x891d, 0x892e, 0x893f, 0x8950, +0x8961, 0x8972, 0x8983, 0x8994, 0x89a5, 0x89b6, +0x89c6, 0x89d7, 0x89e8, 0x89f9, 0x8a0a, 0x8a1b, +0x8a2c, 0x8a3d, 0x8a4e, 0x8a5f, 0x8a70, 0x8a81, +0x8a92, 0x8aa3, 0x8ab3, 0x8ac4, 0x8ad5, 0x8ae6, +0x8af7, 0x8b08, 0x8b19, 0x8b2a, 0x8b3b, 0x8b4b, +0x8b5c, 0x8b6d, 0x8b7e, 0x8b8f, 0x8ba0, 0x8bb1, +0x8bc1, 0x8bd2, 0x8be3, 0x8bf4, 0x8c05, 0x8c16, +0x8c27, 0x8c37, 0x8c48, 0x8c59, 0x8c6a, 0x8c7b, +0x8c8c, 0x8c9c, 0x8cad, 0x8cbe, 0x8ccf, 0x8ce0, +0x8cf0, 0x8d01, 0x8d12, 0x8d23, 0x8d34, 0x8d44, +0x8d55, 0x8d66, 0x8d77, 0x8d87, 0x8d98, 0x8da9, +0x8dba, 0x8dca, 0x8ddb, 0x8dec, 0x8dfd, 0x8e0d, +0x8e1e, 0x8e2f, 0x8e40, 0x8e50, 0x8e61, 0x8e72, +0x8e83, 0x8e93, 0x8ea4, 0x8eb5, 0x8ec5, 0x8ed6, +0x8ee7, 0x8ef8, 0x8f08, 0x8f19, 0x8f2a, 0x8f3a, +0x8f4b, 0x8f5c, 0x8f6c, 0x8f7d, 0x8f8e, 0x8f9e, +0x8faf, 0x8fc0, 0x8fd0, 0x8fe1, 0x8ff2, 0x9002, +0x9013, 0x9024, 0x9034, 0x9045, 0x9056, 0x9066, +0x9077, 0x9088, 0x9098, 0x90a9, 0x90b9, 0x90ca, +0x90db, 0x90eb, 0x90fc, 0x910c, 0x911d, 0x912e, +0x913e, 0x914f, 0x915f, 0x9170, 0x9181, 0x9191, +0x91a2, 0x91b2, 0x91c3, 0x91d3, 0x91e4, 0x91f5, +0x9205, 0x9216, 0x9226, 0x9237, 0x9247, 0x9258, +0x9268, 0x9279, 0x9289, 0x929a, 0x92aa, 0x92bb, +0x92cc, 0x92dc, 0x92ed, 0x92fd, 0x930e, 0x931e, +0x932f, 0x933f, 0x9350, 0x9360, 0x9370, 0x9381, +0x9391, 0x93a2, 0x93b2, 0x93c3, 0x93d3, 0x93e4, +0x93f4, 0x9405, 0x9415, 0x9426, 0x9436, 0x9447, +0x9457, 0x9467, 0x9478, 0x9488, 0x9499, 0x94a9, +0x94ba, 0x94ca, 0x94da, 0x94eb, 0x94fb, 0x950c, +0x951c, 0x952c, 0x953d, 0x954d, 0x955e, 0x956e, +0x957e, 0x958f, 0x959f, 0x95af, 0x95c0, 0x95d0, +0x95e1, 0x95f1, 0x9601, 0x9612, 0x9622, 0x9632, +0x9643, 0x9653, 0x9663, 0x9674, 0x9684, 0x9694, +0x96a5, 0x96b5, 0x96c5, 0x96d6, 0x96e6, 0x96f6, +0x9707, 0x9717, 0x9727, 0x9738, 0x9748, 0x9758, +0x9768, 0x9779, 0x9789, 0x9799, 0x97aa, 0x97ba, +0x97ca, 0x97da, 0x97eb, 0x97fb, 0x980b, 0x981b, +0x982c, 0x983c, 0x984c, 0x985c, 0x986d, 0x987d, +0x988d, 0x989d, 0x98ad, 0x98be, 0x98ce, 0x98de, +0x98ee, 0x98ff, 0x990f, 0x991f, 0x992f, 0x993f, +0x9950, 0x9960, 0x9970, 0x9980, 0x9990, 0x99a0, +0x99b1, 0x99c1, 0x99d1, 0x99e1, 0x99f1, 0x9a01, +0x9a12, 0x9a22, 0x9a32, 0x9a42, 0x9a52, 0x9a62, +0x9a72, 0x9a83, 0x9a93, 0x9aa3, 0x9ab3, 0x9ac3, +0x9ad3, 0x9ae3, 0x9af3, 0x9b04, 0x9b14, 0x9b24, +0x9b34, 0x9b44, 0x9b54, 0x9b64, 0x9b74, 0x9b84, +0x9b94, 0x9ba4, 0x9bb5, 0x9bc5, 0x9bd5, 0x9be5, +0x9bf5, 0x9c05, 0x9c15, 0x9c25, 0x9c35, 0x9c45, +0x9c55, 0x9c65, 0x9c75, 0x9c85, 0x9c95, 0x9ca5, +0x9cb5, 0x9cc5, 0x9cd5, 0x9ce5, 0x9cf5, 0x9d05, +0x9d15, 0x9d25, 0x9d35, 0x9d45, 0x9d55, 0x9d65, +0x9d75, 0x9d85, 0x9d95, 0x9da5, 0x9db5, 0x9dc5, +0x9dd5, 0x9de5, 0x9df5, 0x9e05, 0x9e15, 0x9e25, +0x9e35, 0x9e45, 0x9e55, 0x9e65, 0x9e74, 0x9e84, +0x9e94, 0x9ea4, 0x9eb4, 0x9ec4, 0x9ed4, 0x9ee4, +0x9ef4, 0x9f04, 0x9f14, 0x9f23, 0x9f33, 0x9f43, +0x9f53, 0x9f63, 0x9f73, 0x9f83, 0x9f93, 0x9fa3, +0x9fb2, 0x9fc2, 0x9fd2, 0x9fe2, 0x9ff2, 0xa002, +0xa012, 0xa021, 0xa031, 0xa041, 0xa051, 0xa061, +0xa071, 0xa080, 0xa090, 0xa0a0, 0xa0b0, 0xa0c0, +0xa0cf, 0xa0df, 0xa0ef, 0xa0ff, 0xa10f, 0xa11e, +0xa12e, 0xa13e, 0xa14e, 0xa15e, 0xa16d, 0xa17d, +0xa18d, 0xa19d, 0xa1ac, 0xa1bc, 0xa1cc, 0xa1dc, +0xa1eb, 0xa1fb, 0xa20b, 0xa21b, 0xa22a, 0xa23a, +0xa24a, 0xa25a, 0xa269, 0xa279, 0xa289, 0xa298, +0xa2a8, 0xa2b8, 0xa2c8, 0xa2d7, 0xa2e7, 0xa2f7, +0xa306, 0xa316, 0xa326, 0xa335, 0xa345, 0xa355, +0xa364, 0xa374, 0xa384, 0xa393, 0xa3a3, 0xa3b3, +0xa3c2, 0xa3d2, 0xa3e2, 0xa3f1, 0xa401, 0xa411, +0xa420, 0xa430, 0xa440, 0xa44f, 0xa45f, 0xa46e, +0xa47e, 0xa48e, 0xa49d, 0xa4ad, 0xa4bc, 0xa4cc, +0xa4dc, 0xa4eb, 0xa4fb, 0xa50a, 0xa51a, 0xa52a, +0xa539, 0xa549, 0xa558, 0xa568, 0xa577, 0xa587, +0xa597, 0xa5a6, 0xa5b6, 0xa5c5, 0xa5d5, 0xa5e4, +0xa5f4, 0xa603, 0xa613, 0xa622, 0xa632, 0xa641, +0xa651, 0xa660, 0xa670, 0xa67f, 0xa68f, 0xa69e, +0xa6ae, 0xa6bd, 0xa6cd, 0xa6dc, 0xa6ec, 0xa6fb, +0xa70b, 0xa71a, 0xa72a, 0xa739, 0xa749, 0xa758, +0xa768, 0xa777, 0xa787, 0xa796, 0xa7a5, 0xa7b5, +0xa7c4, 0xa7d4, 0xa7e3, 0xa7f3, 0xa802, 0xa812, +0xa821, 0xa830, 0xa840, 0xa84f, 0xa85f, 0xa86e, +0xa87d, 0xa88d, 0xa89c, 0xa8ac, 0xa8bb, 0xa8ca, +0xa8da, 0xa8e9, 0xa8f8, 0xa908, 0xa917, 0xa927, +0xa936, 0xa945, 0xa955, 0xa964, 0xa973, 0xa983, +0xa992, 0xa9a1, 0xa9b1, 0xa9c0, 0xa9cf, 0xa9df, +0xa9ee, 0xa9fd, 0xaa0d, 0xaa1c, 0xaa2b, 0xaa3b, +0xaa4a, 0xaa59, 0xaa69, 0xaa78, 0xaa87, 0xaa96, +0xaaa6, 0xaab5, 0xaac4, 0xaad4, 0xaae3, 0xaaf2, +0xab01, 0xab11, 0xab20, 0xab2f, 0xab3e, 0xab4e, +0xab5d, 0xab6c, 0xab7b, 0xab8b, 0xab9a, 0xaba9, +0xabb8, 0xabc7, 0xabd7, 0xabe6, 0xabf5, 0xac04, +0xac14, 0xac23, 0xac32, 0xac41, 0xac50, 0xac60, +0xac6f, 0xac7e, 0xac8d, 0xac9c, 0xacab, 0xacbb, +0xacca, 0xacd9, 0xace8, 0xacf7, 0xad06, 0xad16, +0xad25, 0xad34, 0xad43, 0xad52, 0xad61, 0xad70, +0xad80, 0xad8f, 0xad9e, 0xadad, 0xadbc, 0xadcb, +0xadda, 0xade9, 0xadf8, 0xae08, 0xae17, 0xae26, +0xae35, 0xae44, 0xae53, 0xae62, 0xae71, 0xae80, +0xae8f, 0xae9e, 0xaead, 0xaebd, 0xaecc, 0xaedb, +0xaeea, 0xaef9, 0xaf08, 0xaf17, 0xaf26, 0xaf35, +0xaf44, 0xaf53, 0xaf62, 0xaf71, 0xaf80, 0xaf8f, +0xaf9e, 0xafad, 0xafbc, 0xafcb, 0xafda, 0xafe9, +0xaff8, 0xb007, 0xb016, 0xb025, 0xb034, 0xb043, +0xb052, 0xb061, 0xb070, 0xb07f, 0xb08e, 0xb09d, +0xb0ac, 0xb0bb, 0xb0ca, 0xb0d9, 0xb0e8, 0xb0f6, +0xb105, 0xb114, 0xb123, 0xb132, 0xb141, 0xb150, +0xb15f, 0xb16e, 0xb17d, 0xb18c, 0xb19b, 0xb1aa, +0xb1b8, 0xb1c7, 0xb1d6, 0xb1e5, 0xb1f4, 0xb203, +0xb212, 0xb221, 0xb22f, 0xb23e, 0xb24d, 0xb25c, +0xb26b, 0xb27a, 0xb289, 0xb297, 0xb2a6, 0xb2b5, +0xb2c4, 0xb2d3, 0xb2e2, 0xb2f1, 0xb2ff, 0xb30e, +0xb31d, 0xb32c, 0xb33b, 0xb349, 0xb358, 0xb367, +0xb376, 0xb385, 0xb393, 0xb3a2, 0xb3b1, 0xb3c0, +0xb3cf, 0xb3dd, 0xb3ec, 0xb3fb, 0xb40a, 0xb418, +0xb427, 0xb436, 0xb445, 0xb453, 0xb462, 0xb471, +0xb480, 0xb48e, 0xb49d, 0xb4ac, 0xb4bb, 0xb4c9, +0xb4d8, 0xb4e7, 0xb4f6, 0xb504, 0xb513, 0xb522, +0xb530, 0xb53f, 0xb54e, 0xb55c, 0xb56b, 0xb57a, +0xb588, 0xb597, 0xb5a6, 0xb5b5, 0xb5c3, 0xb5d2, +0xb5e1, 0xb5ef, 0xb5fe, 0xb60d, 0xb61b, 0xb62a, +0xb638, 0xb647, 0xb656, 0xb664, 0xb673, 0xb682, +0xb690, 0xb69f, 0xb6ae, 0xb6bc, 0xb6cb, 0xb6d9, +0xb6e8, 0xb6f7, 0xb705, 0xb714, 0xb722, 0xb731, +0xb740, 0xb74e, 0xb75d, 0xb76b, 0xb77a, 0xb788, +0xb797, 0xb7a6, 0xb7b4, 0xb7c3, 0xb7d1, 0xb7e0, +0xb7ee, 0xb7fd, 0xb80b, 0xb81a, 0xb829, 0xb837, +0xb846, 0xb854, 0xb863, 0xb871, 0xb880, 0xb88e, +0xb89d, 0xb8ab, 0xb8ba, 0xb8c8, 0xb8d7, 0xb8e5, +0xb8f4, 0xb902, 0xb911, 0xb91f, 0xb92e, 0xb93c, +0xb94b, 0xb959, 0xb968, 0xb976, 0xb984, 0xb993, +0xb9a1, 0xb9b0, 0xb9be, 0xb9cd, 0xb9db, 0xb9ea, +0xb9f8, 0xba06, 0xba15, 0xba23, 0xba32, 0xba40, +0xba4f, 0xba5d, 0xba6b, 0xba7a, 0xba88, 0xba97, +0xbaa5, 0xbab3, 0xbac2, 0xbad0, 0xbade, 0xbaed, +0xbafb, 0xbb0a, 0xbb18, 0xbb26, 0xbb35, 0xbb43, +0xbb51, 0xbb60, 0xbb6e, 0xbb7c, 0xbb8b, 0xbb99, +0xbba8, 0xbbb6, 0xbbc4, 0xbbd3, 0xbbe1, 0xbbef, +0xbbfd, 0xbc0c, 0xbc1a, 0xbc28, 0xbc37, 0xbc45, +0xbc53, 0xbc62, 0xbc70, 0xbc7e, 0xbc8c, 0xbc9b, +0xbca9, 0xbcb7, 0xbcc6, 0xbcd4, 0xbce2, 0xbcf0, +0xbcff, 0xbd0d, 0xbd1b, 0xbd29, 0xbd38, 0xbd46, +0xbd54, 0xbd62, 0xbd71, 0xbd7f, 0xbd8d, 0xbd9b, +0xbdaa, 0xbdb8, 0xbdc6, 0xbdd4, 0xbde2, 0xbdf1, +0xbdff, 0xbe0d, 0xbe1b, 0xbe29, 0xbe38, 0xbe46, +0xbe54, 0xbe62, 0xbe70, 0xbe7f, 0xbe8d, 0xbe9b, +0xbea9, 0xbeb7, 0xbec5, 0xbed4, 0xbee2, 0xbef0, +0xbefe, 0xbf0c, 0xbf1a, 0xbf28, 0xbf37, 0xbf45, +0xbf53, 0xbf61, 0xbf6f, 0xbf7d, 0xbf8b, 0xbf99, +0xbfa7, 0xbfb6, 0xbfc4, 0xbfd2, 0xbfe0, 0xbfee, +0xbffc, 0xc00a, 0xc018, 0xc026, 0xc034, 0xc042, +0xc051, 0xc05f, 0xc06d, 0xc07b, 0xc089, 0xc097, +0xc0a5, 0xc0b3, 0xc0c1, 0xc0cf, 0xc0dd, 0xc0eb, +0xc0f9, 0xc107, 0xc115, 0xc123, 0xc131, 0xc13f, +0xc14d, 0xc15b, 0xc169, 0xc177, 0xc185, 0xc193, +0xc1a1, 0xc1af, 0xc1bd, 0xc1cb, 0xc1d9, 0xc1e7, +0xc1f5, 0xc203, 0xc211, 0xc21f, 0xc22d, 0xc23b, +0xc249, 0xc257, 0xc265, 0xc273, 0xc281, 0xc28f, +0xc29d, 0xc2ab, 0xc2b8, 0xc2c6, 0xc2d4, 0xc2e2, +0xc2f0, 0xc2fe, 0xc30c, 0xc31a, 0xc328, 0xc336, +0xc344, 0xc352, 0xc35f, 0xc36d, 0xc37b, 0xc389, +0xc397, 0xc3a5, 0xc3b3, 0xc3c1, 0xc3ce, 0xc3dc, +0xc3ea, 0xc3f8, 0xc406, 0xc414, 0xc422, 0xc42f, +0xc43d, 0xc44b, 0xc459, 0xc467, 0xc475, 0xc482, +0xc490, 0xc49e, 0xc4ac, 0xc4ba, 0xc4c7, 0xc4d5, +0xc4e3, 0xc4f1, 0xc4ff, 0xc50d, 0xc51a, 0xc528, +0xc536, 0xc544, 0xc551, 0xc55f, 0xc56d, 0xc57b, +0xc589, 0xc596, 0xc5a4, 0xc5b2, 0xc5c0, 0xc5cd, +0xc5db, 0xc5e9, 0xc5f7, 0xc604, 0xc612, 0xc620, +0xc62d, 0xc63b, 0xc649, 0xc657, 0xc664, 0xc672, +0xc680, 0xc68d, 0xc69b, 0xc6a9, 0xc6b7, 0xc6c4, +0xc6d2, 0xc6e0, 0xc6ed, 0xc6fb, 0xc709, 0xc716, +0xc724, 0xc732, 0xc73f, 0xc74d, 0xc75b, 0xc768, +0xc776, 0xc784, 0xc791, 0xc79f, 0xc7ad, 0xc7ba, +0xc7c8, 0xc7d6, 0xc7e3, 0xc7f1, 0xc7fe, 0xc80c, +0xc81a, 0xc827, 0xc835, 0xc842, 0xc850, 0xc85e, +0xc86b, 0xc879, 0xc886, 0xc894, 0xc8a2, 0xc8af, +0xc8bd, 0xc8ca, 0xc8d8, 0xc8e5, 0xc8f3, 0xc901, +0xc90e, 0xc91c, 0xc929, 0xc937, 0xc944, 0xc952, +0xc95f, 0xc96d, 0xc97b, 0xc988, 0xc996, 0xc9a3, +0xc9b1, 0xc9be, 0xc9cc, 0xc9d9, 0xc9e7, 0xc9f4, +0xca02, 0xca0f, 0xca1d, 0xca2a, 0xca38, 0xca45, +0xca53, 0xca60, 0xca6e, 0xca7b, 0xca89, 0xca96, +0xcaa4, 0xcab1, 0xcabe, 0xcacc, 0xcad9, 0xcae7, +0xcaf4, 0xcb02, 0xcb0f, 0xcb1d, 0xcb2a, 0xcb37, +0xcb45, 0xcb52, 0xcb60, 0xcb6d, 0xcb7b, 0xcb88, +0xcb95, 0xcba3, 0xcbb0, 0xcbbe, 0xcbcb, 0xcbd8, +0xcbe6, 0xcbf3, 0xcc01, 0xcc0e, 0xcc1b, 0xcc29, +0xcc36, 0xcc43, 0xcc51, 0xcc5e, 0xcc6c, 0xcc79, +0xcc86, 0xcc94, 0xcca1, 0xccae, 0xccbc, 0xccc9, +0xccd6, 0xcce4, 0xccf1, 0xccfe, 0xcd0c, 0xcd19, +0xcd26, 0xcd34, 0xcd41, 0xcd4e, 0xcd5b, 0xcd69, +0xcd76, 0xcd83, 0xcd91, 0xcd9e, 0xcdab, 0xcdb9, +0xcdc6, 0xcdd3, 0xcde0, 0xcdee, 0xcdfb, 0xce08, +0xce15, 0xce23, 0xce30, 0xce3d, 0xce4a, 0xce58, +0xce65, 0xce72, 0xce7f, 0xce8d, 0xce9a, 0xcea7, +0xceb4, 0xcec2, 0xcecf, 0xcedc, 0xcee9, 0xcef6, +0xcf04, 0xcf11, 0xcf1e, 0xcf2b, 0xcf38, 0xcf46, +0xcf53, 0xcf60, 0xcf6d, 0xcf7a, 0xcf87, 0xcf95, +0xcfa2, 0xcfaf, 0xcfbc, 0xcfc9, 0xcfd6, 0xcfe4, +0xcff1, 0xcffe, 0xd00b, 0xd018, 0xd025, 0xd032, +0xd040, 0xd04d, 0xd05a, 0xd067, 0xd074, 0xd081, +0xd08e, 0xd09b, 0xd0a9, 0xd0b6, 0xd0c3, 0xd0d0, +0xd0dd, 0xd0ea, 0xd0f7, 0xd104, 0xd111, 0xd11e, +0xd12b, 0xd139, 0xd146, 0xd153, 0xd160, 0xd16d, +0xd17a, 0xd187, 0xd194, 0xd1a1, 0xd1ae, 0xd1bb, +0xd1c8, 0xd1d5, 0xd1e2, 0xd1ef, 0xd1fc, 0xd209, +0xd216, 0xd223, 0xd230, 0xd23d, 0xd24a, 0xd257, +0xd264, 0xd271, 0xd27e, 0xd28b, 0xd298, 0xd2a5, +0xd2b2, 0xd2bf, 0xd2cc, 0xd2d9, 0xd2e6, 0xd2f3, +0xd300, 0xd30d, 0xd31a, 0xd327, 0xd334, 0xd341, +0xd34e, 0xd35b, 0xd368, 0xd375, 0xd382, 0xd38f, +0xd39c, 0xd3a8, 0xd3b5, 0xd3c2, 0xd3cf, 0xd3dc, +0xd3e9, 0xd3f6, 0xd403, 0xd410, 0xd41d, 0xd42a, +0xd436, 0xd443, 0xd450, 0xd45d, 0xd46a, 0xd477, +0xd484, 0xd491, 0xd49e, 0xd4aa, 0xd4b7, 0xd4c4, +0xd4d1, 0xd4de, 0xd4eb, 0xd4f8, 0xd504, 0xd511, +0xd51e, 0xd52b, 0xd538, 0xd545, 0xd551, 0xd55e, +0xd56b, 0xd578, 0xd585, 0xd591, 0xd59e, 0xd5ab, +0xd5b8, 0xd5c5, 0xd5d1, 0xd5de, 0xd5eb, 0xd5f8, +0xd605, 0xd611, 0xd61e, 0xd62b, 0xd638, 0xd645, +0xd651, 0xd65e, 0xd66b, 0xd678, 0xd684, 0xd691, +0xd69e, 0xd6ab, 0xd6b7, 0xd6c4, 0xd6d1, 0xd6de, +0xd6ea, 0xd6f7, 0xd704, 0xd710, 0xd71d, 0xd72a, +0xd737, 0xd743, 0xd750, 0xd75d, 0xd769, 0xd776, +0xd783, 0xd78f, 0xd79c, 0xd7a9, 0xd7b6, 0xd7c2, +0xd7cf, 0xd7dc, 0xd7e8, 0xd7f5, 0xd802, 0xd80e, +0xd81b, 0xd828, 0xd834, 0xd841, 0xd84e, 0xd85a, +0xd867, 0xd873, 0xd880, 0xd88d, 0xd899, 0xd8a6, +0xd8b3, 0xd8bf, 0xd8cc, 0xd8d8, 0xd8e5, 0xd8f2, +0xd8fe, 0xd90b, 0xd917, 0xd924, 0xd931, 0xd93d, +0xd94a, 0xd956, 0xd963, 0xd970, 0xd97c, 0xd989, +0xd995, 0xd9a2, 0xd9ae, 0xd9bb, 0xd9c8, 0xd9d4, +0xd9e1, 0xd9ed, 0xd9fa, 0xda06, 0xda13, 0xda1f, +0xda2c, 0xda38, 0xda45, 0xda51, 0xda5e, 0xda6a, +0xda77, 0xda84, 0xda90, 0xda9d, 0xdaa9, 0xdab6, +0xdac2, 0xdacf, 0xdadb, 0xdae7, 0xdaf4, 0xdb00, +0xdb0d, 0xdb19, 0xdb26, 0xdb32, 0xdb3f, 0xdb4b, +0xdb58, 0xdb64, 0xdb71, 0xdb7d, 0xdb8a, 0xdb96, +0xdba2, 0xdbaf, 0xdbbb, 0xdbc8, 0xdbd4, 0xdbe1, +0xdbed, 0xdbf9, 0xdc06, 0xdc12, 0xdc1f, 0xdc2b, +0xdc38, 0xdc44, 0xdc50, 0xdc5d, 0xdc69, 0xdc76, +0xdc82, 0xdc8e, 0xdc9b, 0xdca7, 0xdcb3, 0xdcc0, +0xdccc, 0xdcd9, 0xdce5, 0xdcf1, 0xdcfe, 0xdd0a, +0xdd16, 0xdd23, 0xdd2f, 0xdd3b, 0xdd48, 0xdd54, +0xdd60, 0xdd6d, 0xdd79, 0xdd85, 0xdd92, 0xdd9e, +0xddaa, 0xddb7, 0xddc3, 0xddcf, 0xdddc, 0xdde8, +0xddf4, 0xde01, 0xde0d, 0xde19, 0xde25, 0xde32, +0xde3e, 0xde4a, 0xde57, 0xde63, 0xde6f, 0xde7b, +0xde88, 0xde94, 0xdea0, 0xdeac, 0xdeb9, 0xdec5, +0xded1, 0xdedd, 0xdeea, 0xdef6, 0xdf02, 0xdf0e, +0xdf1b, 0xdf27, 0xdf33, 0xdf3f, 0xdf4c, 0xdf58, +0xdf64, 0xdf70, 0xdf7c, 0xdf89, 0xdf95, 0xdfa1, +0xdfad, 0xdfb9, 0xdfc6, 0xdfd2, 0xdfde, 0xdfea, +0xdff6, 0xe003, 0xe00f, 0xe01b, 0xe027, 0xe033, +0xe03f, 0xe04c, 0xe058, 0xe064, 0xe070, 0xe07c, +0xe088, 0xe094, 0xe0a1, 0xe0ad, 0xe0b9, 0xe0c5, +0xe0d1, 0xe0dd, 0xe0e9, 0xe0f5, 0xe102, 0xe10e, +0xe11a, 0xe126, 0xe132, 0xe13e, 0xe14a, 0xe156, +0xe162, 0xe16e, 0xe17b, 0xe187, 0xe193, 0xe19f, +0xe1ab, 0xe1b7, 0xe1c3, 0xe1cf, 0xe1db, 0xe1e7, +0xe1f3, 0xe1ff, 0xe20b, 0xe217, 0xe223, 0xe22f, +0xe23c, 0xe248, 0xe254, 0xe260, 0xe26c, 0xe278, +0xe284, 0xe290, 0xe29c, 0xe2a8, 0xe2b4, 0xe2c0, +0xe2cc, 0xe2d8, 0xe2e4, 0xe2f0, 0xe2fc, 0xe308, +0xe314, 0xe320, 0xe32c, 0xe338, 0xe344, 0xe350, +0xe35c, 0xe368, 0xe374, 0xe380, 0xe38b, 0xe397, +0xe3a3, 0xe3af, 0xe3bb, 0xe3c7, 0xe3d3, 0xe3df, +0xe3eb, 0xe3f7, 0xe403, 0xe40f, 0xe41b, 0xe427, +0xe433, 0xe43f, 0xe44a, 0xe456, 0xe462, 0xe46e, +0xe47a, 0xe486, 0xe492, 0xe49e, 0xe4aa, 0xe4b6, +0xe4c1, 0xe4cd, 0xe4d9, 0xe4e5, 0xe4f1, 0xe4fd, +0xe509, 0xe515, 0xe520, 0xe52c, 0xe538, 0xe544, +0xe550, 0xe55c, 0xe567, 0xe573, 0xe57f, 0xe58b, +0xe597, 0xe5a3, 0xe5af, 0xe5ba, 0xe5c6, 0xe5d2, +0xe5de, 0xe5ea, 0xe5f5, 0xe601, 0xe60d, 0xe619, +0xe625, 0xe630, 0xe63c, 0xe648, 0xe654, 0xe660, +0xe66b, 0xe677, 0xe683, 0xe68f, 0xe69a, 0xe6a6, +0xe6b2, 0xe6be, 0xe6ca, 0xe6d5, 0xe6e1, 0xe6ed, +0xe6f9, 0xe704, 0xe710, 0xe71c, 0xe727, 0xe733, +0xe73f, 0xe74b, 0xe756, 0xe762, 0xe76e, 0xe77a, +0xe785, 0xe791, 0xe79d, 0xe7a8, 0xe7b4, 0xe7c0, +0xe7cb, 0xe7d7, 0xe7e3, 0xe7ef, 0xe7fa, 0xe806, +0xe812, 0xe81d, 0xe829, 0xe835, 0xe840, 0xe84c, +0xe858, 0xe863, 0xe86f, 0xe87b, 0xe886, 0xe892, +0xe89e, 0xe8a9, 0xe8b5, 0xe8c0, 0xe8cc, 0xe8d8, +0xe8e3, 0xe8ef, 0xe8fb, 0xe906, 0xe912, 0xe91d, +0xe929, 0xe935, 0xe940, 0xe94c, 0xe958, 0xe963, +0xe96f, 0xe97a, 0xe986, 0xe991, 0xe99d, 0xe9a9, +0xe9b4, 0xe9c0, 0xe9cb, 0xe9d7, 0xe9e3, 0xe9ee, +0xe9fa, 0xea05, 0xea11, 0xea1c, 0xea28, 0xea33, +0xea3f, 0xea4a, 0xea56, 0xea62, 0xea6d, 0xea79, +0xea84, 0xea90, 0xea9b, 0xeaa7, 0xeab2, 0xeabe, +0xeac9, 0xead5, 0xeae0, 0xeaec, 0xeaf7, 0xeb03, +0xeb0e, 0xeb1a, 0xeb25, 0xeb31, 0xeb3c, 0xeb48, +0xeb53, 0xeb5f, 0xeb6a, 0xeb76, 0xeb81, 0xeb8d, +0xeb98, 0xeba3, 0xebaf, 0xebba, 0xebc6, 0xebd1, +0xebdd, 0xebe8, 0xebf4, 0xebff, 0xec0a, 0xec16, +0xec21, 0xec2d, 0xec38, 0xec44, 0xec4f, 0xec5a, +0xec66, 0xec71, 0xec7d, 0xec88, 0xec93, 0xec9f, +0xecaa, 0xecb6, 0xecc1, 0xeccc, 0xecd8, 0xece3, +0xecef, 0xecfa, 0xed05, 0xed11, 0xed1c, 0xed27, +0xed33, 0xed3e, 0xed4a, 0xed55, 0xed60, 0xed6c, +0xed77, 0xed82, 0xed8e, 0xed99, 0xeda4, 0xedb0, +0xedbb, 0xedc6, 0xedd2, 0xeddd, 0xede8, 0xedf4, +0xedff, 0xee0a, 0xee15, 0xee21, 0xee2c, 0xee37, +0xee43, 0xee4e, 0xee59, 0xee65, 0xee70, 0xee7b, +0xee86, 0xee92, 0xee9d, 0xeea8, 0xeeb3, 0xeebf, +0xeeca, 0xeed5, 0xeee1, 0xeeec, 0xeef7, 0xef02, +0xef0e, 0xef19, 0xef24, 0xef2f, 0xef3a, 0xef46, +0xef51, 0xef5c, 0xef67, 0xef73, 0xef7e, 0xef89, +0xef94, 0xef9f, 0xefab, 0xefb6, 0xefc1, 0xefcc, +0xefd7, 0xefe3, 0xefee, 0xeff9, 0xf004, 0xf00f, +0xf01b, 0xf026, 0xf031, 0xf03c, 0xf047, 0xf052, +0xf05e, 0xf069, 0xf074, 0xf07f, 0xf08a, 0xf095, +0xf0a1, 0xf0ac, 0xf0b7, 0xf0c2, 0xf0cd, 0xf0d8, +0xf0e3, 0xf0ef, 0xf0fa, 0xf105, 0xf110, 0xf11b, +0xf126, 0xf131, 0xf13c, 0xf147, 0xf153, 0xf15e, +0xf169, 0xf174, 0xf17f, 0xf18a, 0xf195, 0xf1a0, +0xf1ab, 0xf1b6, 0xf1c2, 0xf1cd, 0xf1d8, 0xf1e3, +0xf1ee, 0xf1f9, 0xf204, 0xf20f, 0xf21a, 0xf225, +0xf230, 0xf23b, 0xf246, 0xf251, 0xf25c, 0xf267, +0xf272, 0xf27d, 0xf288, 0xf293, 0xf29f, 0xf2aa, +0xf2b5, 0xf2c0, 0xf2cb, 0xf2d6, 0xf2e1, 0xf2ec, +0xf2f7, 0xf302, 0xf30d, 0xf318, 0xf323, 0xf32e, +0xf339, 0xf344, 0xf34f, 0xf35a, 0xf364, 0xf36f, +0xf37a, 0xf385, 0xf390, 0xf39b, 0xf3a6, 0xf3b1, +0xf3bc, 0xf3c7, 0xf3d2, 0xf3dd, 0xf3e8, 0xf3f3, +0xf3fe, 0xf409, 0xf414, 0xf41f, 0xf42a, 0xf435, +0xf43f, 0xf44a, 0xf455, 0xf460, 0xf46b, 0xf476, +0xf481, 0xf48c, 0xf497, 0xf4a2, 0xf4ad, 0xf4b7, +0xf4c2, 0xf4cd, 0xf4d8, 0xf4e3, 0xf4ee, 0xf4f9, +0xf504, 0xf50f, 0xf519, 0xf524, 0xf52f, 0xf53a, +0xf545, 0xf550, 0xf55b, 0xf565, 0xf570, 0xf57b, +0xf586, 0xf591, 0xf59c, 0xf5a6, 0xf5b1, 0xf5bc, +0xf5c7, 0xf5d2, 0xf5dd, 0xf5e7, 0xf5f2, 0xf5fd, +0xf608, 0xf613, 0xf61d, 0xf628, 0xf633, 0xf63e, +0xf649, 0xf653, 0xf65e, 0xf669, 0xf674, 0xf67f, +0xf689, 0xf694, 0xf69f, 0xf6aa, 0xf6b4, 0xf6bf, +0xf6ca, 0xf6d5, 0xf6e0, 0xf6ea, 0xf6f5, 0xf700, +0xf70b, 0xf715, 0xf720, 0xf72b, 0xf736, 0xf740, +0xf74b, 0xf756, 0xf760, 0xf76b, 0xf776, 0xf781, +0xf78b, 0xf796, 0xf7a1, 0xf7ab, 0xf7b6, 0xf7c1, +0xf7cc, 0xf7d6, 0xf7e1, 0xf7ec, 0xf7f6, 0xf801, +0xf80c, 0xf816, 0xf821, 0xf82c, 0xf836, 0xf841, +0xf84c, 0xf856, 0xf861, 0xf86c, 0xf876, 0xf881, +0xf88c, 0xf896, 0xf8a1, 0xf8ac, 0xf8b6, 0xf8c1, +0xf8cc, 0xf8d6, 0xf8e1, 0xf8ec, 0xf8f6, 0xf901, +0xf90b, 0xf916, 0xf921, 0xf92b, 0xf936, 0xf941, +0xf94b, 0xf956, 0xf960, 0xf96b, 0xf976, 0xf980, +0xf98b, 0xf995, 0xf9a0, 0xf9aa, 0xf9b5, 0xf9c0, +0xf9ca, 0xf9d5, 0xf9df, 0xf9ea, 0xf9f4, 0xf9ff, +0xfa0a, 0xfa14, 0xfa1f, 0xfa29, 0xfa34, 0xfa3e, +0xfa49, 0xfa53, 0xfa5e, 0xfa69, 0xfa73, 0xfa7e, +0xfa88, 0xfa93, 0xfa9d, 0xfaa8, 0xfab2, 0xfabd, +0xfac7, 0xfad2, 0xfadc, 0xfae7, 0xfaf1, 0xfafc, +0xfb06, 0xfb11, 0xfb1b, 0xfb26, 0xfb30, 0xfb3b, +0xfb45, 0xfb50, 0xfb5a, 0xfb65, 0xfb6f, 0xfb7a, +0xfb84, 0xfb8f, 0xfb99, 0xfba4, 0xfbae, 0xfbb8, +0xfbc3, 0xfbcd, 0xfbd8, 0xfbe2, 0xfbed, 0xfbf7, +0xfc02, 0xfc0c, 0xfc16, 0xfc21, 0xfc2b, 0xfc36, +0xfc40, 0xfc4b, 0xfc55, 0xfc5f, 0xfc6a, 0xfc74, +0xfc7f, 0xfc89, 0xfc93, 0xfc9e, 0xfca8, 0xfcb3, +0xfcbd, 0xfcc7, 0xfcd2, 0xfcdc, 0xfce7, 0xfcf1, +0xfcfb, 0xfd06, 0xfd10, 0xfd1a, 0xfd25, 0xfd2f, +0xfd3a, 0xfd44, 0xfd4e, 0xfd59, 0xfd63, 0xfd6d, +0xfd78, 0xfd82, 0xfd8c, 0xfd97, 0xfda1, 0xfdab, +0xfdb6, 0xfdc0, 0xfdca, 0xfdd5, 0xfddf, 0xfde9, +0xfdf4, 0xfdfe, 0xfe08, 0xfe13, 0xfe1d, 0xfe27, +0xfe32, 0xfe3c, 0xfe46, 0xfe50, 0xfe5b, 0xfe65, +0xfe6f, 0xfe7a, 0xfe84, 0xfe8e, 0xfe98, 0xfea3, +0xfead, 0xfeb7, 0xfec1, 0xfecc, 0xfed6, 0xfee0, +0xfeeb, 0xfef5, 0xfeff, 0xff09, 0xff14, 0xff1e, +0xff28, 0xff32, 0xff3c, 0xff47, 0xff51, 0xff5b, +0xff65, 0xff70, 0xff7a, 0xff84, 0xff8e, 0xff98, +0xffa3, 0xffad, 0xffb7, 0xffc1, 0xffcc, 0xffd6, +0xffe0, 0xffea, 0xfff4, 0xffff +}; + +// max value is pi/4 +constexpr double SCALING_FACTOR = 4. / M_PI * 0xFFFF; + +inline double atan2_lookup(double y, double x) +{ + if (std::abs(x) < std::numeric_limits::epsilon()) + { + if (y >= 0.) + { + return M_PI / 2.; + } + else + { + return -M_PI / 2.; + } + } + + unsigned octant = 0; + + if (x < 0.) + { + octant = 1; + x = -x; + } + if (y < 0.) + { + octant |= 2; + y = -y; + } + + double t = y / x; + if (t > 1.0) + { + octant |= 4; + t = 1.0 / t; + } + + double angle = atan_table[(unsigned)(t * 4095)] / SCALING_FACTOR; + + switch (octant) + { + case 0: + break; + case 1: + angle = M_PI - angle; + break; + case 2: + angle = -angle; + break; + case 3: + angle = -M_PI + angle; + break; + case 4: + angle = M_PI / 2.0 - angle; + break; + case 5: + angle = M_PI / 2.0 + angle; + break; + case 6: + angle = -M_PI / 2.0 + angle; + break; + case 7: + angle = -M_PI / 2.0 - angle; + break; + } + return angle; +} + +#endif // TRIGONOMETRY_TABLES_H From a122a1e8c7ffff4bd7e40cf450c170be1df6ede5 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Sun, 18 May 2014 14:32:00 +0200 Subject: [PATCH 15/36] remove comment --- DataStructures/Coordinate.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/DataStructures/Coordinate.cpp b/DataStructures/Coordinate.cpp index 61afb4c2060..da5b8f58658 100644 --- a/DataStructures/Coordinate.cpp +++ b/DataStructures/Coordinate.cpp @@ -148,7 +148,6 @@ double FixedPointCoordinate::ApproximateEuclideanDistance(const int lat1, return sqrt(x * x + y * y) * earth_radius; } -// Yuck! Code duplication. This function is also in EgdeBasedNode.h double FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordinate &point, const FixedPointCoordinate &segA, const FixedPointCoordinate &segB) From 75a2d4d00a46a173264579b44c28643a2034d3a3 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Sun, 18 May 2014 22:44:19 +0200 Subject: [PATCH 16/36] minor code refactoring, wip --- Algorithms/PolylineCompressor.cpp | 2 + Algorithms/StronglyConnectedComponents.h | 19 +- Descriptors/DescriptionFactory.cpp | 17 +- Descriptors/DescriptionFactory.h | 9 +- Extractor/ExtractionContainers.cpp | 13 +- Extractor/ExtractorCallbacks.cpp | 166 +++++----- Plugins/LocatePlugin.h | 9 +- Plugins/NearestPlugin.h | 9 +- Plugins/ViaRoutePlugin.h | 370 +++++++++++------------ RoutingAlgorithms/ShortestPathRouting.h | 13 +- Server/RequestParser.cpp | 6 +- prepare.cpp | 2 + 12 files changed, 313 insertions(+), 322 deletions(-) diff --git a/Algorithms/PolylineCompressor.cpp b/Algorithms/PolylineCompressor.cpp index cc8ec9f3feb..6faac02e5b1 100644 --- a/Algorithms/PolylineCompressor.cpp +++ b/Algorithms/PolylineCompressor.cpp @@ -28,6 +28,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "PolylineCompressor.h" #include "../Util/StringUtil.h" +//TODO: return vector of start indices for each leg + void PolylineCompressor::encodeVectorSignedNumber(std::vector &numbers, std::string &output) const { diff --git a/Algorithms/StronglyConnectedComponents.h b/Algorithms/StronglyConnectedComponents.h index 1d4f3b953f8..6f7e3aa3e5c 100644 --- a/Algorithms/StronglyConnectedComponents.h +++ b/Algorithms/StronglyConnectedComponents.h @@ -339,14 +339,17 @@ class TarjanSCC << " many components, marking small components"; // TODO/C++11: prime candidate for lambda function - unsigned size_one_counter = 0; - for (unsigned i = 0, end = component_size_vector.size(); i < end; ++i) - { - if (1 == component_size_vector[i]) - { - ++size_one_counter; - } - } + // unsigned size_one_counter = 0; + // for (unsigned i = 0, end = component_size_vector.size(); i < end; ++i) + // { + // if (1 == component_size_vector[i]) + // { + // ++size_one_counter; + // } + // } + unsigned size_one_counter = std::count_if(component_size_vector.begin(), + component_size_vector.end(), + [] (unsigned value) { return 1 == value;}); SimpleLogger().Write() << "identified " << size_one_counter << " SCCs of size 1"; diff --git a/Descriptors/DescriptionFactory.cpp b/Descriptors/DescriptionFactory.cpp index 0d95ddeda7e..1ca122b2420 100644 --- a/Descriptors/DescriptionFactory.cpp +++ b/Descriptors/DescriptionFactory.cpp @@ -29,8 +29,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. DescriptionFactory::DescriptionFactory() : entireLength(0) {} -DescriptionFactory::~DescriptionFactory() {} - void DescriptionFactory::SetStartSegment(const PhantomNode &source) { start_phantom = source; @@ -50,15 +48,14 @@ void DescriptionFactory::AppendSegment(const FixedPointCoordinate &coordinate, if ((1 == path_description.size()) && (path_description.back().location == coordinate)) { path_description.back().name_id = path_point.name_id; + return; } - else - { - path_description.emplace_back(coordinate, - path_point.name_id, - path_point.segment_duration, - 0, - path_point.turn_instruction); - } + + path_description.emplace_back(coordinate, + path_point.name_id, + path_point.segment_duration, + 0, + path_point.turn_instruction); } JSON::Value DescriptionFactory::AppendEncodedPolylineString(const bool return_encoded) diff --git a/Descriptors/DescriptionFactory.h b/Descriptors/DescriptionFactory.h index 6562d0c0796..c5581c1c95a 100644 --- a/Descriptors/DescriptionFactory.h +++ b/Descriptors/DescriptionFactory.h @@ -76,7 +76,6 @@ class DescriptionFactory // I know, declaring this public is considered bad. I'm lazy std::vector path_description; DescriptionFactory(); - virtual ~DescriptionFactory(); JSON::Value AppendUnencodedPolylineString() const; void AppendSegment(const FixedPointCoordinate &coordinate, const PathData &data); void BuildRouteSummary(const double distance, const unsigned time); @@ -176,14 +175,14 @@ class DescriptionFactory target_phantom.name_id = (path_description.end() - 2)->name_id; } } - if (std::numeric_limits::epsilon() > path_description[0].length) + if (std::numeric_limits::epsilon() > path_description.front().length) { if (path_description.size() > 2) { path_description.erase(path_description.begin()); - path_description[0].turn_instruction = TurnInstruction::HeadOn; - path_description[0].necessary = true; - start_phantom.name_id = path_description[0].name_id; + path_description.front().turn_instruction = TurnInstruction::HeadOn; + path_description.front().necessary = true; + start_phantom.name_id = path_description.front().name_id; } } diff --git a/Extractor/ExtractionContainers.cpp b/Extractor/ExtractionContainers.cpp index 1b321a89c03..438b6d16e45 100644 --- a/Extractor/ExtractionContainers.cpp +++ b/Extractor/ExtractionContainers.cpp @@ -217,14 +217,15 @@ void ExtractionContainers::PrepareData(const std::string &output_file_name, restrictions_out_stream.open(restrictions_file_name.c_str(), std::ios::binary); restrictions_out_stream.write((char *)&uuid, sizeof(UUID)); restrictions_out_stream.write((char *)&number_of_useable_restrictions, sizeof(unsigned)); - for (restrictions_iterator = restrictions_list.begin(); - restrictions_iterator != restrictions_list.end(); - ++restrictions_iterator) + // for (restrictions_iterator = restrictions_list.begin(); + // restrictions_iterator != restrictions_list.end(); + // ++restrictions_iterator) + for(const auto & restriction_container : restrictions_list) { - if (std::numeric_limits::max() != restrictions_iterator->restriction.fromNode && - std::numeric_limits::max() != restrictions_iterator->restriction.toNode) + if (std::numeric_limits::max() != restriction_container.restriction.fromNode && + std::numeric_limits::max() != restriction_container.restriction.toNode) { - restrictions_out_stream.write((char *)&(restrictions_iterator->restriction), + restrictions_out_stream.write((char *)&(restriction_container.restriction), sizeof(TurnRestriction)); } } diff --git a/Extractor/ExtractorCallbacks.cpp b/Extractor/ExtractorCallbacks.cpp index 8a019fb5753..4acc19a4f29 100644 --- a/Extractor/ExtractorCallbacks.cpp +++ b/Extractor/ExtractorCallbacks.cpp @@ -63,102 +63,104 @@ bool ExtractorCallbacks::ProcessRestriction(const InputRestrictionContainer &res /** warning: caller needs to take care of synchronization! */ void ExtractorCallbacks::ProcessWay(ExtractionWay &parsed_way) { - if ((0 < parsed_way.speed) || (0 < parsed_way.duration)) + if ((0 >= parsed_way.speed) && (0 >= parsed_way.duration)) { // Only true if the way is specified by the speed profile - if (std::numeric_limits::max() == parsed_way.id) - { - SimpleLogger().Write(logDEBUG) << "found bogus way with id: " << parsed_way.id - << " of size " << parsed_way.path.size(); - return; - } + return; + } - if (0 < parsed_way.duration) - { - // TODO: iterate all way segments and set duration corresponding to the length of each - // segment - parsed_way.speed = parsed_way.duration / (parsed_way.path.size() - 1); - } + if (std::numeric_limits::max() == parsed_way.id) + { + SimpleLogger().Write(logDEBUG) << "found bogus way with id: " << parsed_way.id + << " of size " << parsed_way.path.size(); + return; + } - if (std::numeric_limits::epsilon() >= std::abs(-1. - parsed_way.speed)) - { - SimpleLogger().Write(logDEBUG) << "found way with bogus speed, id: " << parsed_way.id; - return; - } + if (0 < parsed_way.duration) + { + // TODO: iterate all way segments and set duration corresponding to the length of each + // segment + parsed_way.speed = parsed_way.duration / (parsed_way.path.size() - 1); + } - // Get the unique identifier for the street name - const auto &string_map_iterator = string_map.find(parsed_way.name); - if (string_map.end() == string_map_iterator) - { - parsed_way.nameID = external_memory.name_list.size(); - external_memory.name_list.push_back(parsed_way.name); - string_map.insert(std::make_pair(parsed_way.name, parsed_way.nameID)); - } - else - { - parsed_way.nameID = string_map_iterator->second; - } + if (std::numeric_limits::epsilon() >= std::abs(-1. - parsed_way.speed)) + { + SimpleLogger().Write(logDEBUG) << "found way with bogus speed, id: " << parsed_way.id; + return; + } - if (ExtractionWay::opposite == parsed_way.direction) - { - std::reverse(parsed_way.path.begin(), parsed_way.path.end()); - parsed_way.direction = ExtractionWay::oneway; - } + // Get the unique identifier for the street name + const auto &string_map_iterator = string_map.find(parsed_way.name); + if (string_map.end() == string_map_iterator) + { + parsed_way.nameID = external_memory.name_list.size(); + external_memory.name_list.push_back(parsed_way.name); + string_map.insert(std::make_pair(parsed_way.name, parsed_way.nameID)); + } + else + { + parsed_way.nameID = string_map_iterator->second; + } - const bool split_bidirectional_edge = - (parsed_way.backward_speed > 0) && (parsed_way.speed != parsed_way.backward_speed); + if (ExtractionWay::opposite == parsed_way.direction) + { + std::reverse(parsed_way.path.begin(), parsed_way.path.end()); + parsed_way.direction = ExtractionWay::oneway; + } + + const bool split_bidirectional_edge = + (parsed_way.backward_speed > 0) && (parsed_way.speed != parsed_way.backward_speed); - for (unsigned n = 0; n < parsed_way.path.size() - 1; ++n) + for (unsigned n = 0; n < parsed_way.path.size() - 1; ++n) + { + external_memory.all_edges_list.push_back(InternalExtractorEdge( + parsed_way.path[n], + parsed_way.path[n + 1], + parsed_way.type, + (split_bidirectional_edge ? ExtractionWay::oneway : parsed_way.direction), + parsed_way.speed, + parsed_way.nameID, + parsed_way.roundabout, + parsed_way.ignoreInGrid, + (0 < parsed_way.duration), + parsed_way.isAccessRestricted, + false, + split_bidirectional_edge)); + external_memory.used_node_id_list.push_back(parsed_way.path[n]); + } + external_memory.used_node_id_list.push_back(parsed_way.path.back()); + + // The following information is needed to identify start and end segments of restrictions + external_memory.way_start_end_id_list.push_back( + WayIDStartAndEndEdge(parsed_way.id, + parsed_way.path[0], + parsed_way.path[1], + parsed_way.path[parsed_way.path.size() - 2], + parsed_way.path.back())); + + if (split_bidirectional_edge) + { // Only true if the way should be split + std::reverse(parsed_way.path.begin(), parsed_way.path.end()); + for (std::vector::size_type n = 0; n < parsed_way.path.size() - 1; ++n) { - external_memory.all_edges_list.push_back(InternalExtractorEdge( - parsed_way.path[n], - parsed_way.path[n + 1], - parsed_way.type, - (split_bidirectional_edge ? ExtractionWay::oneway : parsed_way.direction), - parsed_way.speed, - parsed_way.nameID, - parsed_way.roundabout, - parsed_way.ignoreInGrid, - (0 < parsed_way.duration), - parsed_way.isAccessRestricted, - false, - split_bidirectional_edge)); - external_memory.used_node_id_list.push_back(parsed_way.path[n]); + external_memory.all_edges_list.push_back( + InternalExtractorEdge(parsed_way.path[n], + parsed_way.path[n + 1], + parsed_way.type, + ExtractionWay::oneway, + parsed_way.backward_speed, + parsed_way.nameID, + parsed_way.roundabout, + parsed_way.ignoreInGrid, + (0 < parsed_way.duration), + parsed_way.isAccessRestricted, + (ExtractionWay::oneway == parsed_way.direction), + split_bidirectional_edge)); } - external_memory.used_node_id_list.push_back(parsed_way.path.back()); - - // The following information is needed to identify start and end segments of restrictions external_memory.way_start_end_id_list.push_back( WayIDStartAndEndEdge(parsed_way.id, parsed_way.path[0], parsed_way.path[1], parsed_way.path[parsed_way.path.size() - 2], parsed_way.path.back())); - - if (split_bidirectional_edge) - { // Only true if the way should be split - std::reverse(parsed_way.path.begin(), parsed_way.path.end()); - for (std::vector::size_type n = 0; n < parsed_way.path.size() - 1; ++n) - { - external_memory.all_edges_list.push_back( - InternalExtractorEdge(parsed_way.path[n], - parsed_way.path[n + 1], - parsed_way.type, - ExtractionWay::oneway, - parsed_way.backward_speed, - parsed_way.nameID, - parsed_way.roundabout, - parsed_way.ignoreInGrid, - (0 < parsed_way.duration), - parsed_way.isAccessRestricted, - (ExtractionWay::oneway == parsed_way.direction), - split_bidirectional_edge)); - } - external_memory.way_start_end_id_list.push_back( - WayIDStartAndEndEdge(parsed_way.id, - parsed_way.path[0], - parsed_way.path[1], - parsed_way.path[parsed_way.path.size() - 2], - parsed_way.path.back())); - } } } diff --git a/Plugins/LocatePlugin.h b/Plugins/LocatePlugin.h index a5e89c684c6..518259d7048 100644 --- a/Plugins/LocatePlugin.h +++ b/Plugins/LocatePlugin.h @@ -44,13 +44,8 @@ template class LocatePlugin : public BasePlugin void HandleRequest(const RouteParameters &route_parameters, http::Reply &reply) { // check number of parameters - if (route_parameters.coordinates.empty()) - { - reply = http::Reply::StockReply(http::Reply::badRequest); - return; - } - // check if queried location is sane - if (!route_parameters.coordinates.front().isValid()) + if (route_parameters.coordinates.empty() || + !route_parameters.coordinates.front().isValid()) { reply = http::Reply::StockReply(http::Reply::badRequest); return; diff --git a/Plugins/NearestPlugin.h b/Plugins/NearestPlugin.h index 8dafc2208e3..0b556671c3e 100644 --- a/Plugins/NearestPlugin.h +++ b/Plugins/NearestPlugin.h @@ -51,13 +51,8 @@ template class NearestPlugin : public BasePlugin void HandleRequest(const RouteParameters &route_parameters, http::Reply &reply) { // check number of parameters - if (route_parameters.coordinates.empty()) - { - reply = http::Reply::StockReply(http::Reply::badRequest); - return; - } - // check if queried location is sane - if (!route_parameters.coordinates.front().isValid()) + if (route_parameters.coordinates.empty() || + !route_parameters.coordinates.front().isValid()) { reply = http::Reply::StockReply(http::Reply::badRequest); return; diff --git a/Plugins/ViaRoutePlugin.h b/Plugins/ViaRoutePlugin.h index 89bbb2e443d..8f0c2c1fee1 100644 --- a/Plugins/ViaRoutePlugin.h +++ b/Plugins/ViaRoutePlugin.h @@ -1,188 +1,182 @@ -/* - -Copyright (c) 2013, Project OSRM, Dennis Luxen, others -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this list -of conditions and the following disclaimer. -Redistributions in binary form must reproduce the above copyright notice, this -list of conditions and the following disclaimer in the documentation and/or -other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef VIA_ROUTE_PLUGIN_H -#define VIA_ROUTE_PLUGIN_H - -#include "BasePlugin.h" - -#include "../Algorithms/ObjectToBase64.h" - -#include "../DataStructures/QueryEdge.h" -#include "../DataStructures/SearchEngine.h" -#include "../Descriptors/BaseDescriptor.h" -#include "../Descriptors/GPXDescriptor.h" -#include "../Descriptors/JSONDescriptor.h" -#include "../Util/SimpleLogger.h" -#include "../Util/StringUtil.h" -#include "../Util/TimingUtil.h" - -#include - -#include -#include -#include -#include -#include - -template class ViaRoutePlugin : public BasePlugin -{ - private: - std::unordered_map descriptor_table; - std::shared_ptr> search_engine_ptr; - - public: - explicit ViaRoutePlugin(DataFacadeT *facade) : descriptor_string("viaroute"), facade(facade) - { - search_engine_ptr = std::make_shared>(facade); - - descriptor_table.emplace("json", 0); - descriptor_table.emplace("gpx", 1); - // descriptor_table.emplace("geojson", 2); - } - - virtual ~ViaRoutePlugin() {} - - const std::string GetDescriptor() const { return descriptor_string; } - - void HandleRequest(const RouteParameters &route_parameters, http::Reply &reply) - { - // check number of parameters - if (2 > route_parameters.coordinates.size()) - { - reply = http::Reply::StockReply(http::Reply::badRequest); - return; - } - - RawRouteData raw_route; - raw_route.check_sum = facade->GetCheckSum(); - - if (std::any_of(begin(route_parameters.coordinates), - end(route_parameters.coordinates), - [&](FixedPointCoordinate coordinate) - { return !coordinate.isValid(); })) - { - reply = http::Reply::StockReply(http::Reply::badRequest); - return; - } - - for (const FixedPointCoordinate &coordinate : route_parameters.coordinates) - { - raw_route.raw_via_node_coordinates.emplace_back(coordinate); - } - - std::vector phantom_node_vector(raw_route.raw_via_node_coordinates.size()); - const bool checksum_OK = (route_parameters.check_sum == raw_route.check_sum); - - for (unsigned i = 0; i < raw_route.raw_via_node_coordinates.size(); ++i) - { - if (checksum_OK && i < route_parameters.hints.size() && - !route_parameters.hints[i].empty()) - { - DecodeObjectFromBase64(route_parameters.hints[i], phantom_node_vector[i]); - if (phantom_node_vector[i].isValid(facade->GetNumberOfNodes())) - { - continue; - } - } - facade->FindPhantomNodeForCoordinate(raw_route.raw_via_node_coordinates[i], - phantom_node_vector[i], - route_parameters.zoom_level); - } - - PhantomNodes current_phantom_node_pair; - for (unsigned i = 0; i < phantom_node_vector.size() - 1; ++i) - { - current_phantom_node_pair.source_phantom = phantom_node_vector[i]; - current_phantom_node_pair.target_phantom = phantom_node_vector[i + 1]; - raw_route.segment_end_coordinates.emplace_back(current_phantom_node_pair); - } - - const bool is_alternate_requested = route_parameters.alternate_route; - const bool is_only_one_segment = (1 == raw_route.segment_end_coordinates.size()); - TIMER_START(routing); - if (is_alternate_requested && is_only_one_segment) - { - search_engine_ptr->alternative_path(raw_route.segment_end_coordinates.front(), - raw_route); - } - else - { - search_engine_ptr->shortest_path(raw_route.segment_end_coordinates, raw_route); - } - TIMER_STOP(routing); - SimpleLogger().Write() << "routing took " << TIMER_MSEC(routing) << "ms"; - - if (INVALID_EDGE_WEIGHT == raw_route.shortest_path_length) - { - SimpleLogger().Write(logDEBUG) << "Error occurred, single path not found"; - } - reply.status = http::Reply::ok; - - DescriptorConfig descriptor_config; - - auto iter = descriptor_table.find(route_parameters.output_format); - unsigned descriptor_type = (iter != descriptor_table.end() ? iter->second : 0); - - descriptor_config.zoom_level = route_parameters.zoom_level; - descriptor_config.instructions = route_parameters.print_instructions; - descriptor_config.geometry = route_parameters.geometry; - descriptor_config.encode_geometry = route_parameters.compression; - - std::shared_ptr> descriptor; - switch (descriptor_type) - { - // case 0: - // descriptor = std::make_shared>(); - // break; - case 1: - descriptor = std::make_shared>(facade); - break; - // case 2: - // descriptor = std::make_shared>(); - // break; - default: - descriptor = std::make_shared>(facade); - break; - } - - PhantomNodes phantom_nodes; - phantom_nodes.source_phantom = raw_route.segment_end_coordinates.front().source_phantom; - phantom_nodes.target_phantom = raw_route.segment_end_coordinates.back().target_phantom; - descriptor->SetConfig(descriptor_config); - TIMER_START(descriptor); - descriptor->Run(raw_route, phantom_nodes, reply); - TIMER_STOP(descriptor); - SimpleLogger().Write() << "descriptor took " << TIMER_MSEC(descriptor) << "ms"; - } - - private: - std::string descriptor_string; - DataFacadeT *facade; -}; - -#endif // VIA_ROUTE_PLUGIN_H +/* + +Copyright (c) 2013, Project OSRM, Dennis Luxen, others +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef VIA_ROUTE_PLUGIN_H +#define VIA_ROUTE_PLUGIN_H + +#include "BasePlugin.h" + +#include "../Algorithms/ObjectToBase64.h" + +#include "../DataStructures/QueryEdge.h" +#include "../DataStructures/SearchEngine.h" +#include "../Descriptors/BaseDescriptor.h" +#include "../Descriptors/GPXDescriptor.h" +#include "../Descriptors/JSONDescriptor.h" +#include "../Util/SimpleLogger.h" +#include "../Util/StringUtil.h" +#include "../Util/TimingUtil.h" + +#include + +#include +#include +#include +#include +#include + +template class ViaRoutePlugin : public BasePlugin +{ + private: + std::unordered_map descriptor_table; + std::shared_ptr> search_engine_ptr; + + public: + explicit ViaRoutePlugin(DataFacadeT *facade) : descriptor_string("viaroute"), facade(facade) + { + search_engine_ptr = std::make_shared>(facade); + + descriptor_table.emplace("json", 0); + descriptor_table.emplace("gpx", 1); + // descriptor_table.emplace("geojson", 2); + } + + virtual ~ViaRoutePlugin() {} + + const std::string GetDescriptor() const { return descriptor_string; } + + void HandleRequest(const RouteParameters &route_parameters, http::Reply &reply) + { + // check number of parameters + if (2 > route_parameters.coordinates.size() || + std::any_of(begin(route_parameters.coordinates), + end(route_parameters.coordinates), + [&](FixedPointCoordinate coordinate) + { return !coordinate.isValid(); })) + { + reply = http::Reply::StockReply(http::Reply::badRequest); + return; + } + + RawRouteData raw_route; + raw_route.check_sum = facade->GetCheckSum(); + for (const FixedPointCoordinate &coordinate : route_parameters.coordinates) + { + raw_route.raw_via_node_coordinates.emplace_back(coordinate); + } + + std::vector phantom_node_vector(raw_route.raw_via_node_coordinates.size()); + const bool checksum_OK = (route_parameters.check_sum == raw_route.check_sum); + + for (unsigned i = 0; i < raw_route.raw_via_node_coordinates.size(); ++i) + { + if (checksum_OK && i < route_parameters.hints.size() && + !route_parameters.hints[i].empty()) + { + DecodeObjectFromBase64(route_parameters.hints[i], phantom_node_vector[i]); + if (phantom_node_vector[i].isValid(facade->GetNumberOfNodes())) + { + continue; + } + } + facade->FindPhantomNodeForCoordinate(raw_route.raw_via_node_coordinates[i], + phantom_node_vector[i], + route_parameters.zoom_level); + } + + PhantomNodes current_phantom_node_pair; + for (unsigned i = 0; i < phantom_node_vector.size() - 1; ++i) + { + current_phantom_node_pair.source_phantom = phantom_node_vector[i]; + current_phantom_node_pair.target_phantom = phantom_node_vector[i + 1]; + raw_route.segment_end_coordinates.emplace_back(current_phantom_node_pair); + } + + const bool is_alternate_requested = route_parameters.alternate_route; + const bool is_only_one_segment = (1 == raw_route.segment_end_coordinates.size()); + TIMER_START(routing); + if (is_alternate_requested && is_only_one_segment) + { + search_engine_ptr->alternative_path(raw_route.segment_end_coordinates.front(), + raw_route); + } + else + { + search_engine_ptr->shortest_path(raw_route.segment_end_coordinates, raw_route); + } + TIMER_STOP(routing); + SimpleLogger().Write() << "routing took " << TIMER_MSEC(routing) << "ms"; + + if (INVALID_EDGE_WEIGHT == raw_route.shortest_path_length) + { + SimpleLogger().Write(logDEBUG) << "Error occurred, single path not found"; + } + reply.status = http::Reply::ok; + + DescriptorConfig descriptor_config; + + auto iter = descriptor_table.find(route_parameters.output_format); + unsigned descriptor_type = (iter != descriptor_table.end() ? iter->second : 0); + + descriptor_config.zoom_level = route_parameters.zoom_level; + descriptor_config.instructions = route_parameters.print_instructions; + descriptor_config.geometry = route_parameters.geometry; + descriptor_config.encode_geometry = route_parameters.compression; + + std::shared_ptr> descriptor; + switch (descriptor_type) + { + // case 0: + // descriptor = std::make_shared>(); + // break; + case 1: + descriptor = std::make_shared>(facade); + break; + // case 2: + // descriptor = std::make_shared>(); + // break; + default: + descriptor = std::make_shared>(facade); + break; + } + + PhantomNodes phantom_nodes; + phantom_nodes.source_phantom = raw_route.segment_end_coordinates.front().source_phantom; + phantom_nodes.target_phantom = raw_route.segment_end_coordinates.back().target_phantom; + descriptor->SetConfig(descriptor_config); + TIMER_START(descriptor); + descriptor->Run(raw_route, phantom_nodes, reply); + TIMER_STOP(descriptor); + SimpleLogger().Write() << "descriptor took " << TIMER_MSEC(descriptor) << "ms"; + } + + private: + std::string descriptor_string; + DataFacadeT *facade; +}; + +#endif // VIA_ROUTE_PLUGIN_H diff --git a/RoutingAlgorithms/ShortestPathRouting.h b/RoutingAlgorithms/ShortestPathRouting.h index 819e39d89b7..f51d6423532 100644 --- a/RoutingAlgorithms/ShortestPathRouting.h +++ b/RoutingAlgorithms/ShortestPathRouting.h @@ -51,14 +51,15 @@ template class ShortestPathRouting : public BasicRoutingInte void operator()(const std::vector &phantom_nodes_vector, RawRouteData &raw_route_data) const { - for (const PhantomNodes &phantom_node_pair : phantom_nodes_vector) + if(std::any_of(begin(phantom_nodes_vector), + end(phantom_nodes_vector), + [](PhantomNodes phantom_node_pair) + { return phantom_node_pair.AtLeastOnePhantomNodeIsInvalid(); })) { - if (phantom_node_pair.AtLeastOnePhantomNodeIsInvalid()) - { - BOOST_ASSERT(false); - return; - } + BOOST_ASSERT(false); + return; } + int distance1 = 0; int distance2 = 0; bool search_from_1st_node = true; diff --git a/Server/RequestParser.cpp b/Server/RequestParser.cpp index fd26608b6df..b95c299b88e 100644 --- a/Server/RequestParser.cpp +++ b/Server/RequestParser.cpp @@ -262,10 +262,10 @@ RequestParser::consume(Request &req, char input, http::CompressionType *compress return boost::indeterminate; } return false; - case expecting_newline_3: + default: // expecting_newline_3: return (input == '\n'); - default: - return false; + // default: + // return false; } } diff --git a/prepare.cpp b/prepare.cpp index f57d3e1dee5..267e22e5b0d 100644 --- a/prepare.cpp +++ b/prepare.cpp @@ -438,6 +438,7 @@ int main(int argc, char *argv[]) // every target needs to be valid BOOST_ASSERT(current_edge.target < max_used_node_id); +#ifndef NDEBUG if (current_edge.data.distance <= 0) { SimpleLogger().Write(logWARNING) @@ -450,6 +451,7 @@ int main(int argc, char *argv[]) << node_array.size() - 1; return 1; } +#endif hsgr_output_stream.write((char *)¤t_edge, sizeof(StaticGraph::EdgeArrayEntry)); ++number_of_used_edges; From d028a30f87de9d9565292adf6f272666ff5f1156 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Mon, 19 May 2014 12:53:27 +0200 Subject: [PATCH 17/36] fixes issue #1019: - fix ignored turn restriction on chains of degree-2 nodes - add a cucumber test to test for potential regressions --- Contractor/EdgeBasedGraphFactory.cpp | 6 ++++ DataStructures/RestrictionMap.cpp | 45 ++++++++++++++++++++++++++++ DataStructures/RestrictionMap.h | 11 +++---- features/car/restrictions.feature | 31 ++++++++++++++++++- 4 files changed, 87 insertions(+), 6 deletions(-) diff --git a/Contractor/EdgeBasedGraphFactory.cpp b/Contractor/EdgeBasedGraphFactory.cpp index 84331608014..09f5fc2333c 100644 --- a/Contractor/EdgeBasedGraphFactory.cpp +++ b/Contractor/EdgeBasedGraphFactory.cpp @@ -305,6 +305,12 @@ void EdgeBasedGraphFactory::CompressGeometry() continue; } + // check if v is a via node for a turn restriction, i.e. a 'directed' barrier node + if (m_restriction_map->IsNodeAViaNode(v)) + { + continue; + } + const bool reverse_edge_order = !(m_node_based_graph->GetEdgeData(m_node_based_graph->BeginEdges(v)).forward); const EdgeID forward_e2 = m_node_based_graph->BeginEdges(v) + reverse_edge_order; diff --git a/DataStructures/RestrictionMap.cpp b/DataStructures/RestrictionMap.cpp index 2c9ee7b4676..49ae8ac3945 100644 --- a/DataStructures/RestrictionMap.cpp +++ b/DataStructures/RestrictionMap.cpp @@ -1,6 +1,40 @@ +/* + +Copyright (c) 2013, Project OSRM, Dennis Luxen, others +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + #include "RestrictionMap.h" #include "NodeBasedGraph.h" +#include "../Util/SimpleLogger.h" + +bool RestrictionMap::IsNodeAViaNode(const NodeID node) const +{ + return m_no_turn_via_node_set.find(node) != m_no_turn_via_node_set.end(); +} + RestrictionMap::RestrictionMap(const std::shared_ptr &graph, const std::vector &input_restrictions_list) : m_count(0), m_graph(graph) @@ -9,8 +43,14 @@ RestrictionMap::RestrictionMap(const std::shared_ptr &gra // and all end-nodes for (auto &restriction : input_restrictions_list) { + + m_no_turn_via_node_set.insert(restriction.viaNode); + std::pair restriction_source = std::make_pair(restriction.fromNode, restriction.viaNode); + + SimpleLogger().Write(logDEBUG) << "restr from: " << restriction.fromNode << ", v: " << restriction.viaNode << ", to: " << restriction.toNode << ", is_only_: " << (restriction.flags.isOnly ? "y" : "n"); + unsigned index; auto restriction_iter = m_restriction_map.find(restriction_source); if (restriction_iter == m_restriction_map.end()) @@ -70,8 +110,11 @@ void RestrictionMap::FixupArrivingTurnRestriction(const NodeID u, const NodeID v const std::pair restr_start = std::make_pair(x, u); auto restriction_iterator = m_restriction_map.find(restr_start); if (restriction_iterator == m_restriction_map.end()) + { continue; + } + SimpleLogger().Write(logDEBUG) << "fixing arriving instruction at turn <" << u << "," << v << "," << w << ">"; const unsigned index = restriction_iterator->second; auto &bucket = m_restriction_bucket_list.at(index); for (RestrictionTarget &restriction_target : bucket) @@ -98,6 +141,7 @@ void RestrictionMap::FixupStartingTurnRestriction(const NodeID u, const NodeID v auto restriction_iterator = m_restriction_map.find(old_start); if (restriction_iterator != m_restriction_map.end()) { + SimpleLogger().Write(logDEBUG) << "fixing restr start at turn <" << u << "," << v << "," << w << ">"; const unsigned index = restriction_iterator->second; // remove old restriction start (v,w) m_restriction_map.erase(restriction_iterator); @@ -141,6 +185,7 @@ NodeID RestrictionMap::CheckForEmanatingIsOnlyTurn(const NodeID u, const NodeID */ bool RestrictionMap::CheckIfTurnIsRestricted(const NodeID u, const NodeID v, const NodeID w) const { + // SimpleLogger().Write(logDEBUG) << "checking turn <" << u << "," << v << "," << w << ">"; BOOST_ASSERT(u != std::numeric_limits::max()); BOOST_ASSERT(v != std::numeric_limits::max()); BOOST_ASSERT(w != std::numeric_limits::max()); diff --git a/DataStructures/RestrictionMap.h b/DataStructures/RestrictionMap.h index 099397037eb..f0e1df6a829 100644 --- a/DataStructures/RestrictionMap.h +++ b/DataStructures/RestrictionMap.h @@ -36,11 +36,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "NodeBasedGraph.h" #include +#include -/*! - * Makee it efficent to look up if an edge is the start + via node of a TurnRestriction. - * Is needed by EdgeBasedGraphFactory. - */ + +// Make it efficent to look up if an edge is the start + via node of a TurnRestriction +// EdgeBasedEdgeFactory decides by it if edges are inserted or geometry is compressed class RestrictionMap { public: @@ -51,7 +51,7 @@ class RestrictionMap void FixupStartingTurnRestriction(const NodeID u, const NodeID v, const NodeID w); NodeID CheckForEmanatingIsOnlyTurn(const NodeID u, const NodeID v) const; bool CheckIfTurnIsRestricted(const NodeID u, const NodeID v, const NodeID w) const; - + bool IsNodeAViaNode(const NodeID node) const; unsigned size() { return m_count; } private: @@ -66,6 +66,7 @@ class RestrictionMap std::vector m_restriction_bucket_list; //! maps (start, via) -> bucket index boost::unordered_map m_restriction_map; + std::unordered_set m_no_turn_via_node_set; }; #endif diff --git a/features/car/restrictions.feature b/features/car/restrictions.feature index f808c5d91d7..76ce2731f8b 100644 --- a/features/car/restrictions.feature +++ b/features/car/restrictions.feature @@ -30,6 +30,35 @@ Feature: Car - Turn restrictions | s | n | sj,nj | | s | e | sj,ej | + @no_turning + Scenario: Car - No straight on + Given the node map + | a | b | j | d | e | + | v | | | | z | + | | w | x | y | | + + And the ways + | nodes | oneway | + | ab | no | + | bj | no | + | jd | no | + | de | no | + | ej | no | + | av | yes | + | vw | yes | + | wx | yes | + | xy | yes | + | yz | yes | + | ze | yes | + + And the relations + | type | way:from | way:to | node:via | restriction | + | restriction | bj | jd | j | no_straight_on | + + When I route I should get + | from | to | route | + | a | e | av,vw,wx,xy,yz,ze | + @no_turning Scenario: Car - No right turn Given the node map @@ -248,4 +277,4 @@ Feature: Car - Turn restrictions When I route I should get | from | to | route | | s | a | sj,aj | - | s | b | sj,bj | \ No newline at end of file + | s | b | sj,bj | From e490c4afed9db23046e7c99a458fcb32621cb614 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Mon, 19 May 2014 13:00:27 +0200 Subject: [PATCH 18/36] use consistent typedef'ed types --- DataStructures/DynamicGraph.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DataStructures/DynamicGraph.h b/DataStructures/DynamicGraph.h index 271ad842369..50690a40b9e 100644 --- a/DataStructures/DynamicGraph.h +++ b/DataStructures/DynamicGraph.h @@ -135,7 +135,7 @@ template class DynamicGraph return EdgeIterator(m_nodes[n].firstEdge + m_nodes[n].edges); } - EdgeRange GetAdjacentEdgeRange(const NodeID node) const + EdgeRange GetAdjacentEdgeRange(const NodeIterator node) const { return boost::irange(BeginEdges(node), EndEdges(node)); } From 0574a60bc2dd7e143d24378d2a8e5b5ee83a8626 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Mon, 19 May 2014 13:02:41 +0200 Subject: [PATCH 19/36] replace boost::unordered_map, move hash function for pairs into its own header --- Algorithms/StronglyConnectedComponents.h | 12 +------ DataStructures/RestrictionMap.h | 7 ++-- Util/StdHashExtensions.h | 46 ++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 14 deletions(-) create mode 100644 Util/StdHashExtensions.h diff --git a/Algorithms/StronglyConnectedComponents.h b/Algorithms/StronglyConnectedComponents.h index 6f7e3aa3e5c..30c830ff627 100644 --- a/Algorithms/StronglyConnectedComponents.h +++ b/Algorithms/StronglyConnectedComponents.h @@ -38,6 +38,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "../DataStructures/TurnInstructions.h" #include "../Util/SimpleLogger.h" +#include "../Util/StdHashExtensions.h" #include @@ -60,17 +61,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -namespace std -{ -template <> struct hash> -{ - size_t operator()(const std::pair &pair) const - { - return std::hash()(pair.first) ^ std::hash()(pair.second); - } -}; -} - class TarjanSCC { private: diff --git a/DataStructures/RestrictionMap.h b/DataStructures/RestrictionMap.h index f0e1df6a829..5a695a57afc 100644 --- a/DataStructures/RestrictionMap.h +++ b/DataStructures/RestrictionMap.h @@ -30,12 +30,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include -#include "../typedefs.h" #include "DynamicGraph.h" #include "Restriction.h" #include "NodeBasedGraph.h" +#include "../Util/StdHashExtensions.h" +#include "../typedefs.h" -#include +#include #include @@ -65,7 +66,7 @@ class RestrictionMap //! index -> list of (target, isOnly) std::vector m_restriction_bucket_list; //! maps (start, via) -> bucket index - boost::unordered_map m_restriction_map; + std::unordered_map m_restriction_map; std::unordered_set m_no_turn_via_node_set; }; diff --git a/Util/StdHashExtensions.h b/Util/StdHashExtensions.h new file mode 100644 index 00000000000..29663f1d3e9 --- /dev/null +++ b/Util/StdHashExtensions.h @@ -0,0 +1,46 @@ +/* + +Copyright (c) 2014, Project OSRM, Dennis Luxen, others +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef STD_HASH_EXTENSIONS_H +#define STD_HASH_EXTENSIONS_H + +#include "../typedefs.h" + +#include + +namespace std +{ +template <> struct hash> +{ + size_t operator()(const std::pair &pair) const + { + return std::hash()(pair.first) ^ std::hash()(pair.second); + } +}; +} + +#endif // STD_HASH_EXTENSIONS_H From 4fc329a1eb37dc1130c8c52c800532066d82a85c Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Mon, 19 May 2014 16:04:24 +0200 Subject: [PATCH 20/36] remove superflous way in test setup --- features/car/restrictions.feature | 1 - 1 file changed, 1 deletion(-) diff --git a/features/car/restrictions.feature b/features/car/restrictions.feature index 76ce2731f8b..547b8131a6d 100644 --- a/features/car/restrictions.feature +++ b/features/car/restrictions.feature @@ -43,7 +43,6 @@ Feature: Car - Turn restrictions | bj | no | | jd | no | | de | no | - | ej | no | | av | yes | | vw | yes | | wx | yes | From 4ec9f2c00f31de49435f847f8b64ce331350f67e Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Tue, 20 May 2014 12:11:19 +0200 Subject: [PATCH 21/36] fix #1021, always check if files exist --- Server/DataStructures/InternalDataFacade.h | 15 +++++++++++---- Util/IniFileUtil.h | 18 +++++++++++++++--- Util/ProgramOptions.h | 3 +++ 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/Server/DataStructures/InternalDataFacade.h b/Server/DataStructures/InternalDataFacade.h index 063e13765d0..20999af0b7e 100644 --- a/Server/DataStructures/InternalDataFacade.h +++ b/Server/DataStructures/InternalDataFacade.h @@ -79,13 +79,13 @@ template class InternalDataFacade : public BaseDataFacade class InternalDataFacade : public BaseDataFacade #include @@ -40,9 +42,19 @@ inline std::string ReadIniFileAndLowerContents(const boost::filesystem::path &pa boost::filesystem::fstream config_stream(path); std::string input_str((std::istreambuf_iterator(config_stream)), std::istreambuf_iterator()); - std::regex regex("^([^=]*)"); // match from start of line to '=' - std::string format("\\L$1\\E"); // replace with downcased substring - return std::regex_replace(input_str, regex, format); + std::regex regex("\\w+="); + + std::string output = input_str; + const std::sregex_token_iterator end; + for (std::sregex_token_iterator i(input_str.begin(), input_str.end(), regex); i != end; ++i) + { + std::string match = *i; + std::string new_regex = *i; + std::transform(new_regex.begin(), new_regex.end(), new_regex.begin(), ::tolower); + SimpleLogger().Write() << match << " - " << new_regex; + output = std::regex_replace(output, std::regex(match), new_regex); + } + return output; } #endif // INI_FILE_UTIL_H diff --git a/Util/ProgramOptions.h b/Util/ProgramOptions.h index 9fdd4325f74..c0d6b36364a 100644 --- a/Util/ProgramOptions.h +++ b/Util/ProgramOptions.h @@ -150,6 +150,7 @@ inline unsigned GenerateServerProgramOptions(const int argc, // parse config file ServerPaths::iterator path_iterator = paths.find("config"); + bool use_ini_file = false; if (path_iterator != paths.end() && boost::filesystem::is_regular_file(path_iterator->second) && !option_variables.count("base")) { @@ -159,6 +160,8 @@ inline unsigned GenerateServerProgramOptions(const int argc, boost::program_options::store(parse_config_file(config_stream, config_file_options), option_variables); boost::program_options::notify(option_variables); + use_ini_file = true; + return INIT_OK_START_ENGINE; } if (1 > requested_num_threads) From b8acbae3e83d6ac4d2a5f72bf3cf69a54205b592 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Tue, 20 May 2014 12:12:33 +0200 Subject: [PATCH 22/36] fix #1021, always check if files exist --- Util/BoostFileSystemFix.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Util/BoostFileSystemFix.h b/Util/BoostFileSystemFix.h index 998eb75ad9b..1e1e98e0e14 100644 --- a/Util/BoostFileSystemFix.h +++ b/Util/BoostFileSystemFix.h @@ -140,13 +140,8 @@ inline void AssertPathExists(const boost::filesystem::path &path) { if (!boost::filesystem::is_regular_file(path)) { - SimpleLogger().Write(logDEBUG) << path << " check failed"; throw OSRMException(path.string() + " not found."); } - else - { - SimpleLogger().Write(logDEBUG) << path << " exists"; - } } #endif /* BOOST_FILE_SYSTEM_FIX_H */ From bf6ca22b00f7fd14d77a5477bbd9296b5ec80859 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Tue, 20 May 2014 12:29:40 +0200 Subject: [PATCH 23/36] fix #1021, always check if files exist --- features/options/routed/invalid.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/options/routed/invalid.feature b/features/options/routed/invalid.feature index a25a039c00d..a6d62d7e0a4 100644 --- a/features/options/routed/invalid.feature +++ b/features/options/routed/invalid.feature @@ -15,5 +15,5 @@ Feature: osrm-routed command line options: invalid options When I run "osrm-routed over-the-rainbow.osrm" Then stdout should contain "over-the-rainbow.osrm" And stderr should contain "exception" - And stderr should contain "does not exist" + And stderr should contain "not found" And it should exit with code 1 From e28e45f38e7ab0ca91c73ac15207766c2819693e Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Tue, 20 May 2014 14:19:25 +0200 Subject: [PATCH 24/36] remove unused variable --- Util/ProgramOptions.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/Util/ProgramOptions.h b/Util/ProgramOptions.h index c0d6b36364a..c6e5448a690 100644 --- a/Util/ProgramOptions.h +++ b/Util/ProgramOptions.h @@ -150,7 +150,6 @@ inline unsigned GenerateServerProgramOptions(const int argc, // parse config file ServerPaths::iterator path_iterator = paths.find("config"); - bool use_ini_file = false; if (path_iterator != paths.end() && boost::filesystem::is_regular_file(path_iterator->second) && !option_variables.count("base")) { @@ -160,7 +159,6 @@ inline unsigned GenerateServerProgramOptions(const int argc, boost::program_options::store(parse_config_file(config_stream, config_file_options), option_variables); boost::program_options::notify(option_variables); - use_ini_file = true; return INIT_OK_START_ENGINE; } From d53eb881c2e3e21c88ce3ac8cb502351689c4f79 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Tue, 20 May 2014 14:20:09 +0200 Subject: [PATCH 25/36] revert to old boost based regex as GCC does not properly implement it prior to GCC 4.90 --- Util/IniFileUtil.h | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/Util/IniFileUtil.h b/Util/IniFileUtil.h index 812db8e6a52..08f1aae5f8b 100644 --- a/Util/IniFileUtil.h +++ b/Util/IniFileUtil.h @@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include #include #include @@ -40,21 +41,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. inline std::string ReadIniFileAndLowerContents(const boost::filesystem::path &path) { boost::filesystem::fstream config_stream(path); - std::string input_str((std::istreambuf_iterator(config_stream)), + std::string ini_file_content((std::istreambuf_iterator(config_stream)), std::istreambuf_iterator()); - std::regex regex("\\w+="); - - std::string output = input_str; - const std::sregex_token_iterator end; - for (std::sregex_token_iterator i(input_str.begin(), input_str.end(), regex); i != end; ++i) - { - std::string match = *i; - std::string new_regex = *i; - std::transform(new_regex.begin(), new_regex.end(), new_regex.begin(), ::tolower); - SimpleLogger().Write() << match << " - " << new_regex; - output = std::regex_replace(output, std::regex(match), new_regex); - } - return output; + boost::regex regex( "^([^=]*)" ); //match from start of line to '=' + std::string format( "\\L$1\\E" ); //replace with downcased substring + return boost::regex_replace( ini_file_content, regex, format ); } #endif // INI_FILE_UTIL_H From 69ad3f33656ae60df11ba323a78f8e5a5212082c Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Tue, 20 May 2014 15:37:18 +0200 Subject: [PATCH 26/36] add curly braces to one line if --- DataStructures/TurnInstructions.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DataStructures/TurnInstructions.h b/DataStructures/TurnInstructions.h index 4e0d6255f99..61de3b11b7f 100644 --- a/DataStructures/TurnInstructions.h +++ b/DataStructures/TurnInstructions.h @@ -80,7 +80,9 @@ struct TurnInstructionsClass static inline bool TurnIsNecessary(const TurnInstruction turn_instruction) { if (TurnInstruction::NoTurn == turn_instruction || TurnInstruction::StayOnRoundAbout == turn_instruction) + { return false; + } return true; } }; From 9a2d701e2e6eb87539334d2bde5a9ede8e56d788 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Tue, 20 May 2014 15:40:14 +0200 Subject: [PATCH 27/36] fix issue #1025: - add function to count directed outgoing edges - generate correct instruction for staying on a roundabout - move test from @bug namespace to the general one --- Contractor/EdgeBasedGraphFactory.cpp | 2 +- DataStructures/DynamicGraph.h | 13 +++++++++++++ features/testbot/roundabout.feature | 3 +-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Contractor/EdgeBasedGraphFactory.cpp b/Contractor/EdgeBasedGraphFactory.cpp index 09f5fc2333c..9e1a012f1ac 100644 --- a/Contractor/EdgeBasedGraphFactory.cpp +++ b/Contractor/EdgeBasedGraphFactory.cpp @@ -728,7 +728,7 @@ TurnInstruction EdgeBasedGraphFactory::AnalyzeTurn(const NodeID u, if (data1.roundabout && data2.roundabout) { // Is a turn possible? If yes, we stay on the roundabout! - if (1 == m_node_based_graph->GetOutDegree(v)) + if (1 == m_node_based_graph->GetDirectedOutDegree(v)) { // No turn possible. return TurnInstruction::NoTurn; diff --git a/DataStructures/DynamicGraph.h b/DataStructures/DynamicGraph.h index 50690a40b9e..934086c08c6 100644 --- a/DataStructures/DynamicGraph.h +++ b/DataStructures/DynamicGraph.h @@ -117,6 +117,19 @@ template class DynamicGraph unsigned GetOutDegree(const NodeIterator n) const { return m_nodes[n].edges; } + unsigned GetDirectedOutDegree(const NodeIterator n) const + { + unsigned degree = 0; + for(EdgeIterator edge = BeginEdges(n); edge < EndEdges(n); ++edge) + { + if (GetEdgeData(edge).forward) + { + ++degree; + } + } + return degree; + } + NodeIterator GetTarget(const EdgeIterator e) const { return NodeIterator(m_edges[e].target); } void SetTarget(const EdgeIterator e, const NodeIterator n) { m_edges[e].target = n; } diff --git a/features/testbot/roundabout.feature b/features/testbot/roundabout.feature index f3e0c303c2a..570b14bed56 100644 --- a/features/testbot/roundabout.feature +++ b/features/testbot/roundabout.feature @@ -35,7 +35,6 @@ Feature: Roundabout Instructions | v | t | vd,tb | head,enter_roundabout-2,destination | | v | u | vd,uc | head,enter_roundabout-3,destination | - @bug Scenario: Testbot - Roundabout with oneway links Given the node map | | | p | o | | | @@ -74,4 +73,4 @@ Feature: Roundabout Instructions | p | i | ph,ai | head,enter_roundabout-1,destination | | p | k | ph,ck | head,enter_roundabout-2,destination | | p | m | ph,em | head,enter_roundabout-3,destination | - | p | o | ph,go | head,enter_roundabout-4,destination | \ No newline at end of file + | p | o | ph,go | head,enter_roundabout-4,destination | From 1802839a224d1d2aae4141e03e70898609704728 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Tue, 20 May 2014 16:23:47 +0200 Subject: [PATCH 28/36] add approximator for perpendicular distance --- DataStructures/Coordinate.cpp | 135 ++++++++++++++++++++++++++++++++++ Include/osrm/Coordinate.h | 17 +++++ 2 files changed, 152 insertions(+) diff --git a/DataStructures/Coordinate.cpp b/DataStructures/Coordinate.cpp index da5b8f58658..5c5544f0cfe 100644 --- a/DataStructures/Coordinate.cpp +++ b/DataStructures/Coordinate.cpp @@ -385,6 +385,141 @@ double FixedPointCoordinate::RadianToDegree(const double radian) return radian * (180. / M_PI); } + +double +FixedPointCoordinate::ApproximatePerpendicularDistance(const FixedPointCoordinate &coord_a, + const FixedPointCoordinate &coord_b, + const FixedPointCoordinate &query_location, + FixedPointCoordinate &nearest_location, + double &ratio, + double precision) const +{ + BOOST_ASSERT(query_location.isValid()); + + const double epsilon = 1.0 / precision; + + // p, q : the end points of the underlying edge + const Point p(lat2y(coord_a.lat / COORDINATE_PRECISION), coord_a.lon / COORDINATE_PRECISION); + const Point q(lat2y(coord_b.lat / COORDINATE_PRECISION), coord_b.lon / COORDINATE_PRECISION); + + // r : query location + const Point r(lat2y(query_location.lat / COORDINATE_PRECISION), + query_location.lon / COORDINATE_PRECISION); + + const Point foot = ComputePerpendicularFoot(p, q, r, epsilon); + ratio = ComputeRatio(p, q, foot, epsilon); + + BOOST_ASSERT(!std::isnan(ratio)); + + nearest_location = ComputeNearestPointOnSegment(coord_a, coord_b, foot, ratio); + + BOOST_ASSERT(nearest_location.isValid()); + + // TODO: Replace with euclidean approximation when k-NN search is done + // const double approximated_distance = FixedPointCoordinate::ApproximateEuclideanDistance( + const double approximated_distance = + FixedPointCoordinate::ApproximateEuclideanDistance(query_location, nearest_location); + + BOOST_ASSERT(0.0 <= approximated_distance); + return approximated_distance; +} + +// Compute the perpendicular foot of point r on the line defined by p and q. +Point FixedPointCoordinate::ComputePerpendicularFoot(const Point &p, + const Point &q, + const Point &r, + double epsilon) const +{ + + // the projection of r onto the line pq + double foot_x, foot_y; + + const bool is_parallel_to_y_axis = std::abs(q.first - p.first) < epsilon; + + if (is_parallel_to_y_axis) + { + foot_x = q.first; + foot_y = r.second; + } + else + { + // the slope of the line through (a|b) and (c|d) + const double m = (q.second - p.second) / (q.first - p.first); + + // Projection of (x|y) onto the line joining (a|b) and (c|d). + foot_x = ((r.first + (m * r.second)) + (m * m * p.first - m * p.second)) / (1.0 + m * m); + foot_y = p.second + m * (foot_x - p.first); + } + + return Point(foot_x, foot_y); +} + +// Compute the ratio of the line segment pr to line segment pq. +double FixedPointCoordinate::ComputeRatio(const Point &p, + const Point &q, + const Point &r, + double epsilon) const +{ + + const bool is_parallel_to_x_axis = std::abs(q.second - p.second) < epsilon; + const bool is_parallel_to_y_axis = std::abs(q.first - p.first) < epsilon; + + double ratio; + + if (!is_parallel_to_y_axis) + { + ratio = (r.first - p.first) / (q.first - p.first); + } + else if (!is_parallel_to_x_axis) + { + ratio = (r.second - p.second) / (q.second - p.second); + } + else + { + // (a|b) and (c|d) are essentially the same point + // by convention, we set the ratio to 0 in this case + // ratio = ((lat2 == query_location.lat) && (lon2 == query_location.lon)) ? 1. : 0.; + ratio = 0.0; + } + + // Round to integer if the ratio is close to 0 or 1. + if (std::abs(ratio) <= epsilon) + { + ratio = 0.0; + } + else if (std::abs(ratio - 1.0) <= epsilon) + { + ratio = 1.0; + } + + return ratio; +} + +// Computes the point on the segment pq which is nearest to a point r = p + lambda * (q-p). +// p and q are the end points of the underlying edge. +FixedPointCoordinate FixedPointCoordinate::ComputeNearestPointOnSegment(const FixedPointCoordinate &coord_a, + const FixedPointCoordinate &coord_b, + const Point &r, + double lambda) const +{ + + if (lambda <= 0.0) + { + return coord_a; + } + else if (lambda >= 1.0) + { + return coord_b; + } + else + { + // r lies between p and q + return FixedPointCoordinate(y2lat(r.first) * COORDINATE_PRECISION, + r.second * COORDINATE_PRECISION); + } +} + + // double PointSegmentDistanceSquared( double px, double py, // double p1x, double p1y, // double p2x, double p2y, diff --git a/Include/osrm/Coordinate.h b/Include/osrm/Coordinate.h index d36cd05bc3f..3ffff900958 100644 --- a/Include/osrm/Coordinate.h +++ b/Include/osrm/Coordinate.h @@ -28,9 +28,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef FIXED_POINT_COORDINATE_H_ #define FIXED_POINT_COORDINATE_H_ +#include #include //for std::ostream constexpr double COORDINATE_PRECISION = 1000000.; +typedef std::pair Point; struct FixedPointCoordinate { @@ -82,6 +84,21 @@ struct FixedPointCoordinate static double DegreeToRadian(const double degree); static double RadianToDegree(const double radian); + + + Point ComputePerpendicularFoot(const Point &p, const Point &q, const Point &r, double epsilon) const; + double ComputeRatio(const Point & p, const Point & q, const Point & r, double epsilon) const ; + FixedPointCoordinate ComputeNearestPointOnSegment(const FixedPointCoordinate &coord_a, + const FixedPointCoordinate &coord_b, + const Point & r, double lambda) const; + double ApproximatePerpendicularDistance(const FixedPointCoordinate &coord_a, + const FixedPointCoordinate &coord_b, + const FixedPointCoordinate &query_location, + FixedPointCoordinate & nearest_location, + double & ratio, + double precision = COORDINATE_PRECISION + ) const; + }; inline std::ostream &operator<<(std::ostream &o, FixedPointCoordinate const &c) From 4aa7420d6af19ed4fbfa7b5c7276c7873c02de9c Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Tue, 20 May 2014 18:54:03 +0200 Subject: [PATCH 29/36] remove unneeded includes --- Plugins/DistanceTablePlugin.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/Plugins/DistanceTablePlugin.h b/Plugins/DistanceTablePlugin.h index c2f8ab19e82..c7293476b0b 100644 --- a/Plugins/DistanceTablePlugin.h +++ b/Plugins/DistanceTablePlugin.h @@ -35,8 +35,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "../DataStructures/QueryEdge.h" #include "../DataStructures/SearchEngine.h" #include "../Descriptors/BaseDescriptor.h" -#include "../Descriptors/GPXDescriptor.h" -#include "../Descriptors/JSONDescriptor.h" #include "../Util/SimpleLogger.h" #include "../Util/StringUtil.h" #include "../Util/TimingUtil.h" From 812cf36d52f5080b5fdcb9d6990bf9a01266b412 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Tue, 20 May 2014 19:29:09 +0200 Subject: [PATCH 30/36] use floats instead of doubles for distance computations --- DataStructures/Coordinate.cpp | 165 +++------------------------------- Include/osrm/Coordinate.h | 19 +--- 2 files changed, 16 insertions(+), 168 deletions(-) diff --git a/DataStructures/Coordinate.cpp b/DataStructures/Coordinate.cpp index 5c5544f0cfe..6938aa723b0 100644 --- a/DataStructures/Coordinate.cpp +++ b/DataStructures/Coordinate.cpp @@ -120,13 +120,13 @@ double FixedPointCoordinate::ApproximateDistance(const FixedPointCoordinate &c1, return ApproximateDistance(c1.lat, c1.lon, c2.lat, c2.lon); } -double FixedPointCoordinate::ApproximateEuclideanDistance(const FixedPointCoordinate &c1, +float FixedPointCoordinate::ApproximateEuclideanDistance(const FixedPointCoordinate &c1, const FixedPointCoordinate &c2) { return ApproximateEuclideanDistance(c1.lat, c1.lon, c2.lat, c2.lon); } -double FixedPointCoordinate::ApproximateEuclideanDistance(const int lat1, +float FixedPointCoordinate::ApproximateEuclideanDistance(const int lat1, const int lon1, const int lat2, const int lon2) @@ -136,15 +136,15 @@ double FixedPointCoordinate::ApproximateEuclideanDistance(const int lat1, BOOST_ASSERT(lat2 != std::numeric_limits::min()); BOOST_ASSERT(lon2 != std::numeric_limits::min()); - const double RAD = 0.017453292519943295769236907684886; - const double float_lat1 = (lat1 / COORDINATE_PRECISION) * RAD; - const double float_lon1 = (lon1 / COORDINATE_PRECISION) * RAD; - const double float_lat2 = (lat2 / COORDINATE_PRECISION) * RAD; - const double float_lon2 = (lon2 / COORDINATE_PRECISION) * RAD; + const float RAD = 0.017453292519943295769236907684886; + const float float_lat1 = (lat1 / COORDINATE_PRECISION) * RAD; + const float float_lon1 = (lon1 / COORDINATE_PRECISION) * RAD; + const float float_lat2 = (lat2 / COORDINATE_PRECISION) * RAD; + const float float_lon2 = (lon2 / COORDINATE_PRECISION) * RAD; - const double x = (float_lon2 - float_lon1) * cos((float_lat1 + float_lat2) / 2.); - const double y = (float_lat2 - float_lat1); - const double earth_radius = 6372797.560856; + const float x = (float_lon2 - float_lon1) * cos((float_lat1 + float_lat2) / 2.); + const float y = (float_lat2 - float_lat1); + const float earth_radius = 6372797.560856; return sqrt(x * x + y * y) * earth_radius; } @@ -211,7 +211,7 @@ double FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordi } BOOST_ASSERT(nearest_location.isValid()); const double approximated_distance = - FixedPointCoordinate::ApproximateDistance(point, nearest_location); + FixedPointCoordinate::ApproximateEuclideanDistance(point, nearest_location); BOOST_ASSERT(0. <= approximated_distance); return approximated_distance; } @@ -269,13 +269,11 @@ double FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordi BOOST_ASSERT(!std::isnan(r)); if (r <= 0.) { - nearest_location.lat = coord_a.lat; - nearest_location.lon = coord_a.lon; + nearest_location = coord_a; } else if (r >= 1.) { - nearest_location.lat = coord_b.lat; - nearest_location.lon = coord_b.lon; + nearest_location = coord_b; } else { @@ -288,7 +286,7 @@ double FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordi // TODO: Replace with euclidean approximation when k-NN search is done // const double approximated_distance = FixedPointCoordinate::ApproximateEuclideanDistance( const double approximated_distance = - FixedPointCoordinate::ApproximateDistance(query_location, nearest_location); + FixedPointCoordinate::ApproximateEuclideanDistance(query_location, nearest_location); BOOST_ASSERT(0. <= approximated_distance); return approximated_distance; } @@ -385,141 +383,6 @@ double FixedPointCoordinate::RadianToDegree(const double radian) return radian * (180. / M_PI); } - -double -FixedPointCoordinate::ApproximatePerpendicularDistance(const FixedPointCoordinate &coord_a, - const FixedPointCoordinate &coord_b, - const FixedPointCoordinate &query_location, - FixedPointCoordinate &nearest_location, - double &ratio, - double precision) const -{ - BOOST_ASSERT(query_location.isValid()); - - const double epsilon = 1.0 / precision; - - // p, q : the end points of the underlying edge - const Point p(lat2y(coord_a.lat / COORDINATE_PRECISION), coord_a.lon / COORDINATE_PRECISION); - const Point q(lat2y(coord_b.lat / COORDINATE_PRECISION), coord_b.lon / COORDINATE_PRECISION); - - // r : query location - const Point r(lat2y(query_location.lat / COORDINATE_PRECISION), - query_location.lon / COORDINATE_PRECISION); - - const Point foot = ComputePerpendicularFoot(p, q, r, epsilon); - ratio = ComputeRatio(p, q, foot, epsilon); - - BOOST_ASSERT(!std::isnan(ratio)); - - nearest_location = ComputeNearestPointOnSegment(coord_a, coord_b, foot, ratio); - - BOOST_ASSERT(nearest_location.isValid()); - - // TODO: Replace with euclidean approximation when k-NN search is done - // const double approximated_distance = FixedPointCoordinate::ApproximateEuclideanDistance( - const double approximated_distance = - FixedPointCoordinate::ApproximateEuclideanDistance(query_location, nearest_location); - - BOOST_ASSERT(0.0 <= approximated_distance); - return approximated_distance; -} - -// Compute the perpendicular foot of point r on the line defined by p and q. -Point FixedPointCoordinate::ComputePerpendicularFoot(const Point &p, - const Point &q, - const Point &r, - double epsilon) const -{ - - // the projection of r onto the line pq - double foot_x, foot_y; - - const bool is_parallel_to_y_axis = std::abs(q.first - p.first) < epsilon; - - if (is_parallel_to_y_axis) - { - foot_x = q.first; - foot_y = r.second; - } - else - { - // the slope of the line through (a|b) and (c|d) - const double m = (q.second - p.second) / (q.first - p.first); - - // Projection of (x|y) onto the line joining (a|b) and (c|d). - foot_x = ((r.first + (m * r.second)) + (m * m * p.first - m * p.second)) / (1.0 + m * m); - foot_y = p.second + m * (foot_x - p.first); - } - - return Point(foot_x, foot_y); -} - -// Compute the ratio of the line segment pr to line segment pq. -double FixedPointCoordinate::ComputeRatio(const Point &p, - const Point &q, - const Point &r, - double epsilon) const -{ - - const bool is_parallel_to_x_axis = std::abs(q.second - p.second) < epsilon; - const bool is_parallel_to_y_axis = std::abs(q.first - p.first) < epsilon; - - double ratio; - - if (!is_parallel_to_y_axis) - { - ratio = (r.first - p.first) / (q.first - p.first); - } - else if (!is_parallel_to_x_axis) - { - ratio = (r.second - p.second) / (q.second - p.second); - } - else - { - // (a|b) and (c|d) are essentially the same point - // by convention, we set the ratio to 0 in this case - // ratio = ((lat2 == query_location.lat) && (lon2 == query_location.lon)) ? 1. : 0.; - ratio = 0.0; - } - - // Round to integer if the ratio is close to 0 or 1. - if (std::abs(ratio) <= epsilon) - { - ratio = 0.0; - } - else if (std::abs(ratio - 1.0) <= epsilon) - { - ratio = 1.0; - } - - return ratio; -} - -// Computes the point on the segment pq which is nearest to a point r = p + lambda * (q-p). -// p and q are the end points of the underlying edge. -FixedPointCoordinate FixedPointCoordinate::ComputeNearestPointOnSegment(const FixedPointCoordinate &coord_a, - const FixedPointCoordinate &coord_b, - const Point &r, - double lambda) const -{ - - if (lambda <= 0.0) - { - return coord_a; - } - else if (lambda >= 1.0) - { - return coord_b; - } - else - { - // r lies between p and q - return FixedPointCoordinate(y2lat(r.first) * COORDINATE_PRECISION, - r.second * COORDINATE_PRECISION); - } -} - - // double PointSegmentDistanceSquared( double px, double py, // double p1x, double p1y, // double p2x, double p2y, diff --git a/Include/osrm/Coordinate.h b/Include/osrm/Coordinate.h index 3ffff900958..ebdb2a7b0cf 100644 --- a/Include/osrm/Coordinate.h +++ b/Include/osrm/Coordinate.h @@ -49,10 +49,10 @@ struct FixedPointCoordinate static double ApproximateDistance(const int lat1, const int lon1, const int lat2, const int lon2); - static double ApproximateDistance(const FixedPointCoordinate &c1, + static float ApproximateDistance(const FixedPointCoordinate &c1, const FixedPointCoordinate &c2); - static double ApproximateEuclideanDistance(const FixedPointCoordinate &c1, + static float ApproximateEuclideanDistance(const FixedPointCoordinate &c1, const FixedPointCoordinate &c2); static double @@ -84,21 +84,6 @@ struct FixedPointCoordinate static double DegreeToRadian(const double degree); static double RadianToDegree(const double radian); - - - Point ComputePerpendicularFoot(const Point &p, const Point &q, const Point &r, double epsilon) const; - double ComputeRatio(const Point & p, const Point & q, const Point & r, double epsilon) const ; - FixedPointCoordinate ComputeNearestPointOnSegment(const FixedPointCoordinate &coord_a, - const FixedPointCoordinate &coord_b, - const Point & r, double lambda) const; - double ApproximatePerpendicularDistance(const FixedPointCoordinate &coord_a, - const FixedPointCoordinate &coord_b, - const FixedPointCoordinate &query_location, - FixedPointCoordinate & nearest_location, - double & ratio, - double precision = COORDINATE_PRECISION - ) const; - }; inline std::ostream &operator<<(std::ostream &o, FixedPointCoordinate const &c) From 9117b45899b569182b87eee5eec9921d91ac3323 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Wed, 21 May 2014 10:03:30 +0200 Subject: [PATCH 31/36] move more distance calculations to float --- DataStructures/StaticRTree.h | 4 ++-- Include/osrm/Coordinate.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DataStructures/StaticRTree.h b/DataStructures/StaticRTree.h index f30d5328925..b3135f0c1f2 100644 --- a/DataStructures/StaticRTree.h +++ b/DataStructures/StaticRTree.h @@ -152,9 +152,9 @@ class StaticRTree return min_dist; } - inline double GetMinMaxDist(const FixedPointCoordinate &location) const + inline float GetMinMaxDist(const FixedPointCoordinate &location) const { - double min_max_dist = std::numeric_limits::max(); + float min_max_dist = std::numeric_limits::max(); // Get minmax distance to each of the four sides FixedPointCoordinate upper_left(max_lat, min_lon); FixedPointCoordinate upper_right(max_lat, max_lon); diff --git a/Include/osrm/Coordinate.h b/Include/osrm/Coordinate.h index ebdb2a7b0cf..b1823f9e2e2 100644 --- a/Include/osrm/Coordinate.h +++ b/Include/osrm/Coordinate.h @@ -49,13 +49,13 @@ struct FixedPointCoordinate static double ApproximateDistance(const int lat1, const int lon1, const int lat2, const int lon2); - static float ApproximateDistance(const FixedPointCoordinate &c1, + static double ApproximateDistance(const FixedPointCoordinate &c1, const FixedPointCoordinate &c2); static float ApproximateEuclideanDistance(const FixedPointCoordinate &c1, const FixedPointCoordinate &c2); - static double + static float ApproximateEuclideanDistance(const int lat1, const int lon1, const int lat2, const int lon2); static void convertInternalLatLonToString(const int value, std::string &output); From 6a9541833aff164f122fccbb3e1813e73f137dc4 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Wed, 21 May 2014 10:47:34 +0200 Subject: [PATCH 32/36] add a leg to roundabout to remove edge case --- features/testbot/oneway.feature | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/features/testbot/oneway.feature b/features/testbot/oneway.feature index 42f50acdab2..c21d8b1cb6c 100644 --- a/features/testbot/oneway.feature +++ b/features/testbot/oneway.feature @@ -6,10 +6,11 @@ Feature: Testbot - oneways Scenario: Routing on a oneway roundabout Given the node map - | | d | c | | - | e | | | b | - | f | | | a | - | | g | h | | + | x | | | v | | | + | | | d | c | | | + | | e | | | b | | + | | f | | | a | | + | | | g | h | | | And the ways | nodes | oneway | @@ -21,6 +22,7 @@ Feature: Testbot - oneways | fg | yes | | gh | yes | | ha | yes | + | vx | yes | When I route I should get | from | to | route | @@ -39,4 +41,4 @@ Feature: Testbot - oneways | f | e | fg,gh,ha,ab,bc,cd,de | | g | f | gh,ha,ab,bc,cd,de,ef | | h | g | ha,ab,bc,cd,de,ef,fg | - | a | h | ab,bc,cd,de,ef,fg,gh | \ No newline at end of file + | a | h | ab,bc,cd,de,ef,fg,gh | From c2a750a690507585d7d4e62f685baa5999f60c87 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Wed, 21 May 2014 12:29:45 +0200 Subject: [PATCH 33/36] use 100 locations at max for dist table --- Plugins/DistanceTablePlugin.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Plugins/DistanceTablePlugin.h b/Plugins/DistanceTablePlugin.h index c7293476b0b..908745e5cb8 100644 --- a/Plugins/DistanceTablePlugin.h +++ b/Plugins/DistanceTablePlugin.h @@ -64,8 +64,6 @@ template class DistanceTablePlugin : public BasePlugin void HandleRequest(const RouteParameters &route_parameters, http::Reply &reply) { - SimpleLogger().Write() << "running DT plugin"; - // check number of parameters if (2 > route_parameters.coordinates.size()) { @@ -81,8 +79,6 @@ template class DistanceTablePlugin : public BasePlugin [&](FixedPointCoordinate coordinate) { return !coordinate.isValid(); })) { - SimpleLogger().Write() << "invalid coordinate"; - reply = http::Reply::StockReply(http::Reply::badRequest); return; } @@ -90,13 +86,12 @@ template class DistanceTablePlugin : public BasePlugin for (const FixedPointCoordinate &coordinate : route_parameters.coordinates) { raw_route.raw_via_node_coordinates.emplace_back(coordinate); - SimpleLogger().Write() << "adding coordinate " << coordinate; } std::vector phantom_node_vector(raw_route.raw_via_node_coordinates.size()); const bool checksum_OK = (route_parameters.check_sum == raw_route.check_sum); - unsigned max_locations = std::min((std::size_t)25, raw_route.raw_via_node_coordinates.size()); + unsigned max_locations = std::min((std::size_t)100, raw_route.raw_via_node_coordinates.size()); for (unsigned i = 0; i < max_locations; ++i) { if (checksum_OK && i < route_parameters.hints.size() && From a8ff3231a8d81b0e23dd35aadbc0414d31cf6c7e Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Wed, 21 May 2014 12:33:14 +0200 Subject: [PATCH 34/36] reduce debug verbosity --- Plugins/DistanceTablePlugin.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Plugins/DistanceTablePlugin.h b/Plugins/DistanceTablePlugin.h index 908745e5cb8..c4df393def5 100644 --- a/Plugins/DistanceTablePlugin.h +++ b/Plugins/DistanceTablePlugin.h @@ -100,11 +100,9 @@ template class DistanceTablePlugin : public BasePlugin DecodeObjectFromBase64(route_parameters.hints[i], phantom_node_vector[i]); if (phantom_node_vector[i].isValid(facade->GetNumberOfNodes())) { - SimpleLogger().Write() << "decoded phantom node for " << phantom_node_vector[i].location; continue; } } - SimpleLogger().Write() << "looking up coordinate in tree"; facade->FindPhantomNodeForCoordinate(raw_route.raw_via_node_coordinates[i], phantom_node_vector[i], route_parameters.zoom_level); @@ -114,15 +112,12 @@ template class DistanceTablePlugin : public BasePlugin TIMER_START(distance_table); std::shared_ptr> result_table = search_engine_ptr->distance_table(phantom_node_vector); TIMER_STOP(distance_table); - SimpleLogger().Write() << "table computation took " << TIMER_MSEC(distance_table) << "ms"; if (!result_table) { - SimpleLogger().Write() << "computation failed"; reply = http::Reply::StockReply(http::Reply::badRequest); return; } - SimpleLogger().Write() << "computation successful"; JSON::Object json_object; JSON::Array json_array; const unsigned number_of_locations = phantom_node_vector.size(); From 493b13364f8b89f08d805ac783ddf1328bc2c880 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Wed, 21 May 2014 12:33:54 +0200 Subject: [PATCH 35/36] move geographical distance computation to floats --- DataStructures/Coordinate.cpp | 204 ++++++++-------------------- DataStructures/StaticRTree.h | 55 ++++---- Include/osrm/Coordinate.h | 25 ++-- Util/MercatorUtil.h | 10 +- features/testbot/projection.feature | 2 +- 5 files changed, 104 insertions(+), 192 deletions(-) diff --git a/DataStructures/Coordinate.cpp b/DataStructures/Coordinate.cpp index 6938aa723b0..3e99f8ec84d 100644 --- a/DataStructures/Coordinate.cpp +++ b/DataStructures/Coordinate.cpp @@ -148,20 +148,20 @@ float FixedPointCoordinate::ApproximateEuclideanDistance(const int lat1, return sqrt(x * x + y * y) * earth_radius; } -double FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordinate &point, +float FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordinate &point, const FixedPointCoordinate &segA, const FixedPointCoordinate &segB) { - const double x = lat2y(point.lat / COORDINATE_PRECISION); - const double y = point.lon / COORDINATE_PRECISION; - const double a = lat2y(segA.lat / COORDINATE_PRECISION); - const double b = segA.lon / COORDINATE_PRECISION; - const double c = lat2y(segB.lat / COORDINATE_PRECISION); - const double d = segB.lon / COORDINATE_PRECISION; - double p, q, nY; - if (std::abs(a - c) > std::numeric_limits::epsilon()) + const float x = lat2y(point.lat / COORDINATE_PRECISION); + const float y = point.lon / COORDINATE_PRECISION; + const float a = lat2y(segA.lat / COORDINATE_PRECISION); + const float b = segA.lon / COORDINATE_PRECISION; + const float c = lat2y(segB.lat / COORDINATE_PRECISION); + const float d = segB.lon / COORDINATE_PRECISION; + float p, q, nY; + if (std::abs(a - c) > std::numeric_limits::epsilon()) { - const double m = (d - b) / (c - a); // slope + const float m = (d - b) / (c - a); // slope // Projection of (x,y) on line joining (a,b) and (c,d) p = ((x + (m * y)) + (m * m * a - m * b)) / (1. + m * m); q = b + m * (p - a); @@ -179,16 +179,16 @@ double FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordi nY = 0.; } - double r = (p - nY * a) / c; + float r = (p - nY * a) / c; if (std::isnan(r)) { r = ((segB.lat == point.lat) && (segB.lon == point.lon)) ? 1. : 0.; } - else if (std::abs(r) <= std::numeric_limits::epsilon()) + else if (std::abs(r) <= std::numeric_limits::epsilon()) { r = 0.; } - else if (std::abs(r - 1.) <= std::numeric_limits::epsilon()) + else if (std::abs(r - 1.) <= std::numeric_limits::epsilon()) { r = 1.; } @@ -210,30 +210,30 @@ double FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordi nearest_location.lon = q * COORDINATE_PRECISION; } BOOST_ASSERT(nearest_location.isValid()); - const double approximated_distance = + const float approximated_distance = FixedPointCoordinate::ApproximateEuclideanDistance(point, nearest_location); BOOST_ASSERT(0. <= approximated_distance); return approximated_distance; } -double FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordinate &coord_a, +float FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordinate &coord_a, const FixedPointCoordinate &coord_b, const FixedPointCoordinate &query_location, FixedPointCoordinate &nearest_location, - double &r) + float &r) { BOOST_ASSERT(query_location.isValid()); - const double x = lat2y(query_location.lat / COORDINATE_PRECISION); - const double y = query_location.lon / COORDINATE_PRECISION; - const double a = lat2y(coord_a.lat / COORDINATE_PRECISION); - const double b = coord_a.lon / COORDINATE_PRECISION; - const double c = lat2y(coord_b.lat / COORDINATE_PRECISION); - const double d = coord_b.lon / COORDINATE_PRECISION; - double p, q /*,mX*/, nY; - if (std::abs(a - c) > std::numeric_limits::epsilon()) + const float x = lat2y(query_location.lat / COORDINATE_PRECISION); + const float y = query_location.lon / COORDINATE_PRECISION; + const float a = lat2y(coord_a.lat / COORDINATE_PRECISION); + const float b = coord_a.lon / COORDINATE_PRECISION; + const float c = lat2y(coord_b.lat / COORDINATE_PRECISION); + const float d = coord_b.lon / COORDINATE_PRECISION; + float p, q /*,mX*/, nY; + if (std::abs(a - c) > std::numeric_limits::epsilon()) { - const double m = (d - b) / (c - a); // slope + const float m = (d - b) / (c - a); // slope // Projection of (x,y) on line joining (a,b) and (c,d) p = ((x + (m * y)) + (m * m * a - m * b)) / (1. + m * m); q = b + m * (p - a); @@ -258,11 +258,11 @@ double FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordi { r = ((coord_b.lat == query_location.lat) && (coord_b.lon == query_location.lon)) ? 1. : 0.; } - else if (std::abs(r) <= std::numeric_limits::epsilon()) + else if (std::abs(r) <= std::numeric_limits::epsilon()) { r = 0.; } - else if (std::abs(r - 1.) <= std::numeric_limits::epsilon()) + else if (std::abs(r - 1.) <= std::numeric_limits::epsilon()) { r = 1.; } @@ -284,8 +284,8 @@ double FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordi BOOST_ASSERT(nearest_location.isValid()); // TODO: Replace with euclidean approximation when k-NN search is done - // const double approximated_distance = FixedPointCoordinate::ApproximateEuclideanDistance( - const double approximated_distance = + // const float approximated_distance = FixedPointCoordinate::ApproximateEuclideanDistance( + const float approximated_distance = FixedPointCoordinate::ApproximateEuclideanDistance(query_location, nearest_location); BOOST_ASSERT(0. <= approximated_distance); return approximated_distance; @@ -328,145 +328,53 @@ void FixedPointCoordinate::Output(std::ostream &out) const out << "(" << lat / COORDINATE_PRECISION << "," << lon / COORDINATE_PRECISION << ")"; } -double FixedPointCoordinate::GetBearing(const FixedPointCoordinate &A, const FixedPointCoordinate &B) +float FixedPointCoordinate::GetBearing(const FixedPointCoordinate &A, const FixedPointCoordinate &B) { - double delta_long = DegreeToRadian(B.lon / COORDINATE_PRECISION - A.lon / COORDINATE_PRECISION); - - const double lat1 = DegreeToRadian(A.lat / COORDINATE_PRECISION); - const double lat2 = DegreeToRadian(B.lat / COORDINATE_PRECISION); - - const double y = sin(delta_long) * cos(lat2); - const double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(delta_long); - double result = RadianToDegree(atan2_lookup(y, x)); - while (result < 0.) + const float delta_long = DegreeToRadian(B.lon / COORDINATE_PRECISION - A.lon / COORDINATE_PRECISION); + const float lat1 = DegreeToRadian(A.lat / COORDINATE_PRECISION); + const float lat2 = DegreeToRadian(B.lat / COORDINATE_PRECISION); + const float y = sin(delta_long) * cos(lat2); + const float x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(delta_long); + float result = RadianToDegree(std::atan2(y, x)); + while (result < 0.f) { - result += 360.; + result += 360.f; } - while (result >= 360.) + while (result >= 360.f) { - result -= 360.; + result -= 360.f; } return result; } -double FixedPointCoordinate::GetBearing(const FixedPointCoordinate &other) const +float FixedPointCoordinate::GetBearing(const FixedPointCoordinate &other) const { - double delta_long = DegreeToRadian(lon / COORDINATE_PRECISION - other.lon / COORDINATE_PRECISION); - - const double lat1 = DegreeToRadian(other.lat / COORDINATE_PRECISION); - const double lat2 = DegreeToRadian(lat / COORDINATE_PRECISION); - - const double y = std::sin(delta_long) * std::cos(lat2); - const double x = std::cos(lat1) * std::sin(lat2) - std::sin(lat1) * std::cos(lat2) * std::cos(delta_long); - double result = RadianToDegree(atan2_lookup(y, x)); - - while (result < 0.) + const float delta_long = DegreeToRadian(lon / COORDINATE_PRECISION - other.lon / COORDINATE_PRECISION); + const float lat1 = DegreeToRadian(other.lat / COORDINATE_PRECISION); + const float lat2 = DegreeToRadian(lat / COORDINATE_PRECISION); + const float y = std::sin(delta_long) * std::cos(lat2); + const float x = std::cos(lat1) * std::sin(lat2) - std::sin(lat1) * std::cos(lat2) * std::cos(delta_long); + float result = RadianToDegree(std::atan2(y, x)); + + while (result < 0.f) { - result += 360.; + result += 360.f; } - while (result >= 360.) + while (result >= 360.f) { - result -= 360.; + result -= 360.f; } return result; } -double FixedPointCoordinate::DegreeToRadian(const double degree) +float FixedPointCoordinate::DegreeToRadian(const float degree) { - return degree * (M_PI / 180.); + return degree * (M_PI / 180.f); } -double FixedPointCoordinate::RadianToDegree(const double radian) +float FixedPointCoordinate::RadianToDegree(const float radian) { - return radian * (180. / M_PI); + return radian * (180.f / M_PI); } - -// double PointSegmentDistanceSquared( double px, double py, -// double p1x, double p1y, -// double p2x, double p2y, -// double& t, -// double& qx, double& qy) -// { -// static const double kMinSegmentLenSquared = 0.00000001; // adjust to suit. If you use float, you'll probably want something like 0.000001f -// static const double kEpsilon = 1.0E-14; // adjust to suit. If you use floats, you'll probably want something like 1E-7f -// double dx = p2x - p1x; -// double dy = p2y - p1y; -// double dp1x = px - p1x; -// double dp1y = py - p1y; -// const double segLenSquared = (dx * dx) + (dy * dy); -// if (segLenSquared >= -kMinSegmentLenSquared && segLenSquared <= kMinSegmentLenSquared) -// { -// // segment is a point. -// qx = p1x; -// qy = p1y; -// t = 0.0; -// return ((dp1x * dp1x) + (dp1y * dp1y)); -// } -// else -// { -// // Project a line from p to the segment [p1,p2]. By considering the line -// // extending the segment, parameterized as p1 + (t * (p2 - p1)), -// // we find projection of point p onto the line. -// // It falls where t = [(p - p1) . (p2 - p1)] / |p2 - p1|^2 -// t = ((dp1x * dx) + (dp1y * dy)) / segLenSquared; -// if (t < kEpsilon) -// { -// // intersects at or to the "left" of first segment vertex (p1x, p1y). If t is approximately 0.0, then -// // intersection is at p1. If t is less than that, then there is no intersection (i.e. p is not within -// // the 'bounds' of the segment) -// if (t > -kEpsilon) -// { -// // intersects at 1st segment vertex -// t = 0.0; -// } -// // set our 'intersection' point to p1. -// qx = p1x; -// qy = p1y; -// // Note: If you wanted the ACTUAL intersection point of where the projected lines would intersect if -// // we were doing PointLineDistanceSquared, then qx would be (p1x + (t * dx)) and qy would be (p1y + (t * dy)). -// } -// else if (t > (1.0 - kEpsilon)) -// { -// // intersects at or to the "right" of second segment vertex (p2x, p2y). If t is approximately 1.0, then -// // intersection is at p2. If t is greater than that, then there is no intersection (i.e. p is not within -// // the 'bounds' of the segment) -// if (t < (1.0 + kEpsilon)) -// { -// // intersects at 2nd segment vertex -// t = 1.0; -// } -// // set our 'intersection' point to p2. -// qx = p2x; -// qy = p2y; -// // Note: If you wanted the ACTUAL intersection point of where the projected lines would intersect if -// // we were doing PointLineDistanceSquared, then qx would be (p1x + (t * dx)) and qy would be (p1y + (t * dy)). -// } -// else -// { -// // The projection of the point to the point on the segment that is perpendicular succeeded and the point -// // is 'within' the bounds of the segment. Set the intersection point as that projected point. -// qx = p1x + (t * dx); -// qy = p1y + (t * dy); -// } -// // return the squared distance from p to the intersection point. Note that we return the squared distance -// // as an optimization because many times you just need to compare relative distances and the squared values -// // works fine for that. If you want the ACTUAL distance, just take the square root of this value. -// double dpqx = px - qx; -// double dpqy = py - qy; -// return ((dpqx * dpqx) + (dpqy * dpqy)); -// } -// } - -// public float DistanceOfPointToLine2(PointF p1, PointF p2, PointF p) -// { -// // (y1-y2)x + (x2-x1)y + (x1y2-x2y1) -// //d(P,L) = -------------------------------- -// // sqrt( (x2-x1)pow2 + (y2-y1)pow2 ) - -// double ch = (p1.Y - p2.Y) * p.X + (p2.X - p1.X) * p.Y + (p1.X * p2.Y - p2.X * p1.Y); -// double del = Math.Sqrt(Math.Pow(p2.X - p1.X, 2) + Math.Pow(p2.Y - p1.Y, 2)); -// double d = ch / del; -// return (float)d; -// } diff --git a/DataStructures/StaticRTree.h b/DataStructures/StaticRTree.h index b3135f0c1f2..a1f11908d38 100644 --- a/DataStructures/StaticRTree.h +++ b/DataStructures/StaticRTree.h @@ -128,7 +128,7 @@ class StaticRTree Contains(lower_left)); } - inline double GetMinDist(const FixedPointCoordinate &location) const + inline float GetMinDist(const FixedPointCoordinate &location) const { bool is_contained = Contains(location); if (is_contained) @@ -136,7 +136,7 @@ class StaticRTree return 0.; } - double min_dist = std::numeric_limits::max(); + float min_dist = std::numeric_limits::max(); min_dist = std::min(min_dist, FixedPointCoordinate::ApproximateEuclideanDistance( location.lat, location.lon, max_lat, min_lon)); @@ -154,7 +154,7 @@ class StaticRTree inline float GetMinMaxDist(const FixedPointCoordinate &location) const { - float min_max_dist = std::numeric_limits::max(); + float min_max_dist = std::numeric_limits::max(); // Get minmax distance to each of the four sides FixedPointCoordinate upper_left(max_lat, min_lon); FixedPointCoordinate upper_right(max_lat, max_lon); @@ -240,13 +240,13 @@ class StaticRTree struct QueryCandidate { - explicit QueryCandidate(const uint32_t n_id, const double dist) + explicit QueryCandidate(const uint32_t n_id, const float dist) : node_id(n_id), min_dist(dist) { } - QueryCandidate() : node_id(UINT_MAX), min_dist(std::numeric_limits::max()) {} + QueryCandidate() : node_id(UINT_MAX), min_dist(std::numeric_limits::max()) {} uint32_t node_id; - double min_dist; + float min_dist; inline bool operator<(const QueryCandidate &other) const { return min_dist < other.min_dist; @@ -490,13 +490,13 @@ class StaticRTree { bool ignore_tiny_components = (zoom_level <= 14); DataT nearest_edge; - double min_dist = std::numeric_limits::max(); - double min_max_dist = std::numeric_limits::max(); + float min_dist = std::numeric_limits::max(); + float min_max_dist = std::numeric_limits::max(); bool found_a_nearest_edge = false; // initialize queue with root element std::priority_queue traversal_queue; - double current_min_dist = + float current_min_dist = m_search_tree[0].minimum_bounding_rectangle.GetMinDist(input_coordinate); traversal_queue.emplace(0, current_min_dist); @@ -522,7 +522,7 @@ class StaticRTree continue; } - double current_minimum_distance = + float current_minimum_distance = FixedPointCoordinate::ApproximateEuclideanDistance( input_coordinate.lat, input_coordinate.lon, @@ -563,9 +563,9 @@ class StaticRTree const TreeNode &child_tree_node = m_search_tree[child_id]; const RectangleT &child_rectangle = child_tree_node.minimum_bounding_rectangle; - const double current_min_dist = + const float current_min_dist = child_rectangle.GetMinDist(input_coordinate); - const double current_min_max_dist = + const float current_min_max_dist = child_rectangle.GetMinMaxDist(input_coordinate); if (current_min_max_dist < min_max_dist) { @@ -597,19 +597,19 @@ class StaticRTree const bool ignore_tiny_components = (zoom_level <= 14); DataT nearest_edge; - double min_dist = std::numeric_limits::max(); - double min_max_dist = std::numeric_limits::max(); + float min_dist = std::numeric_limits::max(); + float min_max_dist = std::numeric_limits::max(); bool found_a_nearest_edge = false; FixedPointCoordinate nearest, current_start_coordinate, current_end_coordinate; // initialize queue with root element std::priority_queue traversal_queue; - double current_min_dist = + float current_min_dist = m_search_tree[0].minimum_bounding_rectangle.GetMinDist(input_coordinate); traversal_queue.emplace(0, current_min_dist); - BOOST_ASSERT_MSG(std::numeric_limits::epsilon() > + BOOST_ASSERT_MSG(std::numeric_limits::epsilon() > (0. - traversal_queue.top().min_dist), "Root element in NN Search has min dist != 0."); @@ -635,8 +635,8 @@ class StaticRTree continue; } - double current_ratio = 0.; - const double current_perpendicular_distance = + float current_ratio = 0.; + const float current_perpendicular_distance = FixedPointCoordinate::ComputePerpendicularDistance( m_coordinate_list->at(current_edge.u), m_coordinate_list->at(current_edge.v), @@ -647,7 +647,7 @@ class StaticRTree BOOST_ASSERT(0. <= current_perpendicular_distance); if ((current_perpendicular_distance < min_dist) && - !DoubleEpsilonCompare(current_perpendicular_distance, min_dist)) + !EpsilonCompare(current_perpendicular_distance, min_dist)) { // found a new minimum min_dist = current_perpendicular_distance; // TODO: use assignment c'tor in PhantomNode @@ -685,9 +685,9 @@ class StaticRTree const int32_t child_id = current_tree_node.children[i]; TreeNode &child_tree_node = m_search_tree[child_id]; RectangleT &child_rectangle = child_tree_node.minimum_bounding_rectangle; - const double current_min_dist = + const float current_min_dist = child_rectangle.GetMinDist(input_coordinate); - const double current_min_max_dist = + const float current_min_max_dist = child_rectangle.GetMinMaxDist(input_coordinate); if (current_min_max_dist < min_max_dist) { @@ -717,18 +717,18 @@ class StaticRTree result_phantom_node.location.lat = input_coordinate.lat; } - double ratio = 0.; + float ratio = 0.f; if (found_a_nearest_edge) { - const double distance_1 = FixedPointCoordinate::ApproximateEuclideanDistance( + const float distance_1 = FixedPointCoordinate::ApproximateEuclideanDistance( current_start_coordinate, result_phantom_node.location); - const double distance_2 = FixedPointCoordinate::ApproximateEuclideanDistance( + const float distance_2 = FixedPointCoordinate::ApproximateEuclideanDistance( current_start_coordinate, current_end_coordinate); ratio = distance_1 / distance_2; - ratio = std::min(1., ratio); + ratio = std::min(1.f, ratio); if (SPECIAL_NODEID != result_phantom_node.forward_node_id) { @@ -768,9 +768,10 @@ class StaticRTree return (a == b && c == d) || (a == c && b == d) || (a == d && b == c); } - inline bool DoubleEpsilonCompare(const double d1, const double d2) const + template + inline bool EpsilonCompare(const FloatT d1, const FloatT d2) const { - return (std::abs(d1 - d2) < std::numeric_limits::epsilon()); + return (std::abs(d1 - d2) < std::numeric_limits::epsilon()); } }; diff --git a/Include/osrm/Coordinate.h b/Include/osrm/Coordinate.h index b1823f9e2e2..343133f1297 100644 --- a/Include/osrm/Coordinate.h +++ b/Include/osrm/Coordinate.h @@ -31,8 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include //for std::ostream -constexpr double COORDINATE_PRECISION = 1000000.; -typedef std::pair Point; +constexpr float COORDINATE_PRECISION = 1000000.; struct FixedPointCoordinate { @@ -53,10 +52,12 @@ struct FixedPointCoordinate const FixedPointCoordinate &c2); static float ApproximateEuclideanDistance(const FixedPointCoordinate &c1, - const FixedPointCoordinate &c2); + const FixedPointCoordinate &c2); - static float - ApproximateEuclideanDistance(const int lat1, const int lon1, const int lat2, const int lon2); + static float ApproximateEuclideanDistance(const int lat1, const int lon1, const int lat2, const int lon2); + + static float ApproximateSquaredEuclideanDistance(const FixedPointCoordinate &c1, + const FixedPointCoordinate &c2); static void convertInternalLatLonToString(const int value, std::string &output); @@ -66,24 +67,24 @@ struct FixedPointCoordinate static void convertInternalReversedCoordinateToString(const FixedPointCoordinate &coord, std::string &output); - static double ComputePerpendicularDistance(const FixedPointCoordinate &point, + static float ComputePerpendicularDistance(const FixedPointCoordinate &point, const FixedPointCoordinate &segA, const FixedPointCoordinate &segB); - static double ComputePerpendicularDistance(const FixedPointCoordinate &coord_a, + static float ComputePerpendicularDistance(const FixedPointCoordinate &coord_a, const FixedPointCoordinate &coord_b, const FixedPointCoordinate &query_location, FixedPointCoordinate &nearest_location, - double &r); + float &r); - static double GetBearing(const FixedPointCoordinate &A, const FixedPointCoordinate &B); + static float GetBearing(const FixedPointCoordinate &A, const FixedPointCoordinate &B); - double GetBearing(const FixedPointCoordinate &other) const; + float GetBearing(const FixedPointCoordinate &other) const; void Output(std::ostream &out) const; - static double DegreeToRadian(const double degree); - static double RadianToDegree(const double radian); + static float DegreeToRadian(const float degree); + static float RadianToDegree(const float radian); }; inline std::ostream &operator<<(std::ostream &o, FixedPointCoordinate const &c) diff --git a/Util/MercatorUtil.h b/Util/MercatorUtil.h index 5a350508779..9cae9309e44 100644 --- a/Util/MercatorUtil.h +++ b/Util/MercatorUtil.h @@ -28,16 +28,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef MERCATOR_UTIL_H #define MERCATOR_UTIL_H +#include "SimpleLogger.h" + #include -inline double y2lat(const double a) +inline float y2lat(const float a) { - return 180. / M_PI * (2. * atan(exp(a * M_PI / 180.)) - M_PI / 2.); + return 180.f / M_PI * (2.f * std::atan(std::exp(a * M_PI / 180.f)) - M_PI / 2.f); } -inline double lat2y(const double a) +inline float lat2y(const float a) { - return 180. / M_PI * log(tan(M_PI / 4. + a * (M_PI / 180.) / 2.)); + return 180.f / M_PI * log(tan(M_PI / 4.f + a * (M_PI / 180.f) / 2.f)); } #endif // MERCATOR_UTIL_H diff --git a/features/testbot/projection.feature b/features/testbot/projection.feature index 5b35f47bfd9..b66f03d5208 100644 --- a/features/testbot/projection.feature +++ b/features/testbot/projection.feature @@ -29,7 +29,7 @@ Feature: Projection to nearest point on road | a | d | abc | NE | 45 | 1000m +-7 | | d | a | abc | SW | 225 | 1000m +-7 | | c | d | abc | SW | 225 | 1000m +-7 | - | d | c | abc | NE | 45 +-1 | 1000m +-7 | + | d | c | abc | NE | 45 +-2 | 1000m +-7 | Scenario: Projection onto way at high latitudes, no distance When I route I should get From 35c9021bdfe5dda0a850456777780d767124ccad Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Wed, 21 May 2014 12:36:18 +0200 Subject: [PATCH 36/36] reduce debug verbosity in RestrictionMap --- DataStructures/RestrictionMap.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/DataStructures/RestrictionMap.cpp b/DataStructures/RestrictionMap.cpp index 49ae8ac3945..41e481c67f2 100644 --- a/DataStructures/RestrictionMap.cpp +++ b/DataStructures/RestrictionMap.cpp @@ -49,8 +49,6 @@ RestrictionMap::RestrictionMap(const std::shared_ptr &gra std::pair restriction_source = std::make_pair(restriction.fromNode, restriction.viaNode); - SimpleLogger().Write(logDEBUG) << "restr from: " << restriction.fromNode << ", v: " << restriction.viaNode << ", to: " << restriction.toNode << ", is_only_: " << (restriction.flags.isOnly ? "y" : "n"); - unsigned index; auto restriction_iter = m_restriction_map.find(restriction_source); if (restriction_iter == m_restriction_map.end()) @@ -114,7 +112,6 @@ void RestrictionMap::FixupArrivingTurnRestriction(const NodeID u, const NodeID v continue; } - SimpleLogger().Write(logDEBUG) << "fixing arriving instruction at turn <" << u << "," << v << "," << w << ">"; const unsigned index = restriction_iterator->second; auto &bucket = m_restriction_bucket_list.at(index); for (RestrictionTarget &restriction_target : bucket) @@ -141,7 +138,6 @@ void RestrictionMap::FixupStartingTurnRestriction(const NodeID u, const NodeID v auto restriction_iterator = m_restriction_map.find(old_start); if (restriction_iterator != m_restriction_map.end()) { - SimpleLogger().Write(logDEBUG) << "fixing restr start at turn <" << u << "," << v << "," << w << ">"; const unsigned index = restriction_iterator->second; // remove old restriction start (v,w) m_restriction_map.erase(restriction_iterator); @@ -207,6 +203,5 @@ bool RestrictionMap::CheckIfTurnIsRestricted(const NodeID u, const NodeID v, con } } } - return false; }