Exemplo n.º 1
0
void
NIVissimNodeCluster::dict_addDisturbances(NBDistrictCont& dc,
        NBNodeCont& nc, NBEdgeCont& ec) {
    for (DictType::iterator i = myDict.begin(); i != myDict.end(); i++) {
        const IntVector& disturbances = (*i).second->myDisturbances;
        NBNode* node = nc.retrieve((*i).second->getNodeName());
        for (IntVector::const_iterator j = disturbances.begin(); j != disturbances.end(); j++) {
            NIVissimDisturbance* disturbance = NIVissimDisturbance::dictionary(*j);
            disturbance->addToNode(node, dc, nc, ec);
        }
    }
    NIVissimDisturbance::reportRefused();
}
Exemplo n.º 2
0
void
NWWriter_DlrNavteq::writeNodesUnsplitted(const OptionsCont& oc, NBNodeCont& nc, NBEdgeCont& ec) {
    // For "real" nodes we simply use the node id.
    // For internal nodes (geometry vectors describing edge geometry in the parlance of this format)
    // we use the id of the edge and do not bother with
    // compression (each direction gets its own internal node).
    // XXX add option for generating numerical ids in case the input network has string ids and the target process needs integers
    OutputDevice& device = OutputDevice::getDevice(oc.getString("dlr-navteq-output") + "_nodes_unsplitted.txt");
    writeHeader(device, oc);
    const GeoConvHelper& gch = GeoConvHelper::getFinal();
    const bool haveGeo = gch.usingGeoProjection();
    const SUMOReal geoScale = pow(10.0f, haveGeo ? 5 : 2); // see NIImporter_DlrNavteq::GEO_SCALE
    device.setPrecision(0);
    if (!haveGeo) {
        WRITE_WARNING("DlrNavteq node data will be written in (floating point) cartesian coordinates");
    }
    // write format specifier
    device << "# NODE_ID\tIS_BETWEEN_NODE\tamount_of_geocoordinates\tx1\ty1\t[x2 y2  ... xn  yn]\n";
    // write normal nodes
    for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
        NBNode* n = (*i).second;
        Position pos = n->getPosition();
        gch.cartesian2geo(pos);
        pos.mul(geoScale);
        device << n->getID() << "\t0\t1\t" << pos.x() << "\t" << pos.y() << "\n";
    }
    // write "internal" nodes
    for (std::map<std::string, NBEdge*>::const_iterator i = ec.begin(); i != ec.end(); ++i) {
        NBEdge* e = (*i).second;
        const PositionVector& geom = e->getGeometry();
        if (geom.size() > 2) {
            std::string internalNodeID = e->getID();
            if (internalNodeID == UNDEFINED ||
                    (nc.retrieve(internalNodeID) != 0)) {
                // need to invent a new name to avoid clashing with the id of a 'real' node or a reserved name
                internalNodeID += "_geometry";
            }
            device << internalNodeID << "\t1\t" << geom.size() - 2;
            for (size_t ii = 1; ii < geom.size() - 1; ++ii) {
                Position pos = geom[(int)ii];
                gch.cartesian2geo(pos);
                pos.mul(geoScale);
                device << "\t" << pos.x() << "\t" << pos.y();
            }
            device << "\n";
        }
    }
    device.close();
}
Exemplo n.º 3
0
void
NIImporter_OpenDrive::setNodeSecure(NBNodeCont &nc, OpenDriveEdge &e,
                                    const std::string &nodeID, NIImporter_OpenDrive::LinkType lt) throw(ProcessError) {
    NBNode *n = nc.retrieve(nodeID);
    if (n==0) {
        throw ProcessError("Could not find node '" + nodeID + "'.");
    }
    if (lt==OPENDRIVE_LT_SUCCESSOR) {
        if (e.to!=0&&e.to!=n) {
            throw ProcessError("Edge '" + e.id + "' has two end nodes.");
        }
        e.to = n;
    } else {
        if (e.from!=0&&e.from!=n) {
            throw ProcessError("Edge '" + e.id + "' has two start nodes.");
        }
        e.from = n;
    }
}
Exemplo n.º 4
0
std::pair<NBNode*, NBNode*>
NIVissimEdge::resolveSameNode(NBNodeCont& nc, SUMOReal offset,
                              NBNode* prevFrom, NBNode* prevTo) {
    // check whether the edge is connected to a district
    //  use it if so
    NIVissimDistrictConnection* d =
        NIVissimDistrictConnection::dict_findForEdge(myID);
    if (d != 0) {
        Position pos = d->geomPosition();
        SUMOReal position = d->getPosition();
        // the district is at the begin of the edge
        if (myGeom.length() - position > position) {
            std::string nid = "ParkingPlace" + toString<int>(d->getID());
            NBNode* node = nc.retrieve(nid);
            if (node == 0) {
                node = new NBNode(nid,
                                  pos, NODETYPE_NOJUNCTION);
                if (!nc.insert(node)) {
                    throw 1;
                }
            }
            return std::pair<NBNode*, NBNode*>(node, prevTo);
        }
        // the district is at the end of the edge
        else {
            std::string nid = "ParkingPlace" + toString<int>(d->getID());
            NBNode* node = nc.retrieve(nid);
            if (node == 0) {
                node = new NBNode(nid, pos, NODETYPE_NOJUNCTION);
                if (!nc.insert(node)) {
                    throw 1;
                }
            }
            assert(node != 0);
            return std::pair<NBNode*, NBNode*>(prevFrom, node);
        }
    }
    // otherwise, check whether the edge is some kind of
    //  a dead end...
    // check which end is nearer to the node centre
    if (myConnectionClusters.size() == 1) {
        NBNode* node = prevFrom; // it is the same as getToNode()

        NIVissimConnectionCluster* c = *(myConnectionClusters.begin());
        // no end node given
        if (c->around(myGeom.front(), offset) && !c->around(myGeom.back(), offset)) {
            NBNode* end = new NBNode(
                toString<int>(myID) + "-End",
                myGeom.back(),
                NODETYPE_NOJUNCTION);
            if (!nc.insert(end)) {
                throw 1;
            }
            return std::pair<NBNode*, NBNode*>(node, end);
        }

        // no begin node given
        if (!c->around(myGeom.front(), offset) && c->around(myGeom.back(), offset)) {
            NBNode* beg = new NBNode(
                toString<int>(myID) + "-Begin",
                myGeom.front(),
                NODETYPE_NOJUNCTION);
            if (!nc.insert(beg)) {
                std::cout << "nope, NIVissimDisturbance" << std::endl;
                throw 1;
            }
            return std::pair<NBNode*, NBNode*>(beg, node);
        }

        // self-loop edge - both points lie within the same cluster
        if (c->around(myGeom.front()) && c->around(myGeom.back())) {
            return std::pair<NBNode*, NBNode*>(node, node);
        }
    }
    // what to do in other cases?
    //  It simply is a self-looping edge....
    return std::pair<NBNode*, NBNode*>(prevFrom, prevTo);
}
Exemplo n.º 5
0
bool
NIVissimDisturbance::addToNode(NBNode* node, NBDistrictCont& dc,
                               NBNodeCont& nc, NBEdgeCont& ec) {
    myNode = 0;
    NIVissimConnection* pc =
        NIVissimConnection::dictionary(myEdge.getEdgeID());
    NIVissimConnection* bc =
        NIVissimConnection::dictionary(myDisturbance.getEdgeID());
    if (pc == nullptr && bc == nullptr) {
        // This has not been tested completely, yet
        // Both competing abstract edges are normal edges
        // We have to find a crossing point, build a node here,
        //  split both edges and add the connections
        NIVissimEdge* e1 = NIVissimEdge::dictionary(myEdge.getEdgeID());
        NIVissimEdge* e2 = NIVissimEdge::dictionary(myDisturbance.getEdgeID());
        WRITE_WARNING("Ugly split to prohibit '" + toString<int>(e1->getID()) + "' by '" + toString<int>(e2->getID()) + "'.");
        Position pos = e1->crossesEdgeAtPoint(e2);
        std::string id1 = toString<int>(e1->getID()) + "x" + toString<int>(e2->getID());
        std::string id2 = toString<int>(e2->getID()) + "x" + toString<int>(e1->getID());
        NBNode* node1 = nc.retrieve(id1);
        NBNode* node2 = nc.retrieve(id2);
        NBNode* node = nullptr;
        assert(node1 == 0 || node2 == 0);
        if (node1 == nullptr && node2 == nullptr) {
            refusedProhibits++;
            return false;
            /*            node = new NBNode(id1, pos.x(), pos.y(), "priority");
                        if(!myNodeCont.insert(node)) {
                             "nope, NIVissimDisturbance" << endl;
                            throw 1;
                        }*/
        } else {
            node = node1 == nullptr ? node2 : node1;
        }
        ec.splitAt(dc, ec.retrievePossiblySplit(toString<int>(e1->getID()), myEdge.getPosition()), node);
        ec.splitAt(dc, ec.retrievePossiblySplit(toString<int>(e2->getID()), myDisturbance.getPosition()), node);
        // !!! in some cases, one of the edges is not being build because it's too short
        // !!! what to do in these cases?
        NBEdge* mayDriveFrom = ec.retrieve(toString<int>(e1->getID()) + "[0]");
        NBEdge* mayDriveTo = ec.retrieve(toString<int>(e1->getID()) + "[1]");
        NBEdge* mustStopFrom = ec.retrieve(toString<int>(e2->getID()) + "[0]");
        NBEdge* mustStopTo = ec.retrieve(toString<int>(e2->getID()) + "[1]");
        if (mayDriveFrom != nullptr && mayDriveTo != nullptr && mustStopFrom != nullptr && mustStopTo != nullptr) {
            node->addSortedLinkFoes(
                NBConnection(mayDriveFrom, mayDriveTo),
                NBConnection(mayDriveFrom, mayDriveTo));
        } else {
            refusedProhibits++;
            return false;
            // !!! warning
        }
//        }
    } else if (pc != nullptr && bc == nullptr) {
        // The prohibited abstract edge is a connection, the other
        //  is not;
        // The connection will be prohibitesd by all connections
        //  outgoing from the "real" edge

        NBEdge* e = ec.retrievePossiblySplit(toString<int>(myDisturbance.getEdgeID()), myDisturbance.getPosition());
        if (e == nullptr) {
            WRITE_WARNING("Could not prohibit '" + toString<int>(myEdge.getEdgeID()) + "' by '" + toString<int>(myDisturbance.getEdgeID()) + "'. Have not found disturbance.");
            refusedProhibits++;
            return false;
        }
        if (e->getFromNode() == e->getToNode()) {
            WRITE_WARNING("Could not prohibit '" + toString<int>(myEdge.getEdgeID()) + "' by '" + toString<int>(myDisturbance.getEdgeID()) + "'. Disturbance connects same node.");
            refusedProhibits++;
            // What to do with self-looping edges?
            return false;
        }
        // get the begin of the prohibited connection
        std::string id_pcoe = toString<int>(pc->getFromEdgeID());
        std::string id_pcie = toString<int>(pc->getToEdgeID());
        NBEdge* pcoe = ec.retrievePossiblySplit(id_pcoe, id_pcie, true);
        NBEdge* pcie = ec.retrievePossiblySplit(id_pcie, id_pcoe, false);
        // check whether it's ending node is the node the prohibited
        //  edge end at
        if (pcoe != nullptr && pcie != nullptr && pcoe->getToNode() == e->getToNode()) {
            // if so, simply prohibit the connections
            NBNode* node = e->getToNode();
            const EdgeVector& connected = e->getConnectedEdges();
            for (EdgeVector::const_iterator i = connected.begin(); i != connected.end(); i++) {
                node->addSortedLinkFoes(
                    NBConnection(e, *i),
                    NBConnection(pcoe, pcie));
            }
        } else {
            WRITE_WARNING("Would have to split edge '" + e->getID() + "' to build a prohibition");
            refusedProhibits++;
            // quite ugly - why was it not build?
            return false;
            /*
            std::string nid1 = e->getID() + "[0]";
            std::string nid2 = e->getID() + "[1]";

            if(ec.splitAt(e, node)) {
                node->addSortedLinkFoes(
                        NBConnection(
                            ec.retrieve(nid1),
                            ec.retrieve(nid2)
                        ),
                        getConnection(node, myEdge.getEdgeID())
                    );
            }
            */
        }
    } else if (bc != nullptr && pc == nullptr) {
        // The prohibiting abstract edge is a connection, the other
        //  is not;
        // We have to split the other one and add the prohibition
        //  description

        NBEdge* e = ec.retrievePossiblySplit(toString<int>(myEdge.getEdgeID()), myEdge.getPosition());
        if (e == nullptr) {
            WRITE_WARNING("Could not prohibit '" + toString<int>(myEdge.getEdgeID()) + "' - it was not built.");
            return false;
        }
        std::string nid1 = e->getID() + "[0]";
        std::string nid2 = e->getID() + "[1]";
        if (e->getFromNode() == e->getToNode()) {
            WRITE_WARNING("Could not prohibit '" + toString<int>(myEdge.getEdgeID()) + "' by '" + toString<int>(myDisturbance.getEdgeID()) + "'.");
            refusedProhibits++;
            // What to do with self-looping edges?
            return false;
        }
        // get the begin of the prohibiting connection
        std::string id_bcoe = toString<int>(bc->getFromEdgeID());
        std::string id_bcie = toString<int>(bc->getToEdgeID());
        NBEdge* bcoe = ec.retrievePossiblySplit(id_bcoe, id_bcie, true);
        NBEdge* bcie = ec.retrievePossiblySplit(id_bcie, id_bcoe, false);
        // check whether it's ending node is the node the prohibited
        //  edge end at
        if (bcoe != nullptr && bcie != nullptr && bcoe->getToNode() == e->getToNode()) {
            // if so, simply prohibit the connections
            NBNode* node = e->getToNode();
            const EdgeVector& connected = e->getConnectedEdges();
            for (EdgeVector::const_iterator i = connected.begin(); i != connected.end(); i++) {
                node->addSortedLinkFoes(
                    NBConnection(bcoe, bcie),
                    NBConnection(e, *i));
            }
        } else {
            WRITE_WARNING("Would have to split edge '" + e->getID() + "' to build a prohibition");
            refusedProhibits++;
            return false;
            /*
            // quite ugly - why was it not build?
            if(ec.splitAt(e, node)) {
                node->addSortedLinkFoes(
                        getConnection(node, myDisturbance.getEdgeID()),
                        NBConnection(
                            ec.retrieve(nid1),
                            ec.retrieve(nid2)
                        )
                    );
            }
            */
        }
    } else {
        // both the prohibiting and the prohibited abstract edges
        //  are connections
        // We can retrieve the conected edges and add the desription
        NBConnection conn1 = getConnection(node, myDisturbance.getEdgeID());
        NBConnection conn2 = getConnection(node, myEdge.getEdgeID());
        if (!conn1.check(ec) || !conn2.check(ec)) {
            refusedProhibits++;
            return false;
        }
        node->addSortedLinkFoes(conn1, conn2);
    }
    return true;
}
void
NIVissimDistrictConnection::dict_BuildDistricts(NBDistrictCont& dc,
        NBEdgeCont& ec,
        NBNodeCont& nc/*,
                                                                                NBDistribution &distc*/) {
    // add the sources and sinks
    //  their normalised probability is computed within NBDistrict
    //   to avoid SUMOReal code writing and more securty within the converter
    //  go through the district table
    for (std::map<int, std::vector<int> >::iterator k = myDistrictsConnections.begin(); k != myDistrictsConnections.end(); k++) {
        // get the connections
        const std::vector<int>& connections = (*k).second;
        // retrieve the current district
        NBDistrict* district =
            dc.retrieve(toString<int>((*k).first));
        NBNode* districtNode = nc.retrieve("District" + district->getID());
        assert(district != 0 && districtNode != 0);

        for (std::vector<int>::const_iterator l = connections.begin(); l != connections.end(); l++) {
            NIVissimDistrictConnection* c = dictionary(*l);
            // get the edge to connect the parking place to
            NBEdge* e = ec.retrieve(toString<int>(c->myEdgeID));
            if (e == 0) {
                e = ec.retrievePossiblySplit(toString<int>(c->myEdgeID), c->myPosition);
            }
            if (e == 0) {
                WRITE_WARNING("Could not build district '" + toString<int>((*k).first) + "' - edge '" + toString<int>(c->myEdgeID) + "' is missing.");
                continue;
            }
            std::string id = "ParkingPlace" + toString<int>(*l);
            NBNode* parkingPlace = nc.retrieve(id);
            if (parkingPlace == 0) {
                SUMOReal pos = c->getPosition();
                if (pos < e->getLength() - pos) {
                    parkingPlace = e->getFromNode();
                    parkingPlace->invalidateIncomingConnections();
                } else {
                    parkingPlace = e->getToNode();
                    parkingPlace->invalidateOutgoingConnections();
                }
            }
            assert(
                e->getToNode() == parkingPlace
                ||
                e->getFromNode() == parkingPlace);

            // build the connection to the source
            if (e->getFromNode() == parkingPlace) {
                id = "VissimFromParkingplace" + toString<int>((*k).first) + "-" + toString<int>(c->myID);
                NBEdge* source =
                    new NBEdge(id, districtNode, parkingPlace,
                               "Connection", c->getMeanSpeed(/*distc*/) / (SUMOReal) 3.6, 3, -1,
                               NBEdge::UNSPECIFIED_WIDTH, NBEdge::UNSPECIFIED_OFFSET);
                if (!ec.insert(source)) { // !!! in den Konstruktor
                    throw 1; // !!!
                }
                SUMOReal percNormed =
                    c->myPercentages[(*k).first];
                if (!district->addSource(source, percNormed)) {
                    throw 1;
                }
            }

            // build the connection to the destination
            if (e->getToNode() == parkingPlace) {
                id = "VissimToParkingplace"  + toString<int>((*k).first) + "-" + toString<int>(c->myID);
                NBEdge* destination =
                    new NBEdge(id, parkingPlace, districtNode,
                               "Connection", (SUMOReal) 100 / (SUMOReal) 3.6, 2, -1,
                               NBEdge::UNSPECIFIED_WIDTH, NBEdge::UNSPECIFIED_OFFSET);
                if (!ec.insert(destination)) { // !!! (in den Konstruktor)
                    throw 1; // !!!
                }
                SUMOReal percNormed2 =
                    c->myPercentages[(*k).first];
                if (!district->addSink(destination, percNormed2)) {
                    throw 1; // !!!
                }
            }

            /*
            if(e->getToNode()==districtNode) {
            SUMOReal percNormed =
                c->myPercentages[(*k).first];
            district->addSink(e, percNormed);
            }
            if(e->getFromNode()==districtNode) {
            SUMOReal percNormed =
                c->myPercentages[(*k).first];
            district->addSource(e, percNormed);
            }
            */
        }

        /*
        // add them as sources and sinks to the current district
        for(std::vector<int>::const_iterator l=connections.begin(); l!=connections.end(); l++) {
            // get the current connections
            NIVissimDistrictConnection *c = dictionary(*l);
            // get the edge to connect the parking place to
            NBEdge *e = NBEdgeCont::retrieve(toString<int>(c->myEdgeID));
            Position edgepos = c->geomPosition();
            NBNode *edgeend = e->tryGetNodeAtPosition(c->myPosition,
                e->getLength()/4.0);
            if(edgeend==0) {
                // Edge splitting omitted on build district connections by now
                assert(false);
            }

            // build the district-node if not yet existing
            std::string id = "VissimParkingplace" + district->getID();
            NBNode *districtNode = nc.retrieve(id);
            assert(districtNode!=0);

            if(e->getToNode()==edgeend) {
                // build the connection to the source
                id = std::string("VissimFromParkingplace")
                    + toString<int>((*k).first) + "-"
                    + toString<int>(c->myID);
                NBEdge *source =
                    new NBEdge(id, id, districtNode, edgeend,
                    "Connection", 100/3.6, 2, 100, 0,
                    NBEdge::EDGEFUNCTION_SOURCE);
                NBEdgeCont::insert(source); // !!! (in den Konstruktor)
                SUMOReal percNormed =
                    c->myPercentages[(*k).first];
                district->addSource(source, percNormed);
            } else {
                // build the connection to the destination
                id = std::string("VissimToParkingplace")
                    + toString<int>((*k).first) + "-"
                    + toString<int>(c->myID);
                NBEdge *destination =
                    new NBEdge(id, id, edgeend, districtNode,
                    "Connection", 100/3.6, 2, 100, 0,
                    NBEdge::EDGEFUNCTION_SINK);
                NBEdgeCont::insert(destination); // !!! (in den Konstruktor)

                // add both the source and the sink to the district
                SUMOReal percNormed =
                    c->myPercentages[(*k).first];
                district->addSink(destination, percNormed);
            }
        }
        */
    }
}
Exemplo n.º 7
0
void
NWWriter_DlrNavteq::writeNodesUnsplitted(const OptionsCont& oc, NBNodeCont& nc, NBEdgeCont& ec, std::map<NBEdge*, std::string>& internalNodes) {
    // For "real" nodes we simply use the node id.
    // For internal nodes (geometry vectors describing edge geometry in the parlance of this format)
    // we use the id of the edge and do not bother with
    // compression (each direction gets its own internal node).
    OutputDevice& device = OutputDevice::getDevice(oc.getString("dlr-navteq-output") + "_nodes_unsplitted.txt");
    writeHeader(device, oc);
    const GeoConvHelper& gch = GeoConvHelper::getFinal();
    const bool haveGeo = gch.usingGeoProjection();
    const double geoScale = pow(10.0f, haveGeo ? 5 : 2); // see NIImporter_DlrNavteq::GEO_SCALE
    device.setPrecision(oc.getInt("dlr-navteq.precision"));
    if (!haveGeo) {
        WRITE_WARNING("DlrNavteq node data will be written in (floating point) cartesian coordinates");
    }
    // write format specifier
    device << "# NODE_ID\tIS_BETWEEN_NODE\tamount_of_geocoordinates\tx1\ty1\t[x2 y2  ... xn  yn]\n";
    // write header
    Boundary boundary = gch.getConvBoundary();
    Position min(boundary.xmin(), boundary.ymin());
    Position max(boundary.xmax(), boundary.ymax());
    gch.cartesian2geo(min);
    min.mul(geoScale);
    gch.cartesian2geo(max);
    max.mul(geoScale);
    int multinodes = 0;
    for (std::map<std::string, NBEdge*>::const_iterator i = ec.begin(); i != ec.end(); ++i) {
        if ((*i).second->getGeometry().size() > 2) {
            multinodes++;
        }
    }
    device << "# [xmin_region] " << min.x() << "\n";
    device << "# [xmax_region] " << max.x() << "\n";
    device << "# [ymin_region] " << min.y() << "\n";
    device << "# [ymax_region] " << max.y() << "\n";
    device << "# [elements_multinode] " << multinodes << "\n";
    device << "# [elements_normalnode] " << nc.size() << "\n";
    device << "# [xmin] " << min.x() << "\n";
    device << "# [xmax] " << max.x() << "\n";
    device << "# [ymin] " << min.y() << "\n";
    device << "# [ymax] " << max.y() << "\n";
    // write normal nodes
    for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
        NBNode* n = (*i).second;
        Position pos = n->getPosition();
        gch.cartesian2geo(pos);
        pos.mul(geoScale);
        device << n->getID() << "\t0\t1\t" << pos.x() << "\t" << pos.y() << "\n";
    }
    // write "internal" nodes
    std::vector<std::string> avoid;
    std::set<std::string> reservedNodeIDs;
    const bool numericalIDs = oc.getBool("numerical-ids");
    if (oc.isSet("reserved-ids")) {
        NBHelpers::loadPrefixedIDsFomFile(oc.getString("reserved-ids"), "node:", reservedNodeIDs); // backward compatibility
        NBHelpers::loadPrefixedIDsFomFile(oc.getString("reserved-ids"), "junction:", reservedNodeIDs); // selection format
    }
    if (numericalIDs) {
        avoid = nc.getAllNames();
        std::vector<std::string> avoid2 = ec.getAllNames();
        avoid.insert(avoid.end(), avoid2.begin(), avoid2.end());
        avoid.insert(avoid.end(), reservedNodeIDs.begin(), reservedNodeIDs.end());
    }
    IDSupplier idSupplier("", avoid);
    for (std::map<std::string, NBEdge*>::const_iterator i = ec.begin(); i != ec.end(); ++i) {
        NBEdge* e = (*i).second;
        PositionVector geom = e->getGeometry();
        if (geom.size() > 2) {
            // the import NIImporter_DlrNavteq checks for the presence of a
            // negated edge id to determine spread type. We may need to do some
            // shifting to make this consistent
            const bool hasOppositeID = ec.getOppositeByID(e->getID()) != nullptr;
            if (e->getLaneSpreadFunction() == LANESPREAD_RIGHT && !hasOppositeID) {
                // need to write center-line geometry instead
                try {
                    geom.move2side(e->getTotalWidth() / 2);
                } catch (InvalidArgument& exception) {
                    WRITE_WARNING("Could not reconstruct shape for edge:'" + e->getID() + "' (" + exception.what() + ").");
                }
            } else if (e->getLaneSpreadFunction() == LANESPREAD_CENTER && hasOppositeID) {
                // need to write left-border geometry instead
                try {
                    geom.move2side(-e->getTotalWidth() / 2);
                } catch (InvalidArgument& exception) {
                    WRITE_WARNING("Could not reconstruct shape for edge:'" + e->getID() + "' (" + exception.what() + ").");
                }
            }

            std::string internalNodeID = e->getID();
            if (internalNodeID == UNDEFINED
                    || (nc.retrieve(internalNodeID) != nullptr)
                    || reservedNodeIDs.count(internalNodeID) > 0
               ) {
                // need to invent a new name to avoid clashing with the id of a 'real' node or a reserved name
                if (numericalIDs) {
                    internalNodeID = idSupplier.getNext();
                } else {
                    internalNodeID += "_geometry";
                }
            }
            internalNodes[e] = internalNodeID;
            device << internalNodeID << "\t1\t" << geom.size() - 2;
            for (int ii = 1; ii < (int)geom.size() - 1; ++ii) {
                Position pos = geom[(int)ii];
                gch.cartesian2geo(pos);
                pos.mul(geoScale);
                device << "\t" << pos.x() << "\t" << pos.y();
            }
            device << "\n";
        }
    }
    device.close();
}