#include "generator/transit_generator.hpp" #include "generator/borders.hpp" #include "generator/utils.hpp" #include "routing/index_router.hpp" #include "routing/road_graph.hpp" #include "routing/routing_exceptions.hpp" #include "routing/vehicle_mask.hpp" #include "storage/country_info_getter.hpp" #include "storage/routing_helpers.hpp" #include "transit/transit_types.hpp" #include "indexer/mwm_set.hpp" #include "geometry/point2d.hpp" #include "geometry/rect2d.hpp" #include "geometry/region2d.hpp" #include "coding/file_writer.hpp" #include "platform/country_file.hpp" #include "platform/platform.hpp" #include "base/assert.hpp" #include "base/exception.hpp" #include "base/file_name_utils.hpp" #include "base/logging.hpp" #include #include #include "defines.hpp" #include "3party/jansson/src/jansson.h" using namespace generator; using namespace platform; using namespace routing; using namespace routing::transit; using namespace std; using namespace storage; namespace { void LoadBorders(string const & dir, CountryId const & countryId, vector & borders) { string const polyFile = base::JoinPath(dir, BORDERS_DIR, countryId + BORDERS_EXTENSION); borders.clear(); borders::LoadBorders(polyFile, borders); } void FillOsmIdToFeatureIdsMap(string const & osmIdToFeatureIdsPath, OsmIdToFeatureIdsMap & mapping) { CHECK(ForEachOsmId2FeatureId(osmIdToFeatureIdsPath, [&mapping](auto const & compositeId, auto featureId) { mapping[compositeId.m_mainId].push_back(featureId); }), (osmIdToFeatureIdsPath)); } string GetMwmPath(string const & mwmDir, CountryId const & countryId) { return base::JoinPath(mwmDir, countryId + DATA_FILE_EXTENSION); } /// \brief Calculates best pedestrian segment for every gate in |graphData.m_gates|. /// The result of the calculation is set to |Gate::m_bestPedestrianSegment| of every gate /// from |graphData.m_gates|. /// \note All gates in |graphData.m_gates| must have a valid |m_point| field before the call. void CalculateBestPedestrianSegments(string const & mwmPath, CountryId const & countryId, GraphData & graphData) { // Creating IndexRouter. SingleMwmDataSource dataSource(mwmPath); auto infoGetter = storage::CountryInfoReader::CreateCountryInfoGetter(GetPlatform()); CHECK(infoGetter, ()); auto const countryFileGetter = [&infoGetter](m2::PointD const & pt) { return infoGetter->GetRegionCountryId(pt); }; auto const getMwmRectByName = [&](string const & c) -> m2::RectD { CHECK_EQUAL(countryId, c, ()); return infoGetter->GetLimitRectForLeaf(c); }; CHECK_EQUAL(dataSource.GetMwmId().GetInfo()->GetType(), MwmInfo::COUNTRY, ()); auto numMwmIds = make_shared(); numMwmIds->RegisterFile(CountryFile(countryId)); // Note. |indexRouter| is valid while |dataSource| is valid. IndexRouter indexRouter(VehicleType::Pedestrian, false /* load altitudes */, CountryParentNameGetterFn(), countryFileGetter, getMwmRectByName, numMwmIds, MakeNumMwmTree(*numMwmIds, *infoGetter), traffic::TrafficCache(), dataSource.GetDataSource()); auto worldGraph = indexRouter.MakeSingleMwmWorldGraph(); // Looking for the best segment for every gate. 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 two way segments so // IndexRouter::FindBestSegments() method finds the same segments for |isOutgoing| == true // and |isOutgoing| == false. vector bestEdges; try { if (countryFileGetter(gate.GetPoint()) != countryId) continue; bool dummy = false; if (indexRouter.FindBestEdges(gate.GetPoint(), platform::CountryFile(countryId), m2::PointD::Zero() /* direction */, true /* isOutgoing */, FeaturesRoadGraph::kClosestEdgesRadiusM, *worldGraph, bestEdges, dummy)) { CHECK(!bestEdges.empty(), ()); IndexRouter::BestEdgeComparator bestEdgeComparator(gate.GetPoint(), m2::PointD::Zero() /* direction */); // Looking for the edge which is the closest to |gate.GetPoint()|. // @TODO It should be considered to change the format of transit section to keep all // candidates for every gate. auto const & bestEdge = *min_element( bestEdges.cbegin(), bestEdges.cend(), [&bestEdgeComparator](routing::Edge const & lhs, routing::Edge const & rhs) { return bestEdgeComparator.Compare(lhs, rhs) < 0; }); CHECK(bestEdge.GetFeatureId().IsValid(), ()); graphData.SetGateBestPedestrianSegment(i, SingleMwmSegment( bestEdge.GetFeatureId().m_index, bestEdge.GetSegId(), bestEdge.IsForward())); } } catch (MwmIsNotAliveException const & e) { LOG(LCRITICAL, ("Point of a gate belongs to the processed mwm:", countryId, "," "but the mwm is not alive. Gate:", gate, e.what())); } catch (RootException const & e) { LOG(LCRITICAL, ("Exception while looking for the best segment of a gate. CountryId:", countryId, ". Gate:", gate, e.what())); } } } } // namespace namespace routing { namespace transit { void DeserializeFromJson(OsmIdToFeatureIdsMap const & mapping, string const & transitJsonPath, GraphData & data) { Platform::EFileType fileType; Platform::EError const errCode = Platform::GetFileType(transitJsonPath, fileType); CHECK_EQUAL(errCode, Platform::EError::ERR_OK, ("Transit graph was not found:", transitJsonPath)); CHECK_EQUAL(fileType, Platform::EFileType::FILE_TYPE_REGULAR, ("Transit graph was not found:", transitJsonPath)); string jsonBuffer; try { GetPlatform().GetReader(transitJsonPath)->ReadAsString(jsonBuffer); } catch (RootException const & e) { LOG(LCRITICAL, ("Can't open", transitJsonPath, e.what())); } base::Json root(jsonBuffer.c_str()); CHECK(root.get() != nullptr, ("Cannot parse the json file:", transitJsonPath)); data.Clear(); try { data.DeserializeFromJson(root, mapping); } catch (RootException const & e) { LOG(LCRITICAL, ("Exception while parsing transit graph json. Json file path:", transitJsonPath, e.what())); } } void ProcessGraph(string const & mwmPath, CountryId const & countryId, OsmIdToFeatureIdsMap const & osmIdToFeatureIdsMap, GraphData & data) { CalculateBestPedestrianSegments(mwmPath, countryId, data); data.Sort(); data.CheckValidSortedUnique(); } void BuildTransit(string const & mwmDir, CountryId const & countryId, string const & osmIdToFeatureIdsPath, string const & transitDir) { LOG(LINFO, ("Building transit section for", countryId, "mwmDir:", mwmDir)); Platform::FilesList graphFiles; Platform::GetFilesByExt(base::AddSlashIfNeeded(transitDir), TRANSIT_FILE_EXTENSION, graphFiles); string const mwmPath = GetMwmPath(mwmDir, countryId); OsmIdToFeatureIdsMap mapping; FillOsmIdToFeatureIdsMap(osmIdToFeatureIdsPath, mapping); vector mwmBorders; LoadBorders(mwmDir, countryId, mwmBorders); GraphData jointData; for (auto const & fileName : graphFiles) { auto const filePath = base::JoinPath(transitDir, fileName); GraphData data; DeserializeFromJson(mapping, filePath, data); // @todo(bykoianko) Json should be clipped on feature generation step. It's much more efficient. data.ClipGraph(mwmBorders); jointData.AppendTo(data); } if (jointData.IsEmpty()) return; // Empty transit section. ProcessGraph(mwmPath, countryId, mapping, jointData); jointData.CheckValidSortedUnique(); FilesContainerW cont(mwmPath, FileWriter::OP_WRITE_EXISTING); auto writer = cont.GetWriter(TRANSIT_FILE_TAG); jointData.Serialize(*writer); } } // namespace transit } // namespace routing