From b4da9c41558ec87c16abd64289a09ab77c716ab7 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Thu, 5 Oct 2017 15:16:30 +0300 Subject: [PATCH] Implementation transit data structure. --- generator/generator_tests/transit_test.cpp | 186 +++++++++++++++++- generator/transit_generator.cpp | 22 ++- .../routing_common_tests/transit_test.cpp | 46 +++++ routing_common/transit_types.cpp | 92 ++++++++- routing_common/transit_types.hpp | 94 ++++++++- 5 files changed, 432 insertions(+), 8 deletions(-) diff --git a/generator/generator_tests/transit_test.cpp b/generator/generator_tests/transit_test.cpp index 9bda0fac12..72feb7c58b 100644 --- a/generator/generator_tests/transit_test.cpp +++ b/generator/generator_tests/transit_test.cpp @@ -73,6 +73,44 @@ UNIT_TEST(DeserializerFromJson_Stops) TestDeserializerFromJson(jsonBuffer, "stops", expected); } +UNIT_TEST(DeserializerFromJson_Gates) +{ + string const jsonBuffer = R"( + { + "gates": [ + { + "entrance": true, + "exit": true, + "osm_id": 46116860, + "point": { + "x": 43.8594864, + "y": 68.33320554776377 + }, + "stop_ids": [ 442018474 ], + "weight": 60 + }, + { + "entrance": true, + "exit": true, + "osm_id": 46116861, + "point": { + "x": 43.9290544, + "y": 68.41120791512581 + }, + "stop_ids": [ 442018465 ], + "weight": 60 + } + ]})"; + + vector const expected = { + Gate(46116860 /* feature id */, true /* entrance */, true /* exit */, 60.0 /* weight */, + {442018474} /* stop ids */, {43.8594864, 68.33320554776377} /* point */), + Gate(46116861 /* feature id */, true /* entrance */, true /* exit */, 60.0 /* weight */, + {442018465} /* stop ids */, {43.9290544, 68.41120791512581} /* point */)}; + + TestDeserializerFromJson(jsonBuffer, "gates", expected); +} + UNIT_TEST(DeserializerFromJson_Edges) { string const jsonBuffer = R"( @@ -82,7 +120,6 @@ UNIT_TEST(DeserializerFromJson_Edges) "finish_stop_id": 442018445, "line_id": 72551680, "shape_ids": [5, 7], - "weight" : 234.5, "start_stop_id": 442018444, "transfer": false }, @@ -97,11 +134,156 @@ UNIT_TEST(DeserializerFromJson_Edges) ]})"; vector const expected = { - Edge(442018444 /* start stop id */, 442018445 /* finish stop id */, 234.5 /* weight */, + Edge(442018444 /* start stop id */, 442018445 /* finish stop id */, kInvalidWeight /* weight */, 72551680 /* line id */, false /* transfer */, {5, 7} /* shape ids */), Edge(442018445 /* start stop id */, 442018446 /* finish stop id */, 345.6 /* weight */, 72551680 /* line id */, false /* transfer */, {} /* shape ids */)}; TestDeserializerFromJson(jsonBuffer, "edges", expected); } + +UNIT_TEST(DeserializerFromJson_Transfers) +{ + string const jsonBuffer = R"( + { + "transfers": [ + { + "id": 922337203, + "point": { + "x": 27.5619844, + "y": 64.24325959173672 + }, + "stop_ids": [ + 209186416, + 277039518 + ] + } + ]})"; + + vector const expected = {Transfer(922337203 /* stop id */, + {27.5619844, 64.24325959173672} /* point */, + {209186416, 277039518} /* stopIds */)}; + + TestDeserializerFromJson(jsonBuffer, "transfers", expected); +} + +UNIT_TEST(DeserializerFromJson_Lines) +{ + string const jsonBuffer = R"( + { + "lines": [ + { + "id": 19207936, + "network_id": 2, + "number": "1", + "stop_ids": [ + 343262691, + 343259523, + 343252898, + 209191847, + 2947858576 + ], + "title": "Московская линия", + "type": "subway" + }, + { + "id": 19207937, + "network_id": 2, + "number": "2", + "stop_ids": [ + 246659391, + 246659390, + 209191855, + 209191854, + 209191853, + 209191852, + 209191851 + ], + "title": "Московская линия", + "type": "subway" + } + ]})"; + + vector const expected = {Line(19207936 /* line id */, "1" /* number */, "Московская линия" /* title */, + "subway" /* type */, 2 /* network id */, + {343262691, 343259523, 343252898, 209191847, 2947858576} /* stop ids */), + Line(19207937 /* line id */, "2" /* number */, "Московская линия" /* title */, + "subway" /* type */, 2 /* network id */, + {246659391, 246659390, 209191855, 209191854, 209191853, + 209191852, 209191851} /* stop ids */)}; + + TestDeserializerFromJson(jsonBuffer, "lines", expected); +} + +UNIT_TEST(DeserializerFromJson_Shapes) +{ + string const jsonBuffer = R"( + { + "shapes": [ + { + "id": 1, + "stop1_id": 209186424, + "stop2_id": 248520179, + "polyline": [ + { + "x": 27.5762295, + "y": 64.256768574044699 + }, + { + "x": 27.576325736220355, + "y": 64.256879325696005 + }, + { + "x": 27.576420780761875, + "y": 64.256990221238539 + }, + { + "x": 27.576514659541523, + "y": 64.257101255242176 + } + ] + }, + { + "id": 2, + "stop1_id": 209191850, + "stop2_id": 209191851, + "polyline": [ + { + "x": 27.554025800000002, + "y": 64.250591911669844 + }, + { + "x": 27.553906184631536, + "y": 64.250633404586054 + } + ] + } + ]})"; + + vector const expected = {Shape(1 /* shape id */, 209186424 /* stop 1 id */, 248520179 /* stop 2 id */, + {m2::PointD(27.5762295, 64.256768574044699), + m2::PointD(27.576325736220355, 64.256879325696005), + m2::PointD(27.576420780761875, 64.256990221238539), + m2::PointD(27.576514659541523, 64.257101255242176)} /* polyline */), + Shape(2 /* shape id */, 209191850 /* stop 1 id */, 209191851 /* stop 2 id */, + {m2::PointD(27.554025800000002, 64.250591911669844), + m2::PointD(27.553906184631536, 64.250633404586054)} /* polyline */)}; + + TestDeserializerFromJson(jsonBuffer, "shapes", expected); +} + +UNIT_TEST(DeserializerFromJson_Networks) +{ + string const jsonBuffer = R"( + { + "networks": [ + { + "id": 2, + "title": "Минский метрополитен" + } + ]})"; + + vector const expected = {Network(2 /* network id */, "Минский метрополитен" /* title */)}; + TestDeserializerFromJson(jsonBuffer, "networks", expected); +} } // namespace diff --git a/generator/transit_generator.cpp b/generator/transit_generator.cpp index 707bb776af..61a501ab1a 100644 --- a/generator/transit_generator.cpp +++ b/generator/transit_generator.cpp @@ -60,7 +60,12 @@ namespace transit // DeserializerFromJson --------------------------------------------------------------------------- void DeserializerFromJson::operator()(m2::PointD & p, char const * name) { - json_t * pointItem = my::GetJSONObligatoryField(m_node, name); + json_t * pointItem = nullptr; + if (name == nullptr) + pointItem = m_node; // Array item case + else + pointItem = my::GetJSONObligatoryField(m_node, name); + CHECK(json_is_object(pointItem), ()); FromJSONObject(pointItem, "x", p.x); FromJSONObject(pointItem, "y", p.y); @@ -120,13 +125,22 @@ void BuildTransit(string const & mwmPath, string const & transitDir) SerializeObject(root, "stops", serializer); header.m_gatesOffset = base::checked_cast(w.Pos() - startOffset); - // @TODO(bykoianko) Gates should be added after stops but before edges. + SerializeObject(root, "gates", serializer); + header.m_edgesOffset = base::checked_cast(w.Pos() - startOffset); SerializeObject(root, "edges", serializer); header.m_transfersOffset = base::checked_cast(w.Pos() - startOffset); - // @TODO(bykoianko) It's necessary to serialize other transit graph data here. + SerializeObject(root, "transfers", serializer); + header.m_linesOffset = base::checked_cast(w.Pos() - startOffset); + SerializeObject(root, "lines", serializer); + header.m_shapesOffset = base::checked_cast(w.Pos() - startOffset); + + SerializeObject(root, "shapes", serializer); + header.m_networksOffset = base::checked_cast(w.Pos() - startOffset); + + SerializeObject(root, "networks", serializer); header.m_endOffset = base::checked_cast(w.Pos() - startOffset); // Rewriting header info. @@ -134,7 +148,7 @@ void BuildTransit(string const & mwmPath, string const & transitDir) w.Seek(startOffset); header.Visit(serializer); w.Seek(endOffset); - LOG(LINFO, (TRANSIT_FILE_TAG, "section is ready. Size:", header.m_endOffset)); + LOG(LINFO, (TRANSIT_FILE_TAG, "section is ready. Header:", header)); } } // namespace transit } // namespace routing diff --git a/routing_common/routing_common_tests/transit_test.cpp b/routing_common/routing_common_tests/transit_test.cpp index ef5363588d..042be02f46 100644 --- a/routing_common/routing_common_tests/transit_test.cpp +++ b/routing_common/routing_common_tests/transit_test.cpp @@ -59,10 +59,56 @@ UNIT_TEST(Transit_StopSerialization) } } +UNIT_TEST(Transit_GateSerialization) +{ + Gate gate(12345 /* feature id */, true /* entrance */, false /* exit */, 117.8 /* weight */, + {1, 2, 3} /* stop ids */, {30.0, 50.0} /* point */); + TestSerialization(gate); +} + UNIT_TEST(Transit_EdgeSerialization) { Edge edge(1 /* start stop id */, 2 /* finish stop id */, 123.4 /* weight */, 11 /* line id */, false /* transfer */, {1, 2, 3} /* shape ids */); TestSerialization(edge); } + +UNIT_TEST(Transit_TransferSerialization) +{ + Transfer transfer(1 /* id */, {40.0, 35.0} /* point */, {1, 2, 3} /* stop ids */); + TestSerialization(transfer); +} + +UNIT_TEST(Transit_LineSerialization) +{ + { + Line line(1 /* line id */, "2" /* number */, "Линия" /* title */, + "subway" /* type */, 3 /* network id */, {} /* stop ids */); + TestSerialization(line); + } + { + Line line(10 /* line id */, "11" /* number */, "Линия" /* title */, + "subway" /* type */, 12 /* network id */, {13, 14, 15} /* stop ids */); + TestSerialization(line); + } +} + +UNIT_TEST(Transit_ShapeSerialization) +{ + { + Shape shape(1 /* shape id */, 10 /* stop 1 id */, 11 /* stop 2 id */, {} /* polyline */); + TestSerialization(shape); + } + { + Shape shape(1 /* shape id */, 10 /* stop 1 id */, 11 /* stop 2 id */, + {m2::PointD(20.0, 20.0), m2::PointD(21.0, 21.0), m2::PointD(22.0, 22.0)} /* polyline */); + TestSerialization(shape); + } +} + +UNIT_TEST(Transit_NetworkSerialization) +{ + Network network(0 /* network id */, "Title" /* title */); + TestSerialization(network); +} } // namespace diff --git a/routing_common/transit_types.cpp b/routing_common/transit_types.cpp index 92393cf207..0e0ca73356 100644 --- a/routing_common/transit_types.cpp +++ b/routing_common/transit_types.cpp @@ -2,6 +2,12 @@ #include "routing_common/transit_serdes.hpp" +namespace +{ +double constexpr kWeightEqualEpsilon = 1e-2; +double constexpr kPointsEqualEpsilon = 1e-6; +} // namespace + namespace routing { namespace transit @@ -62,6 +68,27 @@ bool Stop::IsEqualForTesting(Stop const & stop) const m_lineIds == stop.m_lineIds && my::AlmostEqualAbs(m_point, stop.m_point, kPointsEqualEpsilon); } +// Gate ------------------------------------------------------------------------------------------- +Gate::Gate(FeatureId featureId, bool entrance, bool exit, double weight, + std::vector const & stopIds, m2::PointD const & point) + : m_featureId(featureId) + , m_entrance(entrance) + , m_exit(exit) + , m_weight(weight) + , m_stopIds(stopIds) + , m_point(point) +{ +} + +bool Gate::IsEqualForTesting(Gate const & gate) const +{ + return m_featureId == gate.m_featureId && m_entrance == gate.m_entrance && + m_exit == gate.m_exit && + my::AlmostEqualAbs(m_weight, gate.m_weight, kWeightEqualEpsilon) && + m_stopIds == gate.m_stopIds && + my::AlmostEqualAbs(m_point, gate.m_point, kPointsEqualEpsilon); +} + // Edge ------------------------------------------------------------------------------------------- Edge::Edge(StopId startStopId, StopId finishStopId, double weight, LineId lineId, bool transfer, vector const & shapeIds) @@ -76,11 +103,74 @@ Edge::Edge(StopId startStopId, StopId finishStopId, double weight, LineId lineId bool Edge::IsEqualForTesting(Edge const & edge) const { - double constexpr kWeightEqualEpsilon = 1e-2; return m_startStopId == edge.m_startStopId && m_finishStopId == edge.m_finishStopId && my::AlmostEqualAbs(m_weight, edge.m_weight, kWeightEqualEpsilon) && m_lineId == edge.m_lineId && m_transfer == edge.m_transfer && m_shapeIds == edge.m_shapeIds; } + +// Transfer --------------------------------------------------------------------------------------- +Transfer::Transfer(StopId id, m2::PointD const & point, std::vector const & stopIds) + : m_id(id), m_point(point), m_stopIds(stopIds) +{ +} + +bool Transfer::IsEqualForTesting(Transfer const & transfer) const +{ + return m_id == transfer.m_id && + my::AlmostEqualAbs(m_point, transfer.m_point, kPointsEqualEpsilon) && + m_stopIds == transfer.m_stopIds; +} + +// Line ------------------------------------------------------------------------------------------- +Line::Line(LineId id, std::string const & number, std::string const & title, + std::string const & type, NetworkId networkId, std::vector const & stopIds) + : m_id(id) + , m_number(number) + , m_title(title) + , m_type(type) + , m_networkId(networkId) + , m_stopIds(stopIds) +{ +} + +bool Line::IsEqualForTesting(Line const & line) const +{ + return m_id == line.m_id && m_number == line.m_number && m_title == line.m_title && + m_type == line.m_type && m_networkId == line.m_networkId && m_stopIds == line.m_stopIds; +} + +// Shape ------------------------------------------------------------------------------------------ +Shape::Shape(ShapeId id, StopId stop1_id, StopId stop2_id, std::vector const & polyline) + : m_id(id), m_stop1_id(stop1_id), m_stop2_id(stop2_id), m_polyline(polyline) +{ +} + +bool Shape::IsEqualForTesting(Shape const & shape) const +{ + if (!(m_id == shape.m_id && m_stop1_id == shape.m_stop1_id && m_stop2_id == shape.m_stop2_id && + m_polyline.size() == shape.m_polyline.size())) + { + return false; + } + + for (size_t i = 0; i < m_polyline.size(); ++i) + { + if (!my::AlmostEqualAbs(m_polyline[i], shape.m_polyline[i], kPointsEqualEpsilon)) + return false; + } + return true; +} + +// Network ---------------------------------------------------------------------------------------- +Network::Network(NetworkId id, std::string const & title) +: m_id(id), m_title(title) +{ +} + +bool Network::IsEqualForTesting(Network const & shape) const +{ + return m_id == shape.m_id && m_title == shape.m_title; +} } // namespace transit } // namespace routing diff --git a/routing_common/transit_types.hpp b/routing_common/transit_types.hpp index 7b96872029..c71279bd23 100644 --- a/routing_common/transit_types.hpp +++ b/routing_common/transit_types.hpp @@ -81,6 +81,28 @@ private: // and deserialization. }; +class Gate +{ +public: + Gate() = default; + Gate(FeatureId featureId, bool entrance, bool exit, double weight, std::vector const & stopIds, + m2::PointD const & point); + bool IsEqualForTesting(Gate const & gate) const; + + DECLARE_VISITOR_AND_DEBUG_PRINT(Gate, visitor(m_featureId, "osm_id"), + visitor(m_entrance, "entrance"), + visitor(m_exit, "exit"), visitor(m_weight, "weight"), + visitor(m_stopIds, "stop_ids"), visitor(m_point, "point")) + +private: + FeatureId m_featureId = kInvalidFeatureId; + bool m_entrance = true; + bool m_exit = true; + double m_weight = kInvalidWeight; + std::vector m_stopIds; + m2::PointD m_point; +}; + class Edge { public: @@ -104,6 +126,76 @@ private: std::vector m_shapeIds; }; -// @TODO(bykoianko) Data structures and methods for other transit data should be implemented in here. +class Transfer +{ +public: + Transfer() = default; + Transfer(StopId id, m2::PointD const & point, std::vector const & stopIds); + bool IsEqualForTesting(Transfer const & transfer) const; + + DECLARE_VISITOR_AND_DEBUG_PRINT(Transfer, visitor(m_id, "id"), visitor(m_point, "point"), + visitor(m_stopIds, "stop_ids")) + +private: + StopId m_id = kInvalidStopId; + m2::PointD m_point; + std::vector m_stopIds; + + // @TODO(bykoianko) It's necessary to add field m_titleAnchors here and implement serialization + // and deserialization. +}; + +class Line +{ +public: + Line() = default; + Line(LineId id, std::string const & number, std::string const & title, std::string const & type, + NetworkId networkId, std::vector const & stopIds); + bool IsEqualForTesting(Line const & line) const; + + DECLARE_VISITOR_AND_DEBUG_PRINT(Line, visitor(m_id, "id"), visitor(m_number, "number"), + visitor(m_title, "title"), visitor(m_type, "type"), + visitor(m_networkId, "network_id"), + visitor(m_stopIds, "stop_ids")) + +private: + LineId m_id = kInvalidLineId; + std::string m_number; + std::string m_title; + std::string m_type; + NetworkId m_networkId = kInvalidNetworkId; + std::vector m_stopIds; +}; + +class Shape +{ +public: + Shape() = default; + Shape(ShapeId id, StopId stop1_id, StopId stop2_id, std::vector const & polyline); + bool IsEqualForTesting(Shape const & shape) const; + + DECLARE_VISITOR_AND_DEBUG_PRINT(Shape, visitor(m_id, "id"), visitor(m_stop1_id, "stop1_id"), + visitor(m_stop2_id, "stop2_id"), visitor(m_polyline, "polyline")) + +private: + ShapeId m_id = kInvalidShapeId; + StopId m_stop1_id = kInvalidStopId; + StopId m_stop2_id = kInvalidStopId; + std::vector m_polyline; +}; + +class Network +{ +public: + Network() = default; + Network(NetworkId id, std::string const & title); + bool IsEqualForTesting(Network const & shape) const; + + DECLARE_VISITOR_AND_DEBUG_PRINT(Network, visitor(m_id, "id"), visitor(m_title, "title")) + +private: + NetworkId m_id; + std::string m_title; +}; } // namespace transit } // namespace routing