#include <config.h>
#include <algorithm>
#include <ctime>
#include <cmath>
#include <utils/common/MsgHandler.h>
#include <netbuild/NBEdge.h>
#include <netbuild/NBEdgeCont.h>
#include <netbuild/NBNode.h>
#include <netbuild/NBNodeCont.h>
#include <netbuild/NBNetBuilder.h>
#include <utils/common/ToString.h>
#include <utils/common/IDSupplier.h>
#include <utils/common/StringUtils.h>
#include <utils/common/StringTokenizer.h>
#include <utils/options/OptionsCont.h>
#include <utils/iodevices/OutputDevice.h>
#include <utils/geom/GeoConvHelper.h>
#include "NWFrame.h"
#include "NWWriter_DlrNavteq.h"
const std::string NWWriter_DlrNavteq::UNDEFINED("-1");
void
NWWriter_DlrNavteq::writeNetwork(const OptionsCont& oc, NBNetBuilder& nb) {
if (!oc.isSet("dlr-navteq-output")) {
return;
}
std::map<const NBEdge*, std::string> internalNodes;
writeNodesUnsplitted(oc, nb.getNodeCont(), nb.getEdgeCont(), internalNodes);
writeLinksUnsplitted(oc, nb.getEdgeCont(), internalNodes);
writeTrafficSignals(oc, nb.getNodeCont());
writeProhibitedManoeuvres(oc, nb.getNodeCont(), nb.getEdgeCont());
writeConnectedLanes(oc, nb.getNodeCont());
}
void NWWriter_DlrNavteq::writeHeader(OutputDevice& device, const OptionsCont& oc) {
device << "# Format matches Extraction version: V" << oc.getString("dlr-navteq.version") << " \n";
std::stringstream tmp;
oc.writeConfiguration(tmp, true, false, false);
tmp.seekg(std::ios_base::beg);
std::string line;
while (!tmp.eof()) {
std::getline(tmp, line);
device << "# " << line << "\n";
}
device << "#\n";
}
void
NWWriter_DlrNavteq::writeNodesUnsplitted(const OptionsCont& oc, const NBNodeCont& nc, const NBEdgeCont& ec, std::map<const NBEdge*, std::string>& internalNodes) {
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);
device.setPrecision(oc.getInt("dlr-navteq.precision"));
if (!haveGeo) {
WRITE_WARNING(TL("DlrNavteq node data will be written in (floating point) cartesian coordinates"));
}
device << "# NODE_ID\tIS_BETWEEN_NODE\tamount_of_geocoordinates\tx1\ty1\t[x2 y2 ... xn yn]\n";
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";
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";
}
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);
NBHelpers::loadPrefixedIDsFomFile(oc.getString("reserved-ids"), "junction:", reservedNodeIDs);
}
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 (const auto& edgeIt : ec) {
const NBEdge* const e = edgeIt.second;
PositionVector geom = e->getGeometry();
if (geom.size() > 2) {
const bool hasOppositeID = ec.getOppositeByID(e->getID()) != nullptr;
if (e->getLaneSpreadFunction() == LaneSpreadFunction::RIGHT && !hasOppositeID) {
try {
geom.move2side(e->getTotalWidth() / 2);
} catch (InvalidArgument& exception) {
WRITE_WARNINGF(TL("Could not reconstruct shape for edge:'%' (%)."), e->getID(), exception.what());
}
} else if (e->getLaneSpreadFunction() == LaneSpreadFunction::CENTER && hasOppositeID) {
try {
geom.move2side(-e->getTotalWidth() / 2);
} catch (InvalidArgument& exception) {
WRITE_WARNINGF(TL("Could not reconstruct shape for edge:'%' (%)."), e->getID(), exception.what());
}
}
if (geom.size() > 2) {
std::string internalNodeID = e->getID();
if (internalNodeID == UNDEFINED
|| (nc.retrieve(internalNodeID) != nullptr)
|| reservedNodeIDs.count(internalNodeID) > 0
) {
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();
}
void
NWWriter_DlrNavteq::writeLinksUnsplitted(const OptionsCont& oc, const NBEdgeCont& ec, const std::map<const NBEdge*, std::string>& internalNodes) {
const int majorVersion = StringUtils::toInt(StringTokenizer(oc.getString("dlr-navteq.version"), ".").next());
std::map<const std::string, std::string> nameIDs;
OutputDevice& device = OutputDevice::getDevice(oc.getString("dlr-navteq-output") + "_links_unsplitted.txt");
writeHeader(device, oc);
device << "# LINK_ID\tNODE_ID_FROM\tNODE_ID_TO\tBETWEEN_NODE_ID\tLENGTH\tVEHICLE_TYPE\tFORM_OF_WAY\tBRUNNEL_TYPE\t"
<< "FUNCTIONAL_ROAD_CLASS\tSPEED_CATEGORY\tNUMBER_OF_LANES\tSPEED_LIMIT\tSPEED_RESTRICTION\t"
<< "NAME_ID1_REGIONAL\tNAME_ID2_LOCAL\tHOUSENUMBERS_RIGHT\tHOUSENUMBERS_LEFT\tZIP_CODE\t"
<< "AREA_ID\tSUBAREA_ID\tTHROUGH_TRAFFIC\tSPECIAL_RESTRICTIONS\tEXTENDED_NUMBER_OF_LANES\tISRAMP\tCONNECTION";
if (majorVersion > 6) {
device << "\tMAXHEIGHT\tMAXWIDTH\tMAXWEIGHT\tSURFACE";
}
device << "\n";
for (const auto& edgeIt : ec) {
const NBEdge* const e = edgeIt.second;
const int kph = speedInKph(e->getSpeed());
const auto& internalIt = internalNodes.find(e);
const std::string& betweenNodeID = internalIt != internalNodes.end() ? internalIt->second : UNDEFINED;
std::string nameID = UNDEFINED;
std::string nameIDRegional = UNDEFINED;
if (oc.getBool("output.street-names")) {
const std::string& name = e->getStreetName();
if (name != "") {
if (nameIDs.count(name) == 0) {
const int tmp = (int)nameIDs.size();
nameIDs[name] = toString(tmp);
}
nameID = nameIDs[name];
}
const std::string& name2 = e->getParameter("ref", "");
if (name2 != "") {
if (nameIDs.count(name2) == 0) {
const int tmp = (int)nameIDs.size();
nameIDs[name2] = toString(tmp);
}
nameIDRegional = nameIDs[name2];
}
}
device << e->getID() << "\t"
<< e->getFromNode()->getID() << "\t"
<< e->getToNode()->getID() << "\t"
<< betweenNodeID << "\t"
<< getGraphLength(e) << "\t"
<< getAllowedTypes(e->getPermissions()) << "\t"
<< getFormOfWay(e) << "\t"
<< getBrunnelType(e) << "\t"
<< getRoadClass(e) << "\t"
<< getSpeedCategory(kph) << "\t"
<< getNavteqLaneCode(e->getNumLanes()) << "\t"
<< getSpeedCategoryUpperBound(kph) << "\t"
<< kph << "\t"
<< nameIDRegional << "\t"
<< nameID << "\t"
<< UNDEFINED << "\t"
<< UNDEFINED << "\t"
<< getSinglePostalCode(e->getParameter("postal_code", UNDEFINED), e->getID()) << "\t"
<< UNDEFINED << "\t"
<< UNDEFINED << "\t"
<< "1\t"
<< UNDEFINED << "\t"
<< UNDEFINED << "\t"
<< UNDEFINED << "\t"
<< "0";
if (majorVersion > 6) {
device << "\t" << e->getParameter("maxheight", UNDEFINED)
<< "\t" << e->getParameter("maxwidth", UNDEFINED)
<< "\t" << e->getParameter("maxweight", UNDEFINED)
<< "\t" << e->getParameter("surface", UNDEFINED);
}
device << "\n";
}
if (oc.getBool("output.street-names")) {
OutputDevice& namesDevice = OutputDevice::getDevice(oc.getString("dlr-navteq-output") + "_names.txt");
writeHeader(namesDevice, oc);
namesDevice << "# NAME_ID\tPERMANENT_ID_INFO\tName\n";
namesDevice << "# [elements] " << nameIDs.size() << "\n";
for (std::map<const std::string, std::string>::const_iterator i = nameIDs.begin(); i != nameIDs.end(); ++i) {
namesDevice
<< i->second << "\t"
<< 0 << "\t"
<< i->first << "\n";
}
namesDevice.close();
}
device.close();
}
std::string
NWWriter_DlrNavteq::getAllowedTypes(SVCPermissions permissions) {
if (permissions == SVCAll) {
return "100000000000";
}
std::ostringstream oss;
oss << "0";
oss << ((permissions & SVC_PASSENGER) > 0 ? 1 : 0);
oss << ((permissions & SVC_PASSENGER) > 0 ? 1 : 0);
oss << ((permissions & SVC_HOV) > 0 ? 1 : 0);
oss << ((permissions & SVC_EMERGENCY) > 0 ? 1 : 0);
oss << ((permissions & SVC_TAXI) > 0 ? 1 : 0);
oss << ((permissions & (SVC_BUS | SVC_COACH)) > 0 ? 1 : 0);
oss << ((permissions & SVC_DELIVERY) > 0 ? 1 : 0);
oss << ((permissions & (SVC_TRUCK | SVC_TRAILER)) > 0 ? 1 : 0);
oss << ((permissions & SVC_MOTORCYCLE) > 0 ? 1 : 0);
oss << ((permissions & SVC_BICYCLE) > 0 ? 1 : 0);
oss << ((permissions & SVC_PEDESTRIAN) > 0 ? 1 : 0);
return oss.str();
}
int
NWWriter_DlrNavteq::getRoadClass(const NBEdge* const edge) {
std::string type = edge->getTypeID();
if (StringUtils::startsWith(type, "highway.")) {
type = type.substr(8);
}
if (StringUtils::startsWith(type, "motorway")) {
return 0;
} else if (StringUtils::startsWith(type, "trunk")) {
return 1;
} else if (StringUtils::startsWith(type, "primary")) {
return 1;
} else if (StringUtils::startsWith(type, "secondary")) {
return 2;
} else if (StringUtils::startsWith(type, "tertiary")) {
return 3;
} else if (type == "unclassified") {
return 3;
} else if (type == "living_street" || type == "residential" || type == "road" || type == "service" || type == "track" || type == "cycleway" || type == "path" || type == "footway") {
return 4;
}
const int kph = speedInKph(edge->getSpeed());
if ((kph) > 100) {
return 0;
}
if ((kph) > 70) {
return 1;
}
if ((kph) > 50) {
return (edge->getNumLanes() > 1 ? 2 : 3);
}
if ((kph) > 30) {
return 3;
}
return 4;
}
int
NWWriter_DlrNavteq::getSpeedCategory(int kph) {
if ((kph) > 130) {
return 1;
}
if ((kph) > 100) {
return 2;
}
if ((kph) > 90) {
return 3;
}
if ((kph) > 70) {
return 4;
}
if ((kph) > 50) {
return 5;
}
if ((kph) > 30) {
return 6;
}
if ((kph) > 10) {
return 7;
}
return 8;
}
int
NWWriter_DlrNavteq::getSpeedCategoryUpperBound(int kph) {
if ((kph) > 130) {
return 131;
}
if ((kph) > 100) {
return 130;
}
if ((kph) > 90) {
return 100;
}
if ((kph) > 70) {
return 90;
}
if ((kph) > 50) {
return 70;
}
if ((kph) > 30) {
return 50;
}
if ((kph) > 10) {
return 30;
}
return 10;
}
int
NWWriter_DlrNavteq::getNavteqLaneCode(const int numLanes) {
const int code = (numLanes == 1 ? 1 :
(numLanes < 4 ? 2 : 3));
return numLanes * 10 + code;
}
int
NWWriter_DlrNavteq::getBrunnelType(const NBEdge* const edge) {
if (edge->hasParameter("bridge")) {
return 1;
} else if (edge->hasParameter("tunnel")) {
return 4;
} else if (edge->getTypeID() == "route.ferry") {
return 10;
}
return -1;
}
int
NWWriter_DlrNavteq::getFormOfWay(const NBEdge* const edge) {
if (edge->getPermissions() == SVC_PEDESTRIAN) {
return 15;
} else if (edge->getJunctionPriority(edge->getToNode()) == NBEdge::JunctionPriority::ROUNDABOUT) {
return 4;
} else if (edge->getTypeID() == "highway.service") {
return 14;
} else if (edge->getTypeID().find("_link") != std::string::npos) {
return 10;
}
return 3;
}
double
NWWriter_DlrNavteq::getGraphLength(const NBEdge* const edge) {
PositionVector geom = edge->getGeometry();
geom.push_back_noDoublePos(edge->getToNode()->getPosition());
geom.push_front_noDoublePos(edge->getFromNode()->getPosition());
return geom.length();
}
std::string
NWWriter_DlrNavteq::getSinglePostalCode(const std::string& zipCode, const std::string edgeID) {
if (zipCode.find_first_of(" ,;") != std::string::npos) {
WRITE_WARNINGF("ambiguous zip code '%' for edge '%'. (using first value)", zipCode, edgeID);
StringTokenizer st(zipCode, " ,;", true);
std::vector<std::string> ret = st.getVector();
return ret[0];
} else if (zipCode.size() > 16) {
WRITE_WARNINGF("long zip code '%' for edge '%'", zipCode, edgeID);
}
return zipCode;
}
void
NWWriter_DlrNavteq::writeTrafficSignals(const OptionsCont& oc, NBNodeCont& nc) {
OutputDevice& device = OutputDevice::getDevice(oc.getString("dlr-navteq-output") + "_traffic_signals.txt");
writeHeader(device, oc);
const GeoConvHelper& gch = GeoConvHelper::getFinal();
const bool haveGeo = gch.usingGeoProjection();
const double geoScale = pow(10.0f, haveGeo ? 5 : 2);
device.setPrecision(oc.getInt("dlr-navteq.precision"));
device << "#Traffic signal related to LINK_ID and NODE_ID with location relative to driving direction.\n#column format like pointcollection.\n#DESCRIPTION->LOCATION: 1-rechts von LINK; 2-links von LINK; 3-oberhalb LINK -1-keineAngabe\n#RELATREC_ID\tPOICOL_TYPE\tDESCRIPTION\tLONGITUDE\tLATITUDE\tLINK_ID\n";
for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
NBNode* n = (*i).second;
if (n->isTLControlled()) {
Position pos = n->getPosition();
gch.cartesian2geo(pos);
pos.mul(geoScale);
const EdgeVector& incoming = n->getIncomingEdges();
for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
NBEdge* e = *it;
device << e->getID() << "\t"
<< "12\t"
<< "LSA;NODEIDS#" << n->getID() << "#;LOCATION#-1#;\t"
<< pos.x() << "\t"
<< pos.y() << "\t"
<< e->getID() << "\n";
}
}
}
device.close();
}
void
NWWriter_DlrNavteq::writeProhibitedManoeuvres(const OptionsCont& oc, const NBNodeCont& nc, const NBEdgeCont& ec) {
OutputDevice& device = OutputDevice::getDevice(oc.getString("dlr-navteq-output") + "_prohibited_manoeuvres.txt");
writeHeader(device, oc);
std::set<std::string> reservedRelIDs;
if (oc.isSet("reserved-ids")) {
NBHelpers::loadPrefixedIDsFomFile(oc.getString("reserved-ids"), "rel:", reservedRelIDs);
}
std::vector<std::string> avoid = ec.getAllNames();
avoid.insert(avoid.end(), reservedRelIDs.begin(), reservedRelIDs.end());
IDSupplier idSupplier("", avoid);
device << "#No driving allowed from ID1 to ID2 or the complete chain from ID1 to IDn\n";
device << "#RELATREC_ID\tPERMANENT_ID_INFO\tVALIDITY_PERIOD\tTHROUGH_TRAFFIC\tVEHICLE_TYPE\tNAVTEQ_LINK_ID1\t[NAVTEQ_LINK_ID2 ...]\n";
for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
NBNode* n = (*i).second;
const EdgeVector& incoming = n->getIncomingEdges();
const EdgeVector& outgoing = n->getOutgoingEdges();
for (EdgeVector::const_iterator j = incoming.begin(); j != incoming.end(); ++j) {
NBEdge* inEdge = *j;
const SVCPermissions inPerm = inEdge->getPermissions();
for (EdgeVector::const_iterator k = outgoing.begin(); k != outgoing.end(); ++k) {
NBEdge* outEdge = *k;
const SVCPermissions outPerm = outEdge->getPermissions();
const SVCPermissions commonPerm = inPerm & outPerm;
if (commonPerm != 0 && commonPerm != SVC_PEDESTRIAN && !inEdge->isConnectedTo(outEdge)) {
device
<< idSupplier.getNext() << "\t"
<< 1 << "\t"
<< UNDEFINED << "\t"
<< 1 << "\t"
<< getAllowedTypes(SVCAll) << "\t"
<< inEdge->getID() << "\t" << outEdge->getID() << "\n";
}
}
}
}
device.close();
}
void
NWWriter_DlrNavteq::writeConnectedLanes(const OptionsCont& oc, NBNodeCont& nc) {
OutputDevice& device = OutputDevice::getDevice(oc.getString("dlr-navteq-output") + "_connected_lanes.txt");
writeHeader(device, oc);
device << "#Lane connections related to LINK-IDs and NODE-ID.\n";
device << "#column format like pointcollection.\n";
device << "#NODE-ID\tVEHICLE-TYPE\tFROM_LANE\tTO_LANE\tTHROUGH_TRAFFIC\tLINK_IDs[2..*]\n";
for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
NBNode* n = (*i).second;
const EdgeVector& incoming = n->getIncomingEdges();
for (EdgeVector::const_iterator j = incoming.begin(); j != incoming.end(); ++j) {
NBEdge* from = *j;
const SVCPermissions fromPerm = from->getPermissions();
const std::vector<NBEdge::Connection>& connections = from->getConnections();
for (std::vector<NBEdge::Connection>::const_iterator it_c = connections.begin(); it_c != connections.end(); it_c++) {
const NBEdge::Connection& c = *it_c;
device
<< n->getID() << "\t"
<< getAllowedTypes(fromPerm & c.toEdge->getPermissions()) << "\t"
<< c.fromLane + 1 << "\t"
<< c.toLane + 1 << "\t"
<< 1 << "\t"
<< from->getID() << "\t"
<< c.toEdge->getID() << "\t"
<< "\n";
}
}
}
device.close();
}