LCOV - code coverage report
Current view: top level - src/utils/router - CCHGraph.h (source / functions) Coverage Total Hit
Test: lcov.info Lines: 98.4 % 125 123
Test Date: 2026-10-12 15:53:19 Functions: 91.7 % 24 22

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    CCHGraph.h
      15              : /// @author  Pranav Sateesh
      16              : /// @date    2026
      17              : ///
      18              : // Edge-graph <-> RoutingKit CCH graph mapping, shared between the
      19              : // simulation (CCHGraph over MSEdge) and duarouter (RODUACCHGraph over
      20              : // ROEdge) -- the GRAPH template argument of CCHRouter.
      21              : //
      22              : // RoutingKit node = one non-internal, non-taz edge (densely re-indexed).
      23              : // RoutingKit arc  = one edge-to-edge connection (u -> to) from
      24              : //                   getViaSuccessors, with any internal/via edge chain
      25              : //                   between them folded into the arc weight.
      26              : //
      27              : // The topology (arcs/order/CCH) is metric-INDEPENDENT and built once from
      28              : // the class-union successor sets. Weights are metric-DEPENDENT centisecond
      29              : // integers recomputed from the caller's effort function (fillInputWeights /
      30              : // computeArcWeight); forbidden arcs get exactly RoutingKit::inf_weight so
      31              : // the arc set (topology) never changes.
      32              : //
      33              : // ARC WEIGHT CONVENTION (load-bearing):
      34              : //   w(u -> to) = round(100 * ( viaEffort(u..to) + effort(to) ))   [centiseconds]
      35              : // so a path u0->u1->...->uk sums arc weights = via + effort(u1..uk), and the
      36              : // FULL route cost equals  query_distance + effort(u0)  (the source edge's
      37              : // own effort, added back by the caller as the source seed). This is NOT
      38              : // round(100*recomputeCosts({u,to})): that would double-count effort(u) on
      39              : // every hop of a multi-edge path.
      40              : /****************************************************************************/
      41              : #pragma once
      42              : #include <config.h>
      43              : 
      44              : #include <cmath>
      45              : #include <limits>
      46              : #include <map>
      47              : #include <vector>
      48              : #include <utils/common/MsgHandler.h>
      49              : #include <utils/common/StdDefs.h>
      50              : #include <utils/common/SUMOVehicleClass.h>
      51              : #include <utils/common/ToString.h>
      52              : #include <utils/geom/GeoConvHelper.h>
      53              : #include <utils/geom/Position.h>
      54              : #ifdef __GNUC__
      55              : #pragma GCC diagnostic push
      56              : #pragma GCC diagnostic ignored "-Wunused-parameter"
      57              : #endif
      58              : #ifdef _MSC_VER
      59              : #pragma warning(push)
      60              : #pragma warning(disable: 4267 4458)
      61              : #endif
      62              : #include <routingkit/customizable_contraction_hierarchy.h>
      63              : #include <routingkit/nested_dissection.h>
      64              : #ifdef _MSC_VER
      65              : #pragma warning(pop)
      66              : #endif
      67              : #ifdef __GNUC__
      68              : #pragma GCC diagnostic pop
      69              : #endif
      70              : 
      71              : // #define CCH_DEBUG
      72              : 
      73              : 
      74              : // ===========================================================================
      75              : // class definitions
      76              : // ===========================================================================
      77              : /**
      78              :  * @class CCHGraph
      79              :  * @brief Metric-independent RoutingKit CCH topology over the PURE road graph.
      80              :  *
      81              :  * Nodes are the non-internal, non-TAZ edges. TAZ connectors are deliberately
      82              :  * NOT nodes: a connector is a high-degree "star" (it links every member edge
      83              :  * of its zone), and eliminating a degree-d node in a contraction hierarchy
      84              :  * forms a d-clique => treewidth >= d, which poisons the whole hierarchy
      85              :  * regardless of elimination order. Instead, TAZ connectors are kept host-side
      86              :  * as query-time source/target SETS (the "phantom node" pattern) and resolved
      87              :  * with RoutingKit's native multi-source/multi-target query.
      88              :  *
      89              :  * Build once at start; hold a single owned instance and hand out a const
      90              :  * reference to every router clone (the CustomizableContractionHierarchy is
      91              :  * read-only after construction and safe to share across threads).
      92              :  *
      93              :  * @tparam E the edge type (MSEdge / ROEdge)
      94              :  * @tparam V the vehicle type of the effort callback (SUMOVehicle / ROVehicle)
      95              :  */
      96              : template<class E, class V>
      97              : class CCHGraph {
      98              : public:
      99              :     /// @brief effort callback signature, matching SUMOAbstractRouter::Operation
     100              :     typedef double (*EffortOperation)(const E* const, const V* const, double);
     101              : 
     102              :     /// @brief sentinel for "not a routable node"
     103              :     static const unsigned INVALID_NODE;
     104              : 
     105              :     /** @brief Build the union line graph + CCH from the given edges.
     106              :      *
     107              :      * Arcs come from getViaSuccessors(SVC_IGNORING) (the raw, unfiltered
     108              :      * successor set): the arc set must be class-independent so every class
     109              :      * (including e.g. tram-only rail arcs) exists. Per-class permissions are
     110              :      * applied later as inf_weight in the metric, never by removing arcs.
     111              :      */
     112          860 :     explicit CCHGraph(const std::vector<E*>& allEdges) {
     113              :         // 1. dense node indexing over real (non-internal, non-taz) edges.
     114              :         unsigned maxNumID = 0;
     115        49441 :         for (const E* e : allEdges) {
     116        48581 :             maxNumID = MAX2(maxNumID, (unsigned)e->getNumericalID());
     117              :         }
     118          860 :         myEdgeToNode.assign(maxNumID + 1, INVALID_NODE);
     119        49441 :         for (const E* e : allEdges) {
     120              :             // excluded are junction-internal edges and the district STAR
     121              :             // connectors ("<taz>-source"/"-sink", the high-degree phantom
     122              :             // edges -- see the class documentation). Legacy net edges with
     123              :             // function="connector" are ordinary routable geometry for the
     124              :             // exact routers and therefore stay ordinary nodes here; only the
     125              :             // paired district connectors (identified by their
     126              :             // otherTazConnector link) are kept out of the hierarchy.
     127        48581 :             if (e->isInternal() || isStarConnector(e)) {
     128        32963 :                 continue;
     129              :             }
     130        15618 :             const unsigned node = (unsigned)myNodeToEdge.size();
     131        15618 :             myEdgeToNode[e->getNumericalID()] = node;
     132        15618 :             myNodeToEdge.push_back(e);
     133              :         }
     134          860 :         const unsigned nNodes = (unsigned)myNodeToEdge.size();
     135              : 
     136              :         // 2. arcs from the union successors; coordinates for the orderer.
     137          860 :         std::vector<float> lon(nNodes);
     138          860 :         std::vector<float> lat(nNodes);
     139        16478 :         for (unsigned n = 0; n < nNodes; n++) {
     140        15618 :             const E* e = myNodeToEdge[n];
     141              :             Position p;
     142        15618 :             if (!e->getLanes().empty()) {
     143        15618 :                 p = e->getLanes()[0]->geometryPositionAtOffset(e->getLength() * 0.5);
     144              :             } else {
     145            0 :                 const auto* j = e->getToJunction() != nullptr ? e->getToJunction() : e->getFromJunction();
     146            0 :                 p = (j != nullptr) ? j->getPosition() : Position(0., 0.);
     147              :             }
     148        15618 :             if (GeoConvHelper::getFinal().usingGeoProjection()) {
     149         1443 :                 GeoConvHelper::getFinal().cartesian2geo(p);
     150              :             }
     151        15618 :             lon[n] = (float)p.x();
     152        15618 :             lat[n] = (float)p.y();
     153              :         }
     154        16478 :         for (unsigned n = 0; n < nNodes; n++) {
     155        15618 :             const E* u = myNodeToEdge[n];
     156        51516 :             for (const auto& follower : u->getViaSuccessors(SVC_IGNORING)) {
     157        35898 :                 const E* to = follower.first;
     158        35898 :                 if (to == nullptr || to->getNumericalID() >= (int)myEdgeToNode.size()) {
     159         1510 :                     continue;
     160              :                 }
     161        35898 :                 const unsigned toNode = myEdgeToNode[to->getNumericalID()];
     162        35898 :                 if (toNode == INVALID_NODE) {
     163         1510 :                     continue;  // successor is internal/taz -> not a routable node
     164              :                 }
     165        34388 :                 myArcOf[std::make_pair(n, toNode)] = (unsigned)myArcTail.size();
     166        34388 :                 myArcTail.push_back(n);
     167        34388 :                 myArcHead.push_back(toNode);
     168        34388 :                 myArcVia.push_back(follower.second);  // leading internal edge or nullptr
     169              :             }
     170              :         }
     171          860 :         myArcPerm.assign(myArcTail.size(), 0);
     172          860 :         myPrimedClasses = 0;
     173              :         // 2b. reverse image for sparse re-customization: for every arc, the
     174              :         // edges its weight reads at fill time (head edge + folded via chain;
     175              :         // the tail edge is deliberately absent from the weight, see the arc
     176              :         // weight convention above)
     177          860 :         myEdgeToArcs.resize(myEdgeToNode.size());
     178        35248 :         for (unsigned a = 0; a < (unsigned)myArcHead.size(); a++) {
     179        34388 :             myEdgeToArcs[myNodeToEdge[myArcHead[a]]->getNumericalID()].push_back(a);
     180        34388 :             const E* via = myArcVia[a];
     181        64854 :             while (via != nullptr && via->isInternal()) {
     182        30466 :                 if (via->getNumericalID() >= 0 && via->getNumericalID() < (int)myEdgeToArcs.size()) {
     183        30466 :                     myEdgeToArcs[via->getNumericalID()].push_back(a);
     184              :                 }
     185        30466 :                 const auto& vs = via->getViaSuccessors();
     186        30466 :                 via = vs.empty() ? nullptr : vs.front().second;
     187              :             }
     188              :         }
     189              : 
     190              :         // 3. TAZ member sets (query-time "phantom nodes"): successors for a
     191              :         // source connector (entry edges), predecessors for a sink connector.
     192        51565 :         for (const E* e : allEdges) {
     193        46457 :             if (!isStarConnector(e)) {
     194        46457 :                 continue;
     195              :             }
     196          732 :             std::vector<unsigned> srcNodes;
     197         3641 :             for (const auto& follower : e->getViaSuccessors(SVC_IGNORING)) {
     198         3034 :                 const unsigned m = nodeOf(follower.first);
     199         1517 :                 if (m != INVALID_NODE) {
     200         1517 :                     srcNodes.push_back(m);
     201              :                 }
     202              :             }
     203          732 :             std::vector<unsigned> snkNodes;
     204         3634 :             for (const E* pred : e->getPredecessors()) {
     205         1510 :                 const unsigned m = nodeOf(pred);
     206         1510 :                 if (m != INVALID_NODE) {
     207         1510 :                     snkNodes.push_back(m);
     208              :                 }
     209              :             }
     210         2124 :             if (!srcNodes.empty()) {
     211          622 :                 myTazSrcNodes[e] = std::move(srcNodes);
     212              :             }
     213         2124 :             if (!snkNodes.empty()) {
     214          619 :                 myTazSnkNodes[e] = std::move(snkNodes);
     215              :             }
     216              :         }
     217              : 
     218              :         // 4. metric-independent order + CCH topology (the one-time work).
     219              :         // @todo persist the order keyed by a network hash (recompute is fast)
     220          860 :         std::vector<unsigned> order = RoutingKit::compute_nested_node_dissection_order_using_inertial_flow(
     221          860 :                                           nNodes, myArcTail, myArcHead, lat, lon);
     222          860 :         myCCH = RoutingKit::CustomizableContractionHierarchy(order, myArcTail, myArcHead);
     223              : #ifdef CCH_DEBUG
     224              :         std::cout << "CCH: " << nNodes << " road nodes, "
     225              :                   << arcCount() << " arcs, "
     226              :                   << myCCH.cch_arc_count() << " cch-arcs (fill x" << (double)myCCH.cch_arc_count() / MAX2((unsigned)1, arcCount())
     227              :                   << "), " << myTazSrcNodes.size() << " TAZ sources, " << myTazSnkNodes.size() << " TAZ sinks." << std::endl;
     228              : #endif
     229         1414 :     }
     230              : 
     231         1224 :     virtual ~CCHGraph() {}
     232              : 
     233              :     /// @brief the immutable CCH (share const& across clones)
     234              :     const RoutingKit::CustomizableContractionHierarchy& cch() const {
     235      1355381 :         return myCCH;
     236              :     }
     237              : 
     238              :     /// @brief number of input arcs (== number of mapped connections)
     239              :     unsigned arcCount() const {
     240       238443 :         return (unsigned)myArcTail.size();
     241              :     }
     242              : 
     243              :     /// @brief size of the edge numerical-id space the graph was built over
     244              :     /// (for callers keeping per-edge-id side arrays, e.g. CCHMetricFamily)
     245              :     unsigned edgeIdSpace() const {
     246              :         return (unsigned)myEdgeToNode.size();
     247              :     }
     248              : 
     249              :     /// @brief RoutingKit node index for an edge, or INVALID_NODE if not a node
     250              :     unsigned nodeOf(const E* e) const {
     251       284942 :         if (e == nullptr || e->getNumericalID() < 0 || e->getNumericalID() >= (int)myEdgeToNode.size()) {
     252              :             return INVALID_NODE;
     253              :         }
     254       284942 :         return myEdgeToNode[e->getNumericalID()];
     255              :     }
     256              : 
     257              :     /// @brief the edge backing a RoutingKit node
     258              :     const E* edgeOf(unsigned node) const {
     259        89881 :         return myNodeToEdge[node];
     260              :     }
     261              : 
     262              :     /// @brief road member nodes of a TAZ-source connector (its entry edges),
     263              :     /// to seed multi-SOURCE queries; empty if @p taz is not a known TAZ source.
     264          253 :     const std::vector<unsigned>& tazSources(const E* taz) const {
     265          253 :         static const std::vector<unsigned> empty;
     266              :         const auto it = myTazSrcNodes.find(taz);
     267          253 :         return it == myTazSrcNodes.end() ? empty : it->second;
     268              :     }
     269              : 
     270              :     /// @brief road member nodes of a TAZ-sink connector (its exit edges),
     271              :     /// to seed multi-TARGET queries; empty if @p taz is not a known TAZ sink.
     272          268 :     const std::vector<unsigned>& tazSinks(const E* taz) const {
     273          268 :         static const std::vector<unsigned> empty;
     274              :         const auto it = myTazSnkNodes.find(taz);
     275          268 :         return it == myTazSnkNodes.end() ? empty : it->second;
     276              :     }
     277              : 
     278              :     /// @brief the arcs whose weight depends on the given edge (arcs it heads
     279              :     /// plus arcs whose folded via chain contains it), for sparse
     280              :     /// re-customization; empty for edges that are not part of the graph
     281      7212915 :     const std::vector<unsigned>& arcsOfEdge(const E* e) const {
     282      7212915 :         static const std::vector<unsigned> empty;
     283      7212915 :         if (e == nullptr || e->getNumericalID() < 0 || e->getNumericalID() >= (int)myEdgeToArcs.size()) {
     284              :             return empty;
     285              :         }
     286      7212915 :         return myEdgeToArcs[e->getNumericalID()];
     287              :     }
     288              : 
     289              :     /** @brief Map a RoutingKit node path back to the edge sequence.
     290              :      *
     291              :      * The result lists REAL edges only, exactly like the exact routers:
     292              :      * junction-internal edges are never part of SUMO route vectors -- their
     293              :      * cost is folded into the arc weights for the query and re-added from
     294              :      * consecutive real edges by SUMOAbstractRouter::recomputeCosts. */
     295        79754 :     void expandNodePath(const std::vector<unsigned>& nodePath,
     296              :                         std::vector<const E*>& into) const {
     297        79754 :         into.reserve(into.size() + nodePath.size());
     298       442880 :         for (unsigned node : nodePath) {
     299       363126 :             into.push_back(myNodeToEdge[node]);
     300              :         }
     301        79754 :     }
     302              : 
     303              :     /// @brief effort accumulated crossing the internal/via chain that leads from
     304              :     /// one real edge onto its successor (mirrors SUMOAbstractRouter::updateViaEdgeCost).
     305     16453314 :     static double viaChainEffort(const E* via, EffortOperation effort,
     306              :                                  const V* veh, double time) {
     307              :         double sum = 0.;
     308     37232037 :         while (via != nullptr && via->isInternal()) {
     309     20778723 :             sum += effort(via, veh, time);
     310     20778723 :             const auto& vs = via->getViaSuccessors();
     311     20778723 :             via = vs.empty() ? nullptr : vs.front().second;
     312              :         }
     313     16453314 :         return sum;
     314              :     }
     315              : 
     316              : public:
     317              :     /** @brief Recompute the input weight of one arc -- the exact per-arc body
     318              :      * of fillInputWeights (same masking, folding and rounding), exposed so a
     319              :      * sparse update can refresh only the arcs of edges that actually moved.
     320              :      *
     321              :      * Forbidden (inf_weight) if EITHER the connection does not permit the
     322              :      * class (static, connection-level, matches getViaSuccessors(vClass); see
     323              :      * primeClassMask) OR the destination edge does not currently permit the
     324              :      * class (dynamic, edge-level -- catches runtime closures via
     325              :      * MSLane::setPermissions; a no-op where permissions are static). */
     326     17170289 :     unsigned computeArcWeight(unsigned a, EffortOperation effort, SUMOVehicleClass maskClass,
     327              :                               const V* veh, double time) const {
     328              :         if (maskClass != SVC_IGNORING
     329     17170289 :                 && (((myArcPerm[a] & maskClass) == 0)
     330     16453284 :                     || ((edgeOf(myArcHead[a])->getPermissions() & maskClass) == 0))) {
     331              :             return RoutingKit::inf_weight;
     332              :         }
     333     16453314 :         return computeArcWeightRaw(a, effort, veh, time);
     334              :     }
     335              : 
     336              :     /** @brief Fill a centisecond input-weight buffer for one vehicle class.
     337              :      *
     338              :      * Primes the class's connection mask on first use (see primeClassMask for
     339              :      * the synchronization contract), then applies computeArcWeight per arc.
     340              :      * @param[in] effort     the effort Operation
     341              :      * @param[in] maskClass  arcs whose connection or destination edge does NOT
     342              :      *   permit this class become inf_weight; SVC_IGNORING masks nothing
     343              :      * @param[in] veh    reference vehicle for the effort floor (may be null)
     344              :      * @param[in] time   seconds, passed to the effort fn
     345              :      * @param[out] weight resized to arcCount(); w[a] in [0, inf_weight]
     346              :      */
     347         3518 :     void fillInputWeights(EffortOperation effort, SUMOVehicleClass maskClass,
     348              :                           const V* veh, double time,
     349              :                           std::vector<unsigned>& weight) const {
     350         3518 :         primeClassMask(maskClass);
     351         3518 :         weight.resize(arcCount());
     352       238443 :         for (unsigned a = 0; a < arcCount(); a++) {
     353       234925 :             weight[a] = computeArcWeight(a, effort, maskClass, veh, time);
     354              :         }
     355         3518 :     }
     356              : 
     357              :     /** @brief Drop every primed connection mask so the next fill re-primes
     358              :      * from the CURRENT successor lists.
     359              :      *
     360              :      * Runtime permission changes (closingReroute / closingLaneReroute) alter
     361              :      * getViaSuccessors(vClass); a mask primed before the change keeps serving
     362              :      * the old connection set -- a closure active at first customize would
     363              :      * even outlive its re-opening. Same synchronization contract as
     364              :      * primeClassMask: call only while no query is in flight. */
     365              :     void invalidateClassMasks() const {
     366           43 :         myArcPerm.assign(myArcPerm.size(), 0);
     367           43 :         myPrimedClasses = 0;
     368              :     }
     369              : 
     370              : private:
     371              :     /// @brief whether the edge is a district star connector (the paired
     372              :     /// "<taz>-source"/"-sink" phantom edge) as opposed to a legacy
     373              :     /// function="connector" net edge, which stays routable
     374              :     static bool isStarConnector(const E* e) {
     375        66323 :         return e->isTazConnector() && e->getOtherTazConnector() != nullptr;
     376              :     }
     377              : 
     378              :     /** @brief Record which arcs @p vClass may traverse in the per-arc
     379              :      * CONNECTION-level permission bitmask -- exactly the arcs
     380              :      * getViaSuccessors(vClass) yields (edge-level getPermissions() is too
     381              :      * coarse on mixed-lane edges). No-op if the class was primed before.
     382              :      *
     383              :      * NOT internally synchronized: concurrent calls for an unprimed class
     384              :      * must be serialized by the caller. In practice the simulation primes on
     385              :      * the main thread at the customization barrier (no query in flight) and
     386              :      * duarouter primes under the RODUACCHMetrics mutex; primed classes make
     387              :      * this a lock-free bit test on the hot path. */
     388         3518 :     void primeClassMask(SUMOVehicleClass vClass) const {
     389         3518 :         if (vClass == SVC_IGNORING || (myPrimedClasses & vClass) == vClass) {
     390              :             return;
     391              :         }
     392        48597 :         for (unsigned n = 0; n < (unsigned)myNodeToEdge.size(); n++) {
     393       168790 :             for (const auto& follower : myNodeToEdge[n]->getViaSuccessors(vClass)) {
     394       122075 :                 const unsigned toNode = nodeOf(follower.first);
     395       122075 :                 if (toNode == INVALID_NODE) {
     396              :                     continue;
     397              :                 }
     398       117496 :                 const auto it = myArcOf.find(std::make_pair(n, toNode));
     399       117496 :                 if (it != myArcOf.end()) {
     400       117496 :                     myArcPerm[it->second] |= (SVCPermissions)vClass;
     401              :                 }
     402              :             }
     403              :         }
     404         1882 :         myPrimedClasses |= (SVCPermissions)vClass;
     405              :     }
     406              : 
     407              :     /** @brief The unmasked weight of one arc: via-chain effort + head-edge
     408              :      * effort, rounded to centiseconds and clamped below inf_weight (NaN /
     409              :      * inf / overflow become inf_weight = forbidden). */
     410     16453314 :     unsigned computeArcWeightRaw(unsigned a, EffortOperation effort,
     411              :                                  const V* veh, double time) const {
     412     16453314 :         const E* to = myNodeToEdge[myArcHead[a]];
     413              :         const double bigEffort = (double)(RoutingKit::inf_weight - 1) / 100.0;
     414     16453314 :         const double eff = viaChainEffort(myArcVia[a], effort, veh, time)
     415     16453314 :                            + effort(to, veh, time);
     416     16453314 :         if (!(eff < bigEffort)) {
     417              :             return RoutingKit::inf_weight;
     418              :         }
     419     16453314 :         const long long cs = std::llround(eff * 100.0);
     420              :         return (unsigned)(cs < 0 ? 0 : (cs >= (long long)RoutingKit::inf_weight
     421     16453314 :                                         ? RoutingKit::inf_weight - 1 : cs));
     422              :     }
     423              : 
     424              :     /// @brief node index -> backing edge
     425              :     std::vector<const E*> myNodeToEdge;
     426              :     /// @brief edge numerical id -> node index (INVALID_NODE if not a node)
     427              :     std::vector<unsigned> myEdgeToNode;
     428              :     /// @brief per-arc endpoints
     429              :     std::vector<unsigned> myArcTail;
     430              :     std::vector<unsigned> myArcHead;
     431              :     /// @brief per-arc leading via/internal edge (nullptr if none) for path re-expansion
     432              :     std::vector<const E*> myArcVia;
     433              :     /// @brief (tail node, head node) -> arc index
     434              :     std::map<std::pair<unsigned, unsigned>, unsigned> myArcOf;
     435              :     /// @brief per-arc CONNECTION-level permission bitmask, accumulated per
     436              :     /// primed class (see primeClassMask); mutable lazy cache
     437              :     mutable std::vector<SVCPermissions> myArcPerm;
     438              :     /// @brief the classes already primed into myArcPerm
     439              :     mutable SVCPermissions myPrimedClasses;
     440              :     /// @brief edge numerical id -> arcs whose weight reads that edge (head +
     441              :     /// folded via edges); the reverse image of computeArcWeightRaw's inputs
     442              :     std::vector<std::vector<unsigned> > myEdgeToArcs;
     443              :     /// @brief TAZ-source connector edge -> its entry-edge road node ids
     444              :     std::map<const E*, std::vector<unsigned> > myTazSrcNodes;
     445              :     /// @brief TAZ-sink connector edge -> its exit-edge road node ids
     446              :     std::map<const E*, std::vector<unsigned> > myTazSnkNodes;
     447              :     /// @brief the immutable hierarchy
     448              :     RoutingKit::CustomizableContractionHierarchy myCCH;
     449              : 
     450              : private:
     451              :     CCHGraph(const CCHGraph&) = delete;
     452              :     CCHGraph& operator=(const CCHGraph&) = delete;
     453              : };
     454              : 
     455              : 
     456              : template<class E, class V>
     457              : const unsigned CCHGraph<E, V>::INVALID_NODE = RoutingKit::invalid_id;
        

Generated by: LCOV version 2.0-1