diff --git a/algorithms/bfs_components.hpp b/algorithms/bfs_components.hpp new file mode 100644 index 000000000..b48016af4 --- /dev/null +++ b/algorithms/bfs_components.hpp @@ -0,0 +1,174 @@ +/* + +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 BFS_COMPONENTS_HPP_ +#define BFS_COMPONENTS_HPP_ + +#include "../typedefs.h" +#include "../DataStructures/restriction_map.hpp" + +#include +#include + +// Explores the components of the given graph while respecting turn restrictions +// and barriers. +template class BFSComponentExplorer +{ + public: + BFSComponentExplorer(const GraphT &dynamic_graph, + const RestrictionMap &restrictions, + const std::unordered_set &barrier_nodes) + : m_graph(dynamic_graph), m_restriction_map(restrictions), m_barrier_nodes(barrier_nodes) + { + BOOST_ASSERT(m_graph.GetNumberOfNodes() > 0); + } + + /*! + * Returns the size of the component that the node belongs to. + */ + unsigned int GetComponentSize(const NodeID node) const + { + BOOST_ASSERT(node < m_component_index_list.size()); + + return m_component_index_size[m_component_index_list[node]]; + } + + unsigned int GetNumberOfComponents() { return m_component_index_size.size(); } + + /*! + * Computes the component sizes. + */ + void run() + { + std::queue> bfs_queue; + unsigned current_component = 0; + + BOOST_ASSERT(m_component_index_list.empty()); + BOOST_ASSERT(m_component_index_size.empty()); + + unsigned num_nodes = m_graph.GetNumberOfNodes(); + + m_component_index_list.resize(num_nodes, std::numeric_limits::max()); + + BOOST_ASSERT(num_nodes > 0); + + // put unexplorered node with parent pointer into queue + for (NodeID node = 0; node < num_nodes; ++node) + { + if (std::numeric_limits::max() == m_component_index_list[node]) + { + unsigned size = ExploreComponent(bfs_queue, node, current_component); + + // push size into vector + m_component_index_size.emplace_back(size); + ++current_component; + } + } + } + + private: + /*! + * Explores the current component that starts at node using BFS. + */ + unsigned ExploreComponent(std::queue> &bfs_queue, + NodeID node, + unsigned current_component) + { + /* + Graphical representation of variables: + + u v w + *---------->*---------->* + e2 + */ + + bfs_queue.emplace(node, node); + // mark node as read + m_component_index_list[node] = current_component; + + unsigned current_component_size = 1; + + while (!bfs_queue.empty()) + { + // fetch element from BFS queue + std::pair current_queue_item = bfs_queue.front(); + bfs_queue.pop(); + + const NodeID v = current_queue_item.first; // current node + const NodeID u = current_queue_item.second; // parent + // increment size counter of current component + ++current_component_size; + const bool is_barrier_node = (m_barrier_nodes.find(v) != m_barrier_nodes.end()); + if (!is_barrier_node) + { + const NodeID to_node_of_only_restriction = + m_restriction_map.CheckForEmanatingIsOnlyTurn(u, v); + + for (auto e2 : m_graph.GetAdjacentEdgeRange(v)) + { + const NodeID w = m_graph.GetTarget(e2); + + if (to_node_of_only_restriction != std::numeric_limits::max() && + w != to_node_of_only_restriction) + { + // At an only_-restriction but not at the right turn + continue; + } + + if (u != w) + { + // only add an edge if turn is not a U-turn except + // when it is at the end of a dead-end street. + if (!m_restriction_map.CheckIfTurnIsRestricted(u, v, w)) + { + // only add an edge if turn is not prohibited + if (std::numeric_limits::max() == m_component_index_list[w]) + { + // insert next (node, parent) only if w has + // not yet been explored + // mark node as read + m_component_index_list[w] = current_component; + bfs_queue.emplace(w, v); + } + } + } + } + } + } + + return current_component_size; + } + + std::vector m_component_index_list; + std::vector m_component_index_size; + + const GraphT &m_graph; + const RestrictionMap &m_restriction_map; + const std::unordered_set &m_barrier_nodes; +}; + +#endif // BFS_COMPONENTS_HPP_ diff --git a/algorithms/crc32_processor.hpp b/algorithms/crc32_processor.hpp new file mode 100644 index 000000000..a68514dce --- /dev/null +++ b/algorithms/crc32_processor.hpp @@ -0,0 +1,146 @@ +/* + +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 ITERATOR_BASED_CRC32_H +#define ITERATOR_BASED_CRC32_H + +#if defined(__x86_64__) && !defined(__MINGW64__) +#include +#endif + +#include // for boost::crc_32_type + +#include + +class IteratorbasedCRC32 +{ + public: + bool using_hardware() const { return use_hardware_implementation; } + + IteratorbasedCRC32() : crc(0) { use_hardware_implementation = detect_hardware_support(); } + + template unsigned operator()(Iterator iter, const Iterator end) + { + unsigned crc = 0; + while (iter != end) + { + using value_type = typename std::iterator_traits::value_type; + char *data = (char *)(&(*iter)); + + if (use_hardware_implementation) + { + crc = compute_in_hardware(data, sizeof(value_type)); + } + else + { + crc = compute_in_software(data, sizeof(value_type)); + } + ++iter; + } + return crc; + } + + private: + bool detect_hardware_support() const + { + static const int sse42_bit = 0x00100000; + const unsigned ecx = cpuid(); + const bool sse42_found = (ecx & sse42_bit) != 0; + return sse42_found; + } + + unsigned compute_in_software(char *str, unsigned len) + { + crc_processor.process_bytes(str, len); + return crc_processor.checksum(); + } + + // adapted from http://byteworm.com/2010/10/13/crc32/ + unsigned compute_in_hardware(char *str, unsigned len) + { +#if defined(__x86_64__) + unsigned q = len / sizeof(unsigned); + unsigned r = len % sizeof(unsigned); + unsigned *p = (unsigned *)str; + + // crc=0; + while (q--) + { + __asm__ __volatile__(".byte 0xf2, 0xf, 0x38, 0xf1, 0xf1;" + : "=S"(crc) + : "0"(crc), "c"(*p)); + ++p; + } + + str = (char *)p; + while (r--) + { + __asm__ __volatile__(".byte 0xf2, 0xf, 0x38, 0xf1, 0xf1;" + : "=S"(crc) + : "0"(crc), "c"(*str)); + ++str; + } +#endif + return crc; + } + + inline unsigned cpuid() const + { + unsigned eax = 0, ebx = 0, ecx = 0, edx = 0; + // on X64 this calls hardware cpuid(.) instr. otherwise a dummy impl. + __get_cpuid(1, &eax, &ebx, &ecx, &edx); + return ecx; + } + +#if defined(__MINGW64__) || defined(_MSC_VER) + inline void + __get_cpuid(int param, unsigned *eax, unsigned *ebx, unsigned *ecx, unsigned *edx) const + { + *ecx = 0; + } +#endif + + boost::crc_optimal<32, 0x1EDC6F41, 0x0, 0x0, true, true> crc_processor; + unsigned crc; + bool use_hardware_implementation; +}; + +struct RangebasedCRC32 +{ + template + unsigned operator()(const Iteratable &iterable) + { + return crc32(std::begin(iterable), std::end(iterable)); + } + + bool using_hardware() const { return crc32.using_hardware(); } + + private: + IteratorbasedCRC32 crc32; +}; + +#endif /* ITERATOR_BASED_CRC32_H */ diff --git a/algorithms/douglas_peucker.cpp b/algorithms/douglas_peucker.cpp new file mode 100644 index 000000000..d64e22189 --- /dev/null +++ b/algorithms/douglas_peucker.cpp @@ -0,0 +1,166 @@ +/* + +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 "douglas_peucker.hpp" + +#include "../DataStructures/segment_information.hpp" +#include "../Util/integer_range.hpp" + +#include + +#include + +#include + +#include + +namespace +{ +struct CoordinatePairCalculator +{ + CoordinatePairCalculator() = delete; + CoordinatePairCalculator(const FixedPointCoordinate &coordinate_a, + const FixedPointCoordinate &coordinate_b) + { + // initialize distance calculator with two fixed coordinates a, b + const float RAD = 0.017453292519943295769236907684886f; + first_lat = (coordinate_a.lat / COORDINATE_PRECISION) * RAD; + first_lon = (coordinate_a.lon / COORDINATE_PRECISION) * RAD; + second_lat = (coordinate_b.lat / COORDINATE_PRECISION) * RAD; + second_lon = (coordinate_b.lon / COORDINATE_PRECISION) * RAD; + } + + int operator()(FixedPointCoordinate &other) const + { + // set third coordinate c + const float RAD = 0.017453292519943295769236907684886f; + const float earth_radius = 6372797.560856f; + const float float_lat1 = (other.lat / COORDINATE_PRECISION) * RAD; + const float float_lon1 = (other.lon / COORDINATE_PRECISION) * RAD; + + // compute distance (a,c) + const float x_value_1 = (first_lon - float_lon1) * cos((float_lat1 + first_lat) / 2.f); + const float y_value_1 = first_lat - float_lat1; + const float dist1 = sqrt(std::pow(x_value_1, 2) + std::pow(y_value_1, 2)) * earth_radius; + + // compute distance (b,c) + const float x_value_2 = (second_lon - float_lon1) * cos((float_lat1 + second_lat) / 2.f); + const float y_value_2 = second_lat - float_lat1; + const float dist2 = sqrt(std::pow(x_value_2, 2) + std::pow(y_value_2, 2)) * earth_radius; + + // return the minimum + return static_cast(std::min(dist1, dist2)); + } + + float first_lat; + float first_lon; + float second_lat; + float second_lon; +}; +} + +void DouglasPeucker::Run(std::vector &input_geometry, const unsigned zoom_level) +{ + Run(std::begin(input_geometry), std::end(input_geometry), zoom_level); +} + +void DouglasPeucker::Run(RandomAccessIt begin, RandomAccessIt end, const unsigned zoom_level) +{ + unsigned size = std::distance(begin, end); + if (size < 2) + { + return; + } + + begin->necessary = true; + std::prev(end)->necessary = true; + + { + BOOST_ASSERT_MSG(zoom_level < DOUGLAS_PEUCKER_THRESHOLDS.size(), "unsupported zoom level"); + RandomAccessIt left_border = begin; + RandomAccessIt right_border = std::next(begin); + // Sweep over array and identify those ranges that need to be checked + do + { + // traverse list until new border element found + if (right_border->necessary) + { + // sanity checks + BOOST_ASSERT(left_border->necessary); + BOOST_ASSERT(right_border->necessary); + recursion_stack.emplace(left_border, right_border); + left_border = right_border; + } + ++right_border; + } while (right_border != end); + } + + // mark locations as 'necessary' by divide-and-conquer + while (!recursion_stack.empty()) + { + // pop next element + const GeometryRange pair = recursion_stack.top(); + recursion_stack.pop(); + // sanity checks + BOOST_ASSERT_MSG(pair.first->necessary, "left border must be necessary"); + BOOST_ASSERT_MSG(pair.second->necessary, "right border must be necessary"); + BOOST_ASSERT_MSG(std::distance(pair.second, end) > 0, "right border outside of geometry"); + BOOST_ASSERT_MSG(std::distance(pair.first, pair.second) >= 0, + "left border on the wrong side"); + + int max_int_distance = 0; + auto farthest_entry_it = pair.second; + const CoordinatePairCalculator dist_calc(pair.first->location, pair.second->location); + + // sweep over range to find the maximum + for (auto it = std::next(pair.first); it != pair.second; ++it) + { + const int distance = dist_calc(it->location); + // found new feasible maximum? + if (distance > max_int_distance && distance > DOUGLAS_PEUCKER_THRESHOLDS[zoom_level]) + { + farthest_entry_it = it; + max_int_distance = distance; + } + } + + // check if maximum violates a zoom level dependent threshold + if (max_int_distance > DOUGLAS_PEUCKER_THRESHOLDS[zoom_level]) + { + // mark idx as necessary + farthest_entry_it->necessary = true; + if (1 < std::distance(pair.first, farthest_entry_it)) + { + recursion_stack.emplace(pair.first, farthest_entry_it); + } + if (1 < std::distance(farthest_entry_it, pair.second)) + { + recursion_stack.emplace(farthest_entry_it, pair.second); + } + } + } +} diff --git a/algorithms/douglas_peucker.hpp b/algorithms/douglas_peucker.hpp new file mode 100644 index 000000000..417e80a38 --- /dev/null +++ b/algorithms/douglas_peucker.hpp @@ -0,0 +1,80 @@ +/* + +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 DOUGLAS_PEUCKER_HPP_ +#define DOUGLAS_PEUCKER_HPP_ + +#include +#include +#include + +/* This class object computes the bitvector of indicating generalized input + * points according to the (Ramer-)Douglas-Peucker algorithm. + * + * Input is vector of pairs. Each pair consists of the point information and a + * bit indicating if the points is present in the generalization. + * Note: points may also be pre-selected*/ + +struct SegmentInformation; + +static const std::array DOUGLAS_PEUCKER_THRESHOLDS {{ + 512440, // z0 + 256720, // z1 + 122560, // z2 + 56780, // z3 + 28800, // z4 + 14400, // z5 + 7200, // z6 + 3200, // z7 + 2400, // z8 + 1000, // z9 + 600, // z10 + 120, // z11 + 60, // z12 + 45, // z13 + 36, // z14 + 20, // z15 + 8, // z16 + 6, // z17 + 4 // z18 +}}; + +class DouglasPeucker +{ + public: + using RandomAccessIt = std::vector::iterator; + + using GeometryRange = std::pair; + // Stack to simulate the recursion + std::stack recursion_stack; + + public: + void Run(RandomAccessIt begin, RandomAccessIt end, const unsigned zoom_level); + void Run(std::vector &input_geometry, const unsigned zoom_level); +}; + +#endif /* DOUGLAS_PEUCKER_HPP_ */ diff --git a/algorithms/object_encoder.hpp b/algorithms/object_encoder.hpp new file mode 100644 index 000000000..64c03c1be --- /dev/null +++ b/algorithms/object_encoder.hpp @@ -0,0 +1,94 @@ +/* + +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 OBJECT_ENCODER_HPP +#define OBJECT_ENCODER_HPP + +#include "../Util/StringUtil.h" + +#include +#include +#include +#include + +#include +#include +#include + +struct ObjectEncoder +{ + using base64_t = boost::archive::iterators::base64_from_binary< + boost::archive::iterators::transform_width>; + + using binary_t = boost::archive::iterators::transform_width< + boost::archive::iterators::binary_from_base64, + 8, + 6>; + + template + static void EncodeToBase64(const ObjectT &object, std::string &encoded) + { + const char *char_ptr_to_object = (const char *)&object; + std::vector data(sizeof(object)); + std::copy(char_ptr_to_object, char_ptr_to_object + sizeof(ObjectT), data.begin()); + + unsigned char number_of_padded_chars = 0; // is in {0,1,2}; + while (data.size() % 3 != 0) + { + ++number_of_padded_chars; + data.push_back(0x00); + } + + BOOST_ASSERT_MSG(0 == data.size() % 3, "base64 input data size is not a multiple of 3!"); + encoded.resize(sizeof(ObjectT)); + encoded.assign(base64_t(&data[0]), + base64_t(&data[0] + (data.size() - number_of_padded_chars))); + replaceAll(encoded, "+", "-"); + replaceAll(encoded, "/", "_"); + } + + template + static void DecodeFromBase64(const std::string &input, ObjectT &object) + { + try + { + std::string encoded(input); + // replace "-" with "+" and "_" with "/" + replaceAll(encoded, "-", "+"); + replaceAll(encoded, "_", "/"); + + std::copy(binary_t(encoded.begin()), + binary_t(encoded.begin() + encoded.length() - 1), + (char *)&object); + } + catch (...) + { + } + } +}; + +#endif /* OBJECT_ENCODER_HPP */ diff --git a/algorithms/polyline_compressor.cpp b/algorithms/polyline_compressor.cpp new file mode 100644 index 000000000..061103d3d --- /dev/null +++ b/algorithms/polyline_compressor.cpp @@ -0,0 +1,98 @@ +/* + +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. + +*/ + +#include "polyline_compressor.hpp" +#include "../DataStructures/segment_information.hpp" + +#include + +std::string PolylineCompressor::encode_vector(std::vector &numbers) const +{ + std::string output; + const auto end = numbers.size(); + for (std::size_t i = 0; i < end; ++i) + { + numbers[i] <<= 1; + if (numbers[i] < 0) + { + numbers[i] = ~(numbers[i]); + } + } + for (const int number : numbers) + { + output += encode_number(number); + } + return output; +} + +std::string PolylineCompressor::encode_number(int number_to_encode) const +{ + std::string output; + while (number_to_encode >= 0x20) + { + const int next_value = (0x20 | (number_to_encode & 0x1f)) + 63; + output += static_cast(next_value); + if (92 == next_value) + { + output += static_cast(next_value); + } + number_to_encode >>= 5; + } + + number_to_encode += 63; + output += static_cast(number_to_encode); + if (92 == number_to_encode) + { + output += static_cast(number_to_encode); + } + return output; +} + +std::string +PolylineCompressor::get_encoded_string(const std::vector &polyline) const +{ + if (polyline.empty()) + { + return {}; + } + + std::vector delta_numbers; + delta_numbers.reserve((polyline.size() - 1) * 2); + FixedPointCoordinate previous_coordinate = {0, 0}; + for (const auto &segment : polyline) + { + if (segment.necessary) + { + const int lat_diff = segment.location.lat - previous_coordinate.lat; + const int lon_diff = segment.location.lon - previous_coordinate.lon; + delta_numbers.emplace_back(lat_diff); + delta_numbers.emplace_back(lon_diff); + previous_coordinate = segment.location; + } + } + return encode_vector(delta_numbers); +} diff --git a/algorithms/polyline_compressor.hpp b/algorithms/polyline_compressor.hpp new file mode 100644 index 000000000..8bff4a040 --- /dev/null +++ b/algorithms/polyline_compressor.hpp @@ -0,0 +1,47 @@ +/* + +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 POLYLINECOMPRESSOR_H_ +#define POLYLINECOMPRESSOR_H_ + +struct SegmentInformation; + +#include +#include + +class PolylineCompressor +{ + private: + std::string encode_vector(std::vector &numbers) const; + + std::string encode_number(const int number_to_encode) const; + + public: + std::string get_encoded_string(const std::vector &polyline) const; +}; + +#endif /* POLYLINECOMPRESSOR_H_ */ diff --git a/algorithms/polyline_formatter.cpp b/algorithms/polyline_formatter.cpp new file mode 100644 index 000000000..fd638c6a4 --- /dev/null +++ b/algorithms/polyline_formatter.cpp @@ -0,0 +1,56 @@ +/* + +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. + +*/ + +#include "polyline_formatter.hpp" + +#include "polyline_compressor.hpp" +#include "../DataStructures/segment_information.hpp" + +#include + +JSON::String +PolylineFormatter::printEncodedString(const std::vector &polyline) const +{ + return JSON::String(PolylineCompressor().get_encoded_string(polyline)); +} + +JSON::Array +PolylineFormatter::printUnencodedString(const std::vector &polyline) const +{ + JSON::Array json_geometry_array; + for (const auto &segment : polyline) + { + if (segment.necessary) + { + JSON::Array json_coordinate; + json_coordinate.values.push_back(segment.location.lat / COORDINATE_PRECISION); + json_coordinate.values.push_back(segment.location.lon / COORDINATE_PRECISION); + json_geometry_array.values.push_back(json_coordinate); + } + } + return json_geometry_array; +} diff --git a/algorithms/polyline_formatter.hpp b/algorithms/polyline_formatter.hpp new file mode 100644 index 000000000..f2bd6591f --- /dev/null +++ b/algorithms/polyline_formatter.hpp @@ -0,0 +1,45 @@ +/* + +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 POLYLINE_FORMATTER_H_ +#define POLYLINE_FORMATTER_H_ + +struct SegmentInformation; + +#include "../DataStructures/JSONContainer.h" + +#include +#include + +struct PolylineFormatter +{ + JSON::String printEncodedString(const std::vector &polyline) const; + + JSON::Array printUnencodedString(const std::vector &polyline) const; +}; + +#endif /* POLYLINE_FORMATTER_H_ */ diff --git a/algorithms/route_name_extraction.hpp b/algorithms/route_name_extraction.hpp new file mode 100644 index 000000000..519452f98 --- /dev/null +++ b/algorithms/route_name_extraction.hpp @@ -0,0 +1,169 @@ +/* + +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; +}; + +// construct routes names +template struct ExtractRouteNames +{ + private: + SegmentT PickNextLongestSegment(const std::vector &segment_list, + const unsigned blocked_name_id) const + { + SegmentT result_segment; + result_segment.length = 0; + + for (const SegmentT &segment : segment_list) + { + if (segment.name_id != blocked_name_id && segment.length > result_segment.length && segment.name_id != 0) + { + result_segment = segment; + } + } + return result_segment; + } + + public: + RouteNames operator()(std::vector &shortest_path_segments, + std::vector &alternative_path_segments, + const DataFacadeT *facade) const + { + RouteNames route_names; + + SegmentT shortest_segment_1, shortest_segment_2; + SegmentT alternative_segment_1, alternative_segment_2; + + auto length_comperator = [](const SegmentT &a, const SegmentT &b) + { return a.length > b.length; }; + auto name_id_comperator = [](const SegmentT &a, const SegmentT &b) + { return a.name_id < b.name_id; }; + + if (shortest_path_segments.empty()) + { + return route_names; + } + + // pick the longest segment for the shortest path. + 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); + + // also pick the longest segment for the alternative path + alternative_segment_1 = alternative_path_segments[0]; + } + + // compute the set difference (for shortest path) depending on names between shortest and + // alternative + std::vector shortest_path_set_difference(shortest_path_segments.size()); + std::sort(shortest_path_segments.begin(), shortest_path_segments.end(), name_id_comperator); + std::sort(alternative_path_segments.begin(), alternative_path_segments.end(), name_id_comperator); + 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); + + std::sort(shortest_path_set_difference.begin(), + shortest_path_set_difference.end(), + length_comperator); + shortest_segment_2 = + PickNextLongestSegment(shortest_path_set_difference, shortest_segment_1.name_id); + + // compute the set difference (for alternative path) depending on names between shortest and + // alternative + // vectors are still sorted, no need to do again + BOOST_ASSERT(std::is_sorted(shortest_path_segments.begin(), + shortest_path_segments.end(), + name_id_comperator)); + BOOST_ASSERT(std::is_sorted(alternative_path_segments.begin(), + alternative_path_segments.end(), + name_id_comperator)); + + std::vector alternative_path_set_difference(alternative_path_segments.size()); + 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); + + std::sort(alternative_path_set_difference.begin(), + alternative_path_set_difference.end(), + length_comperator); + + if (!alternative_path_segments.empty()) + { + alternative_segment_2 = PickNextLongestSegment(alternative_path_set_difference, + alternative_segment_1.name_id); + } + + // move the segments into the order in which they occur. + 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); + } + + // fetching names for the selected segments + 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/algorithms/tiny_components.hpp b/algorithms/tiny_components.hpp new file mode 100644 index 000000000..8e7b8f018 --- /dev/null +++ b/algorithms/tiny_components.hpp @@ -0,0 +1,459 @@ +/* + +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 STRONGLYCONNECTEDCOMPONENTS_H_ +#define STRONGLYCONNECTEDCOMPONENTS_H_ + +#include "../typedefs.h" +#include "../DataStructures/DeallocatingVector.h" +#include "../DataStructures/DynamicGraph.h" +#include "../DataStructures/ImportEdge.h" +#include "../DataStructures/QueryNode.h" +#include "../DataStructures/Percent.h" +#include "../DataStructures/Restriction.h" +#include "../DataStructures/TurnInstructions.h" + +#include "../Util/integer_range.hpp" +#include "../Util/OSRMException.h" +#include "../Util/simple_logger.hpp" +#include "../Util/StdHashExtensions.h" +#include "../Util/TimingUtil.h" + +#include + +#include +#include + +#include + +#if defined(__APPLE__) || defined (_WIN32) +#include +#include +#else +#include +#include +#endif + +#include + +#include +#include +#include +#include +#include + +class TarjanSCC +{ + private: + struct TarjanNode + { + TarjanNode() : index(SPECIAL_NODEID), low_link(SPECIAL_NODEID), on_stack(false) {} + unsigned index; + unsigned low_link; + bool on_stack; + }; + + struct TarjanEdgeData + { + TarjanEdgeData() : distance(INVALID_EDGE_WEIGHT), name_id(INVALID_NAMEID) {} + TarjanEdgeData(int distance, unsigned name_id) : distance(distance), name_id(name_id) {} + int distance; + unsigned name_id; + }; + + struct TarjanStackFrame + { + explicit TarjanStackFrame(NodeID v, NodeID parent) : v(v), parent(parent) {} + NodeID v; + NodeID parent; + }; + + using TarjanDynamicGraph = DynamicGraph; + using TarjanEdge = TarjanDynamicGraph::InputEdge; + using RestrictionSource = std::pair; + using RestrictionTarget = std::pair; + using EmanatingRestrictionsVector = std::vector; + using RestrictionMap = std::unordered_map; + + std::vector m_coordinate_list; + std::vector m_restriction_bucket_list; + std::shared_ptr m_node_based_graph; + std::unordered_set barrier_node_list; + std::unordered_set traffic_light_list; + unsigned m_restriction_counter; + RestrictionMap m_restriction_map; + + public: + TarjanSCC(int number_of_nodes, + std::vector &input_edges, + std::vector &bn, + std::vector &tl, + std::vector &irs, + std::vector &nI) + : m_coordinate_list(nI), m_restriction_counter(irs.size()) + { + TIMER_START(SCC_LOAD); + for (const TurnRestriction &restriction : irs) + { + std::pair restriction_source = {restriction.from.node, + restriction.via.node}; + unsigned index = 0; + const auto restriction_iterator = m_restriction_map.find(restriction_source); + if (restriction_iterator == m_restriction_map.end()) + { + index = m_restriction_bucket_list.size(); + m_restriction_bucket_list.resize(index + 1); + m_restriction_map.emplace(restriction_source, index); + } + else + { + index = restriction_iterator->second; + // Map already contains an is_only_*-restriction + if (m_restriction_bucket_list.at(index).begin()->second) + { + continue; + } + else if (restriction.flags.is_only) + { + // We are going to insert an is_only_*-restriction. There can be only one. + m_restriction_bucket_list.at(index).clear(); + } + } + + m_restriction_bucket_list.at(index) + .emplace_back(restriction.to.node, restriction.flags.is_only); + } + + barrier_node_list.insert(bn.begin(), bn.end()); + traffic_light_list.insert(tl.begin(), tl.end()); + + DeallocatingVector edge_list; + for (const NodeBasedEdge &input_edge : input_edges) + { + if (input_edge.source == input_edge.target) + { + continue; + } + + if (input_edge.forward) + { + edge_list.emplace_back(input_edge.source, + input_edge.target, + (std::max)((int)input_edge.weight, 1), + input_edge.name_id); + } + if (input_edge.backward) + { + edge_list.emplace_back(input_edge.target, + input_edge.source, + (std::max)((int)input_edge.weight, 1), + input_edge.name_id); + } + } + input_edges.clear(); + input_edges.shrink_to_fit(); + BOOST_ASSERT_MSG(0 == input_edges.size() && 0 == input_edges.capacity(), + "input edge vector not properly deallocated"); + + tbb::parallel_sort(edge_list.begin(), edge_list.end()); + m_node_based_graph = std::make_shared(number_of_nodes, edge_list); + TIMER_STOP(SCC_LOAD); + SimpleLogger().Write() << "Loading data into SCC took " << TIMER_MSEC(SCC_LOAD)/1000. << "s"; + } + + ~TarjanSCC() { m_node_based_graph.reset(); } + + void Run() + { + TIMER_START(SCC_RUN_SETUP); + // remove files from previous run if exist + DeleteFileIfExists("component.dbf"); + DeleteFileIfExists("component.shx"); + DeleteFileIfExists("component.shp"); + + Percent p(m_node_based_graph->GetNumberOfNodes()); + + OGRRegisterAll(); + + const char *pszDriverName = "ESRI Shapefile"; + OGRSFDriver *poDriver = + OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(pszDriverName); + if (nullptr == poDriver) + { + throw OSRMException("ESRI Shapefile driver not available"); + } + OGRDataSource *poDS = poDriver->CreateDataSource("component.shp", nullptr); + + if (nullptr == poDS) + { + throw OSRMException("Creation of output file failed"); + } + + OGRSpatialReference *poSRS = new OGRSpatialReference(); + poSRS->importFromEPSG(4326); + + OGRLayer *poLayer = poDS->CreateLayer("component", poSRS, wkbLineString, nullptr); + + if (nullptr == poLayer) + { + throw OSRMException("Layer creation failed."); + } + TIMER_STOP(SCC_RUN_SETUP); + SimpleLogger().Write() << "shapefile setup took " << TIMER_MSEC(SCC_RUN_SETUP)/1000. << "s"; + + TIMER_START(SCC_RUN); + // The following is a hack to distinguish between stuff that happens + // before the recursive call and stuff that happens after + std::stack recursion_stack; + // true = stuff before, false = stuff after call + std::stack tarjan_stack; + std::vector components_index(m_node_based_graph->GetNumberOfNodes(), + SPECIAL_NODEID); + std::vector component_size_vector; + std::vector tarjan_node_list(m_node_based_graph->GetNumberOfNodes()); + unsigned component_index = 0, size_of_current_component = 0; + int index = 0; + const NodeID last_node = m_node_based_graph->GetNumberOfNodes(); + std::vector processing_node_before_recursion(m_node_based_graph->GetNumberOfNodes(), true); + for(const NodeID node : osrm::irange(0u, last_node)) + { + if (SPECIAL_NODEID == components_index[node]) + { + recursion_stack.emplace(TarjanStackFrame(node, node)); + } + + while (!recursion_stack.empty()) + { + TarjanStackFrame currentFrame = recursion_stack.top(); + const NodeID v = currentFrame.v; + recursion_stack.pop(); + const bool before_recursion = processing_node_before_recursion[v]; + + if (before_recursion && tarjan_node_list[v].index != UINT_MAX) + { + continue; + } + + if (before_recursion) + { + // Mark frame to handle tail of recursion + recursion_stack.emplace(currentFrame); + processing_node_before_recursion[v] = false; + + // Mark essential information for SCC + tarjan_node_list[v].index = index; + tarjan_node_list[v].low_link = index; + tarjan_stack.push(v); + tarjan_node_list[v].on_stack = true; + ++index; + + // Traverse outgoing edges + for (const auto current_edge : m_node_based_graph->GetAdjacentEdgeRange(v)) + { + const TarjanDynamicGraph::NodeIterator vprime = + m_node_based_graph->GetTarget(current_edge); + if (SPECIAL_NODEID == tarjan_node_list[vprime].index) + { + recursion_stack.emplace(TarjanStackFrame(vprime, v)); + } + else + { + if (tarjan_node_list[vprime].on_stack && + tarjan_node_list[vprime].index < tarjan_node_list[v].low_link) + { + tarjan_node_list[v].low_link = tarjan_node_list[vprime].index; + } + } + } + } + else + { + processing_node_before_recursion[v] = true; + tarjan_node_list[currentFrame.parent].low_link = + std::min(tarjan_node_list[currentFrame.parent].low_link, + tarjan_node_list[v].low_link); + // after recursion, lets do cycle checking + // Check if we found a cycle. This is the bottom part of the recursion + if (tarjan_node_list[v].low_link == tarjan_node_list[v].index) + { + NodeID vprime; + do + { + vprime = tarjan_stack.top(); + tarjan_stack.pop(); + tarjan_node_list[vprime].on_stack = false; + components_index[vprime] = component_index; + ++size_of_current_component; + } while (v != vprime); + + component_size_vector.emplace_back(size_of_current_component); + + if (size_of_current_component > 1000) + { + SimpleLogger().Write() << "large component [" << component_index + << "]=" << size_of_current_component; + } + + ++component_index; + size_of_current_component = 0; + } + } + } + } + + TIMER_STOP(SCC_RUN); + SimpleLogger().Write() << "SCC run took: " << TIMER_MSEC(SCC_RUN)/1000. << "s"; + SimpleLogger().Write() << "identified: " << component_size_vector.size() + << " many components, marking small components"; + + TIMER_START(SCC_OUTPUT); + + const 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"; + + uint64_t total_network_distance = 0; + p.reinit(m_node_based_graph->GetNumberOfNodes()); + // const NodeID last_u_node = m_node_based_graph->GetNumberOfNodes(); + for (const NodeID source : osrm::irange(0u, last_node)) + { + p.printIncrement(); + for (const auto current_edge : m_node_based_graph->GetAdjacentEdgeRange(source)) + { + const TarjanDynamicGraph::NodeIterator target = + m_node_based_graph->GetTarget(current_edge); + + if (source < target || + m_node_based_graph->EndEdges(target) == + m_node_based_graph->FindEdge(target, source)) + { + total_network_distance += + 100 * FixedPointCoordinate::ApproximateEuclideanDistance( + m_coordinate_list[source].lat, + m_coordinate_list[source].lon, + m_coordinate_list[target].lat, + m_coordinate_list[target].lon); + + BOOST_ASSERT(current_edge != SPECIAL_EDGEID); + BOOST_ASSERT(source != SPECIAL_NODEID); + BOOST_ASSERT(target != SPECIAL_NODEID); + + const unsigned size_of_containing_component = + std::min(component_size_vector[components_index[source]], + component_size_vector[components_index[target]]); + + // edges that end on bollard nodes may actually be in two distinct components + if (size_of_containing_component < 10) + { + OGRLineString lineString; + lineString.addPoint(m_coordinate_list[source].lon / COORDINATE_PRECISION, + m_coordinate_list[source].lat / COORDINATE_PRECISION); + lineString.addPoint(m_coordinate_list[target].lon / COORDINATE_PRECISION, + m_coordinate_list[target].lat / COORDINATE_PRECISION); + + OGRFeature *poFeature = OGRFeature::CreateFeature(poLayer->GetLayerDefn()); + + poFeature->SetGeometry(&lineString); + if (OGRERR_NONE != poLayer->CreateFeature(poFeature)) + { + throw OSRMException("Failed to create feature in shapefile."); + } + OGRFeature::DestroyFeature(poFeature); + } + } + } + } + OGRDataSource::DestroyDataSource(poDS); + component_size_vector.clear(); + component_size_vector.shrink_to_fit(); + BOOST_ASSERT_MSG(0 == component_size_vector.size() && 0 == component_size_vector.capacity(), + "component_size_vector not properly deallocated"); + + components_index.clear(); + components_index.shrink_to_fit(); + BOOST_ASSERT_MSG(0 == components_index.size() && 0 == components_index.capacity(), + "components_index not properly deallocated"); + TIMER_STOP(SCC_OUTPUT); + SimpleLogger().Write() << "generating output took: " << TIMER_MSEC(SCC_OUTPUT)/1000. << "s"; + + SimpleLogger().Write() << "total network distance: " + << (uint64_t)total_network_distance / 100 / 1000. << " km"; + } + + private: + unsigned CheckForEmanatingIsOnlyTurn(const NodeID u, const NodeID v) const + { + std::pair restriction_source = {u, v}; + const auto restriction_iterator = m_restriction_map.find(restriction_source); + if (restriction_iterator != m_restriction_map.end()) + { + const unsigned index = restriction_iterator->second; + for (const RestrictionSource &restriction_target : m_restriction_bucket_list.at(index)) + { + if (restriction_target.second) + { + return restriction_target.first; + } + } + } + return SPECIAL_NODEID; + } + + bool CheckIfTurnIsRestricted(const NodeID u, const NodeID v, const NodeID w) const + { + // only add an edge if turn is not a U-turn except it is the end of dead-end street. + std::pair restriction_source = {u, v}; + const auto restriction_iterator = m_restriction_map.find(restriction_source); + if (restriction_iterator != m_restriction_map.end()) + { + const unsigned index = restriction_iterator->second; + for (const RestrictionTarget &restriction_target : m_restriction_bucket_list.at(index)) + { + if (w == restriction_target.first) + { + return true; + } + } + } + return false; + } + + void DeleteFileIfExists(const std::string &file_name) const + { + if (boost::filesystem::exists(file_name)) + { + boost::filesystem::remove(file_name); + } + } +}; + +#endif /* STRONGLYCONNECTEDCOMPONENTS_H_ */