diff --git a/generator/transit_generator.cpp b/generator/transit_generator.cpp index 9572a33798..a8ae865399 100644 --- a/generator/transit_generator.cpp +++ b/generator/transit_generator.cpp @@ -4,43 +4,34 @@ #include "generator/borders_loader.hpp" #include "generator/utils.hpp" -#include "traffic/traffic_cache.hpp" - +#include "routing/routing_exceptions.hpp" #include "routing/index_router.hpp" #include "routing/routing_exceptions.hpp" +#include "routing/vehicle_mask.hpp" -#include "routing_common/transit_serdes.hpp" -#include "routing_common/transit_speed_limits.hpp" +#include "routing_common/transit_types.hpp" #include "storage/country_info_getter.hpp" #include "storage/routing_helpers.hpp" -#include "indexer/index.hpp" +#include "indexer/mwm_set.hpp" -#include "geometry/mercator.hpp" +#include "geometry/point2d.hpp" +#include "geometry/rect2d.hpp" #include "geometry/region2d.hpp" -#include "coding/file_container.hpp" #include "coding/file_name_utils.hpp" #include "coding/file_writer.hpp" -#include "platform/country_file.hpp" -#include "platform/local_country_file.hpp" -#include "platform/local_country_file_utils.hpp" #include "platform/platform.hpp" #include "base/assert.hpp" -#include "base/checked_cast.hpp" +#include "base/exception.hpp" #include "base/logging.hpp" -#include "base/macros.hpp" -#include "base/stl_helpers.hpp" -#include "base/string_utils.hpp" -#include -#include -#include -#include -#include +#include + +#include "defines.hpp" #include "3party/jansson/src/jansson.h" @@ -53,69 +44,6 @@ using namespace storage; namespace { -struct ClearVisitor -{ - template - void operator()(Cont & c, char const * /* name */) const { c.clear(); } -}; - -struct SortVisitor -{ - template - void operator()(Cont & c, char const * /* name */) const - { - sort(c.begin(), c.end()); - } -}; - -struct IsValidVisitor -{ - template - void operator()(Cont const & c, char const * /* name */ ) - { - m_isValid = m_isValid && ::IsValid(c); - } - - bool IsValid() const { return m_isValid; } - -private: - bool m_isValid = true; -}; - -struct IsUniqueVisitor -{ - template - void operator()(Cont const & c, char const * /* name */) - { - m_isUnique = m_isUnique && (adjacent_find(c.cbegin(), c.cend()) == c.cend()); - } - - bool IsUnique() const { return m_isUnique; } - -private: - bool m_isUnique = true; -}; - -struct IsSortedVisitor -{ - template - void operator()(Cont const & c, char const * /* name */) - { - m_isSorted = m_isSorted && is_sorted(c.cbegin(), c.cend()); - } - - bool IsSorted() const { return m_isSorted; } - -private: - bool m_isSorted = true; -}; - -template -void Append(vector const & src, vector & dst) -{ - dst.insert(dst.end(), src.begin(), src.end()); -} - void LoadBorders(string const & dir, TCountryId const & countryId, vector & borders) { string const polyFile = my::JoinPath(dir, BORDERS_DIR, countryId + BORDERS_EXTENSION); @@ -123,58 +51,6 @@ void LoadBorders(string const & dir, TCountryId const & countryId, vector const & stops, StopId stopId) -{ - return binary_search(stops.cbegin(), stops.cend(), Stop(stopId)); -} - -/// \brief Removes from |items| all items which stop ids is not contained in |stops|. -/// \note This method keeps relative order of |items|. -template -void ClipItemsByStops(vector const & stops, vector & items) -{ - CHECK(is_sorted(stops.cbegin(), stops.cend()), ()); - - vector itemsToFill; - for (auto const & item : items) - { - for (auto const stopId : item.GetStopIds()) - { - if (HasStop(stops, stopId)) - { - itemsToFill.push_back(item); - break; - } - } - } - - items.swap(itemsToFill); -} - -/// \returns ref to an item at |items| by |id|. -/// \note |items| must be sorted before a call of this method. -template -Item const & FindById(vector const & items, Id id) -{ - auto const s1Id = equal_range(items.cbegin(), items.cend(), Item(id)); - CHECK_EQUAL(distance(s1Id.first, s1Id.second), 1, - ("An item with id:", id, "is not unique or there's not such item. items:", items)); - return *s1Id.first; -} - -/// \brief Fills |items| with items which have ids from |ids|. -/// \note |items| must be sorted before a call of this method. -template -void UpdateItems(set const & ids, vector & items) -{ - vector itemsToFill; - for (auto const id : ids) - itemsToFill.push_back(FindById(items, id)); - - SortVisitor{}(itemsToFill, nullptr /* name */); - items.swap(itemsToFill); -} - void FillOsmIdToFeatureIdsMap(string const & osmIdToFeatureIdsPath, OsmIdToFeatureIdsMap & map) { CHECK(ForEachOsmId2FeatureId(osmIdToFeatureIdsPath, @@ -189,235 +65,10 @@ string GetMwmPath(string const & mwmDir, TCountryId const & countryId) return my::JoinPath(mwmDir, countryId + DATA_FILE_EXTENSION); } -} // namespace - -namespace routing -{ -namespace transit -{ -// DeserializerFromJson --------------------------------------------------------------------------- -DeserializerFromJson::DeserializerFromJson(json_struct_t * node, - OsmIdToFeatureIdsMap const & osmIdToFeatureIds) - : m_node(node), m_osmIdToFeatureIds(osmIdToFeatureIds) -{ -} - -void DeserializerFromJson::operator()(m2::PointD & p, char const * name) -{ - // @todo(bykoianko) Instead of having a special operator() method for m2::PointD class it's necessary to - // add Point class to transit_types.hpp and process it in DeserializerFromJson with regular method. - json_t * item = nullptr; - if (name == nullptr) - item = m_node; // Array item case - else - item = my::GetJSONObligatoryField(m_node, name); - - CHECK(json_is_object(item), ("Item is not a json object:", name)); - FromJSONObject(item, "x", p.x); - FromJSONObject(item, "y", p.y); -} - -void DeserializerFromJson::operator()(FeatureIdentifiers & id, char const * name) -{ - // Conversion osm id to feature id. - string osmIdStr; - GetField(osmIdStr, name); - CHECK(strings::is_number(osmIdStr), ("Osm id string is not a number:", osmIdStr)); - uint64_t osmIdNum; - CHECK(strings::to_uint64(osmIdStr, osmIdNum), - ("Cann't convert osm id string:", osmIdStr, "to a number.")); - osm::Id const osmId(osmIdNum); - auto const it = m_osmIdToFeatureIds.find(osmId); - if (it != m_osmIdToFeatureIds.cend()) - { - CHECK_GREATER_OR_EQUAL(it->second.size(), 1, ("Osm id:", osmId, "(encoded", osmId.EncodedId(), - ") from transit graph does not correspond to any feature.")); - if (it->second.size() != 1) - { - // Note. |osmId| corresponds several feature ids. It may happen in case of stops; - // if a stop is present as relation. It's a rare case. - LOG(LWARNING, ("Osm id:", osmId, "(encoded", osmId.EncodedId(), "corresponds to", - it->second.size(), "feature ids.")); - } - id.SetFeatureId(it->second[0]); - } - id.SetOsmId(osmId.EncodedId()); -} - -void DeserializerFromJson::operator()(EdgeFlags & edgeFlags, char const * name) -{ - bool transfer = false; - (*this)(transfer, name); - // Note. Only |transfer| field of |edgeFlags| may be set at this point because the - // other fields of |edgeFlags| are unknown. - edgeFlags.SetFlags(0); - edgeFlags.m_transfer = transfer; -} - -void DeserializerFromJson::operator()(StopIdRanges & rs, char const * name) -{ - vector stopIds; - (*this)(stopIds, name); - rs = StopIdRanges({stopIds}); -} - -// GraphData -------------------------------------------------------------------------------------- -void GraphData::DeserializeFromJson(my::Json const & root, OsmIdToFeatureIdsMap const & mapping) -{ - DeserializerFromJson deserializer(root.get(), mapping); - Visit(deserializer); - - // Removes equivalent edges from |m_edges|. If there are several equivalent edges only - // the most lightweight edge is left. - // Note. It's possible that two stops are connected with the same line several times - // in the same direction. It happens in Oslo metro (T-banen): - // https://en.wikipedia.org/wiki/Oslo_Metro#/media/File:Oslo_Metro_Map.svg branch 5. - my::SortUnique(m_edges, - [](Edge const & e1, Edge const & e2) { - if (e1 != e2) - return e1 < e2; - return e1.GetWeight() < e2.GetWeight(); - }, - [](Edge const & e1, Edge const & e2) { return e1 == e2; }); -} - -void GraphData::Serialize(Writer & writer) const -{ - TransitHeader header; - - auto const startOffset = writer.Pos(); - Serializer serializer(writer); - FixedSizeSerializer numberSerializer(writer); - numberSerializer(header); - header.m_stopsOffset = base::checked_cast(writer.Pos() - startOffset); - - serializer(m_stops); - header.m_gatesOffset = base::checked_cast(writer.Pos() - startOffset); - - serializer(m_gates); - header.m_edgesOffset = base::checked_cast(writer.Pos() - startOffset); - - serializer(m_edges); - header.m_transfersOffset = base::checked_cast(writer.Pos() - startOffset); - - serializer(m_transfers); - header.m_linesOffset = base::checked_cast(writer.Pos() - startOffset); - - serializer(m_lines); - header.m_shapesOffset = base::checked_cast(writer.Pos() - startOffset); - - serializer(m_shapes); - header.m_networksOffset = base::checked_cast(writer.Pos() - startOffset); - - serializer(m_networks); - header.m_endOffset = base::checked_cast(writer.Pos() - startOffset); - - // Rewriting header info. - CHECK(header.IsValid(), (header)); - auto const endOffset = writer.Pos(); - writer.Seek(startOffset); - numberSerializer(header); - writer.Seek(endOffset); - - LOG(LINFO, (TRANSIT_FILE_TAG, "section is ready. Header:", header)); -} - -void GraphData::Deserialize(Reader & reader) -{ - NonOwningReaderSource src(reader); - transit::Deserializer deserializer(src); - transit::FixedSizeDeserializer numberDeserializer(src); - - transit::TransitHeader header; - numberDeserializer(header); - CHECK(header.IsValid(), ()); - - CHECK_EQUAL(src.Pos(), header.m_stopsOffset, ("Wrong", TRANSIT_FILE_TAG, "section format.")); - deserializer(m_stops); - CHECK(transit::IsValidSortedUnique(m_stops), ()); - - CHECK_EQUAL(src.Pos(), header.m_gatesOffset, ("Wrong", TRANSIT_FILE_TAG, "section format.")); - deserializer(m_gates); - CHECK(transit::IsValidSortedUnique(m_gates), ()); - - CHECK_EQUAL(src.Pos(), header.m_edgesOffset, ("Wrong", TRANSIT_FILE_TAG, "section format.")); - deserializer(m_edges); - CHECK(transit::IsValidSortedUnique(m_edges), ()); - - CHECK_EQUAL(src.Pos(), header.m_transfersOffset, ("Wrong", TRANSIT_FILE_TAG, "section format.")); - deserializer(m_transfers); - CHECK(transit::IsValidSortedUnique(m_transfers), ()); - - CHECK_EQUAL(src.Pos(), header.m_linesOffset, ("Wrong", TRANSIT_FILE_TAG, "section format.")); - deserializer(m_lines); - CHECK(transit::IsValidSortedUnique(m_lines), ()); - - CHECK_EQUAL(src.Pos(), header.m_shapesOffset, ("Wrong", TRANSIT_FILE_TAG, "section format.")); - deserializer(m_shapes); - CHECK(transit::IsValidSortedUnique(m_shapes), ()); - - CHECK_EQUAL(src.Pos(), header.m_networksOffset, ("Wrong", TRANSIT_FILE_TAG, "section format.")); - deserializer(m_networks); - CHECK(transit::IsValidSortedUnique(m_networks), ()); - - CHECK_EQUAL(src.Pos(), header.m_endOffset, ("Wrong", TRANSIT_FILE_TAG, "section format.")); -} - -void GraphData::AppendTo(GraphData const & rhs) -{ - ::Append(rhs.m_stops, m_stops); - ::Append(rhs.m_gates, m_gates); - ::Append(rhs.m_edges, m_edges); - ::Append(rhs.m_transfers, m_transfers); - ::Append(rhs.m_lines, m_lines); - ::Append(rhs.m_shapes, m_shapes); - ::Append(rhs.m_networks, m_networks); -} - -void GraphData::Clear() -{ - ClearVisitor const v{}; - Visit(v); -} - -bool GraphData::IsValid() const -{ - if (!IsSorted() || !IsUnique()) - return false; - - IsValidVisitor v; - Visit(v); - return v.IsValid(); -} - -bool GraphData::IsEmpty() const -{ - // Note. |m_transfers| may be empty if GraphData instance is not empty. - return m_stops.empty() || m_gates.empty() || m_edges.empty() || m_lines.empty() - || m_shapes.empty() || m_networks.empty(); -} - -void GraphData::Sort() -{ - SortVisitor const v{}; - Visit(v); -} - -void GraphData::ClipGraph(vector const & borders) -{ - Sort(); - CHECK(IsValid(), ()); - ClipLines(borders); - ClipStops(); - ClipNetworks(); - ClipGates(); - ClipTransfer(); - ClipEdges(); - ClipShapes(); - CHECK(IsValid(), ()); -} - -void GraphData::CalculateBestPedestrianSegments(string const & mwmPath, TCountryId const & countryId) +/// \brief Calculates best pedestrian segment for every gate in |m_gates|. +/// \note All gates in |m_gates| must have a valid |m_point| field before the call. +void CalculateBestPedestrianSegments(string const & mwmPath, TCountryId const & countryId, + GraphData & graphData) { // Creating IndexRouter. SingleMwmIndex index(mwmPath); @@ -446,8 +97,10 @@ void GraphData::CalculateBestPedestrianSegments(string const & mwmPath, TCountry auto worldGraph = indexRouter.MakeSingleMwmWorldGraph(); // Looking for the best segment for every gate. - for (auto & gate : m_gates) + auto const & gates = graphData.GetGates(); + for (size_t i = 0; i < gates.size(); ++i) { + auto const & gate = gates[i]; if (countryFileGetter(gate.GetPoint()) != countryId) continue; // Note. For pedestrian routing all the segments are considered as twoway segments so @@ -462,7 +115,7 @@ void GraphData::CalculateBestPedestrianSegments(string const & mwmPath, TCountry true /* isOutgoing */, *worldGraph, bestSegment)) { CHECK_EQUAL(bestSegment.GetMwmId(), 0, ()); - gate.SetBestPedestrianSegment(SingleMwmSegment( + graphData.SetGateBestPedestrianSegment(i, SingleMwmSegment( bestSegment.GetFeatureId(), bestSegment.GetSegmentIdx(), bestSegment.IsForward())); } } @@ -478,150 +131,12 @@ void GraphData::CalculateBestPedestrianSegments(string const & mwmPath, TCountry } } } +} // namespace -bool GraphData::IsUnique() const +namespace routing { - IsUniqueVisitor v; - Visit(v); - return v.IsUnique(); -} - -bool GraphData::IsSorted() const +namespace transit { - IsSortedVisitor v; - Visit(v); - return v.IsSorted(); -} - -void GraphData::ClipLines(vector const & borders) -{ - // Set with stop ids with stops which are inside |borders|. - set stopIdInside; - for (auto const & stop : m_stops) - { - if (m2::RegionsContain(borders, stop.GetPoint())) - stopIdInside.insert(stop.GetId()); - } - - set hasNeighborInside; - for (auto const & edge : m_edges) - { - auto const stop1Inside = stopIdInside.count(edge.GetStop1Id()) != 0; - auto const stop2Inside = stopIdInside.count(edge.GetStop2Id()) != 0; - if (stop1Inside && !stop2Inside) - hasNeighborInside.insert(edge.GetStop2Id()); - if (stop2Inside && !stop1Inside) - hasNeighborInside.insert(edge.GetStop1Id()); - } - - stopIdInside.insert(hasNeighborInside.cbegin(), hasNeighborInside.cend()); - - // Filling |lines| with stops inside |borders|. - vector lines; - for (auto const & line : m_lines) - { - // Note. |stopIdsToFill| will be filled with continuous sequences of stop ids. - // In most cases only one sequence of stop ids should be placed to |stopIdsToFill|. - // But if a line is split by |borders| several times then several - // continuous groups of stop ids will be placed to |stopIdsToFill|. - // The loop below goes through all the stop ids belong the line |line| and - // keeps in |stopIdsToFill| continuous groups of stop ids which are inside |borders|. - Ranges stopIdsToFill; - Ranges const & ranges = line.GetStopIds(); - CHECK_EQUAL(ranges.size(), 1, ()); - vector const & stopIds = ranges[0]; - auto it = stopIds.begin(); - while (it != stopIds.end()) { - while (it != stopIds.end() && stopIdInside.count(*it) == 0) - ++it; - auto jt = it; - while (jt != stopIds.end() && stopIdInside.count(*jt) != 0) - ++jt; - if (it != jt) - stopIdsToFill.emplace_back(it, jt); - it = jt; - } - - if (!stopIdsToFill.empty()) - { - lines.emplace_back(line.GetId(), line.GetNumber(), line.GetTitle(), line.GetType(), - line.GetColor(), line.GetNetworkId(), stopIdsToFill, line.GetInterval()); - } - } - - m_lines.swap(lines); -} - -void GraphData::ClipStops() -{ - CHECK(is_sorted(m_stops.cbegin(), m_stops.cend()), ()); - set stopIds; - for (auto const & line : m_lines) - { - for (auto const & range : line.GetStopIds()) - stopIds.insert(range.cbegin(), range.cend()); - } - - UpdateItems(stopIds, m_stops); -} - -void GraphData::ClipNetworks() -{ - CHECK(is_sorted(m_networks.cbegin(), m_networks.cend()), ()); - set networkIds; - for (auto const & line : m_lines) - networkIds.insert(line.GetNetworkId()); - - UpdateItems(networkIds, m_networks); -} - -void GraphData::ClipGates() -{ - ClipItemsByStops(m_stops, m_gates); -} - -void GraphData::ClipTransfer() -{ - ClipItemsByStops(m_stops, m_transfers); -} - -void GraphData::ClipEdges() -{ - CHECK(is_sorted(m_stops.cbegin(), m_stops.cend()), ()); - - vector edges; - for (auto const & edge : m_edges) - { - if (HasStop(m_stops, edge.GetStop1Id()) && HasStop(m_stops, edge.GetStop2Id())) - edges.push_back(edge); - } - - SortVisitor{}(edges, nullptr /* name */); - m_edges.swap(edges); -} - -void GraphData::ClipShapes() -{ - CHECK(is_sorted(m_edges.cbegin(), m_edges.cend()), ()); - - // Set with shape ids contained in m_edges. - set shapeIdInEdges; - for (auto const & edge : m_edges) - { - auto const & shapeIds = edge.GetShapeIds(); - shapeIdInEdges.insert(shapeIds.cbegin(), shapeIds.cend()); - } - - vector shapes; - for (auto const & shape : m_shapes) - { - if (shapeIdInEdges.count(shape.GetId()) != 0) - shapes.push_back(shape); - } - - m_shapes.swap(shapes); -} - void DeserializeFromJson(OsmIdToFeatureIdsMap const & mapping, string const & transitJsonPath, GraphData & data) { @@ -658,7 +173,7 @@ void DeserializeFromJson(OsmIdToFeatureIdsMap const & mapping, void ProcessGraph(string const & mwmPath, TCountryId const & countryId, OsmIdToFeatureIdsMap const & osmIdToFeatureIdsMap, GraphData & data) { - data.CalculateBestPedestrianSegments(mwmPath, countryId); + CalculateBestPedestrianSegments(mwmPath, countryId, data); data.Sort(); CHECK(data.IsValid(), (mwmPath)); } diff --git a/generator/transit_generator.hpp b/generator/transit_generator.hpp index 3ab389f980..2870c26ac3 100644 --- a/generator/transit_generator.hpp +++ b/generator/transit_generator.hpp @@ -1,200 +1,15 @@ #pragma once -#include "routing_common/transit_types.hpp" +#include "routing_common/transit_graph_data.hpp" #include "storage/index.hpp" -#include "geometry/point2d.hpp" -#include "geometry/region2d.hpp" - -#include "coding/reader.hpp" -#include "coding/writer.hpp" - -#include "base/macros.hpp" -#include "base/osm_id.hpp" - -#include "3party/jansson/myjansson.hpp" - -#include -#include -#include #include -#include -#include namespace routing { namespace transit { -using OsmIdToFeatureIdsMap = std::map>; - -class DeserializerFromJson -{ -public: - DeserializerFromJson(json_struct_t * node, OsmIdToFeatureIdsMap const & osmIdToFeatureIds); - - template - typename std::enable_if::value || std::is_enum::value || - std::is_same::value>::type - operator()(T & t, char const * name = nullptr) - { - GetField(t, name); - return; - } - - void operator()(std::string & s, char const * name = nullptr) { GetField(s, name); } - void operator()(m2::PointD & p, char const * name = nullptr); - void operator()(FeatureIdentifiers & id, char const * name = nullptr); - void operator()(EdgeFlags & edgeFlags, char const * name = nullptr); - void operator()(StopIdRanges & rs, char const * name = nullptr); - - template - typename std::enable_if::value || - std::is_same::value>::type - operator()(T & t, char const * name = nullptr) - { - typename T::RepType id; - operator()(id, name); - t.Set(id); - } - - template - void operator()(std::vector & vs, char const * name = nullptr) - { - auto * arr = my::GetJSONOptionalField(m_node, name); - if (arr == nullptr) - return; - - if (!json_is_array(arr)) - MYTHROW(my::Json::Exception, ("The field", name, "must contain a json array.")); - size_t const sz = json_array_size(arr); - vs.resize(sz); - for (size_t i = 0; i < sz; ++i) - { - DeserializerFromJson arrayItem(json_array_get(arr, i), m_osmIdToFeatureIds); - arrayItem(vs[i]); - } - } - - template - typename std::enable_if::value && - !std::is_same::value && - !std::is_same::value>::type - operator()(T & t, char const * name = nullptr) - { - if (name != nullptr && json_is_object(m_node)) - { - json_t * dictNode = my::GetJSONOptionalField(m_node, name); - if (dictNode == nullptr) - return; // No such field in json. - - DeserializerFromJson dict(dictNode, m_osmIdToFeatureIds); - t.Visit(dict); - return; - } - - t.Visit(*this); - } - -private: - template - void GetField(T & t, char const * name = nullptr) - { - if (name == nullptr) - { - // |name| is not set in case of array items - FromJSON(m_node, t); - return; - } - - json_struct_t * field = my::GetJSONOptionalField(m_node, name); - if (field == nullptr) - { - // No optional field |name| at |m_node|. In that case the default value should be set to |t|. - // This default value is set at constructor of corresponding class which is filled with - // |DeserializerFromJson|. And the value (|t|) is not changed at this method. - return; - } - FromJSON(field, t); - } - - json_struct_t * m_node; - OsmIdToFeatureIdsMap const & m_osmIdToFeatureIds; -}; - -/// \brief The class contains all the information to make TRANSIT_FILE_TAG section. -class GraphData -{ -public: - void DeserializeFromJson(my::Json const & root, OsmIdToFeatureIdsMap const & mapping); - void Serialize(Writer & writer) const; - void Deserialize(Reader & reader); - void AppendTo(GraphData const & rhs); - void Clear(); - bool IsValid() const; - bool IsEmpty() const; - - /// \brief Sorts all class fields by their ids. - void Sort(); - /// \brief Removes some items from all the class fields if they are outside |borders|. - /// Please see description for the other Clip*() method for excact rules of clipping. - /// \note Before call of the method every line in |m_stopIds| should contain |m_stopIds| - /// with only one stop range. - void ClipGraph(std::vector const & borders); - /// \brief Calculates best pedestrian segment for every gate in |m_gates|. - /// \note All gates in |m_gates| must have a valid |m_point| field before the call. - void CalculateBestPedestrianSegments(std::string const & mwmPath, std::string const & countryId); - - std::vector const & GetStops() const { return m_stops; } - std::vector const & GetGates() const { return m_gates; } - std::vector const & GetEdges() const { return m_edges; } - std::vector const & GetTransfers() const { return m_transfers; } - std::vector const & GetLines() const { return m_lines; } - std::vector const & GetShapes() const { return m_shapes; } - std::vector const & GetNetworks() const { return m_networks; } - -private: - DECLARE_VISITOR_AND_DEBUG_PRINT(GraphData, visitor(m_stops, "stops"), visitor(m_gates, "gates"), - visitor(m_edges, "edges"), visitor(m_transfers, "transfers"), - visitor(m_lines, "lines"), visitor(m_shapes, "shapes"), - visitor(m_networks, "networks")) - - bool IsUnique() const; - bool IsSorted() const; - - /// \brief Clipping |m_lines| with |borders|. - /// \details After a call of the method the following stop ids in |m_lines| are left: - /// * stops inside |borders| - /// * stops which are connected with an edge from |m_edges| with stops inside |borders| - /// \note Lines without stops are removed from |m_lines|. - /// \note Before call of the method every line in |m_lines| should contain |m_stopIds| - /// with only one stop range. - void ClipLines(std::vector const & borders); - /// \brief Removes all stops from |m_stops| which are not contained in |m_lines| at field |m_stopIds|. - /// \note Only stops which stop ids contained in |m_lines| will left in |m_stops| - /// after call of this method. - void ClipStops(); - /// \brief Removes all networks from |m_networks| which are not contained in |m_lines| - /// at field |m_networkId|. - void ClipNetworks(); - /// \brief Removes gates from |m_gates| if there's no stop in |m_stops| with their stop ids. - void ClipGates(); - /// \brief Removes transfers from |m_transfers| if there's no stop in |m_stops| with their stop ids. - void ClipTransfer(); - /// \brief Removes edges from |m_edges| if their ends are not contained in |m_stops|. - void ClipEdges(); - /// \brief Removes all shapes from |m_shapes| which are not reffered form |m_edges|. - void ClipShapes(); - - std::vector m_stops; - std::vector m_gates; - std::vector m_edges; - std::vector m_transfers; - std::vector m_lines; - std::vector m_shapes; - std::vector m_networks; -}; - /// \brief Fills |data| according to a transit graph (|transitJsonPath|). /// \note Some fields of |data| contain feature ids of a certain mwm. These fields are filled /// iff the mapping (|osmIdToFeatureIdsPath|) contains them. Otherwise the fields have default value. @@ -207,7 +22,7 @@ void ProcessGraph(std::string const & mwmPath, storage::TCountryId const & count OsmIdToFeatureIdsMap const & osmIdToFeatureIdsMap, GraphData & data); /// \brief Builds the transit section in the mwm based on transit graph in json which represents -/// trasit graph clipped by the mwm borders. +/// transit graph clipped by the mwm borders. /// \param mwmDir relative or full path to a directory where mwm is located. /// \param countryId is an mwm name without extension of the processed mwm. /// \param osmIdToFeatureIdsPath is a path to a file with osm id to feature ids mapping. diff --git a/routing_common/CMakeLists.txt b/routing_common/CMakeLists.txt index 6a7f7f53e4..be1e559629 100644 --- a/routing_common/CMakeLists.txt +++ b/routing_common/CMakeLists.txt @@ -1,5 +1,9 @@ project(routing_common) +include_directories( + ${OMIM_ROOT}/3party/jansson/src +) + set( SRC bicycle_model.cpp @@ -9,6 +13,8 @@ set( num_mwm_id.hpp pedestrian_model.cpp pedestrian_model.hpp + transit_graph_data.cpp + transit_graph_data.hpp transit_serdes.hpp transit_speed_limits.hpp transit_types.cpp diff --git a/routing_common/routing_common.pro b/routing_common/routing_common.pro index a8cfd46106..b26cc8a8dc 100644 --- a/routing_common/routing_common.pro +++ b/routing_common/routing_common.pro @@ -16,6 +16,7 @@ SOURCES += \ bicycle_model.cpp \ car_model.cpp \ pedestrian_model.cpp \ + transit_graph_data.cpp \ transit_types.cpp \ vehicle_model.cpp \ @@ -25,6 +26,7 @@ HEADERS += \ car_model.hpp \ num_mwm_id.hpp \ pedestrian_model.hpp \ + transit_graph_data.hpp \ transit_serdes.hpp \ transit_speed_limits.hpp \ transit_types.hpp \ diff --git a/routing_common/transit_graph_data.cpp b/routing_common/transit_graph_data.cpp new file mode 100644 index 0000000000..3dd8445720 --- /dev/null +++ b/routing_common/transit_graph_data.cpp @@ -0,0 +1,509 @@ +#include "routing_common/transit_graph_data.hpp" + +#include "routing_common/transit_serdes.hpp" + +#include "base/assert.hpp" +#include "base/checked_cast.hpp" +#include "base/logging.hpp" +#include "base/stl_helpers.hpp" +#include "base/string_utils.hpp" + +#include +#include +#include + +#include "defines.hpp" + +using namespace routing; +using namespace routing::transit; +using namespace std; + +namespace +{ +struct ClearVisitor +{ + template + void operator()(Cont & c, char const * /* name */) const { c.clear(); } +}; + +struct SortVisitor +{ + template + void operator()(Cont & c, char const * /* name */) const + { + sort(c.begin(), c.end()); + } +}; + +struct IsValidVisitor +{ + template + void operator()(Cont const & c, char const * /* name */ ) + { + m_isValid = m_isValid && ::IsValid(c); + } + + bool IsValid() const { return m_isValid; } + +private: + bool m_isValid = true; +}; + +struct IsUniqueVisitor +{ + template + void operator()(Cont const & c, char const * /* name */) + { + m_isUnique = m_isUnique && (adjacent_find(c.cbegin(), c.cend()) == c.cend()); + } + + bool IsUnique() const { return m_isUnique; } + +private: + bool m_isUnique = true; +}; + +struct IsSortedVisitor +{ + template + void operator()(Cont const & c, char const * /* name */) + { + m_isSorted = m_isSorted && is_sorted(c.cbegin(), c.cend()); + } + + bool IsSorted() const { return m_isSorted; } + +private: + bool m_isSorted = true; +}; + +template +void Append(vector const & src, vector & dst) +{ + dst.insert(dst.end(), src.begin(), src.end()); +} + +bool HasStop(vector const & stops, StopId stopId) +{ + return binary_search(stops.cbegin(), stops.cend(), Stop(stopId)); +} + +/// \brief Removes from |items| all items which stop ids is not contained in |stops|. +/// \note This method keeps relative order of |items|. +template +void ClipItemsByStops(vector const & stops, vector & items) +{ + CHECK(is_sorted(stops.cbegin(), stops.cend()), ()); + + vector itemsToFill; + for (auto const & item : items) + { + for (auto const stopId : item.GetStopIds()) + { + if (HasStop(stops, stopId)) + { + itemsToFill.push_back(item); + break; + } + } + } + + items.swap(itemsToFill); +} + +/// \returns ref to an item at |items| by |id|. +/// \note |items| must be sorted before a call of this method. +template +Item const & FindById(vector const & items, Id id) +{ + auto const s1Id = equal_range(items.cbegin(), items.cend(), Item(id)); + CHECK_EQUAL(distance(s1Id.first, s1Id.second), 1, + ("An item with id:", id, "is not unique or there's not such item. items:", items)); + return *s1Id.first; +} + +/// \brief Fills |items| with items which have ids from |ids|. +/// \note |items| must be sorted before a call of this method. +template +void UpdateItems(set const & ids, vector & items) +{ + vector itemsToFill; + for (auto const id : ids) + itemsToFill.push_back(FindById(items, id)); + + SortVisitor{}(itemsToFill, nullptr /* name */); + items.swap(itemsToFill); +} +} // namespace + +namespace routing +{ +namespace transit +{ +// DeserializerFromJson --------------------------------------------------------------------------- +DeserializerFromJson::DeserializerFromJson(json_struct_t * node, + OsmIdToFeatureIdsMap const & osmIdToFeatureIds) + : m_node(node), m_osmIdToFeatureIds(osmIdToFeatureIds) +{ +} + +void DeserializerFromJson::operator()(m2::PointD & p, char const * name) +{ + // @todo(bykoianko) Instead of having a special operator() method for m2::PointD class it's + // necessary to add Point class to transit_types.hpp and process it in DeserializerFromJson with + // regular method. + json_t * item = nullptr; + if (name == nullptr) + item = m_node; // Array item case + else + item = my::GetJSONObligatoryField(m_node, name); + + CHECK(json_is_object(item), ("Item is not a json object:", name)); + FromJSONObject(item, "x", p.x); + FromJSONObject(item, "y", p.y); +} + +void DeserializerFromJson::operator()(FeatureIdentifiers & id, char const * name) +{ + // Conversion osm id to feature id. + string osmIdStr; + GetField(osmIdStr, name); + CHECK(strings::is_number(osmIdStr), ("Osm id string is not a number:", osmIdStr)); + uint64_t osmIdNum; + CHECK(strings::to_uint64(osmIdStr, osmIdNum), + ("Cann't convert osm id string:", osmIdStr, "to a number.")); + osm::Id const osmId(osmIdNum); + auto const it = m_osmIdToFeatureIds.find(osmId); + if (it != m_osmIdToFeatureIds.cend()) + { + CHECK_EQUAL(it->second.size(), 1, ("Osm id:", osmId, "(encoded", osmId.EncodedId(), + ") from transit graph corresponds to", it->second.size(), "features." + "But osm id should be represented be one feature.")); + id.SetFeatureId(it->second[0]); + } + id.SetOsmId(osmId.EncodedId()); +} + +void DeserializerFromJson::operator()(EdgeFlags & edgeFlags, char const * name) +{ + bool transfer = false; + (*this)(transfer, name); + // Note. Only |transfer| field of |edgeFlags| may be set at this point because the + // other fields of |edgeFlags| are unknown. + edgeFlags.SetFlags(0); + edgeFlags.m_transfer = transfer; +} + +void DeserializerFromJson::operator()(StopIdRanges & rs, char const * name) +{ + vector stopIds; + (*this)(stopIds, name); + rs = StopIdRanges({stopIds}); +} + +// GraphData -------------------------------------------------------------------------------------- +void GraphData::DeserializeFromJson(my::Json const & root, OsmIdToFeatureIdsMap const & mapping) +{ + DeserializerFromJson deserializer(root.get(), mapping); + Visit(deserializer); + + // Removes equivalent edges from |m_edges|. If there are several equivalent edges only + // the most lightweight edge is left. + // Note. It's possible that two stops are connected with the same line several times + // in the same direction. It happens in Oslo metro (T-banen): + // https://en.wikipedia.org/wiki/Oslo_Metro#/media/File:Oslo_Metro_Map.svg branch 5. + my::SortUnique(m_edges, + [](Edge const & e1, Edge const & e2) { + if (e1 != e2) + return e1 < e2; + return e1.GetWeight() < e2.GetWeight(); + }, + [](Edge const & e1, Edge const & e2) { return e1 == e2; }); +} + +void GraphData::Serialize(Writer & writer) const +{ + TransitHeader header; + + auto const startOffset = writer.Pos(); + Serializer serializer(writer); + FixedSizeSerializer numberSerializer(writer); + numberSerializer(header); + header.m_stopsOffset = base::checked_cast(writer.Pos() - startOffset); + + serializer(m_stops); + header.m_gatesOffset = base::checked_cast(writer.Pos() - startOffset); + + serializer(m_gates); + header.m_edgesOffset = base::checked_cast(writer.Pos() - startOffset); + + serializer(m_edges); + header.m_transfersOffset = base::checked_cast(writer.Pos() - startOffset); + + serializer(m_transfers); + header.m_linesOffset = base::checked_cast(writer.Pos() - startOffset); + + serializer(m_lines); + header.m_shapesOffset = base::checked_cast(writer.Pos() - startOffset); + + serializer(m_shapes); + header.m_networksOffset = base::checked_cast(writer.Pos() - startOffset); + + serializer(m_networks); + header.m_endOffset = base::checked_cast(writer.Pos() - startOffset); + + // Rewriting header info. + CHECK(header.IsValid(), (header)); + auto const endOffset = writer.Pos(); + writer.Seek(startOffset); + numberSerializer(header); + writer.Seek(endOffset); + + LOG(LINFO, (TRANSIT_FILE_TAG, "section is ready. Header:", header)); +} + +void GraphData::Deserialize(Reader & reader) +{ + NonOwningReaderSource src(reader); + transit::Deserializer deserializer(src); + transit::FixedSizeDeserializer numberDeserializer(src); + + transit::TransitHeader header; + numberDeserializer(header); + CHECK(header.IsValid(), ()); + + CHECK_EQUAL(src.Pos(), header.m_stopsOffset, ("Wrong", TRANSIT_FILE_TAG, "section format.")); + deserializer(m_stops); + CHECK(transit::IsValidSortedUnique(m_stops), ()); + + CHECK_EQUAL(src.Pos(), header.m_gatesOffset, ("Wrong", TRANSIT_FILE_TAG, "section format.")); + deserializer(m_gates); + CHECK(transit::IsValidSortedUnique(m_gates), ()); + + CHECK_EQUAL(src.Pos(), header.m_edgesOffset, ("Wrong", TRANSIT_FILE_TAG, "section format.")); + deserializer(m_edges); + CHECK(transit::IsValidSortedUnique(m_edges), ()); + + CHECK_EQUAL(src.Pos(), header.m_transfersOffset, ("Wrong", TRANSIT_FILE_TAG, "section format.")); + deserializer(m_transfers); + CHECK(transit::IsValidSortedUnique(m_transfers), ()); + + CHECK_EQUAL(src.Pos(), header.m_linesOffset, ("Wrong", TRANSIT_FILE_TAG, "section format.")); + deserializer(m_lines); + CHECK(transit::IsValidSortedUnique(m_lines), ()); + + CHECK_EQUAL(src.Pos(), header.m_shapesOffset, ("Wrong", TRANSIT_FILE_TAG, "section format.")); + deserializer(m_shapes); + CHECK(transit::IsValidSortedUnique(m_shapes), ()); + + CHECK_EQUAL(src.Pos(), header.m_networksOffset, ("Wrong", TRANSIT_FILE_TAG, "section format.")); + deserializer(m_networks); + CHECK(transit::IsValidSortedUnique(m_networks), ()); + + CHECK_EQUAL(src.Pos(), header.m_endOffset, ("Wrong", TRANSIT_FILE_TAG, "section format.")); +} + +void GraphData::AppendTo(GraphData const & rhs) +{ + ::Append(rhs.m_stops, m_stops); + ::Append(rhs.m_gates, m_gates); + ::Append(rhs.m_edges, m_edges); + ::Append(rhs.m_transfers, m_transfers); + ::Append(rhs.m_lines, m_lines); + ::Append(rhs.m_shapes, m_shapes); + ::Append(rhs.m_networks, m_networks); +} + +void GraphData::Clear() +{ + ClearVisitor const v{}; + Visit(v); +} + +bool GraphData::IsValid() const +{ + if (!IsSorted() || !IsUnique()) + return false; + + IsValidVisitor v; + Visit(v); + return v.IsValid(); +} + +bool GraphData::IsEmpty() const +{ + // Note. |m_transfers| may be empty if GraphData instance is not empty. + return m_stops.empty() || m_gates.empty() || m_edges.empty() || m_lines.empty() + || m_shapes.empty() || m_networks.empty(); +} + +void GraphData::Sort() +{ + SortVisitor const v{}; + Visit(v); +} + +void GraphData::ClipGraph(vector const & borders) +{ + Sort(); + CHECK(IsValid(), ()); + ClipLines(borders); + ClipStops(); + ClipNetworks(); + ClipGates(); + ClipTransfer(); + ClipEdges(); + ClipShapes(); + CHECK(IsValid(), ()); +} + +void GraphData::SetGateBestPedestrianSegment(size_t gateIdx, SingleMwmSegment const & s) +{ + CHECK_LESS(gateIdx, m_gates.size(), ()); + m_gates[gateIdx].SetBestPedestrianSegment(s); +} + +bool GraphData::IsUnique() const +{ + IsUniqueVisitor v; + Visit(v); + return v.IsUnique(); +} + +bool GraphData::IsSorted() const +{ + IsSortedVisitor v; + Visit(v); + return v.IsSorted(); +} + +void GraphData::ClipLines(vector const & borders) +{ + // Set with stop ids with stops which are inside |borders|. + set stopIdInside; + for (auto const & stop : m_stops) + { + if (m2::RegionsContain(borders, stop.GetPoint())) + stopIdInside.insert(stop.GetId()); + } + + set hasNeighborInside; + for (auto const & edge : m_edges) + { + auto const stop1Inside = stopIdInside.count(edge.GetStop1Id()) != 0; + auto const stop2Inside = stopIdInside.count(edge.GetStop2Id()) != 0; + if (stop1Inside && !stop2Inside) + hasNeighborInside.insert(edge.GetStop2Id()); + if (stop2Inside && !stop1Inside) + hasNeighborInside.insert(edge.GetStop1Id()); + } + + stopIdInside.insert(hasNeighborInside.cbegin(), hasNeighborInside.cend()); + + // Filling |lines| with stops inside |borders|. + vector lines; + for (auto const & line : m_lines) + { + // Note. |stopIdsToFill| will be filled with continuous sequences of stop ids. + // In most cases only one sequence of stop ids should be placed to |stopIdsToFill|. + // But if a line is split by |borders| several times then several + // continuous groups of stop ids will be placed to |stopIdsToFill|. + // The loop below goes through all the stop ids belong the line |line| and + // keeps in |stopIdsToFill| continuous groups of stop ids which are inside |borders|. + Ranges stopIdsToFill; + Ranges const & ranges = line.GetStopIds(); + CHECK_EQUAL(ranges.size(), 1, ()); + vector const & stopIds = ranges[0]; + auto it = stopIds.begin(); + while (it != stopIds.end()) { + while (it != stopIds.end() && stopIdInside.count(*it) == 0) + ++it; + auto jt = it; + while (jt != stopIds.end() && stopIdInside.count(*jt) != 0) + ++jt; + if (it != jt) + stopIdsToFill.emplace_back(it, jt); + it = jt; + } + + if (!stopIdsToFill.empty()) + { + lines.emplace_back(line.GetId(), line.GetNumber(), line.GetTitle(), line.GetType(), + line.GetColor(), line.GetNetworkId(), stopIdsToFill, line.GetInterval()); + } + } + + m_lines.swap(lines); +} + +void GraphData::ClipStops() +{ + CHECK(is_sorted(m_stops.cbegin(), m_stops.cend()), ()); + set stopIds; + for (auto const & line : m_lines) + { + for (auto const & range : line.GetStopIds()) + stopIds.insert(range.cbegin(), range.cend()); + } + + UpdateItems(stopIds, m_stops); +} + +void GraphData::ClipNetworks() +{ + CHECK(is_sorted(m_networks.cbegin(), m_networks.cend()), ()); + set networkIds; + for (auto const & line : m_lines) + networkIds.insert(line.GetNetworkId()); + + UpdateItems(networkIds, m_networks); +} + +void GraphData::ClipGates() +{ + ClipItemsByStops(m_stops, m_gates); +} + +void GraphData::ClipTransfer() +{ + ClipItemsByStops(m_stops, m_transfers); +} + +void GraphData::ClipEdges() +{ + CHECK(is_sorted(m_stops.cbegin(), m_stops.cend()), ()); + + vector edges; + for (auto const & edge : m_edges) + { + if (HasStop(m_stops, edge.GetStop1Id()) && HasStop(m_stops, edge.GetStop2Id())) + edges.push_back(edge); + } + + SortVisitor{}(edges, nullptr /* name */); + m_edges.swap(edges); +} + +void GraphData::ClipShapes() +{ + CHECK(is_sorted(m_edges.cbegin(), m_edges.cend()), ()); + + // Set with shape ids contained in m_edges. + set shapeIdInEdges; + for (auto const & edge : m_edges) + { + auto const & shapeIds = edge.GetShapeIds(); + shapeIdInEdges.insert(shapeIds.cbegin(), shapeIds.cend()); + } + + vector shapes; + for (auto const & shape : m_shapes) + { + if (shapeIdInEdges.count(shape.GetId()) != 0) + shapes.push_back(shape); + } + + m_shapes.swap(shapes); +} +} // namespace transit +} // namespace routing diff --git a/routing_common/transit_graph_data.hpp b/routing_common/transit_graph_data.hpp new file mode 100644 index 0000000000..b05ec3c40b --- /dev/null +++ b/routing_common/transit_graph_data.hpp @@ -0,0 +1,195 @@ +#pragma once + +#include "routing_common/transit_types.hpp" + +#include "geometry/point2d.hpp" +#include "geometry/region2d.hpp" + +#include "coding/reader.hpp" +#include "coding/writer.hpp" + +#include "base/exception.hpp" +#include "base/osm_id.hpp" +#include "base/visitor.hpp" + +#include "3party/jansson/myjansson.hpp" + +#include +#include +#include +#include +#include +#include + +namespace routing +{ +namespace transit +{ +using OsmIdToFeatureIdsMap = std::map>; + +class DeserializerFromJson +{ +public: + DeserializerFromJson(json_struct_t * node, OsmIdToFeatureIdsMap const & osmIdToFeatureIds); + + template + typename std::enable_if::value || std::is_enum::value || + std::is_same::value>::type + operator()(T & t, char const * name = nullptr) + { + GetField(t, name); + return; + } + + void operator()(std::string & s, char const * name = nullptr) { GetField(s, name); } + void operator()(m2::PointD & p, char const * name = nullptr); + void operator()(FeatureIdentifiers & id, char const * name = nullptr); + void operator()(EdgeFlags & edgeFlags, char const * name = nullptr); + void operator()(StopIdRanges & rs, char const * name = nullptr); + + template + typename std::enable_if::value || + std::is_same::value>::type + operator()(T & t, char const * name = nullptr) + { + typename T::RepType id; + operator()(id, name); + t.Set(id); + } + + template + void operator()(std::vector & vs, char const * name = nullptr) + { + auto * arr = my::GetJSONOptionalField(m_node, name); + if (arr == nullptr) + return; + + if (!json_is_array(arr)) + MYTHROW(my::Json::Exception, ("The field", name, "must contain a json array.")); + size_t const sz = json_array_size(arr); + vs.resize(sz); + for (size_t i = 0; i < sz; ++i) + { + DeserializerFromJson arrayItem(json_array_get(arr, i), m_osmIdToFeatureIds); + arrayItem(vs[i]); + } + } + + template + typename std::enable_if::value && + !std::is_same::value && + !std::is_same::value>::type + operator()(T & t, char const * name = nullptr) + { + if (name != nullptr && json_is_object(m_node)) + { + json_t * dictNode = my::GetJSONOptionalField(m_node, name); + if (dictNode == nullptr) + return; // No such field in json. + + DeserializerFromJson dict(dictNode, m_osmIdToFeatureIds); + t.Visit(dict); + return; + } + + t.Visit(*this); + } + +private: + template + void GetField(T & t, char const * name = nullptr) + { + if (name == nullptr) + { + // |name| is not set in case of array items + FromJSON(m_node, t); + return; + } + + json_struct_t * field = my::GetJSONOptionalField(m_node, name); + if (field == nullptr) + { + // No optional field |name| at |m_node|. In that case the default value should be set to |t|. + // This default value is set at constructor of corresponding class which is filled with + // |DeserializerFromJson|. And the value (|t|) is not changed at this method. + return; + } + FromJSON(field, t); + } + + json_struct_t * m_node; + OsmIdToFeatureIdsMap const & m_osmIdToFeatureIds; +}; + +/// \brief The class contains all the information to make TRANSIT_FILE_TAG section. +class GraphData +{ +public: + void DeserializeFromJson(my::Json const & root, OsmIdToFeatureIdsMap const & mapping); + void Serialize(Writer & writer) const; + void Deserialize(Reader & reader); + void AppendTo(GraphData const & rhs); + void Clear(); + bool IsValid() const; + bool IsEmpty() const; + + /// \brief Sorts all class fields by their ids. + void Sort(); + /// \brief Removes some items from all the class fields if they are outside |borders|. + /// Please see description for the other Clip*() method for excact rules of clipping. + /// \note Before call of the method every line in |m_stopIds| should contain |m_stopIds| + /// with only one stop range. + void ClipGraph(std::vector const & borders); + void SetGateBestPedestrianSegment(size_t gateIdx, SingleMwmSegment const & s); + + std::vector const & GetStops() const { return m_stops; } + std::vector const & GetGates() const { return m_gates; } + std::vector const & GetEdges() const { return m_edges; } + std::vector const & GetTransfers() const { return m_transfers; } + std::vector const & GetLines() const { return m_lines; } + std::vector const & GetShapes() const { return m_shapes; } + std::vector const & GetNetworks() const { return m_networks; } + +private: + DECLARE_VISITOR_AND_DEBUG_PRINT(GraphData, visitor(m_stops, "stops"), visitor(m_gates, "gates"), + visitor(m_edges, "edges"), visitor(m_transfers, "transfers"), + visitor(m_lines, "lines"), visitor(m_shapes, "shapes"), + visitor(m_networks, "networks")) + + bool IsUnique() const; + bool IsSorted() const; + + /// \brief Clipping |m_lines| with |borders|. + /// \details After a call of the method the following stop ids in |m_lines| are left: + /// * stops inside |borders| + /// * stops which are connected with an edge from |m_edges| with stops inside |borders| + /// \note Lines without stops are removed from |m_lines|. + /// \note Before call of the method every line in |m_lines| should contain |m_stopIds| + /// with only one stop range. + void ClipLines(std::vector const & borders); + /// \brief Removes all stops from |m_stops| which are not contained in |m_lines| at field |m_stopIds|. + /// \note Only stops which stop ids contained in |m_lines| will left in |m_stops| + /// after call of this method. + void ClipStops(); + /// \brief Removes all networks from |m_networks| which are not contained in |m_lines| + /// at field |m_networkId|. + void ClipNetworks(); + /// \brief Removes gates from |m_gates| if there's no stop in |m_stops| with their stop ids. + void ClipGates(); + /// \brief Removes transfers from |m_transfers| if there's no stop in |m_stops| with their stop ids. + void ClipTransfer(); + /// \brief Removes edges from |m_edges| if their ends are not contained in |m_stops|. + void ClipEdges(); + /// \brief Removes all shapes from |m_shapes| which are not reffered form |m_edges|. + void ClipShapes(); + + std::vector m_stops; + std::vector m_gates; + std::vector m_edges; + std::vector m_transfers; + std::vector m_lines; + std::vector m_shapes; + std::vector m_networks; +}; +} // namespace transit +} // namespace routing diff --git a/xcode/generator_tool/generator_tool.xcodeproj/project.pbxproj b/xcode/generator_tool/generator_tool.xcodeproj/project.pbxproj index 5035c91ba4..0a04f5850e 100644 --- a/xcode/generator_tool/generator_tool.xcodeproj/project.pbxproj +++ b/xcode/generator_tool/generator_tool.xcodeproj/project.pbxproj @@ -29,6 +29,8 @@ 34F558571DBF3CD800A4FC11 /* libstdc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 34F558561DBF3CD800A4FC11 /* libstdc++.tbd */; }; 562147271F6AA36A002D2214 /* libbsdiff.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 562147261F6AA36A002D2214 /* libbsdiff.a */; }; 562147291F6AA37E002D2214 /* libmwm_diff.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 562147281F6AA37E002D2214 /* libmwm_diff.a */; }; + 562D42941FD8460500A995F3 /* libugc.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 562D42951FD8460500A995F3 /* libugc.a */; }; + 562D42961FD8463700A995F3 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 670E7BC61EF992F600A8E9ED /* libsqlite3.tbd */; }; 56E2EDAA1F7E3FBB0092E9C2 /* transit_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56E2EDA91F7E3FBB0092E9C2 /* transit_test.cpp */; }; 670E7BBC1EF9832200A8E9ED /* libicu.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 670E7BBB1EF9832200A8E9ED /* libicu.a */; }; 670E7BBE1EF9839C00A8E9ED /* librouting_common.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 670E7BBD1EF9839C00A8E9ED /* librouting_common.a */; }; @@ -204,6 +206,7 @@ 34F558561DBF3CD800A4FC11 /* libstdc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libstdc++.tbd"; path = "usr/lib/libstdc++.tbd"; sourceTree = SDKROOT; }; 562147261F6AA36A002D2214 /* libbsdiff.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libbsdiff.a; path = ../bsdiff/build/Debug/libbsdiff.a; sourceTree = ""; }; 562147281F6AA37E002D2214 /* libmwm_diff.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmwm_diff.a; path = "../../../../Library/Developer/Xcode/DerivedData/omim-gsfdicnjgjjbizhdmwedavcucpok/Build/Products/Debug/libmwm_diff.a"; sourceTree = ""; }; + 562D42951FD8460500A995F3 /* libugc.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libugc.a; sourceTree = BUILT_PRODUCTS_DIR; }; 56E2EDA91F7E3FBB0092E9C2 /* transit_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = transit_test.cpp; sourceTree = ""; }; 670E7BBB1EF9832200A8E9ED /* libicu.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libicu.a; path = "../../../../Library/Developer/Xcode/DerivedData/omim-gzleizqujktwggdwiejzkgjrsgvp/Build/Products/Debug/libicu.a"; sourceTree = ""; }; 670E7BBD1EF9839C00A8E9ED /* librouting_common.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = librouting_common.a; path = "../../../../Library/Developer/Xcode/DerivedData/omim-gzleizqujktwggdwiejzkgjrsgvp/Build/Products/Debug/librouting_common.a"; sourceTree = ""; }; @@ -342,6 +345,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 562D42961FD8463700A995F3 /* libsqlite3.tbd in Frameworks */, + 562D42941FD8460500A995F3 /* libugc.a in Frameworks */, 562147291F6AA37E002D2214 /* libmwm_diff.a in Frameworks */, 562147271F6AA36A002D2214 /* libbsdiff.a in Frameworks */, 670E7BC01EF983A400A8E9ED /* libtraffic.a in Frameworks */, @@ -430,6 +435,7 @@ 34F558551DBF3CD800A4FC11 /* Frameworks */ = { isa = PBXGroup; children = ( + 562D42951FD8460500A995F3 /* libugc.a */, 562147281F6AA37E002D2214 /* libmwm_diff.a */, 562147261F6AA36A002D2214 /* libbsdiff.a */, 670E7BC61EF992F600A8E9ED /* libsqlite3.tbd */, @@ -773,6 +779,7 @@ "$(inherited)", "$(OMIM_ROOT)/3party/gflags/src/", "$(OMIM_ROOT)/3party/glm", + "$(OMIM_ROOT)/3party/jansson/src", ); }; name = Debug; @@ -785,6 +792,7 @@ "$(inherited)", "$(OMIM_ROOT)/3party/gflags/src/", "$(OMIM_ROOT)/3party/glm", + "$(OMIM_ROOT)/3party/jansson/src", ); }; name = Release; @@ -829,6 +837,7 @@ "$(inherited)", "$(OMIM_ROOT)/3party/gflags/src/", "$(OMIM_ROOT)/3party/glm", + "$(OMIM_ROOT)/3party/jansson/src", ); }; name = "Production Full"; diff --git a/xcode/routing_common/routing_common.xcodeproj/project.pbxproj b/xcode/routing_common/routing_common.xcodeproj/project.pbxproj index 65238fa3d5..781c690aac 100644 --- a/xcode/routing_common/routing_common.xcodeproj/project.pbxproj +++ b/xcode/routing_common/routing_common.xcodeproj/project.pbxproj @@ -8,6 +8,8 @@ /* Begin PBXBuildFile section */ 40FF45D01F388EF80046BD40 /* vehicle_model_for_country_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 40FF45CF1F388EF80046BD40 /* vehicle_model_for_country_test.cpp */; }; + 562D42921FD83FE100A995F3 /* transit_graph_data.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 562D42901FD83FE000A995F3 /* transit_graph_data.hpp */; }; + 562D42931FD83FE100A995F3 /* transit_graph_data.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 562D42911FD83FE100A995F3 /* transit_graph_data.cpp */; }; 5647A4511F72BEB600DE1125 /* libicu.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5647A4521F72BEB600DE1125 /* libicu.a */; }; 5667C1DD1F751F2700C6B31B /* transit_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5647A4531F72BF2B00DE1125 /* transit_test.cpp */; }; 56D0E47D1F8E335D0084B18C /* num_mwm_id.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56D0E47C1F8E335D0084B18C /* num_mwm_id.hpp */; }; @@ -43,6 +45,8 @@ /* Begin PBXFileReference section */ 40FF45CF1F388EF80046BD40 /* vehicle_model_for_country_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = vehicle_model_for_country_test.cpp; sourceTree = ""; }; + 562D42901FD83FE000A995F3 /* transit_graph_data.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = transit_graph_data.hpp; sourceTree = ""; }; + 562D42911FD83FE100A995F3 /* transit_graph_data.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = transit_graph_data.cpp; sourceTree = ""; }; 5647A4521F72BEB600DE1125 /* libicu.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libicu.a; sourceTree = BUILT_PRODUCTS_DIR; }; 5647A4531F72BF2B00DE1125 /* transit_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = transit_test.cpp; sourceTree = ""; }; 5667C1DE1F751F4200C6B31B /* routing_common.pro */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = routing_common.pro; sourceTree = ""; }; @@ -138,6 +142,8 @@ 671E78741E6A3BE200B2859B /* routing_common */ = { isa = PBXGroup; children = ( + 562D42911FD83FE100A995F3 /* transit_graph_data.cpp */, + 562D42901FD83FE000A995F3 /* transit_graph_data.hpp */, 56E6E73F1F95D5690022CBD3 /* transit_speed_limits.hpp */, 56D0E47C1F8E335D0084B18C /* num_mwm_id.hpp */, 56E2EDA31F7E3F890092E9C2 /* transit_serdes.hpp */, @@ -203,6 +209,7 @@ 56E41D881F72B42F00E28E2D /* transit_types.hpp in Headers */, 56E2EDA61F7E3F8A0092E9C2 /* transit_serdes.hpp in Headers */, 56E6E7401F95D5690022CBD3 /* transit_speed_limits.hpp in Headers */, + 562D42921FD83FE100A995F3 /* transit_graph_data.hpp in Headers */, 671E78891E6A3C5D00B2859B /* bicycle_model.hpp in Headers */, 671E788B1E6A3C5D00B2859B /* car_model.hpp in Headers */, 671E788F1E6A3C5D00B2859B /* vehicle_model.hpp in Headers */, @@ -300,6 +307,7 @@ buildActionMask = 2147483647; files = ( 56E2EDA81F7E3F8A0092E9C2 /* transit_types.cpp in Sources */, + 562D42931FD83FE100A995F3 /* transit_graph_data.cpp in Sources */, 671E788A1E6A3C5D00B2859B /* car_model.cpp in Sources */, 671E78881E6A3C5D00B2859B /* bicycle_model.cpp in Sources */, 671E788E1E6A3C5D00B2859B /* vehicle_model.cpp in Sources */, @@ -325,6 +333,12 @@ isa = XCBuildConfiguration; baseConfigurationReference = 671E78901E6A3C9C00B2859B /* common-debug.xcconfig */; buildSettings = { + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(OMIM_ROOT)", + "$(BOOST_ROOT)", + "$(OMIM_ROOT)/3party/jansson/src", + ); }; name = Debug; }; @@ -332,6 +346,12 @@ isa = XCBuildConfiguration; baseConfigurationReference = 671E78911E6A3C9C00B2859B /* common-release.xcconfig */; buildSettings = { + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(OMIM_ROOT)", + "$(BOOST_ROOT)", + "$(OMIM_ROOT)/3party/jansson/src", + ); }; name = Release; }; @@ -373,6 +393,12 @@ isa = XCBuildConfiguration; baseConfigurationReference = 671E78911E6A3C9C00B2859B /* common-release.xcconfig */; buildSettings = { + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(OMIM_ROOT)", + "$(BOOST_ROOT)", + "$(OMIM_ROOT)/3party/jansson/src", + ); }; name = "Production Full"; };