void
NIXMLEdgesHandler::myEndElement(int element) {
    if (element == SUMO_TAG_EDGE && myCurrentEdge != 0) {
        if (!myIsUpdate) {
            try {
                if (!myEdgeCont.insert(myCurrentEdge)) {
                    WRITE_ERROR("Duplicate edge occured. ID='" + myCurrentID + "'");
                    delete myCurrentEdge;
                }
            } catch (InvalidArgument& e) {
                WRITE_ERROR(e.what());
                throw;
            } catch (...) {
                WRITE_ERROR("An important information is missing in edge '" + myCurrentID + "'.");
            }
        }
        if (mySplits.size() != 0) {
            std::vector<Split>::iterator i;
            NBEdge* e = myCurrentEdge;
            sort(mySplits.begin(), mySplits.end(), split_sorter());
            unsigned int noLanesMax = e->getNumLanes();
            // compute the node positions and sort the lanes
            for (i = mySplits.begin(); i != mySplits.end(); ++i) {
                (*i).gpos = e->getGeometry().positionAtLengthPosition((*i).pos);
                sort((*i).lanes.begin(), (*i).lanes.end());
                noLanesMax = MAX2(noLanesMax, (unsigned int)(*i).lanes.size());
            }
            // split the edge
            std::vector<int> currLanes;
            for (unsigned int l = 0; l < e->getNumLanes(); ++l) {
                currLanes.push_back(l);
            }
            std::string edgeid = e->getID();
            SUMOReal seen = 0;
            for (i = mySplits.begin(); i != mySplits.end(); ++i) {
                const Split& exp = *i;
                assert(exp.lanes.size() != 0);
                if (exp.pos > 0 && e->getGeometry().length() + seen > exp.pos && exp.pos > seen) {
                    std::string nid = edgeid + "." +  toString(exp.nameid);
                    NBNode* rn = new NBNode(nid, exp.gpos);
                    if (myNodeCont.insert(rn)) {
                        //  split the edge
                        std::string nid = myCurrentID + "." +  toString(exp.nameid);
                        std::string pid = e->getID();
                        myEdgeCont.splitAt(myDistrictCont, e, exp.pos - seen, rn,
                                           pid, nid, e->getNumLanes(), (unsigned int) exp.lanes.size());
                        seen = exp.pos;
                        std::vector<int> newLanes = exp.lanes;
                        NBEdge* pe = myEdgeCont.retrieve(pid);
                        NBEdge* ne = myEdgeCont.retrieve(nid);
                        // reconnect lanes
                        pe->invalidateConnections(true);
                        //  new on right
                        unsigned int rightMostP = currLanes[0];
                        unsigned int rightMostN = newLanes[0];
                        for (int l = 0; l < (int) rightMostP - (int) rightMostN; ++l) {
                            pe->addLane2LaneConnection(0, ne, l, NBEdge::L2L_VALIDATED, true);
                        }
                        //  new on left
                        unsigned int leftMostP = currLanes.back();
                        unsigned int leftMostN = newLanes.back();
                        for (int l = 0; l < (int) leftMostN - (int) leftMostP; ++l) {
                            pe->addLane2LaneConnection(pe->getNumLanes() - 1, ne, leftMostN - l - rightMostN, NBEdge::L2L_VALIDATED, true);
                        }
                        //  all other connected
                        for (unsigned int l = 0; l < noLanesMax; ++l) {
                            if (find(currLanes.begin(), currLanes.end(), l) == currLanes.end()) {
                                continue;
                            }
                            if (find(newLanes.begin(), newLanes.end(), l) == newLanes.end()) {
                                continue;
                            }
                            pe->addLane2LaneConnection(l - rightMostP, ne, l - rightMostN, NBEdge::L2L_VALIDATED, true);
                        }
                        // move to next
                        e = ne;
                        currLanes = newLanes;
                    } else {
                        WRITE_WARNING("Error on parsing a split (edge '" + myCurrentID + "').");
                    }
                }  else if (exp.pos == 0) {
                    if (e->getNumLanes() < exp.lanes.size()) {
                        e->incLaneNo((int) exp.lanes.size() - e->getNumLanes());
                    } else {
                        e->decLaneNo(e->getNumLanes() - (int) exp.lanes.size());
                    }
                    currLanes = exp.lanes;
                } else {
                    WRITE_WARNING("Split at '" + toString(exp.pos) + "' lies beyond the edge's length (edge '" + myCurrentID + "').");
                }
            }
            // patch lane offsets
            e = myEdgeCont.retrieve(edgeid);
            i = mySplits.begin();
            if ((*i).pos != 0) {
                e = e->getToNode()->getOutgoingEdges()[0];
            }
            for (; i != mySplits.end(); ++i) {
                unsigned int maxLeft = (*i).lanes.back();
                SUMOReal offset = 0;
                if (maxLeft < noLanesMax) {
                    if (e->getLaneSpreadFunction() == LANESPREAD_RIGHT) {
                        offset = SUMO_const_laneWidthAndOffset * (noLanesMax - 1 - maxLeft);
                    } else {
                        offset = SUMO_const_halfLaneAndOffset * (noLanesMax - 1 - maxLeft);
                    }
                }
                unsigned int maxRight = (*i).lanes.front();
                if (maxRight > 0 && e->getLaneSpreadFunction() == LANESPREAD_CENTER) {
                    offset -= SUMO_const_halfLaneAndOffset * maxRight;
                }
                if (offset != 0) {
                    PositionVector g = e->getGeometry();
                    g.move2side(offset);
                    e->setGeometry(g);
                }
                if (e->getToNode()->getOutgoingEdges().size() != 0) {
                    e = e->getToNode()->getOutgoingEdges()[0];
                }
            }
        }
    }
}
void
NBRampsComputer::buildOffRamp(NBNode* cur, NBNodeCont& nc, NBEdgeCont& ec, NBDistrictCont& dc, SUMOReal rampLength, bool dontSplit, std::set<NBEdge*>& incremented) {
    NBEdge* potHighway, *potRamp, *prev;
    getOffRampEdges(cur, &potHighway, &potRamp, &prev);
    // compute the number of lanes to append
    const unsigned int firstLaneNumber = prev->getNumLanes();
    int toAdd = (potRamp->getNumLanes() + potHighway->getNumLanes()) - firstLaneNumber;
    NBEdge* first = prev;
    NBEdge* last = prev;
    NBEdge* curr = prev;
    if (toAdd > 0 && find(incremented.begin(), incremented.end(), prev) == incremented.end()) {
        SUMOReal currLength = 0;
        while (curr != 0 && currLength + curr->getGeometry().length() - POSITION_EPS < rampLength) {
            if (find(incremented.begin(), incremented.end(), curr) == incremented.end()) {
                curr->incLaneNo(toAdd);
                curr->invalidateConnections(true);
                incremented.insert(curr);
                moveRampRight(curr, toAdd);
                currLength += curr->getLength(); // !!! loaded length?
                last = curr;
            }
            NBNode* prevN = curr->getFromNode();
            if (prevN->getIncomingEdges().size() == 1) {
                curr = prevN->getIncomingEdges()[0];
                if (curr->getNumLanes() != firstLaneNumber) {
                    // the number of lanes changes along the computation; we'll stop...
                    curr = 0;
                } else if (last->isTurningDirectionAt(curr)) {
                    // turnarounds certainly should not be included in a ramp
                    curr = 0;
                } else if (curr == potHighway || curr == potRamp) {
                    // circular connectivity. do not split!
                    curr = 0;
                }
            } else {
                // ambigous; and, in fact, what should it be? ...stop
                curr = 0;
            }
        }
        // check whether a further split is necessary
        if (curr != 0 && !dontSplit && currLength - POSITION_EPS < rampLength && curr->getNumLanes() == firstLaneNumber && find(incremented.begin(), incremented.end(), curr) == incremented.end()) {
            // there is enough place to build a ramp; do it
            bool wasFirst = first == curr;
            Position pos = curr->getGeometry().positionAtOffset(curr->getGeometry().length() - (rampLength  - currLength));
            NBNode* rn = new NBNode(curr->getID() + "-AddedOffRampNode", pos);
            if (!nc.insert(rn)) {
                throw ProcessError("Ups - could not build on-ramp for edge '" + curr->getID() + "' (node could not be build)!");
            }
            std::string name = curr->getID();
            bool ok = ec.splitAt(dc, curr, rn, curr->getID(), curr->getID() + "-AddedOffRampEdge", curr->getNumLanes(), curr->getNumLanes() + toAdd);
            if (!ok) {
                WRITE_ERROR("Ups - could not build on-ramp for edge '" + curr->getID() + "'!");
                return;
            }
            curr = ec.retrieve(name + "-AddedOffRampEdge");
            incremented.insert(curr);
            last = curr;
            moveRampRight(curr, toAdd);
            if (wasFirst) {
                first = curr;
            }
        }
        if (curr == prev && dontSplit) {
            WRITE_WARNING("Could not build off-ramp for edge '"  + curr->getID() + "' due to option '--ramps.no-split'");
            return;
        }
    }
    // set connections from added ramp to ramp/highway
    if (!first->addLane2LaneConnections(potRamp->getNumLanes(), potHighway, 0, MIN2(first->getNumLanes() - 1, potHighway->getNumLanes()), NBEdge::L2L_VALIDATED, true)) {
        throw ProcessError("Could not set connection!");
    }
    if (!first->addLane2LaneConnections(0, potRamp, 0, potRamp->getNumLanes(), NBEdge::L2L_VALIDATED, false)) {
        throw ProcessError("Could not set connection!");
    }
    // patch ramp geometry
    PositionVector p = potRamp->getGeometry();
    p.pop_front();
    p.push_front(first->getLaneShape(0)[-1]);
    potRamp->setGeometry(p);
}
void
NIImporter_VISUM::parse_Lanes() {
    // get the node
    NBNode* node = getNamedNode("KNOTNR");
    // get the edge
    NBEdge* baseEdge = getNamedEdge("STRNR");
    NBEdge* edge = getNamedEdgeContinuating("STRNR", node);
    // check
    if (node == 0 || edge == 0) {
        return;
    }
    // get the lane
    std::string laneS = myLineParser.know("FSNR")
                        ? NBHelpers::normalIDRepresentation(myLineParser.get("FSNR"))
                        : NBHelpers::normalIDRepresentation(myLineParser.get("NR"));
    int lane = -1;
    try {
        lane = TplConvert::_2int(laneS.c_str());
    } catch (NumberFormatException&) {
        WRITE_ERROR("A lane number for edge '" + edge->getID() + "' is not numeric (" + laneS + ").");
        return;
    }
    lane -= 1;
    if (lane < 0) {
        WRITE_ERROR("A lane number for edge '" + edge->getID() + "' is not positive (" + laneS + ").");
        return;
    }
    // get the direction
    std::string dirS = NBHelpers::normalIDRepresentation(myLineParser.get("RICHTTYP"));
    int prevLaneNo = baseEdge->getNumLanes();
    if ((dirS == "1" && !(node->hasIncoming(edge))) || (dirS == "0" && !(node->hasOutgoing(edge)))) {
        // get the last part of the turnaround direction
        edge = getReversedContinuating(edge, node);
    }
    // get the length
    std::string lengthS = NBHelpers::normalIDRepresentation(myLineParser.get("LAENGE"));
    SUMOReal length = -1;
    try {
        length = TplConvert::_2SUMOReal(lengthS.c_str());
    } catch (NumberFormatException&) {
        WRITE_ERROR("A lane length for edge '" + edge->getID() + "' is not numeric (" + lengthS + ").");
        return;
    }
    if (length < 0) {
        WRITE_ERROR("A lane length for edge '" + edge->getID() + "' is not positive (" + lengthS + ").");
        return;
    }
    //
    if (dirS == "1") {
        lane -= prevLaneNo;
    }
    //
    if (length == 0) {
        if ((int) edge->getNumLanes() > lane) {
            // ok, we know this already...
            return;
        }
        // increment by one
        edge->incLaneNo(1);
    } else {
        // check whether this edge already has been created
        if (edge->getID().substr(edge->getID().length() - node->getID().length() - 1) == "_" + node->getID()) {
            if (edge->getID().substr(edge->getID().find('_')) == "_" + toString(length) + "_" + node->getID()) {
                if ((int) edge->getNumLanes() > lane) {
                    // ok, we know this already...
                    return;
                }
                // increment by one
                edge->incLaneNo(1);
                return;
            }
        }
        // nope, we have to split the edge...
        //  maybe it is not the proper edge to split - VISUM seems not to sort the splits...
        bool mustRecheck = true;
        SUMOReal seenLength = 0;
        while (mustRecheck) {
            if (edge->getID().substr(edge->getID().length() - node->getID().length() - 1) == "_" + node->getID()) {
                // ok, we have a previously created edge here
                std::string sub = edge->getID();
                sub = sub.substr(sub.rfind('_', sub.rfind('_') - 1));
                sub = sub.substr(1, sub.find('_', 1) - 1);
                SUMOReal dist = TplConvert::_2SUMOReal(sub.c_str());
                if (dist < length) {
                    seenLength += edge->getLength();
                    if (dirS == "1") {
                        // incoming -> move back
                        edge = edge->getFromNode()->getIncomingEdges()[0];
                    } else {
                        // outgoing -> move forward
                        edge = edge->getToNode()->getOutgoingEdges()[0];
                    }
                } else {
                    mustRecheck = false;
                }
            } else {
                // we have the center edge - do not continue...
                mustRecheck = false;
            }
        }
        // compute position
        Position p;
        SUMOReal useLength = length - seenLength;
        useLength = edge->getLength() - useLength;
        std::string edgeID = edge->getID();
        p = edge->getGeometry().positionAtLengthPosition(useLength);
        if (edgeID.substr(edgeID.length() - node->getID().length() - 1) == "_" + node->getID()) {
            edgeID = edgeID.substr(0, edgeID.find('_'));
        }
        NBNode* rn = new NBNode(edgeID + "_" +  toString((size_t) length) + "_" + node->getID(), p);
        if (!myNetBuilder.getNodeCont().insert(rn)) {
            throw ProcessError("Ups - could not insert node!");
        }
        std::string nid = edgeID + "_" +  toString((size_t) length) + "_" + node->getID();
        myNetBuilder.getEdgeCont().splitAt(myNetBuilder.getDistrictCont(), edge, useLength, rn,
                                           edge->getID(), nid, edge->getNumLanes() + 0, edge->getNumLanes() + 1);
        NBEdge* nedge = myNetBuilder.getEdgeCont().retrieve(nid);
        nedge = nedge->getToNode()->getOutgoingEdges()[0];
        while (nedge->getID().substr(nedge->getID().length() - node->getID().length() - 1) == "_" + node->getID()) {
            assert(nedge->getToNode()->getOutgoingEdges().size() > 0);
            nedge->incLaneNo(1);
            nedge = nedge->getToNode()->getOutgoingEdges()[0];
        }
    }
}
Example #4
0
void
NIXMLEdgesHandler::myEndElement(int element) {
    if (element == SUMO_TAG_EDGE && myCurrentEdge != 0) {
        // add bike lane, wait until lanes are loaded to avoid building if it already exists
        if (myBikeLaneWidth != NBEdge::UNSPECIFIED_WIDTH) {
            myCurrentEdge->addBikeLane(myBikeLaneWidth);
        }
        // add sidewalk, wait until lanes are loaded to avoid building if it already exists
        if (mySidewalkWidth != NBEdge::UNSPECIFIED_WIDTH) {
            myCurrentEdge->addSidewalk(mySidewalkWidth);
        }
        if (!myIsUpdate) {
            try {
                if (!myEdgeCont.insert(myCurrentEdge)) {
                    WRITE_ERROR("Duplicate edge occured. ID='" + myCurrentID + "'");
                    delete myCurrentEdge;
                }
            } catch (InvalidArgument& e) {
                WRITE_ERROR(e.what());
                throw;
            } catch (...) {
                WRITE_ERROR("An important information is missing in edge '" + myCurrentID + "'.");
            }
        }
        if (mySplits.size() != 0) {
            std::vector<Split>::iterator i;
            NBEdge* e = myCurrentEdge;
            sort(mySplits.begin(), mySplits.end(), split_sorter());
            unsigned int noLanesMax = e->getNumLanes();
            // compute the node positions and sort the lanes
            for (i = mySplits.begin(); i != mySplits.end(); ++i) {
                sort((*i).lanes.begin(), (*i).lanes.end());
                noLanesMax = MAX2(noLanesMax, (unsigned int)(*i).lanes.size());
            }
            // split the edge
            std::vector<int> currLanes;
            for (unsigned int l = 0; l < e->getNumLanes(); ++l) {
                currLanes.push_back(l);
            }
            if (e->getNumLanes() != mySplits.back().lanes.size()) {
                // invalidate traffic light definitions loaded from a SUMO network
                // XXX it would be preferable to reconstruct the phase definitions heuristically
                e->getToNode()->invalidateTLS(myTLLogicCont);
                // if the number of lanes changes the connections should be
                // recomputed
                e->invalidateConnections(true);
            }

            std::string edgeid = e->getID();
            SUMOReal seen = 0;
            for (i = mySplits.begin(); i != mySplits.end(); ++i) {
                const Split& exp = *i;
                assert(exp.lanes.size() != 0);
                if (exp.pos > 0 && e->getGeometry().length() + seen > exp.pos && exp.pos > seen) {
                    if (myNodeCont.insert(exp.node)) {
                        myNodeCont.markAsSplit(exp.node);
                        //  split the edge
                        std::string pid = e->getID();
                        myEdgeCont.splitAt(myDistrictCont, e, exp.pos - seen, exp.node,
                                           pid, exp.node->getID(), e->getNumLanes(), (unsigned int) exp.lanes.size(), exp.speed);
                        seen = exp.pos;
                        std::vector<int> newLanes = exp.lanes;
                        NBEdge* pe = myEdgeCont.retrieve(pid);
                        NBEdge* ne = myEdgeCont.retrieve(exp.node->getID());
                        // reconnect lanes
                        pe->invalidateConnections(true);
                        //  new on right
                        unsigned int rightMostP = currLanes[0];
                        unsigned int rightMostN = newLanes[0];
                        for (int l = 0; l < (int) rightMostP - (int) rightMostN; ++l) {
                            pe->addLane2LaneConnection(0, ne, l, NBEdge::L2L_VALIDATED, true);
                        }
                        //  new on left
                        unsigned int leftMostP = currLanes.back();
                        unsigned int leftMostN = newLanes.back();
                        for (int l = 0; l < (int) leftMostN - (int) leftMostP; ++l) {
                            pe->addLane2LaneConnection(pe->getNumLanes() - 1, ne, leftMostN - l - rightMostN, NBEdge::L2L_VALIDATED, true);
                        }
                        //  all other connected
                        for (unsigned int l = 0; l < noLanesMax; ++l) {
                            if (find(currLanes.begin(), currLanes.end(), l) == currLanes.end()) {
                                continue;
                            }
                            if (find(newLanes.begin(), newLanes.end(), l) == newLanes.end()) {
                                continue;
                            }
                            pe->addLane2LaneConnection(l - rightMostP, ne, l - rightMostN, NBEdge::L2L_VALIDATED, true);
                        }
                        // move to next
                        e = ne;
                        currLanes = newLanes;
                    } else {
                        WRITE_WARNING("Error on parsing a split (edge '" + myCurrentID + "').");
                    }
                }  else if (exp.pos == 0) {
                    if (e->getNumLanes() < exp.lanes.size()) {
                        e->incLaneNo((int) exp.lanes.size() - e->getNumLanes());
                    } else {
                        e->decLaneNo(e->getNumLanes() - (int) exp.lanes.size());
                    }
                    currLanes = exp.lanes;
                    // invalidate traffic light definition loaded from a SUMO network
                    // XXX it would be preferable to reconstruct the phase definitions heuristically
                    e->getFromNode()->invalidateTLS(myTLLogicCont);
                } else {
                    WRITE_WARNING("Split at '" + toString(exp.pos) + "' lies beyond the edge's length (edge '" + myCurrentID + "').");
                }
            }
            // patch lane offsets
            e = myEdgeCont.retrieve(edgeid);
            if (mySplits.front().pos != 0) {
                // add a dummy split at the beginning to ensure correct offset
                Split start;
                start.pos = 0;
                for (int lane = 0; lane < (int)e->getNumLanes(); ++lane) {
                    start.lanes.push_back(lane);
                }
                mySplits.insert(mySplits.begin(), start);
            }
            i = mySplits.begin();
            for (; i != mySplits.end(); ++i) {
                unsigned int maxLeft = (*i).lanes.back();
                SUMOReal offset = 0;
                if (maxLeft < noLanesMax) {
                    if (e->getLaneSpreadFunction() == LANESPREAD_RIGHT) {
                        offset = SUMO_const_laneWidthAndOffset * (noLanesMax - 1 - maxLeft);
                    } else {
                        offset = SUMO_const_halfLaneAndOffset * (noLanesMax - 1 - maxLeft);
                    }
                }
                unsigned int maxRight = (*i).lanes.front();
                if (maxRight > 0 && e->getLaneSpreadFunction() == LANESPREAD_CENTER) {
                    offset -= SUMO_const_halfLaneAndOffset * maxRight;
                }
                if (offset != 0) {
                    PositionVector g = e->getGeometry();
                    g.move2side(offset);
                    e->setGeometry(g);
                }
                if (e->getToNode()->getOutgoingEdges().size() != 0) {
                    e = e->getToNode()->getOutgoingEdges()[0];
                }
            }
        }
    }
}
void
NBRampsComputer::buildOnRamp(NBNode* cur, NBNodeCont& nc, NBEdgeCont& ec, NBDistrictCont& dc, SUMOReal rampLength, bool dontSplit, std::set<NBEdge*>& incremented) {
    NBEdge* potHighway, *potRamp, *cont;
    getOnRampEdges(cur, &potHighway, &potRamp, &cont);
    // compute the number of lanes to append
    const unsigned int firstLaneNumber = potHighway->getNumLanes();
    int toAdd = (potRamp->getNumLanes() + firstLaneNumber) - cont->getNumLanes();
    NBEdge* first = cont;
    NBEdge* last = cont;
    NBEdge* curr = cont;
    if (toAdd > 0 && find(incremented.begin(), incremented.end(), cont) == incremented.end()) {
        SUMOReal currLength = 0;
        while (curr != 0 && currLength + curr->getGeometry().length() - POSITION_EPS < rampLength) {
            if (find(incremented.begin(), incremented.end(), curr) == incremented.end()) {
                curr->incLaneNo(toAdd);
                curr->invalidateConnections(true);
                incremented.insert(curr);
                moveRampRight(curr, toAdd);
                currLength += curr->getLength(); // !!! loaded length?
                last = curr;
            }
            NBNode* nextN = curr->getToNode();
            if (nextN->getOutgoingEdges().size() == 1) {
                curr = nextN->getOutgoingEdges()[0];
                if (curr->getNumLanes() != firstLaneNumber) {
                    // the number of lanes changes along the computation; we'll stop...
                    curr = 0;
                }
            } else {
                // ambigous; and, in fact, what should it be? ...stop
                curr = 0;
            }
        }
        // check whether a further split is necessary
        if (curr != 0 && !dontSplit && currLength - POSITION_EPS < rampLength && curr->getNumLanes() == firstLaneNumber && find(incremented.begin(), incremented.end(), curr) == incremented.end()) {
            // there is enough place to build a ramp; do it
            bool wasFirst = first == curr;
            NBNode* rn = new NBNode(curr->getID() + "-AddedOnRampNode", curr->getGeometry().positionAtLengthPosition(rampLength - currLength));
            if (!nc.insert(rn)) {
                throw ProcessError("Ups - could not build on-ramp for edge '" + curr->getID() + "' (node could not be build)!");
            }
            std::string name = curr->getID();
            bool ok = ec.splitAt(dc, curr, rn, curr->getID() + ADDED_ON_RAMP_EDGE, curr->getID(), curr->getNumLanes() + toAdd, curr->getNumLanes());
            if (!ok) {
                WRITE_ERROR("Ups - could not build on-ramp for edge '" + curr->getID() + "'!");
                return;
            }
            //ec.retrieve(name)->invalidateConnections();
            curr = ec.retrieve(name + ADDED_ON_RAMP_EDGE);
            curr->invalidateConnections(true);
            incremented.insert(curr);
            last = curr;
            moveRampRight(curr, toAdd);
            if (wasFirst) {
                first = curr;
            }
        }
    }
    // set connections from ramp/highway to added ramp
    if (!potHighway->addLane2LaneConnections(0, first, potRamp->getNumLanes(), MIN2(first->getNumLanes() - potRamp->getNumLanes(), potHighway->getNumLanes()), NBEdge::L2L_VALIDATED, true, true)) {
        throw ProcessError("Could not set connection!");
    }
    if (!potRamp->addLane2LaneConnections(0, first, 0, potRamp->getNumLanes(), NBEdge::L2L_VALIDATED, true, true)) {
        throw ProcessError("Could not set connection!");
    }
    // patch ramp geometry
    PositionVector p = potRamp->getGeometry();
    p.pop_back();
    p.push_back(first->getLaneShape(0)[0]);
    potRamp->setGeometry(p);
    // set connections from added ramp to following highway
    NBNode* nextN = last->getToNode();
    if (nextN->getOutgoingEdges().size() == 1) {
        NBEdge* next = nextN->getOutgoingEdges()[0];//const EdgeVector& o1 = cont->getToNode()->getOutgoingEdges();
        if (next->getNumLanes() < last->getNumLanes()) {
            last->addLane2LaneConnections(last->getNumLanes() - next->getNumLanes(), next, 0, next->getNumLanes(), NBEdge::L2L_VALIDATED);
        }
    }
}