diff --git a/siteupdate/cplusplus/classes/Arguments.cpp b/siteupdate/cplusplus/classes/Arguments.cpp index 977a17e7..bedf62c6 100644 --- a/siteupdate/cplusplus/classes/Arguments.cpp +++ b/siteupdate/cplusplus/classes/Arguments.cpp @@ -1,5 +1,3 @@ -//FIXME: argv[n+1] can smash the stack. -//FIXME: Try breaking --numthreads with garbage arguments class Arguments { public: /* w */ std::string highwaydatapath; @@ -11,8 +9,9 @@ class Arguments /* g */ std::string graphfilepath; /* k */ bool skipgraphs; /* n */ std::string nmpmergepath; + /* p */ std::string splitregion, splitregionpath; /* U */ std::list userlist; - /* t */ unsigned int numthreads; + /* t */ int numthreads; /* e */ bool errorcheck; /* h */ bool help; @@ -27,6 +26,7 @@ class Arguments /* g */ graphfilepath = "."; /* k */ skipgraphs = 0; /* n */ nmpmergepath = ""; + /* p */ splitregionpath = ""; /* U */ // nothing to do here /* t */ numthreads = 4; /* e */ errorcheck = 0; @@ -34,31 +34,33 @@ class Arguments // parsing for (unsigned int n = 1; n < argc; n++) - { if ( !strcmp(argv[n], "-w") || !strcmp(argv[n], "--highwaydatapath") ) { + { if ( n+1 < argc && !strcmp(argv[n], "-w") || !strcmp(argv[n], "--highwaydatapath") ) { highwaydatapath = argv[n+1]; n++; } - else if ( !strcmp(argv[n], "-s") || !strcmp(argv[n], "--systemsfile") ) { + else if ( n+1 < argc && !strcmp(argv[n], "-s") || !strcmp(argv[n], "--systemsfile") ) { systemsfile = argv[n+1]; n++; } - else if ( !strcmp(argv[n], "-u") || !strcmp(argv[n], "--userlistfilepath") ) { + else if ( n+1 < argc && !strcmp(argv[n], "-u") || !strcmp(argv[n], "--userlistfilepath") ) { userlistfilepath = argv[n+1]; n++; } - else if ( !strcmp(argv[n], "-d") || !strcmp(argv[n], "--databasename") ) { + else if ( n+1 < argc && !strcmp(argv[n], "-d") || !strcmp(argv[n], "--databasename") ) { databasename = argv[n+1]; n++; } - else if ( !strcmp(argv[n], "-l") || !strcmp(argv[n], "--logfilepath") ) { + else if ( n+1 < argc && !strcmp(argv[n], "-l") || !strcmp(argv[n], "--logfilepath") ) { logfilepath = argv[n+1]; n++; } - else if ( !strcmp(argv[n], "-c") || !strcmp(argv[n], "--csvstatfilepath") ) { + else if ( n+1 < argc && !strcmp(argv[n], "-c") || !strcmp(argv[n], "--csvstatfilepath") ) { csvstatfilepath = argv[n+1]; n++; } - else if ( !strcmp(argv[n], "-g") || !strcmp(argv[n], "--graphfilepath") ) { + else if ( n+1 < argc && !strcmp(argv[n], "-g") || !strcmp(argv[n], "--graphfilepath") ) { graphfilepath = argv[n+1]; n++; } - else if ( !strcmp(argv[n], "-k") || !strcmp(argv[n], "--skipgraphs") ) + else if ( !strcmp(argv[n], "-k") || !strcmp(argv[n], "--skipgraphs") ) skipgraphs = 1; - else if ( !strcmp(argv[n], "-n") || !strcmp(argv[n], "--nmpmergepath") ) { + else if ( n+1 < argc && !strcmp(argv[n], "-n") || !strcmp(argv[n], "--nmpmergepath") ) { nmpmergepath = argv[n+1]; n++; } - else if ( !strcmp(argv[n], "-t") || !strcmp(argv[n], "--numthreads") ) { - numthreads = strtol(argv[n+1], 0, 10); n++; } - else if ( !strcmp(argv[n], "-e") || !strcmp(argv[n], "--errorcheck") ) + else if ( n+2 < argc && !strcmp(argv[n], "-p") || !strcmp(argv[n], "--splitregion") ) { + splitregionpath = argv[n+1]; splitregion = argv[n+2]; n +=2; } + else if ( n+1 < argc && !strcmp(argv[n], "-t") || !strcmp(argv[n], "--numthreads") ) { + numthreads = strtol(argv[n+1], 0, 10); n++; if (numthreads<1) numthreads=1; } + else if ( !strcmp(argv[n], "-e") || !strcmp(argv[n], "--errorcheck") ) errorcheck = 1; - else if ( !strcmp(argv[n], "-h") || !strcmp(argv[n], "--help") ) { + else if ( !strcmp(argv[n], "-h") || !strcmp(argv[n], "--help") ) { help = 1; show_help(); } - else if ( !strcmp(argv[n], "-U") || !strcmp(argv[n], "--userlist") ) + else if ( n+1 < argc && !strcmp(argv[n], "-U") || !strcmp(argv[n], "--userlist") ) while (n+1 < argc && argv[n+1][0] != '-') { userlist.push_back(argv[n+1]); n++; @@ -70,8 +72,8 @@ class Arguments { std::cout << "usage: siteupdate.py [-h] [-w HIGHWAYDATAPATH] [-s SYSTEMSFILE]\n"; std::cout << " [-u USERLISTFILEPATH] [-d DATABASENAME] [-l LOGFILEPATH]\n"; std::cout << " [-c CSVSTATFILEPATH] [-g GRAPHFILEPATH] [-k]\n"; - std::cout << " [-n NMPMERGEPATH] [-U USERLIST [USERLIST ...]]\n"; - std::cout << " [-t NUMTHREADS] [-e]\n"; + std::cout << " [-n NMPMERGEPATH] [-p SPLITREGIONPATH SPLITREGION]\n"; + std::cout << " [-U USERLIST [USERLIST ...]] [-t NUMTHREADS] [-e]\n"; std::cout << "\n"; std::cout << "Create SQL, stats, graphs, and log files from highway and user data for the\n"; std::cout << "Travel Mapping project.\n"; @@ -98,6 +100,10 @@ class Arguments std::cout << " -n NMPMERGEPATH, --nmpmergepath NMPMERGEPATH\n"; std::cout << " Path to write data with NMPs merged (generated only if\n"; std::cout << " specified)\n"; + std::cout << " -p SPLITREGIONPATH SPLITREGION, --splitregion SPLITREGIONPATH SPLITREGION\n"; + std::cout << " Path to logs & .lists for a specific...\n"; + std::cout << " Region being split into rubregions.\n"; + std::cout << " For Development.\n"; std::cout << " -U USERLIST [USERLIST ...], --userlist USERLIST [USERLIST ...]\n"; std::cout << " For Development: list of users to use in dataset\n"; std::cout << " -t NUMTHREADS, --numthreads NUMTHREADS\n"; diff --git a/siteupdate/cplusplus/classes/ConnectedRoute.cpp b/siteupdate/cplusplus/classes/ConnectedRoute.cpp index 862f581a..34a9a28f 100644 --- a/siteupdate/cplusplus/classes/ConnectedRoute.cpp +++ b/siteupdate/cplusplus/classes/ConnectedRoute.cpp @@ -74,6 +74,7 @@ class ConnectedRoute { Route *root = route_by_root(token, route_list); if (!root) el.add_error("Could not find Route matching root " + std::string(token) + " in system " + system->systemname + '.'); else { roots.push_back(root); + root->con_route = this; // save order of route in connected route root->rootOrder = rootOrder; } @@ -107,4 +108,20 @@ class ConnectedRoute if (!groupname.empty()) ans += " (" + groupname + ")"; return ans; } + + std::string list_lines(int pos, int len, std::string newline, size_t indent) + { // return .list file lines marking (len) consecutive + // segments, starting at waypoint (pos) segments into route + //std::cout << "\nDEBUG: list_lines for " << readable_name() << " (" << roots.size() << " connected root(s))" << std::endl; + std::string lines; + for (Route *r : roots) + { //std::cout << "DEBUG: [" << pos << " + " << len << " = " << pos+len << "] " << r->str() << std::endl; + std::string line = std::string(indent, ' ') + r->list_line(pos, pos+len); + if (line.size() > indent) lines += line + newline; + pos -= r->segment_list.size(); + } + // strip final newline + while (lines.back() == '\n' || lines.back() == '\r') lines.pop_back(); + return lines; + } }; diff --git a/siteupdate/cplusplus/classes/GraphGeneration/GraphListEntry.cpp b/siteupdate/cplusplus/classes/GraphGeneration/GraphListEntry.cpp index 6265639e..23188593 100644 --- a/siteupdate/cplusplus/classes/GraphGeneration/GraphListEntry.cpp +++ b/siteupdate/cplusplus/classes/GraphGeneration/GraphListEntry.cpp @@ -34,6 +34,7 @@ std::string GraphListEntry::filename() { switch (form) { case 's': return root+"-simple.tmg"; case 'c': return root+".tmg"; + case 't': return root+"-traveled.tmg"; default : return std::string("ERROR: GraphListEntry::filename() unexpected format token ('")+form+"')"; } } @@ -42,6 +43,7 @@ std::string GraphListEntry::format() { switch (form) { case 's': return "simple"; case 'c': return "collapsed"; + case 't': return "traveled"; default : return std::string("ERROR: GraphListEntry::format() unexpected format token ('")+form+"')"; } } diff --git a/siteupdate/cplusplus/classes/GraphGeneration/HGEdge.cpp b/siteupdate/cplusplus/classes/GraphGeneration/HGEdge.cpp new file mode 100644 index 00000000..04bb7209 --- /dev/null +++ b/siteupdate/cplusplus/classes/GraphGeneration/HGEdge.cpp @@ -0,0 +1,283 @@ +// constants for more human-readable format masks +const unsigned char HGEdge::simple = 1; +const unsigned char HGEdge::collapsed = 2; +const unsigned char HGEdge::traveled = 4; + +HGEdge::HGEdge(HighwaySegment *s, HighwayGraph *graph) +{ // initial construction is based on a HighwaySegment + s_written = 0; // simple + c_written = 0; // collapsed + t_written = 0; // traveled + vertex1 = graph->vertices.at(s->waypoint1->hashpoint()); + vertex2 = graph->vertices.at(s->waypoint2->hashpoint()); + // checks for the very unusual cases where an edge ends up + // in the system as itself and its "reverse" + for (HGEdge *e : vertex1->incident_s_edges) + if (e->vertex1 == vertex2 && e->vertex2 == vertex1) + { delete this; + return; + } + for (HGEdge *e : vertex2->incident_s_edges) + if (e->vertex1 == vertex2 && e->vertex2 == vertex1) + { delete this; + return; + } + format = simple | collapsed | traveled; + segment_name = s->segment_name(); + vertex1->incident_s_edges.push_back(this); + vertex2->incident_s_edges.push_back(this); + vertex1->incident_c_edges.push_back(this); + vertex2->incident_c_edges.push_back(this); + vertex1->incident_t_edges.push_back(this); + vertex2->incident_t_edges.push_back(this); + // canonical segment, used to reference region and list of travelers + // assumption: each edge/segment lives within a unique region + // and a 'multi-edge' would not be able to span regions as there + // would be a required visible waypoint at the border + segment = s; + s->route->region->edges.insert(this); + // a list of route name/system pairs + if (!s->concurrent) + { route_names_and_systems.emplace_back(s->route->list_entry_name(), s->route->system); + s->route->system->edges.insert(this); + } + else for (HighwaySegment *cs : *(s->concurrent)) + { if (cs->route->system->devel()) continue; + route_names_and_systems.emplace_back(cs->route->list_entry_name(), cs->route->system); + cs->route->system->edges.insert(this); + } +} + +HGEdge::HGEdge(HGVertex *vertex, unsigned char fmt_mask) +{ // build by collapsing two existing edges around a common + // hidden vertex waypoint, whose information is given in + // vertex + c_written = 0; + t_written = 0; + format = fmt_mask; + // we know there are exactly 2 incident edges, as we + // checked for that, and we will replace these two + // with the single edge we are constructing here... + HGEdge *edge1 = 0; + HGEdge *edge2 = 0; + if (fmt_mask & collapsed) + { // ...in the compressed graph, and/or... + edge1 = vertex->incident_c_edges.front(); + edge2 = vertex->incident_c_edges.back(); + } + if (fmt_mask & traveled) + { // ...in the traveled graph, as appropriate + edge1 = vertex->incident_t_edges.front(); + edge2 = vertex->incident_t_edges.back(); + } + // segment names should match as routes should not start or end + // nor should concurrencies begin or end at a hidden point + if (edge1->segment_name != edge2->segment_name) + { std::cout << "ERROR: segment name mismatch in HGEdge collapse constructor: "; + std::cout << "edge1 named " << edge1->segment_name << " edge2 named " << edge2->segment_name << '\n' << std::endl; + } + segment_name = edge1->segment_name; + /*std::cout << "\nDEBUG: collapsing edges |"; + if (fmt_mask & collapsed) std::cout << 'c'; else std::cout << '-'; + if (fmt_mask & traveled) std::cout << 't'; else std::cout << '-'; + std::cout << "| along " << segment_name << " at vertex " << *(vertex->unique_name); + std::cout << "\n edge1 is " << edge1->str(); + std::cout << "\n edge2 is " << edge2->str() << std::endl;//*/ + // segment and route names/systems should also match, but not + // doing that sanity check here, as the above check should take + // care of that + segment = edge1->segment; + route_names_and_systems = edge1->route_names_and_systems; + + // figure out and remember which endpoints are not the + // vertex we are collapsing and set them as our new + // endpoints, and at the same time, build up our list of + // intermediate vertices + intermediate_points = edge1->intermediate_points; + //std::cout << "DEBUG: copied edge1 intermediates" << intermediate_point_string() << std::endl; + + if (edge1->vertex1 == vertex) + { //std::cout << "DEBUG: vertex1 getting edge1->vertex2: " << *(edge1->vertex2->unique_name) << " and reversing edge1 intermediates" << std::endl; + vertex1 = edge1->vertex2; + intermediate_points.reverse(); + } + else { //std::cout << "DEBUG: vertex1 getting edge1->vertex1: " << *(edge1->vertex1->unique_name) << std::endl; + vertex1 = edge1->vertex1; + } + + //std::cout << "DEBUG: appending to intermediates: " << *(vertex->unique_name) << std::endl; + intermediate_points.push_back(vertex); + + if (edge2->vertex1 == vertex) + { //std::cout << "DEBUG: vertex2 getting edge2->vertex2: " << *(edge2->vertex2->unique_name) << std::endl; + vertex2 = edge2->vertex2; + intermediate_points.insert(intermediate_points.end(), edge2->intermediate_points.begin(), edge2->intermediate_points.end()); + } + else { //std::cout << "DEBUG: vertex2 getting edge2->vertex1: " << *(edge2->vertex1->unique_name) << " and reversing edge2 intermediates" << std::endl; + vertex2 = edge2->vertex1; + intermediate_points.insert(intermediate_points.end(), edge2->intermediate_points.rbegin(), edge2->intermediate_points.rend()); + } + + //std::cout << "DEBUG: intermediates complete: from " << *(vertex1->unique_name) << " via " << \ + intermediate_point_string() << " to " << *(vertex2->unique_name) << std::endl; + //std::cout << "DEBUG: new " << str() << std::endl; + + // replace edge references at our endpoints with ourself + edge1->detach(fmt_mask); + edge2->detach(fmt_mask); + if (!edge1->format) delete edge1; + if (!edge2->format) delete edge2; + if (fmt_mask & collapsed) + { vertex1->incident_c_edges.push_back(this); + vertex2->incident_c_edges.push_back(this); + } + if (fmt_mask & traveled) + { vertex1->incident_t_edges.push_back(this); + vertex2->incident_t_edges.push_back(this); + } +} + +void HGEdge::detach(unsigned char fmt_mask) +{ if (fmt_mask & simple) + { for ( std::list::iterator e = vertex1->incident_s_edges.begin(); + e != vertex1->incident_s_edges.end(); + e++ + ) if (*e == this) + { vertex1->incident_s_edges.erase(e); + break; + } + for ( std::list::iterator e = vertex2->incident_s_edges.begin(); + e != vertex2->incident_s_edges.end(); + e++ + ) if (*e == this) + { vertex2->incident_s_edges.erase(e); + break; + } + } + if (fmt_mask & collapsed) + { for ( std::list::iterator e = vertex1->incident_c_edges.begin(); + e != vertex1->incident_c_edges.end(); + e++ + ) if (*e == this) + { vertex1->incident_c_edges.erase(e); + break; + } + for ( std::list::iterator e = vertex2->incident_c_edges.begin(); + e != vertex2->incident_c_edges.end(); + e++ + ) if (*e == this) + { vertex2->incident_c_edges.erase(e); + break; + } + } + if (fmt_mask & traveled) + { for ( std::list::iterator e = vertex1->incident_t_edges.begin(); + e != vertex1->incident_t_edges.end(); + e++ + ) if (*e == this) + { vertex1->incident_t_edges.erase(e); + break; + } + for ( std::list::iterator e = vertex2->incident_t_edges.begin(); + e != vertex2->incident_t_edges.end(); + e++ + ) if (*e == this) + { vertex2->incident_t_edges.erase(e); + break; + } + } + format &= ~fmt_mask; +} + +HGEdge::~HGEdge() +{ /*if (format) + { std::cout << '~'; + if (format & simple) std::cout << 's'; + if (format & collapsed) std::cout << 'c'; + std::cout << std::endl; + }//*/ + detach(format); + segment_name.clear(); + intermediate_points.clear(); + route_names_and_systems.clear(); +} + +// compute an edge label, optionally resticted by systems +std::string HGEdge::label(std::list *systems) +{ std::string the_label; + for (std::pair &ns : route_names_and_systems) + { // test whether system in systems + bool sys_in_sys = 0; + if (systems) for (HighwaySystem *h : *systems) + if (h == ns.second) + { sys_in_sys = 1; + break; + } + if (!systems || sys_in_sys) + if (the_label.empty()) + the_label = ns.first; + else the_label += "," + ns.first; + } + return the_label; +} + +// line appropriate for a tmg collapsed edge file +std::string HGEdge::collapsed_tmg_line(std::list *systems, unsigned int threadnum) +{ std::string line = std::to_string(vertex1->c_vertex_num[threadnum]) + " " + std::to_string(vertex2->c_vertex_num[threadnum]) + " " + label(systems); + char fstr[43]; + for (HGVertex *intermediate : intermediate_points) + { sprintf(fstr, " %.15g %.15g", intermediate->lat, intermediate->lng); + line += fstr; + } + return line; +} + +// line appropriate for a tmg traveled edge file +std::string HGEdge::traveled_tmg_line(std::list *systems, std::list *traveler_lists, unsigned int threadnum) +{ std::string line = std::to_string(vertex1->t_vertex_num[threadnum]) + " " + std::to_string(vertex2->t_vertex_num[threadnum]) + " " + label(systems); + line += " " + segment->clinchedby_code(traveler_lists); + char fstr[43]; + for (HGVertex *intermediate : intermediate_points) + { sprintf(fstr, " %.15g %.15g", intermediate->lat, intermediate->lng); + line += fstr; + } + return line; +} + +// line appropriate for a tmg collapsed edge file, with debug info +std::string HGEdge::debug_tmg_line(std::list *systems, unsigned int threadnum) +{ std::string line = std::to_string(vertex1->c_vertex_num[threadnum]) + " [" + *vertex1->unique_name + "] " \ + + std::to_string(vertex2->c_vertex_num[threadnum]) + " [" + *vertex2->unique_name + "] " + label(systems); + char fstr[44]; + for (HGVertex *intermediate : intermediate_points) + { sprintf(fstr, "] %.15g %.15g", intermediate->lat, intermediate->lng); + line += " [" + *intermediate->unique_name + fstr; + } + return line; +} + +// printable string for this edge +std::string HGEdge::str() +{ std::string str = "HGEdge |"; + if (format & simple) str += 's'; else str += '-'; + if (format & collapsed) str += 'c'; else str += '-'; + if (format & traveled) str += 't'; else str += '-'; + str += "|: " + segment_name + + " from " + *vertex1->unique_name + + " to " + *vertex2->unique_name + + " via " + std::to_string(intermediate_points.size()) + " points {" + + std::to_string((long long unsigned int)this) + '}'; + return str; +} + +// return the intermediate points as a string +std::string HGEdge::intermediate_point_string() +{ if (intermediate_points.empty()) return " None"; + std::string line = ""; + char fstr[42]; + for (HGVertex *i : intermediate_points) + { sprintf(fstr, "%.15g %.15g", i->lat, i->lng); + line += " [" + *i->unique_name + "] " + fstr; + } + return line; +} diff --git a/siteupdate/cplusplus/classes/GraphGeneration/HGEdge.h b/siteupdate/cplusplus/classes/GraphGeneration/HGEdge.h new file mode 100644 index 00000000..7b6374e0 --- /dev/null +++ b/siteupdate/cplusplus/classes/GraphGeneration/HGEdge.h @@ -0,0 +1,30 @@ +class HGEdge +{ /* This class encapsulates information needed for a highway graph + edge that can incorporate intermediate points. + */ + public: + bool s_written, c_written, t_written; // simple, collapsed, traveled + std::string segment_name; + HGVertex *vertex1, *vertex2; + std::list intermediate_points; // if more than 1, will go from vertex1 to vertex2 + HighwaySegment *segment; + std::list> route_names_and_systems; + unsigned char format; + + // constants for more human-readable format masks + static const unsigned char simple; + static const unsigned char collapsed; + static const unsigned char traveled; + + HGEdge(HighwaySegment *, HighwayGraph *); + HGEdge(HGVertex *, unsigned char); + ~HGEdge(); + + void detach(unsigned char); + std::string label(std::list *); + std::string collapsed_tmg_line(std::list *, unsigned int); + std::string traveled_tmg_line(std::list *, std::list *, unsigned int); + std::string debug_tmg_line(std::list *, unsigned int); + std::string str(); + std::string intermediate_point_string(); +}; diff --git a/siteupdate/cplusplus/classes/GraphGeneration/HighwayGraphVertexInfo.cpp b/siteupdate/cplusplus/classes/GraphGeneration/HGVertex.cpp similarity index 60% rename from siteupdate/cplusplus/classes/GraphGeneration/HighwayGraphVertexInfo.cpp rename to siteupdate/cplusplus/classes/GraphGeneration/HGVertex.cpp index 62feb11a..ae9c1364 100644 --- a/siteupdate/cplusplus/classes/GraphGeneration/HighwayGraphVertexInfo.cpp +++ b/siteupdate/cplusplus/classes/GraphGeneration/HGVertex.cpp @@ -1,32 +1,39 @@ -class HighwayGraphVertexInfo +class HGVertex { /* This class encapsulates information needed for a highway graph vertex. */ public: double lat, lng; const std::string *unique_name; - bool is_hidden; + char visibility; Waypoint *first_waypoint; std::unordered_set regions; std::unordered_set systems; - std::list incident_edges; - std::list incident_collapsed_edges; - int *vertex_num; - int *vis_vertex_num; + std::list incident_s_edges; // simple + std::list incident_c_edges; // collapsed + std::list incident_t_edges; // traveled + int *s_vertex_num; + int *c_vertex_num; + int *t_vertex_num; - HighwayGraphVertexInfo(Waypoint *wpt, const std::string *n, DatacheckEntryList *datacheckerrors, unsigned int numthreads) + HGVertex(Waypoint *wpt, const std::string *n, DatacheckEntryList *datacheckerrors, unsigned int numthreads) { lat = wpt->lat; lng = wpt->lng; - vertex_num = new int[numthreads]; - vis_vertex_num = new int[numthreads]; + s_vertex_num = new int[numthreads]; + c_vertex_num = new int[numthreads]; + t_vertex_num = new int[numthreads]; + // deleted by ~HGVertex, called by HighwayGraph::clear unique_name = n; - // will consider hidden iff all colocated waypoints are hidden - is_hidden = 1; + visibility = 0; + // permitted values: + // 0: never visible outside of simple graphs + // 1: visible only in traveled graph; hidden in collapsed graph + // 2: visible in both traveled & collapsed graphs // note: if saving the first waypoint, no longer need // lat & lng and can replace with methods first_waypoint = wpt; if (!wpt->colocated) - { if (!wpt->is_hidden) is_hidden = 0; + { if (!wpt->is_hidden) visibility = 2; regions.insert(wpt->route->region); systems.insert(wpt->route->system); wpt->route->region->vertices.insert(this); @@ -34,7 +41,8 @@ class HighwayGraphVertexInfo return; } for (Waypoint *w : *(wpt->colocated)) - { if (!w->is_hidden) is_hidden = 0; + { // will consider hidden iff all colocated waypoints are hidden + if (!w->is_hidden) visibility = 2; regions.insert(w->route->region); systems.insert(w->route->system); w->route->region->vertices.insert(this); @@ -57,12 +65,12 @@ class HighwayGraphVertexInfo } } - ~HighwayGraphVertexInfo() + ~HGVertex() { //std::cout << "deleting vertex at " << first_waypoint->str() << std::endl; - while (incident_edges.size()) delete incident_edges.front(); - while (incident_collapsed_edges.size()) delete incident_collapsed_edges.front(); - delete[] vertex_num; - delete[] vis_vertex_num; + while (incident_s_edges.size()) delete incident_s_edges.front(); + while (incident_c_edges.size()) delete incident_c_edges.front(); + delete[] s_vertex_num; + delete[] c_vertex_num; regions.clear(); systems.clear(); } diff --git a/siteupdate/cplusplus/classes/GraphGeneration/HighwayGraph.cpp b/siteupdate/cplusplus/classes/GraphGeneration/HighwayGraph.cpp index 9ab3db1f..1e3e0eb5 100644 --- a/siteupdate/cplusplus/classes/GraphGeneration/HighwayGraph.cpp +++ b/siteupdate/cplusplus/classes/GraphGeneration/HighwayGraph.cpp @@ -4,9 +4,10 @@ class HighwayGraph On construction, build a set of unique vertex names and determine edges, at most one per concurrent segment. - Create two sets of edges - one for the full graph - and one for the graph with hidden waypoints compressed into - multi-point edges. + Create three sets of edges: + - one for the simple graph + - one for the collapsed graph with hidden waypoints compressed into multi-point edges + - one for the traveled graph: collapsed edges split at endpoints of users' travels */ public: @@ -15,7 +16,7 @@ class HighwayGraph // to track the waypoint name compressions, add log entries // to this list std::list waypoint_naming_log; - std::unordered_map vertices; + std::unordered_map vertices; HighwayGraph ( WaypointQuadtree &all_waypoints, @@ -70,14 +71,14 @@ class HighwayGraph // we're good; now construct a vertex if (!w->colocated) - vertices[w] = new HighwayGraphVertexInfo(w, &*(vertex_names.insert(point_name).first), datacheckerrors, numthreads); - else vertices[w] = new HighwayGraphVertexInfo(w->colocated->front(), &*(vertex_names.insert(point_name).first), datacheckerrors, numthreads); - // vertices are deleted by HighwayGraph::clear + vertices[w] = new HGVertex(w, &*(vertex_names.insert(point_name).first), datacheckerrors, numthreads); + else vertices[w] = new HGVertex(w->colocated->front(), &*(vertex_names.insert(point_name).first), datacheckerrors, numthreads); + // deleted by HighwayGraph::clear } std::cout << '!' << std::endl; //#include "../../debug/unique_names.cpp" - // add edges, which end up in two separate vertex adjacency lists, + // create edges counter = 0; std::cout << et.et() << "Creating edges" << std::flush; for (HighwaySystem *h : highway_systems) @@ -87,86 +88,125 @@ class HighwayGraph for (Route &r : h->route_list) for (HighwaySegment *s : r.segment_list) if (!s->concurrent || s == s->concurrent->front()) - { // first one copy for the full simple graph - bool duplicate = 0; - HighwayGraphEdgeInfo *e = new HighwayGraphEdgeInfo(s, this, duplicate); - // and again for a graph where hidden waypoints - // are merged into the edge structures - if (!duplicate) new HighwayGraphCollapsedEdgeInfo(e); - // edges & collapsed edges are deleted by ~HighwayGraphVertexInfo, called by HighwayGraph::clear - } + new HGEdge(s, this); + // deleted by ~HGVertex, called by HighwayGraph::clear } - std::cout << "!\n" << et.et() << "Full graph has " << vertices.size() << " vertices, " << edge_count() << " edges." << std::endl; + std::cout << '!' << std::endl; // compress edges adjacent to hidden vertices counter = 0; std::cout << et.et() << "Compressing collapsed edges" << std::flush; - for (std::pair wv : vertices) + for (std::pair wv : vertices) { if (counter % 10000 == 0) std::cout << '.' << std::flush; counter++; - if (wv.second->is_hidden) - { if (wv.second->incident_collapsed_edges.size() < 2) - { // these cases are flagged as HIDDEN_TERMINUS - wv.second->is_hidden = 0; + if (!wv.second->visibility) + { // cases with only one edge are flagged as HIDDEN_TERMINUS + if (wv.second->incident_c_edges.size() < 2) + { wv.second->visibility = 2; continue; } - if (wv.second->incident_collapsed_edges.size() > 2) + // if >2 edges, flag HIDDEN_JUNCTION, mark as visible, and do not compress + if (wv.second->incident_c_edges.size() > 2) { std::list::iterator it = wv.second->first_waypoint->colocated->begin(); Waypoint *dcw = *it; // "datacheck waypoint" for (it++; it != wv.second->first_waypoint->colocated->end(); it++) if (dcw->root_at_label() > (*it)->root_at_label()) - dcw->root_at_label() = (*it)->root_at_label(); - datacheckerrors->add(dcw->route, dcw->label, "", "", "HIDDEN_JUNCTION", std::to_string(wv.second->incident_collapsed_edges.size())); - wv.second->is_hidden = 0; + dcw->root_at_label() = (*it)->root_at_label(); //TODO: WHAT THE...? How is this actually working? (Or is it?) + datacheckerrors->add(dcw->route, dcw->label, "", "", "HIDDEN_JUNCTION", std::to_string(wv.second->incident_c_edges.size())); + wv.second->visibility = 2; continue; } - // construct from vertex_info this time - new HighwayGraphCollapsedEdgeInfo(wv.second); - // collapsed edges are deleted by ~HighwayGraphVertexInfo, called by HighwayGraph::clear + // if edge clinched_by sets mismatch, set visibility to 1 + // (visible in traveled graph; hidden in collapsed graph) + // first, the easy check, for whether set sizes mismatch + if (wv.second->incident_t_edges.front()->segment->clinched_by.size() + != wv.second->incident_t_edges.back()->segment->clinched_by.size()) + wv.second->visibility = 1; + // next, compare clinched_by sets; look for any element in the 1st not in the 2nd + else for (TravelerList *t : wv.second->incident_t_edges.front()->segment->clinched_by) + if (wv.second->incident_t_edges.back()->segment->clinched_by.find(t) + == wv.second->incident_t_edges.back()->segment->clinched_by.end()) + { wv.second->visibility = 1; + break; + } + // construct from vertex this time + if (wv.second->visibility == 1) + new HGEdge(wv.second, HGEdge::collapsed); + else if ((wv.second->incident_c_edges.front() == wv.second->incident_t_edges.front() + && wv.second->incident_c_edges.back() == wv.second->incident_t_edges.back()) + || (wv.second->incident_c_edges.front() == wv.second->incident_t_edges.back() + && wv.second->incident_c_edges.back() == wv.second->incident_t_edges.front())) + new HGEdge(wv.second, HGEdge::collapsed | HGEdge::traveled); + else { new HGEdge(wv.second, HGEdge::collapsed); + new HGEdge(wv.second, HGEdge::traveled); + // Final collapsed edges are deleted by ~HGVertex, called by HighwayGraph::clear. + // Partially collapsed edges created during the compression process are deleted + // upon detachment from all graphs. + } } } + std::cout << '!' << std::endl; // print summary info - std::cout << "!\n" << et.et() << "Edge compressed graph has " << num_visible_vertices() - << " vertices, " << collapsed_edge_count() << " edges." << std::endl; + std::cout << et.et() << " Simple graph has " << vertices.size() << " vertices, " << simple_edge_count() << " edges." << std::endl; + std::cout << et.et() << "Collapsed graph has " << num_collapsed_vertices() << " vertices, " << collapsed_edge_count() << " edges." << std::endl; + std::cout << et.et() << " Traveled graph has " << num_traveled_vertices() << " vertices, " << traveled_edge_count() << " edges." << std::endl; } // end ctor - unsigned int num_visible_vertices() + unsigned int num_collapsed_vertices() { unsigned int count = 0; - for (std::pair wv : vertices) - if (!wv.second->is_hidden) count ++; + for (std::pair wv : vertices) + if (wv.second->visibility == 2) count ++; return count; } - unsigned int edge_count() + unsigned int num_traveled_vertices() + { unsigned int count = 0; + for (std::pair wv : vertices) + if (wv.second->visibility >= 1) count ++; + return count; + } + + unsigned int simple_edge_count() { unsigned int edges = 0; - for (std::pair wv : vertices) - edges += wv.second->incident_edges.size(); + for (std::pair wv : vertices) + edges += wv.second->incident_s_edges.size(); return edges/2; } unsigned int collapsed_edge_count() { unsigned int edges = 0; - for (std::pair wv : vertices) - if (!wv.second->is_hidden) - edges += wv.second->incident_collapsed_edges.size(); + for (std::pair wv : vertices) + if (wv.second->visibility == 2) + edges += wv.second->incident_c_edges.size(); + return edges/2; + } + + unsigned int traveled_edge_count() + { unsigned int edges = 0; + for (std::pair wv : vertices) + if (wv.second->visibility >= 1) + edges += wv.second->incident_t_edges.size(); return edges/2; } void clear() - { for (std::pair wv : vertices) delete wv.second; + { for (std::pair wv : vertices) delete wv.second; vertex_names.clear(); waypoint_naming_log.clear(); vertices.clear(); } - std::unordered_set matching_vertices(GraphListEntry &g, unsigned int &vis) - { // return a set of vertices from the graph, optionally - // restricted by region or system or placeradius area - vis = 0; - std::unordered_set vertex_set; - std::unordered_set rg_vertex_set; - std::unordered_set sys_vertex_set; + std::unordered_set matching_vertices(GraphListEntry &g, unsigned int &cv_count, unsigned int &tv_count) + { // Return a set of vertices from the graph, optionally + // restricted by region or system or placeradius area. + // Keep a count of collapsed & traveled vertices as we + // go, and pass them by reference. + cv_count = 0; + tv_count = 0; + std::unordered_set vertex_set; + std::unordered_set rg_vertex_set; + std::unordered_set sys_vertex_set; // rg_vertex_set is the union of all sets in regions if (g.regions) for (Region *r : *g.regions) rg_vertex_set.insert(r->vertices.begin(), r->vertices.end()); @@ -176,43 +216,48 @@ class HighwayGraph // determine which vertices are within our region(s) and/or system(s) if (g.regions) - { vertex_set = rg_vertex_set; if (g.systems) - // both regions & systems populated: vertex_set is + { // both regions & systems populated: vertex_set is // intersection of rg_vertex_set & sys_vertex_set - for (HighwayGraphVertexInfo *v : sys_vertex_set) - vertex_set.erase(v); - } + for (HGVertex *v : sys_vertex_set) + if (rg_vertex_set.find(v) != rg_vertex_set.end()) + vertex_set.insert(v); + } + else // only regions populated + vertex_set = rg_vertex_set; else if (g.systems) // only systems populated vertex_set = sys_vertex_set; else // neither are populated; include all vertices... - for (std::pair wv : vertices) + for (std::pair wv : vertices) // ...unless a PlaceRadius is specified - if (!g.placeradius || g.placeradius->contains_vertex_info(wv.second)) + if (!g.placeradius || g.placeradius->contains_vertex(wv.second)) vertex_set.insert(wv.second); // if placeradius is provided along with region or // system parameters, erase vertices outside placeradius if (g.placeradius && (g.systems || g.regions)) - { std::unordered_set::iterator v = vertex_set.begin(); + { std::unordered_set::iterator v = vertex_set.begin(); while(v != vertex_set.end()) - if (!g.placeradius->contains_vertex_info(*v)) + if (!g.placeradius->contains_vertex(*v)) v = vertex_set.erase(v); else v++; } - // find number of visible vertices - for (HighwayGraphVertexInfo *v : vertex_set) - if (!v->is_hidden) vis++; + // find number of collapsed/traveled vertices + for (HGVertex *v : vertex_set) + if (v->visibility >= 1) + { tv_count++; + if (v->visibility == 2) cv_count++; + } return vertex_set; }//*/ - std::unordered_set matching_edges(std::unordered_set &mv, GraphListEntry &g) + std::unordered_set matching_simple_edges(std::unordered_set &mv, GraphListEntry &g) { // return a set of edges from the graph, optionally // restricted by region or system or placeradius area - std::unordered_set edge_set; - std::unordered_set rg_edge_set; - std::unordered_set sys_edge_set; + std::unordered_set edge_set; + std::unordered_set rg_edge_set; + std::unordered_set sys_edge_set; // rg_edge_set is the union of all sets in regions if (g.regions) for (Region *r : *g.regions) rg_edge_set.insert(r->edges.begin(), r->edges.end()); @@ -222,19 +267,21 @@ class HighwayGraph // determine which edges are within our region(s) and/or system(s) if (g.regions) - { edge_set = rg_edge_set; if (g.systems) - // both regions & systems populated: edge_set is + { // both regions & systems populated: edge_set is // intersection of rg_edge_set & sys_edge_set - for (HighwayGraphEdgeInfo *v : sys_edge_set) - edge_set.erase(v); - } + for (HGEdge *e : sys_edge_set) + if (rg_edge_set.find(e) != rg_edge_set.end()) + edge_set.insert(e); + } + else // only regions populated + edge_set = rg_edge_set; else if (g.systems) // only systems populated edge_set = sys_edge_set; else // neither are populated; include all edges/// - for (HighwayGraphVertexInfo *v : mv) - for (HighwayGraphEdgeInfo *e : v->incident_edges) + for (HGVertex *v : mv) + for (HGEdge *e : v->incident_s_edges) // ...unless a PlaceRadius is specified if (!g.placeradius || g.placeradius->contains_edge(e)) edge_set.insert(e); @@ -242,7 +289,7 @@ class HighwayGraph // if placeradius is provided along with non-empty region // or system parameters, erase edges outside placeradius if (g.placeradius && (g.systems || g.regions)) - { std::unordered_set::iterator e = edge_set.begin(); + { std::unordered_set::iterator e = edge_set.begin(); while(e != edge_set.end()) if (!g.placeradius->contains_edge(*e)) e = edge_set.erase(e); @@ -251,18 +298,50 @@ class HighwayGraph return edge_set; } - std::unordered_set matching_collapsed_edges(std::unordered_set &mv, GraphListEntry &g) - { // return a set of edges from the graph edges for the collapsed - // edge format, optionally restricted by region or system or - // placeradius area - std::unordered_set edge_set; - for (HighwayGraphVertexInfo *v : mv) - { if (v->is_hidden) continue; - for (HighwayGraphCollapsedEdgeInfo *e : v->incident_collapsed_edges) + std::unordered_set matching_collapsed_edges(std::unordered_set &mv, GraphListEntry &g) + { // return a set of edges for the collapsed edge graph format, + // optionally restricted by region or system or placeradius + std::unordered_set edge_set; + for (HGVertex *v : mv) + { if (v->visibility < 2) continue; + for (HGEdge *e : v->incident_c_edges) + if (!g.placeradius || g.placeradius->contains_edge(e)) + { bool rg_in_rg = 0; + if (g.regions) for (Region *r : *g.regions) + if (r == e->segment->route->region) + { rg_in_rg = 1; + break; + } + if (!g.regions || rg_in_rg) + { bool system_match = !g.systems; + if (!system_match) + for (std::pair &rs : e->route_names_and_systems) + { bool sys_in_sys = 0; + if (g.systems) for (HighwaySystem *s : *g.systems) + if (s == rs.second) + { sys_in_sys = 1; + break; + } + if (sys_in_sys) system_match = 1; + } + if (system_match) edge_set.insert(e); + } + } + } + return edge_set; + } + + std::unordered_set matching_traveled_edges(std::unordered_set &mv, GraphListEntry &g) + { // return a set of edges for the traveled graph format, + // optionally restricted by region or system or placeradius + std::unordered_set edge_set; + for (HGVertex *v : mv) + { if (v->visibility < 1) continue; + for (HGEdge *e : v->incident_t_edges) if (!g.placeradius || g.placeradius->contains_edge(e)) { bool rg_in_rg = 0; if (g.regions) for (Region *r : *g.regions) - if (r == e->region) + if (r == e->segment->route->region) { rg_in_rg = 1; break; } @@ -298,41 +377,40 @@ class HighwayGraph void write_master_tmg_simple(GraphListEntry *msptr, std::string filename) { std::ofstream tmgfile(filename.data()); tmgfile << "TMG 1.0 simple\n"; - tmgfile << vertices.size() << ' ' << edge_count() << '\n'; - // number waypoint entries as we go to support original .gra - // format output - int vertex_num = 0; - for (std::pair wv : vertices) + tmgfile << vertices.size() << ' ' << simple_edge_count() << '\n'; + // number waypoint entries as we go + int s_vertex_num = 0; + for (std::pair wv : vertices) { char fstr[42]; sprintf(fstr, "%.15g %.15g", wv.second->lat, wv.second->lng); tmgfile << *(wv.second->unique_name) << ' ' << fstr << '\n'; - wv.second->vertex_num[0] = vertex_num; - vertex_num++; + wv.second->s_vertex_num[0] = s_vertex_num; + s_vertex_num++; } // sanity check - if (vertices.size() != vertex_num) - std::cout << "ERROR: computed " << vertices.size() << " waypoints but wrote " << vertex_num << std::endl; + if (vertices.size() != s_vertex_num) + std::cout << "ERROR: computed " << vertices.size() << " waypoints but wrote " << s_vertex_num << std::endl; // now edges, only print if not already printed int edge = 0; - for (std::pair wv : vertices) - for (HighwayGraphEdgeInfo *e : wv.second->incident_edges) - if (!e->written) - { e->written = 1; - tmgfile << e->vertex1->vertex_num[0] << ' ' << e->vertex2->vertex_num[0] << ' ' << e->label(0) << '\n'; + for (std::pair wv : vertices) + for (HGEdge *e : wv.second->incident_s_edges) + if (!e->s_written) + { e->s_written = 1; + tmgfile << e->vertex1->s_vertex_num[0] << ' ' << e->vertex2->s_vertex_num[0] << ' ' << e->label(0) << '\n'; edge++; } // sanity checks - for (std::pair wv : vertices) - for (HighwayGraphEdgeInfo *e : wv.second->incident_edges) - if (!e->written) - std::cout << "ERROR: never wrote edge " << e->vertex1->vertex_num << ' ' << e->vertex2->vertex_num[0] << ' ' << e->label(0) << std::endl; - if (edge_count() != edge) - std::cout << "ERROR: computed " << edge_count() << " edges but wrote " << edge << std::endl; + for (std::pair wv : vertices) + for (HGEdge *e : wv.second->incident_s_edges) + if (!e->s_written) + std::cout << "ERROR: never wrote edge " << e->vertex1->s_vertex_num << ' ' << e->vertex2->s_vertex_num[0] << ' ' << e->label(0) << std::endl; + if (simple_edge_count() != edge) + std::cout << "ERROR: computed " << simple_edge_count() << " edges but wrote " << edge << std::endl; tmgfile.close(); msptr->vertices = vertices.size(); - msptr->edges = edge_count(); + msptr->edges = simple_edge_count(); } // write the entire set of data in the tmg collapsed edge format @@ -340,25 +418,25 @@ class HighwayGraph { std::ofstream tmgfile(filename.data()); unsigned int num_collapsed_edges = collapsed_edge_count(); tmgfile << "TMG 1.0 collapsed\n"; - tmgfile << num_visible_vertices() << " " << num_collapsed_edges << '\n'; + tmgfile << num_collapsed_vertices() << " " << num_collapsed_edges << '\n'; // write visible vertices - int vis_vertex_num = 0; - for (std::pair wv : vertices) - if (!wv.second->is_hidden) + int c_vertex_num = 0; + for (std::pair wv : vertices) + if (wv.second->visibility == 2) { char fstr[42]; sprintf(fstr, "%.15g %.15g", wv.second->lat, wv.second->lng); tmgfile << *(wv.second->unique_name) << ' ' << fstr << '\n'; - wv.second->vis_vertex_num[threadnum] = vis_vertex_num; - vis_vertex_num++; + wv.second->c_vertex_num[threadnum] = c_vertex_num; + c_vertex_num++; } // write collapsed edges int edge = 0; - for (std::pair wv : vertices) - if (!wv.second->is_hidden) - for (HighwayGraphCollapsedEdgeInfo *e : wv.second->incident_collapsed_edges) - if (!e->written) - { e->written = 1; + for (std::pair wv : vertices) + if (wv.second->visibility == 2) + for (HGEdge *e : wv.second->incident_c_edges) + if (!e->c_written) + { e->c_written = 1; tmgfile << e->collapsed_tmg_line(0, threadnum) << '\n'; edge++; } @@ -367,61 +445,119 @@ class HighwayGraph std::cout << "ERROR: computed " << num_collapsed_edges << " collapsed edges, but wrote " << edge << '\n'; tmgfile.close(); - mcptr->vertices = num_visible_vertices(); + mcptr->vertices = num_collapsed_vertices(); mcptr->edges = num_collapsed_edges; } + // write the entire set of data in the tmg traveled format + void write_master_tmg_traveled(GraphListEntry *mtptr, std::string filename, std::list *traveler_lists, unsigned int threadnum) + { std::ofstream tmgfile(filename.data()); + unsigned int num_traveled_edges = traveled_edge_count(); + tmgfile << "TMG 2.0 traveled\n"; + tmgfile << num_traveled_vertices() << " " << num_traveled_edges << '\n'; + + // write visible vertices + int t_vertex_num = 0; + for (std::pair wv : vertices) + if (wv.second->visibility >= 1) + { char fstr[42]; + sprintf(fstr, "%.15g %.15g", wv.second->lat, wv.second->lng); + tmgfile << *(wv.second->unique_name) << ' ' << fstr << '\n'; + wv.second->t_vertex_num[threadnum] = t_vertex_num; + t_vertex_num++; + } + // write traveled edges + int edge = 0; + for (std::pair wv : vertices) + if (wv.second->visibility >= 1) + for (HGEdge *e : wv.second->incident_t_edges) + if (!e->t_written) + { e->t_written = 1; + tmgfile << e->traveled_tmg_line(0, traveler_lists, threadnum) << '\n'; + edge++; + } + // traveler names + for (TravelerList *t : *traveler_lists) + tmgfile << t->traveler_name << ' '; + + // sanity check on edges written + if (num_traveled_edges != edge) + std::cout << "ERROR: computed " << num_traveled_edges << " traveled edges, but wrote " << edge << '\n'; + + tmgfile.close(); + mtptr->vertices = num_traveled_vertices(); + mtptr->edges = num_traveled_edges; + } + // write a subset of the data, - // in both simple and collapsed formats, + // in simple, collapsed and traveled formats, // restricted by regions in the list if given, - // by system in the list if given, + // by systems in the list if given, // or to within a given area if placeradius is given - void write_subgraphs_tmg(std::vector &graph_vector, std::string path, size_t graphnum, unsigned int threadnum) - { unsigned int visible_v; - std::string simplefilename = path+graph_vector[graphnum].filename(); - std::string collapfilename = path+graph_vector[graphnum+1].filename(); - std::ofstream simplefile(simplefilename.data()); - std::ofstream collapfile(collapfilename.data()); - std::unordered_set mv = matching_vertices(graph_vector[graphnum], visible_v); - std::unordered_set mse = matching_edges(mv, graph_vector[graphnum]); - std::unordered_set mce = matching_collapsed_edges(mv, graph_vector[graphnum]); + void write_subgraphs_tmg(std::vector &graph_vector, std::string path, size_t graphnum, + unsigned int threadnum, std::list *traveler_lists) + { unsigned int cv_count, tv_count; + std::ofstream simplefile((path+graph_vector[graphnum].filename()).data()); + std::ofstream collapfile((path+graph_vector[graphnum+1].filename()).data()); + std::ofstream travelfile((path+graph_vector[graphnum+2].filename()).data()); + std::unordered_set mv = matching_vertices(graph_vector[graphnum], cv_count, tv_count); + std::unordered_set mse = matching_simple_edges(mv, graph_vector[graphnum]); + std::unordered_set mce = matching_collapsed_edges(mv, graph_vector[graphnum]); + std::unordered_set mte = matching_traveled_edges(mv, graph_vector[graphnum]); std::cout << graph_vector[graphnum].tag() << '(' << mv.size() << ',' << mse.size() << ") " - << '(' << visible_v << ',' << mce.size() << ") " << std::flush; + << '(' << cv_count << ',' << mce.size() << ") " + << '(' << tv_count << ',' << mte.size() << ") " << std::flush; simplefile << "TMG 1.0 simple\n"; collapfile << "TMG 1.0 collapsed\n"; + travelfile << "TMG 2.0 traveled\n"; simplefile << mv.size() << ' ' << mse.size() << '\n'; - collapfile << visible_v << ' ' << mce.size() << '\n'; + collapfile << cv_count << ' ' << mce.size() << '\n'; + travelfile << tv_count << ' ' << mte.size() << '\n'; // write vertices unsigned int sv = 0; unsigned int cv = 0; - for (HighwayGraphVertexInfo *v : mv) + unsigned int tv = 0; + for (HGVertex *v : mv) { char fstr[43]; sprintf(fstr, " %.15g %.15g", v->lat, v->lng); - // all vertices, for simple graph + // all vertices for simple graph simplefile << *(v->unique_name) << fstr << '\n'; - v->vertex_num[threadnum] = sv; + v->s_vertex_num[threadnum] = sv; sv++; - // visible vertices, for collapsed graph - if (!v->is_hidden) - { collapfile << *(v->unique_name) << fstr << '\n'; - v->vis_vertex_num[threadnum] = cv; - cv++; + // visible vertices... + if (v->visibility >= 1) + { // for traveled graph, + travelfile << *(v->unique_name) << fstr << '\n'; + v->t_vertex_num[threadnum] = tv; + tv++; + if (v->visibility == 2) + { // and for collapsed graph + collapfile << *(v->unique_name) << fstr << '\n'; + v->c_vertex_num[threadnum] = cv; + cv++; + } } } // write edges - for (HighwayGraphEdgeInfo *e : mse) - simplefile << e->vertex1->vertex_num[threadnum] << ' ' - << e->vertex2->vertex_num[threadnum] << ' ' + for (HGEdge *e : mse) + simplefile << e->vertex1->s_vertex_num[threadnum] << ' ' + << e->vertex2->s_vertex_num[threadnum] << ' ' << e->label(graph_vector[graphnum].systems) << '\n'; - for (HighwayGraphCollapsedEdgeInfo *e : mce) + for (HGEdge *e : mce) collapfile << e->collapsed_tmg_line(graph_vector[graphnum].systems, threadnum) << '\n'; + for (HGEdge *e : mte) + travelfile << e->traveled_tmg_line(graph_vector[graphnum].systems, traveler_lists, threadnum) << '\n'; + // traveler names + for (TravelerList *t : *traveler_lists) + travelfile << t->traveler_name << ' '; simplefile.close(); collapfile.close(); + travelfile.close(); graph_vector[graphnum].vertices = mv.size(); - graph_vector[graphnum+1].vertices = visible_v; + graph_vector[graphnum+1].vertices = cv_count; graph_vector[graphnum].edges = mse.size(); graph_vector[graphnum+1].edges = mce.size(); } diff --git a/siteupdate/cplusplus/classes/GraphGeneration/HighwayGraphCollapsedEdgeInfo.cpp b/siteupdate/cplusplus/classes/GraphGeneration/HighwayGraphCollapsedEdgeInfo.cpp deleted file mode 100644 index 4ef85d99..00000000 --- a/siteupdate/cplusplus/classes/GraphGeneration/HighwayGraphCollapsedEdgeInfo.cpp +++ /dev/null @@ -1,161 +0,0 @@ -HighwayGraphCollapsedEdgeInfo::HighwayGraphCollapsedEdgeInfo(HighwayGraphEdgeInfo *e) -{ // initial construction is based on a HighwayGraphEdgeInfo - written = 0; - segment_name = e->segment_name; - vertex1 = e->vertex1; - vertex2 = e->vertex2; - // assumption: each edge/segment lives within a unique region - // and a 'multi-edge' would not be able to span regions as there - // would be a required visible waypoint at the border - region = e->region; - // a list of route name/system pairs - route_names_and_systems = e->route_names_and_systems; - vertex1->incident_collapsed_edges.push_back(this); - vertex2->incident_collapsed_edges.push_back(this); -} - -HighwayGraphCollapsedEdgeInfo::HighwayGraphCollapsedEdgeInfo(HighwayGraphVertexInfo *vertex_info) -{ // build by collapsing two existing edges around a common - // hidden vertex waypoint, whose information is given in - // vertex_info - written = 0; - // we know there are exactly 2 incident edges, as we - // checked for that, and we will replace these two - // with the single edge we are constructing here - HighwayGraphCollapsedEdgeInfo *edge1 = vertex_info->incident_collapsed_edges.front(); - HighwayGraphCollapsedEdgeInfo *edge2 = vertex_info->incident_collapsed_edges.back(); - // segment names should match as routes should not start or end - // nor should concurrencies begin or end at a hidden point - if (edge1->segment_name != edge2->segment_name) - { std::cout << "ERROR: segment name mismatch in HighwayGraphCollapsedEdgeInfo: "; - std::cout << "edge1 named " << edge1->segment_name << " edge2 named " << edge2->segment_name << '\n' << std::endl; - } - segment_name = edge1->segment_name; - //std::cout << "\nDEBUG: collapsing edges along " << segment_name << " at vertex " << \ - *(vertex_info->unique_name) << ", edge1 is " << edge1->str() << " and edge2 is " << edge2->str() << std::endl; - // region and route names/systems should also match, but not - // doing that sanity check here, as the above check should take - // care of that - region = edge1->region; - route_names_and_systems = edge1->route_names_and_systems; - - // figure out and remember which endpoints are not the - // vertex we are collapsing and set them as our new - // endpoints, and at the same time, build up our list of - // intermediate vertices - intermediate_points = edge1->intermediate_points; - //std::cout << "DEBUG: copied edge1 intermediates" << intermediate_point_string() << std::endl; - - if (edge1->vertex1 == vertex_info) - { //std::cout << "DEBUG: vertex1 getting edge1->vertex2: " << *(edge1->vertex2->unique_name) << " and reversing edge1 intermediates" << std::endl; - vertex1 = edge1->vertex2; - intermediate_points.reverse(); - } - else { //std::cout << "DEBUG: vertex1 getting edge1->vertex1: " << *(edge1->vertex1->unique_name) << std::endl; - vertex1 = edge1->vertex1; - } - - //std::cout << "DEBUG: appending to intermediates: " << *(vertex_info->unique_name) << std::endl; - intermediate_points.push_back(vertex_info); - - if (edge2->vertex1 == vertex_info) - { //std::cout << "DEBUG: vertex2 getting edge2->vertex2: " << *(edge2->vertex2->unique_name) << std::endl; - vertex2 = edge2->vertex2; - } - else { //std:: cout << "DEBUG: vertex2 getting edge2->vertex1: " << *(edge2->vertex1->unique_name) << " and reversing edge2 intermediates" << std::endl; - vertex2 = edge2->vertex1; - edge2->intermediate_points.reverse(); - } - intermediate_points.splice(intermediate_points.end(), edge2->intermediate_points); - - //std::cout << "DEBUG: intermediates complete: from " << *(vertex1->unique_name) << " via " \ - << intermediate_point_string() << " to " << *(vertex2->unique_name) << std::endl; - - // replace edge references at our endpoints with ourself - delete edge1; // destructor removes edge from adjacency lists - delete edge2; // destructor removes edge from adjacency lists - vertex1->incident_collapsed_edges.push_back(this); - vertex2->incident_collapsed_edges.push_back(this); -} - -HighwayGraphCollapsedEdgeInfo::~HighwayGraphCollapsedEdgeInfo() -{ for ( std::list::iterator e = vertex1->incident_collapsed_edges.begin(); - e != vertex1->incident_collapsed_edges.end(); - e++ - ) if (*e == this) - { vertex1->incident_collapsed_edges.erase(e); - break; - } - for ( std::list::iterator e = vertex2->incident_collapsed_edges.begin(); - e != vertex2->incident_collapsed_edges.end(); - e++ - ) if (*e == this) - { vertex2->incident_collapsed_edges.erase(e); - break; - } - segment_name.clear(); - intermediate_points.clear(); - route_names_and_systems.clear(); -} - -// compute an edge label, optionally resticted by systems -std::string HighwayGraphCollapsedEdgeInfo::label(std::list *systems) -{ std::string the_label; - for (std::pair &ns : route_names_and_systems) - { // test whether system in systems - bool sys_in_sys = 0; - if (systems) for (HighwaySystem *h : *systems) - if (h == ns.second) - { sys_in_sys = 1; - break; - } - if (!systems || sys_in_sys) - if (the_label.empty()) - the_label = ns.first; - else the_label += "," + ns.first; - } - return the_label; -} - -// line appropriate for a tmg collapsed edge file -std::string HighwayGraphCollapsedEdgeInfo::collapsed_tmg_line(std::list *systems, unsigned int threadnum) -{ std::string line = std::to_string(vertex1->vis_vertex_num[threadnum]) + " " + std::to_string(vertex2->vis_vertex_num[threadnum]) + " " + label(systems); - char fstr[43]; - for (HighwayGraphVertexInfo *intermediate : intermediate_points) - { sprintf(fstr, " %.15g %.15g", intermediate->lat, intermediate->lng); - line += fstr; - } - return line; -} - -// line appropriate for a tmg collapsed edge file, with debug info -std::string HighwayGraphCollapsedEdgeInfo::debug_tmg_line(std::list *systems, unsigned int threadnum) -{ std::string line = std::to_string(vertex1->vis_vertex_num[threadnum]) + " [" + *vertex1->unique_name + "] " \ - + std::to_string(vertex2->vis_vertex_num[threadnum]) + " [" + *vertex2->unique_name + "] " + label(systems); - char fstr[44]; - for (HighwayGraphVertexInfo *intermediate : intermediate_points) - { sprintf(fstr, "] %.15g %.15g", intermediate->lat, intermediate->lng); - line += " [" + *intermediate->unique_name + fstr; - } - return line; -} - -// printable string for this edge -std::string HighwayGraphCollapsedEdgeInfo::str() -{ return "HighwayGraphCollapsedEdgeInfo: " + segment_name - + " from " + *vertex1->unique_name - + " to " + *vertex2->unique_name - + " via " + std::to_string(intermediate_points.size()) + " points"; -} - -// return the intermediate points as a string -std::string HighwayGraphCollapsedEdgeInfo::intermediate_point_string() -{ if (intermediate_points.empty()) return " None"; - std::string line = ""; - char fstr[42]; - for (HighwayGraphVertexInfo *i : intermediate_points) - { sprintf(fstr, "%.15g %.15g", i->lat, i->lng); - line += " [" + *i->unique_name + "] " + fstr; - } - return line; -} diff --git a/siteupdate/cplusplus/classes/GraphGeneration/HighwayGraphCollapsedEdgeInfo.h b/siteupdate/cplusplus/classes/GraphGeneration/HighwayGraphCollapsedEdgeInfo.h deleted file mode 100644 index 8534f1a2..00000000 --- a/siteupdate/cplusplus/classes/GraphGeneration/HighwayGraphCollapsedEdgeInfo.h +++ /dev/null @@ -1,22 +0,0 @@ -class HighwayGraphCollapsedEdgeInfo -{ /* This class encapsulates information needed for a highway graph - edge that can incorporate intermediate points. - */ - public: - bool written; - std::string segment_name; - HighwayGraphVertexInfo *vertex1, *vertex2; - std::list intermediate_points; // if more than 1, will go from vertex1 to vertex2 - Region *region; - std::list> route_names_and_systems; - - HighwayGraphCollapsedEdgeInfo(HighwayGraphEdgeInfo *); - HighwayGraphCollapsedEdgeInfo(HighwayGraphVertexInfo *); - ~HighwayGraphCollapsedEdgeInfo(); - - std::string label(std::list *); - std::string collapsed_tmg_line(std::list *, unsigned int); - std::string debug_tmg_line(std::list *, unsigned int); - std::string str(); - std::string intermediate_point_string(); -}; diff --git a/siteupdate/cplusplus/classes/GraphGeneration/HighwayGraphEdgeInfo.cpp b/siteupdate/cplusplus/classes/GraphGeneration/HighwayGraphEdgeInfo.cpp deleted file mode 100644 index 61ac8ce9..00000000 --- a/siteupdate/cplusplus/classes/GraphGeneration/HighwayGraphEdgeInfo.cpp +++ /dev/null @@ -1,75 +0,0 @@ -HighwayGraphEdgeInfo::HighwayGraphEdgeInfo(HighwaySegment *s, HighwayGraph *graph, bool &duplicate) -{ // temp debug [sic] - written = 0; - segment_name = s->segment_name(); - vertex1 = graph->vertices.at(s->waypoint1->hashpoint()); - vertex2 = graph->vertices.at(s->waypoint2->hashpoint()); - // checks for the very unusual cases where an edge ends up - // in the system as itself and its "reverse" - for (HighwayGraphEdgeInfo *e : vertex1->incident_edges) - if (e->vertex1 == vertex2 && e->vertex2 == vertex1) duplicate = 1; - for (HighwayGraphEdgeInfo *e : vertex2->incident_edges) - if (e->vertex1 == vertex2 && e->vertex2 == vertex1) duplicate = 1; - if (duplicate) - { delete this; - return; - } - vertex1->incident_edges.push_back(this); - vertex2->incident_edges.push_back(this); - // assumption: each edge/segment lives within a unique region - region = s->route->region; - region->edges.insert(this); - // a list of route name/system pairs - if (!s->concurrent) - { route_names_and_systems.emplace_back(s->route->list_entry_name(), s->route->system); - s->route->system->edges.insert(this); - } - else for (HighwaySegment *cs : *(s->concurrent)) - { if (cs->route->system->devel()) continue; - route_names_and_systems.emplace_back(cs->route->list_entry_name(), cs->route->system); - cs->route->system->edges.insert(this); - } -} - -HighwayGraphEdgeInfo::~HighwayGraphEdgeInfo() -{ for ( std::list::iterator e = vertex1->incident_edges.begin(); - e != vertex1->incident_edges.end(); - e++ - ) if (*e == this) - { vertex1->incident_edges.erase(e); - break; - } - for ( std::list::iterator e = vertex2->incident_edges.begin(); - e != vertex2->incident_edges.end(); - e++ - ) if (*e == this) - { vertex2->incident_edges.erase(e); - break; - } - segment_name.clear(); - route_names_and_systems.clear(); -} - -// compute an edge label, optionally resticted by systems -std::string HighwayGraphEdgeInfo::label(std::list *systems) -{ std::string the_label; - for (std::pair &ns : route_names_and_systems) - { // test whether system in systems - bool sys_in_sys = 0; - if (systems) for (HighwaySystem *h : *systems) - if (h == ns.second) - { sys_in_sys = 1; - break; - } - if (!systems || sys_in_sys) - if (the_label.empty()) - the_label = ns.first; - else the_label += "," + ns.first; - } - return the_label; -} - -// printable string for this edge -std::string HighwayGraphEdgeInfo::str() -{ return "HighwayGraphEdgeInfo: " + segment_name + " from " + *vertex1->unique_name + " to " + *vertex2->unique_name; -} diff --git a/siteupdate/cplusplus/classes/GraphGeneration/HighwayGraphEdgeInfo.h b/siteupdate/cplusplus/classes/GraphGeneration/HighwayGraphEdgeInfo.h deleted file mode 100644 index 4a1b7e02..00000000 --- a/siteupdate/cplusplus/classes/GraphGeneration/HighwayGraphEdgeInfo.h +++ /dev/null @@ -1,17 +0,0 @@ -class HighwayGraphEdgeInfo -{ /* This class encapsulates information needed for a 'standard' - highway graph edge. - */ - public: - bool written; - std::string segment_name; - HighwayGraphVertexInfo *vertex1, *vertex2; - Region *region; - std::list> route_names_and_systems; - - HighwayGraphEdgeInfo(HighwaySegment *, HighwayGraph *, bool &); - ~HighwayGraphEdgeInfo(); - - std::string label(std::list *); - std::string str(); -}; diff --git a/siteupdate/cplusplus/classes/GraphGeneration/PlaceRadius.cpp b/siteupdate/cplusplus/classes/GraphGeneration/PlaceRadius.cpp index c8f7efb3..71f6515e 100644 --- a/siteupdate/cplusplus/classes/GraphGeneration/PlaceRadius.cpp +++ b/siteupdate/cplusplus/classes/GraphGeneration/PlaceRadius.cpp @@ -19,13 +19,13 @@ class PlaceRadius r = strtoul(R, 0, 10); } - bool contains_vertex_info(HighwayGraphVertexInfo *vinfo) - { /* return whether vinfo's coordinates are within this area */ + bool contains_vertex(HGVertex *v) + { /* return whether v's coordinates are within this area */ // convert to radians to compute distance double rlat1 = lat * Waypoint::pi/180; double rlng1 = lng * Waypoint::pi/180; - double rlat2 = vinfo->lat * Waypoint::pi/180; - double rlng2 = vinfo->lng * Waypoint::pi/180; + double rlat2 = v->lat * Waypoint::pi/180; + double rlng2 = v->lng * Waypoint::pi/180; double ans = acos(cos(rlat1)*cos(rlng1)*cos(rlat2)*cos(rlng2) +\ cos(rlat1)*sin(rlng1)*cos(rlat2)*sin(rlng2) +\ @@ -33,13 +33,8 @@ class PlaceRadius return ans <= r; } - bool contains_edge(HighwayGraphEdgeInfo *e) + bool contains_edge(HGEdge *e) { /* return whether both endpoints of edge e are within this area */ - return contains_vertex_info(e->vertex1) and contains_vertex_info(e->vertex2); - } - - bool contains_edge(HighwayGraphCollapsedEdgeInfo *e) - { /* return whether both endpoints of edge e are within this area */ - return contains_vertex_info(e->vertex1) and contains_vertex_info(e->vertex2); + return contains_vertex(e->vertex1) and contains_vertex(e->vertex2); } }; diff --git a/siteupdate/cplusplus/classes/HighwaySegment/HighwaySegment.cpp b/siteupdate/cplusplus/classes/HighwaySegment/HighwaySegment.cpp index 2ec2c91c..2020cc52 100644 --- a/siteupdate/cplusplus/classes/HighwaySegment/HighwaySegment.cpp +++ b/siteupdate/cplusplus/classes/HighwaySegment/HighwaySegment.cpp @@ -11,13 +11,9 @@ std::string HighwaySegment::str() bool HighwaySegment::add_clinched_by(TravelerList *traveler) { clin_mtx.lock(); - for (TravelerList *t : clinched_by) if (t == traveler) - { clin_mtx.unlock(); - return 0; - } - clinched_by.push_front(traveler); + bool result = clinched_by.insert(traveler).second; clin_mtx.unlock(); - return 1; + return result; } std::string HighwaySegment::csv_line(unsigned int id) @@ -54,4 +50,48 @@ unsigned int HighwaySegment::index() return -1; // error; this segment not found in vector } +/*std::string HighwaySegment::concurrent_travelers_sanity_check() +{ if (route->system->devel()) return ""; + if (concurrent) + for (HighwaySegment *conc : *concurrent) + { if (clinched_by.size() != conc->clinched_by.size()) + { if (conc->route->system->devel()) continue; + return "[" + str() + "] clinched by " + std::to_string(clinched_by.size()) + " travelers; [" \ + + conc->str() + "] clinched by " + std::to_string(conc->clinched_by.size()) + '\n'; + } + else for (TravelerList *t : clinched_by) + { std::list::iterator ct; + for (ct = conc->clinched_by.begin(); ct != conc->clinched_by.end(); ct++) + if (*ct == t) break; + if (ct == conc->clinched_by.end()) + return t->traveler_name + " has clinched [" + str() + "], but not [" + conc->str() + "]\n"; + } + } + return ""; +}//*/ + +std::string HighwaySegment::clinchedby_code(std::list *traveler_lists) +{ // Return a hexadecimal string encoding which travelers have clinched this segment, for use in "traveled" graph files + // Each character stores info for traveler #n thru traveler #n+3 + // The first character stores traveler 0 thru traveler 3, + // The second character stores traveler 4 thru traveler 7, etc. + // For each character, the low-order bit stores traveler n, and the high bit traveler n+3. + std::string code(ceil(double(traveler_lists->size())/4), '0'); + //std::cout << str() << " code string initialized (" << clinched_by.size() << '/' << traveler_lists->size() << ')' << std::endl; + //unsigned int num = 0; + for (TravelerList* t : clinched_by) + { //std::cout << "\t" << num << ": TravNum = " << t->traveler_num << ": " << t->traveler_name << std::endl; + //std::cout << "\t" << num << ": TravNum/4 = " << t->traveler_num/4 << std::endl; + //std::cout << "\t" << num << ": TravNum%4 = " << TravNum%4 << std::endl; + //std::cout << "\t" << num << ": 2 ^ TravNum%4 = " << pow(2, TravNum%4) << std::endl; + //std::cout << "\t" << num << ": code[" << TravNum/4 << "] += int(" << pow(2, TravNum%4) << ")" << std::endl; + code[t->traveler_num/4] += int(pow(2, t->traveler_num%4)); + //num++; + } + //std::cout << "travelers written to array" << std::endl; + for (char &nibble : code) if (nibble > '9') nibble += 7; + //std::cout << "nibbles >9 -> letters" << std::endl; + return code; +} + #include "compute_stats.cpp" diff --git a/siteupdate/cplusplus/classes/HighwaySegment/HighwaySegment.h b/siteupdate/cplusplus/classes/HighwaySegment/HighwaySegment.h index a0d531b4..6b3c0176 100644 --- a/siteupdate/cplusplus/classes/HighwaySegment/HighwaySegment.h +++ b/siteupdate/cplusplus/classes/HighwaySegment/HighwaySegment.h @@ -7,16 +7,18 @@ class HighwaySegment Waypoint *waypoint2; Route *route; std::list *concurrent; - std::forward_list clinched_by; //FIXME try unordered_set? + std::unordered_set clinched_by; std::mutex clin_mtx; HighwaySegment(Waypoint *, Waypoint *, Route *); std::string str(); - bool add_clinched_by(TravelerList *); //FIXME rework to use unordered_set? Simpler coding, probably faster + bool add_clinched_by(TravelerList *); std::string csv_line(unsigned int); double length(); std::string segment_name(); unsigned int index(); + //std::string concurrent_travelers_sanity_check(); + std::string clinchedby_code(std::list *); void compute_stats(); }; diff --git a/siteupdate/cplusplus/classes/HighwaySystem.cpp b/siteupdate/cplusplus/classes/HighwaySystem.cpp index b27314f1..d0423c85 100644 --- a/siteupdate/cplusplus/classes/HighwaySystem.cpp +++ b/siteupdate/cplusplus/classes/HighwaySystem.cpp @@ -27,8 +27,8 @@ class HighwaySystem std::list route_list; std::list con_route_list; std::unordered_map mileage_by_region; - std::unordered_set vertices; - std::unordered_set edges; + std::unordered_set vertices; + std::unordered_set edges; HighwaySystem(std::string &line, ErrorList &el, std::string path, std::string &systemsfile, std::list> &countries, diff --git a/siteupdate/cplusplus/classes/Region.cpp b/siteupdate/cplusplus/classes/Region.cpp index 31425dc6..b879c3e5 100644 --- a/siteupdate/cplusplus/classes/Region.cpp +++ b/siteupdate/cplusplus/classes/Region.cpp @@ -40,8 +40,8 @@ class Region double active_preview_mileage; double overall_mileage; std::mutex *ao_mi_mtx, *ap_mi_mtx, *ov_mi_mtx; - std::unordered_set vertices; - std::unordered_set edges; + std::unordered_set vertices; + std::unordered_set edges; Region (std::string &line, std::list> &countries, @@ -53,6 +53,7 @@ class Region ao_mi_mtx = new std::mutex; ap_mi_mtx = new std::mutex; ov_mi_mtx = new std::mutex; + // deleted on termination of program char *c_country = 0; char *c_continent = 0; // parse CSV line diff --git a/siteupdate/cplusplus/classes/Route/Route.cpp b/siteupdate/cplusplus/classes/Route/Route.cpp index 3e076e6d..7eaf704a 100644 --- a/siteupdate/cplusplus/classes/Route/Route.cpp +++ b/siteupdate/cplusplus/classes/Route/Route.cpp @@ -5,6 +5,7 @@ std::mutex Route::awf_mtx; Route::Route(std::string &line, HighwaySystem *sys, ErrorList &el, std::list &all_regions) { /* initialize object from a .csv file line, but do not yet read in waypoint file */ + con_route = 0; mileage = 0; rootOrder = -1; // order within connected route if (line.back() == 0x0D) line.erase(line.end()-1); // trim DOS newlines @@ -180,6 +181,17 @@ bool Route::is_valid() return 1; } +std::string Route::list_line(int beg, int end) +{ /* Return a .list file line from (beg) to (end), + these being indices to the point_list vector. + These values can be "out-of-bounds" when getting lines + for connected routes. If so, truncate or return "". */ + if (beg >= int(point_list.size()) || end <= 0) return ""; + if (end >= int(point_list.size())) end = point_list.size()-1; + if (beg < 0) beg = 0; + return readable_name() + " " + point_list[beg]->label + " " + point_list[end]->label; +} + void Route::write_nmp_merged(std::string filename) { mkdir(filename.data(), 0777); filename += "/" + system->systemname; diff --git a/siteupdate/cplusplus/classes/Route/Route.h b/siteupdate/cplusplus/classes/Route/Route.h index 9d749ad0..006079cd 100644 --- a/siteupdate/cplusplus/classes/Route/Route.h +++ b/siteupdate/cplusplus/classes/Route/Route.h @@ -39,6 +39,7 @@ class Route public: HighwaySystem *system; Region *region; + ConnectedRoute *con_route; std::string route; std::string banner; std::string abbrev; @@ -49,10 +50,10 @@ class Route std::vector point_list; std::unordered_set labels_in_use; std::unordered_set unused_alt_labels; + static std::mutex awf_mtx; // for locking the all_wpt_files set when erasing processed WPTs static std::mutex liu_mtx; // for locking the labels_in_use set when inserting labels during TravelerList processing static std::mutex ual_mtx; // for locking the unused_alt_labels set when removing in-use alt_labels - // with one for each route, rather than static, no discernable speed difference. Saving a wee bit of RAM. - static std::mutex awf_mtx; // for locking the all_wpt_files set when erasing processed WPTs + // with one for each route, rather than static, no discernable speed difference. Saving a wee bit of RAM. std::vector segment_list; double mileage; int rootOrder; @@ -70,5 +71,6 @@ class Route std::string name_no_abbrev(); double clinched_by_traveler(TravelerList *); bool is_valid(); + std::string list_line(int, int); void write_nmp_merged(std::string); }; diff --git a/siteupdate/cplusplus/classes/Route/read_wpt.cpp b/siteupdate/cplusplus/classes/Route/read_wpt.cpp index aa863c37..e99c29fb 100644 --- a/siteupdate/cplusplus/classes/Route/read_wpt.cpp +++ b/siteupdate/cplusplus/classes/Route/read_wpt.cpp @@ -46,6 +46,7 @@ void Route::read_wpt } if (lines[l][0] == 0) continue; Waypoint *w = new Waypoint(lines[l], this, strtok_mtx, datacheckerrors); + // deleted on termination of program point_list.push_back(w); // populate unused alt labels for (size_t i = 0; i < w->alt_labels.size(); i++) @@ -64,6 +65,7 @@ void Route::read_wpt { // see if this is the first point colocated with other_w if (!other_w->colocated) { other_w->colocated = new std::list; + // deleted on termination of program other_w->colocated->push_front(other_w); } other_w->colocated->push_front(w); @@ -89,6 +91,7 @@ void Route::read_wpt { w->distance_update(datacheckerrors, fstr, vis_dist, point_list[point_list.size()-2]); // add HighwaySegment, if not first point segment_list.push_back(new HighwaySegment(point_list[point_list.size()-2], w, this)); + // deleted on termination of program } // checks for visible points if (!w->is_hidden) @@ -104,6 +107,7 @@ void Route::read_wpt w->label_looks_hidden(datacheckerrors); } } + delete[] wptdata; // per-route datachecks diff --git a/siteupdate/cplusplus/classes/TravelerList/TravelerList.cpp b/siteupdate/cplusplus/classes/TravelerList/TravelerList.cpp index 35278797..690c30eb 100644 --- a/siteupdate/cplusplus/classes/TravelerList/TravelerList.cpp +++ b/siteupdate/cplusplus/classes/TravelerList/TravelerList.cpp @@ -23,10 +23,12 @@ class TravelerList std::unordered_map routes_traveled; // mileage per traveled route std::unordered_map con_routes_clinched; // clinch count per system //std::unordered_map routes_clinched; // commented out in original siteupdate.py + unsigned int traveler_num; unsigned int active_systems_traveled; unsigned int active_systems_clinched; unsigned int preview_systems_traveled; unsigned int preview_systems_clinched; + static std::mutex alltrav_mtx; // for locking the traveler_lists list when reading .lists from disk TravelerList(std::string travname, std::unordered_map *route_hash, Arguments *args, std::mutex *strtok_mtx) { active_systems_traveled = 0; @@ -36,10 +38,13 @@ class TravelerList traveler_name = travname.substr(0, travname.size()-5); // strip ".list" from end of travname std::string filename = args->logfilepath+"/users/"+traveler_name+".log"; std::ofstream log(filename.data()); + std::ofstream splist; + if (args->splitregionpath != "") splist.open((args->splitregionpath+"/list_files/"+travname).data()); time_t StartTime = time(0); log << "Log file created at: " << ctime(&StartTime); filename = args->userlistfilepath+"/"+travname; std::vector lines; + std::vector endlines; std::ifstream file(filename.data()); // we can't getline here because it only allows one delimiter, and we need two; '\r' and '\n'. // at least one .list file contains newlines using only '\r' (0x0D): @@ -51,13 +56,35 @@ class TravelerList file.read(listdata, listdatasize); listdata[listdatasize] = 0; // add null terminator file.close(); - strtok_mtx->lock(); - for (char *token = strtok(listdata, "\r\n"); token; token = strtok(0, "\r\n") ) lines.push_back(token); - strtok_mtx->unlock(); + + // get canonical newline for writing splitregion .list files + std::string newline; + unsigned long c = 0; + while (listdata[c] != '\r' && listdata[c] != '\n' && c < listdatasize) c++; + if (listdata[c] == '\r') + if (listdata[c+1] == '\n') newline = "\r\n"; + else newline = "\r"; + else if (listdata[c] == '\n') newline = "\n"; + // Use CRLF as failsafe if .list file contains no newlines. + else newline = "\r\n"; + + // separate listdata into series of lines & newlines + size_t spn = 0; + for (char *c = listdata; *c; c += spn) + { endlines.push_back(""); + spn = strcspn(c, "\r\n"); + while (c[spn] == '\r' || c[spn] == '\n') + { endlines.back().push_back(c[spn]); + c[spn] = 0; + spn++; + } + lines.push_back(c); + } lines.push_back(listdata+listdatasize+1); // add a dummy "past-the-end" element to make lines[l+1]-2 work for (unsigned int l = 0; l < lines.size()-1; l++) - { // strip whitespace + { std::string orig_line(lines[l]); + // strip whitespace while (lines[l][0] == ' ' || lines[l][0] == '\t') lines[l]++; char * endchar = lines[l+1]-2; // -2 skips over the 0 inserted by strtok if (*endchar == 0) endchar--; // skip back one more for CRLF cases FIXME what about lines followed by blank lines? @@ -65,9 +92,12 @@ class TravelerList { *endchar = 0; endchar--; } - std::string origline(lines[l]); + std::string trim_line(lines[l]); // ignore empty or "comment" lines - if (lines[l][0] == 0 || lines[l][0] == '#') continue; + if (lines[l][0] == 0 || lines[l][0] == '#') + { splist << orig_line << endlines[l]; + continue; + } // process fields in line std::vector fields; strtok_mtx->lock(); @@ -76,9 +106,10 @@ class TravelerList if (fields.size() != 4) // OK if 5th field exists and starts with # if (fields.size() < 5 || fields[4][0] != '#') - { for (size_t c = 0; c < origline.size(); c++) - if (origline[c] < 0x20 || origline[c] >= 0x7F) origline[c] = '?'; - log << "Incorrect format line: " << origline << '\n'; + { for (size_t c = 0; c < trim_line.size(); c++) + if (trim_line[c] < 0x20 || trim_line[c] >= 0x7F) trim_line[c] = '?'; + log << "Incorrect format line: " << trim_line << '\n'; + splist << orig_line << endlines[l]; continue; } @@ -89,11 +120,12 @@ class TravelerList for (std:: string a : r->alt_route_names) if (route_entry == lower(a)) { log << "Note: deprecated route name " << fields[1] - << " -> canonical name " << r->list_entry_name() << " in line " << origline << '\n'; + << " -> canonical name " << r->list_entry_name() << " in line " << trim_line << '\n'; break; } if (r->system->devel()) - { log << "Ignoring line matching highway in system in development: " << origline << '\n'; + { log << "Ignoring line matching highway in system in development: " << trim_line << '\n'; + splist << orig_line << endlines[l]; continue; } // r is a route match, r.root is our root, and we need to find @@ -134,14 +166,15 @@ class TravelerList } if (canonical_waypoints.size() != 2) { bool invalid_char = 0; - for (size_t c = 0; c < origline.size(); c++) - if (origline[c] < 0x20 || origline[c] >= 0x7F) - { origline[c] = '?'; + for (size_t c = 0; c < trim_line.size(); c++) + if (trim_line[c] < 0x20 || trim_line[c] >= 0x7F) + { trim_line[c] = '?'; invalid_char = 1; } - log << "Waypoint label(s) not found in line: " << origline; + log << "Waypoint label(s) not found in line: " << trim_line; if (invalid_char) log << " [line contains invalid character(s)]"; log << '\n'; + splist << orig_line << endlines[l]; } else { list_entries.emplace_back(/**line,*/ r, canonical_waypoint_indices[0], canonical_waypoint_indices[1]); // find the segments we just matched and store this traveler with the @@ -152,23 +185,26 @@ class TravelerList hs->add_clinched_by(this); clinched_segments.insert(hs); } + #include "splitregion.cpp" } } catch (const std::out_of_range& oor) { bool invalid_char = 0; - for (size_t c = 0; c < origline.size(); c++) - if (origline[c] < 0x20 || origline[c] >= 0x7F) - { origline[c] = '?'; + for (size_t c = 0; c < trim_line.size(); c++) + if (trim_line[c] < 0x20 || trim_line[c] >= 0x7F) + { trim_line[c] = '?'; invalid_char = 1; } - log << "Unknown region/highway combo in line: " << origline; + log << "Unknown region/highway combo in line: " << trim_line; if (invalid_char) log << " [line contains invalid character(s)]"; log << '\n'; + splist << orig_line << endlines[l]; } } delete[] listdata; log << "Processed " << list_entries.size() << " good lines marking " << clinched_segments.size() << " segments traveled.\n"; log.close(); + splist.close(); } /* Return active mileage across all regions */ @@ -195,6 +231,8 @@ class TravelerList #include "userlog.cpp" }; +std::mutex TravelerList::alltrav_mtx; + bool sort_travelers_by_name(const TravelerList *t1, const TravelerList *t2) { return t1->traveler_name < t2->traveler_name; } diff --git a/siteupdate/cplusplus/classes/TravelerList/splitregion.cpp b/siteupdate/cplusplus/classes/TravelerList/splitregion.cpp new file mode 100644 index 00000000..e2a38dfe --- /dev/null +++ b/siteupdate/cplusplus/classes/TravelerList/splitregion.cpp @@ -0,0 +1,27 @@ +// new .list lines for region split-ups +if (args->splitregion == upper(fields[0])) +{ // first, comment out original line + splist << "##### " << orig_line << newline; + HighwaySegment *orig_hs = r->segment_list[canonical_waypoint_indices[0]]; + HighwaySegment *new_hs = 0; + if (!orig_hs->concurrent) + std::cout << "ERROR: " << orig_hs->str() << " not concurrent" << std::endl; + else { size_t count = 0; + // find concurrent segment with same name in different region + for (HighwaySegment *cs : *(orig_hs->concurrent)) + if (cs->route->name_no_abbrev() == r->name_no_abbrev() && cs->route->region != r->region) + { count++; + new_hs = cs; + } + if (!new_hs) std::cout << "ERROR: concurrent segment not found for " << orig_hs->str() << std::endl; + else { if (count > 1) std::cout << "DEBUG: multiple matches found for " << orig_hs->str() << std::endl; + // get lines from associated connected route. + // assumption: each chopped route in old full region corresponds 1:1 to a connected route in new chopped regions + splist << new_hs->route->con_route->list_lines(canonical_waypoint_indices[0], + canonical_waypoint_indices[1] - + canonical_waypoint_indices[0], + newline, 2) << endlines[l]; + } + } +} +else splist << orig_line << endlines[l]; diff --git a/siteupdate/cplusplus/classes/WaypointQuadtree/WaypointQuadtree.cpp b/siteupdate/cplusplus/classes/WaypointQuadtree/WaypointQuadtree.cpp index 7305256e..3c1bdc8c 100644 --- a/siteupdate/cplusplus/classes/WaypointQuadtree/WaypointQuadtree.cpp +++ b/siteupdate/cplusplus/classes/WaypointQuadtree/WaypointQuadtree.cpp @@ -26,6 +26,7 @@ void WaypointQuadtree::refine() ne_child = new WaypointQuadtree(mid_lat, mid_lng, max_lat, max_lng); sw_child = new WaypointQuadtree(min_lat, min_lng, mid_lat, mid_lng); se_child = new WaypointQuadtree(min_lat, mid_lng, mid_lat, max_lng); + // deleted on termination of program for (Waypoint *p : points) insert(p); points.clear(); } diff --git a/siteupdate/cplusplus/functions/concurrency_detection.cpp b/siteupdate/cplusplus/functions/concurrency_detection.cpp new file mode 100644 index 00000000..907374bc --- /dev/null +++ b/siteupdate/cplusplus/functions/concurrency_detection.cpp @@ -0,0 +1,74 @@ +// concurrency detection -- will augment our structure with list of concurrent +// segments with each segment (that has a concurrency) +cout << et.et() << "Concurrent segment detection." << flush; +filename = args.logfilepath+"/concurrencies.log"; +ofstream concurrencyfile(filename.data()); +timestamp = time(0); +concurrencyfile << "Log file created at: " << ctime(×tamp); +for (HighwaySystem *h : highway_systems) +{ cout << '.' << flush; + for (Route &r : h->route_list) + for (HighwaySegment *s : r.segment_list) + if (s->waypoint1->colocated && s->waypoint2->colocated) + for ( Waypoint *w1 : *(s->waypoint1->colocated) ) + if (w1->route != &r) + for ( Waypoint *w2 : *(s->waypoint2->colocated) ) + if (w1->route == w2->route) + { HighwaySegment *other = w1->route->find_segment_by_waypoints(w1,w2); + if (other) + if (!s->concurrent) + { s->concurrent = new list; + // deleted on termination of program + other->concurrent = s->concurrent; + s->concurrent->push_back(s); + s->concurrent->push_back(other); + concurrencyfile << "New concurrency [" << s->str() << "][" << other->str() << "] (" << s->concurrent->size() << ")\n"; + } + else + { other->concurrent = s->concurrent; + std::list::iterator it = s->concurrent->begin(); //FIXME + while (it != s->concurrent->end() && *it != other) it++; //see HighwaySegment.h + if (it == s->concurrent->end()) + { s->concurrent->push_back(other); + //concurrencyfile << "Added concurrency [" << s->str() << "]-[" \ + << other->str() << "] (" << s->concurrent->size() << ")\n"; + concurrencyfile << "Extended concurrency "; + for (HighwaySegment *x : *(s->concurrent)) + concurrencyfile << '[' << x->str() << ']'; + concurrencyfile << " (" << s->concurrent->size() << ")\n"; + } + } + } + // see https://github.com/TravelMapping/DataProcessing/issues/137 + // changes not yet implemented in either the original Python or this C++ version. +} +cout << "!\n"; + +// When splitting a region, perform a sanity check on concurrencies in its systems +if (args.splitregion != "") +{ for (HighwaySystem *h : highway_systems) + { if (splitsystems.find(h->systemname) == splitsystems.end()) continue; + ofstream fralog; + if (args.splitregionpath != "") fralog.open((args.splitregionpath + "/logs/" + h->systemname + "-concurrencies.log").data()); + for (Route &r : h->route_list) + { if (r.region->code.substr(0, args.splitregion.size()) != args.splitregion) continue; + for (HighwaySegment *s : r.segment_list) + if (!s->concurrent) + fralog << s->str() << " has no concurrencies\n"; + else if (s->concurrent->size() % 2) + { fralog << "Odd number of concurrencies:\n"; + for (HighwaySegment *cs : *(s->concurrent)) + fralog << '\t' << cs->str() << '\n'; + } + else { // check for concurrent segment with same name+banner in different region + unsigned int matches = 0; + for (HighwaySegment *cs : *(s->concurrent)) + if (cs->route->name_no_abbrev() == s->route->name_no_abbrev() && cs->route->region != s->route->region) + matches++; + if (matches != 1) + fralog << matches << " concurrent segments with same name+banner in different region: " << s->str() << '\n'; + } + } + fralog.close(); + } +} diff --git a/siteupdate/cplusplus/functions/crawl_hwy_data.cpp b/siteupdate/cplusplus/functions/crawl_hwy_data.cpp index 4824d404..6e60bf0f 100644 --- a/siteupdate/cplusplus/functions/crawl_hwy_data.cpp +++ b/siteupdate/cplusplus/functions/crawl_hwy_data.cpp @@ -1,4 +1,4 @@ -void crawl_hwy_data(std::string path, std::unordered_set &all_wpt_files) +void crawl_hwy_data(std::string path, std::unordered_set &all_wpt_files, std::unordered_set &splitsystems, std::string &splitregion, bool get_ss) { DIR *dir; dirent *ent; struct stat buf; @@ -8,7 +8,12 @@ void crawl_hwy_data(std::string path, std::unordered_set &all_wpt_f stat(entry.data(), &buf); if (S_ISDIR(buf.st_mode)) { if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, "..") && strcmp(ent->d_name, "_boundaries")) - crawl_hwy_data(entry, all_wpt_files); + { if (get_ss) + { splitsystems.insert(ent->d_name); + splitsystems.insert(std::string(ent->d_name)+'r'); + } + crawl_hwy_data(entry, all_wpt_files, splitsystems, splitregion, splitregion==ent->d_name); + } } else if (entry.substr(entry.size()-4) == ".wpt") all_wpt_files.insert(entry); diff --git a/siteupdate/cplusplus/functions/graph_generation.cpp b/siteupdate/cplusplus/functions/graph_generation.cpp index 10566c4b..750f33d4 100644 --- a/siteupdate/cplusplus/functions/graph_generation.cpp +++ b/siteupdate/cplusplus/functions/graph_generation.cpp @@ -24,16 +24,19 @@ else { list *regions; graph_vector.emplace_back("tm-master", "All Travel Mapping Data", 's', 'M', (list*)0, (list*)0, (PlaceRadius*)0); graph_vector.emplace_back("tm-master", "All Travel Mapping Data", 'c', 'M', (list*)0, (list*)0, (PlaceRadius*)0); + graph_vector.emplace_back("tm-master", "All Travel Mapping Data", 't', 'M', (list*)0, (list*)0, (PlaceRadius*)0); - #ifdef threading_enabled + /*#ifdef threading_enabled if (args.numthreads <= 1) - #endif + #endif//*/ { cout << et.et() << "Writing master TM simple graph file, tm-master-simple.tmg" << endl; graph_data.write_master_tmg_simple(&graph_vector[0], args.graphfilepath+"/tm-master-simple.tmg"); cout << et.et() << "Writing master TM collapsed graph file, tm-master.tmg." << endl; graph_data.write_master_tmg_collapsed(&graph_vector[1], args.graphfilepath+"/tm-master.tmg", 0); + cout << et.et() << "Writing master TM traveled graph file, tm-master.tmg." << endl; + graph_data.write_master_tmg_traveled(&graph_vector[2], args.graphfilepath+"/tm-master-traveled.tmg", &traveler_lists, 0); } - #ifdef threading_enabled + /*#ifdef threading_enabled else { cout << et.et() << "Writing master TM simple graph file, tm-master-simple.tmg" << endl; thr[0] = new thread(MasterTmgSimpleThread, &graph_data, &graph_vector[0], args.graphfilepath+"/tm-master-simple.tmg"); cout << et.et() << "Writing master TM collapsed graph file, tm-master.tmg." << endl; @@ -43,10 +46,10 @@ else { list *regions; delete thr[0]; delete thr[1]; } - #endif + #endif//*/ graph_types.push_back({"master", "All Travel Mapping Data", "These graphs contain all routes currently plotted in the Travel Mapping project."}); - size_t graphnum = 2; + size_t graphnum = 3; // graphs restricted by place/area - from areagraphs.csv file @@ -76,20 +79,22 @@ else { list *regions; 's', 'a', (list*)0, (list*)0, &a); graph_vector.emplace_back(a.base + to_string(a.r) + "-area", a.place + " (" + to_string(a.r) + " mi radius)", 'c', 'a', (list*)0, (list*)0, &a); + graph_vector.emplace_back(a.base + to_string(a.r) + "-area", a.place + " (" + to_string(a.r) + " mi radius)", + 't', 'a', (list*)0, (list*)0, &a); } // write new graph_vector entries to disk #ifdef threading_enabled // set up for threaded subgraph generation for (unsigned int t = 0; t < args.numthreads; t++) - thr[t] = new thread(SubgraphThread, t, &graph_data, &graph_vector, &graphnum, &list_mtx, args.graphfilepath + "/"); + thr[t] = new thread(SubgraphThread, t, &graph_data, &graph_vector, &graphnum, &list_mtx, args.graphfilepath + "/", &traveler_lists); for (unsigned int t = 0; t < args.numthreads; t++) thr[t]->join(); for (unsigned int t = 0; t < args.numthreads; t++) delete thr[t]; #else while (graphnum < graph_vector.size()) - { graph_data.write_subgraphs_tmg(graph_vector, args.graphfilepath + "/", graphnum, 0); - graphnum += 2; + { graph_data.write_subgraphs_tmg(graph_vector, args.graphfilepath + "/", graphnum, 0, &traveler_lists); + graphnum += 3; } #endif graph_types.push_back({"area", "Routes Within a Given Radius of a Place", @@ -108,24 +113,27 @@ else { list *regions; for (Region ®ion : all_regions) { if (region.active_preview_mileage == 0) continue; regions = new list(1, ®ion); + // deleted on termination of program graph_vector.emplace_back(region.code + "-region", region.name + " (" + region.type + ")", 's', 'r', regions, (list*)0, (PlaceRadius*)0); graph_vector.emplace_back(region.code + "-region", region.name + " (" + region.type + ")", 'c', 'r', (list*)0, (list*)0, (PlaceRadius*)0); + graph_vector.emplace_back(region.code + "-region", region.name + " (" + region.type + ")", + 't', 'r', (list*)0, (list*)0, (PlaceRadius*)0); } // write new graph_vector entries to disk #ifdef threading_enabled // set up for threaded subgraph generation for (unsigned int t = 0; t < args.numthreads; t++) - thr[t] = new thread(SubgraphThread, t, &graph_data, &graph_vector, &graphnum, &list_mtx, args.graphfilepath + "/"); + thr[t] = new thread(SubgraphThread, t, &graph_data, &graph_vector, &graphnum, &list_mtx, args.graphfilepath + "/", &traveler_lists); for (unsigned int t = 0; t < args.numthreads; t++) thr[t]->join(); for (unsigned int t = 0; t < args.numthreads; t++) delete thr[t]; #else while (graphnum < graph_vector.size()) - { graph_data.write_subgraphs_tmg(graph_vector, args.graphfilepath + "/", graphnum, 0); - graphnum += 2; + { graph_data.write_subgraphs_tmg(graph_vector, args.graphfilepath + "/", graphnum, 0, &traveler_lists); + graphnum += 3; } #endif graph_types.push_back({"region", "Routes Within a Single Region", "These graphs contain all routes currently plotted within the given region."}); @@ -152,10 +160,13 @@ else { list *regions; } if (h) { systems = new list(1, h); + // deleted on termination of program graph_vector.emplace_back(h->systemname + "-system", h->systemname + " (" + h->fullname + ")", 's', 's', (list*)0, systems, (PlaceRadius*)0); graph_vector.emplace_back(h->systemname + "-system", h->systemname + " (" + h->fullname + ")", 'c', 's', (list*)0, (list*)0, (PlaceRadius*)0); + graph_vector.emplace_back(h->systemname + "-system", h->systemname + " (" + h->fullname + ")", + 't', 's', (list*)0, (list*)0, (PlaceRadius*)0); } } file.close(); @@ -163,15 +174,15 @@ else { list *regions; #ifdef threading_enabled // set up for threaded subgraph generation for (unsigned int t = 0; t < args.numthreads; t++) - thr[t] = new thread(SubgraphThread, t, &graph_data, &graph_vector, &graphnum, &list_mtx, args.graphfilepath + "/"); + thr[t] = new thread(SubgraphThread, t, &graph_data, &graph_vector, &graphnum, &list_mtx, args.graphfilepath + "/", &traveler_lists); for (unsigned int t = 0; t < args.numthreads; t++) thr[t]->join(); for (unsigned int t = 0; t < args.numthreads; t++) delete thr[t]; #else while (graphnum < graph_vector.size()) - { graph_data.write_subgraphs_tmg(graph_vector, args.graphfilepath + "/", graphnum, 0); - graphnum += 2; + { graph_data.write_subgraphs_tmg(graph_vector, args.graphfilepath + "/", graphnum, 0, &traveler_lists); + graphnum += 3; } #endif if (h) graph_types.push_back({"system", "Routes Within a Single Highway System", @@ -198,20 +209,16 @@ else { list *regions; continue; } systems = new list; - list selected_systems; - //FIXME rewrite this whole bit to be more compact - for(char* token = strtok(fields[2], ","); token; token = strtok(0, ",")) selected_systems.push_back(token); - for (HighwaySystem *h : highway_systems) - { bool sys_sel = 0; - for (string &s : selected_systems) - if (s == h->systemname) - { sys_sel = 1; - break; - } - if (sys_sel) systems->push_back(h); - } + // deleted on termination of program + for(char* s = strtok(fields[2], ","); s; s = strtok(0, ",")) + for (HighwaySystem *h : highway_systems) + if (s == h->systemname) + { systems->push_back(h); + break; + } graph_vector.emplace_back(fields[1], fields[0], 's', 'S', (list*)0, systems, (PlaceRadius*)0); graph_vector.emplace_back(fields[1], fields[0], 'c', 'S', (list*)0, (list*)0, (PlaceRadius*)0); + graph_vector.emplace_back(fields[1], fields[0], 't', 'S', (list*)0, (list*)0, (PlaceRadius*)0); delete[] cline; } file.close(); @@ -219,15 +226,15 @@ else { list *regions; #ifdef threading_enabled // set up for threaded subgraph generation for (unsigned int t = 0; t < args.numthreads; t++) - thr[t] = new thread(SubgraphThread, t, &graph_data, &graph_vector, &graphnum, &list_mtx, args.graphfilepath + "/"); + thr[t] = new thread(SubgraphThread, t, &graph_data, &graph_vector, &graphnum, &list_mtx, args.graphfilepath + "/", &traveler_lists); for (unsigned int t = 0; t < args.numthreads; t++) thr[t]->join(); for (unsigned int t = 0; t < args.numthreads; t++) delete thr[t]; #else while (graphnum < graph_vector.size()) - { graph_data.write_subgraphs_tmg(graph_vector, args.graphfilepath + "/", graphnum, 0); - graphnum += 2; + { graph_data.write_subgraphs_tmg(graph_vector, args.graphfilepath + "/", graphnum, 0, &traveler_lists); + graphnum += 3; } #endif graph_types.push_back({"multisystem", "Routes Within Multiple Highway Systems", "These graphs contain the routes within a set of highway systems."}); @@ -253,20 +260,16 @@ else { list *regions; continue; } regions = new list; - list selected_regions; - //FIXME rewrite this whole bit to be more compact - for(char* token = strtok(fields[2], ","); token; token = strtok(0, ",")) selected_regions.push_back(token); - for (Region &r : all_regions) - { bool rg_sel = 0; - for (string &rg : selected_regions) - if (rg == r.code) - { rg_sel = 1; - break; - } - if (rg_sel && r.active_preview_mileage) regions->push_back(&r); - } + // deleted on termination of program + for(char* rg = strtok(fields[2], ","); rg; rg = strtok(0, ",")) + for (Region &r : all_regions) + if (rg == r.code) + { regions->push_back(&r); + break; + } graph_vector.emplace_back(fields[1], fields[0], 's', 'R', regions, (list*)0, (PlaceRadius*)0); graph_vector.emplace_back(fields[1], fields[0], 'c', 'R', (list*)0, (list*)0, (PlaceRadius*)0); + graph_vector.emplace_back(fields[1], fields[0], 't', 'R', (list*)0, (list*)0, (PlaceRadius*)0); delete[] cline; } file.close(); @@ -274,15 +277,15 @@ else { list *regions; #ifdef threading_enabled // set up for threaded subgraph generation for (unsigned int t = 0; t < args.numthreads; t++) - thr[t] = new thread(SubgraphThread, t, &graph_data, &graph_vector, &graphnum, &list_mtx, args.graphfilepath + "/"); + thr[t] = new thread(SubgraphThread, t, &graph_data, &graph_vector, &graphnum, &list_mtx, args.graphfilepath + "/", &traveler_lists); for (unsigned int t = 0; t < args.numthreads; t++) thr[t]->join(); for (unsigned int t = 0; t < args.numthreads; t++) delete thr[t]; #else while (graphnum < graph_vector.size()) - { graph_data.write_subgraphs_tmg(graph_vector, args.graphfilepath + "/", graphnum, 0); - graphnum += 2; + { graph_data.write_subgraphs_tmg(graph_vector, args.graphfilepath + "/", graphnum, 0, &traveler_lists); + graphnum += 3; } #endif graph_types.push_back({"multiregion", "Routes Within Multiple Regions", "These graphs contain the routes within a set of regions."}); @@ -296,6 +299,7 @@ else { list *regions; // add entries to graph_vector for (pair &c : countries) { regions = new list; + // deleted on termination of program for (Region &r : all_regions) // does it match this country and have routes? if (&c == r.country && r.active_preview_mileage) @@ -307,21 +311,23 @@ else { list *regions; 's', 'c', regions, (list*)0, (PlaceRadius*)0); graph_vector.emplace_back(c.first + "-country", c.second + " All Routes in Country", 'c', 'c', (list*)0, (list*)0, (PlaceRadius*)0); + graph_vector.emplace_back(c.first + "-country", c.second + " All Routes in Country", + 't', 't', (list*)0, (list*)0, (PlaceRadius*)0); } } // write new graph_vector entries to disk #ifdef threading_enabled // set up for threaded subgraph generation for (unsigned int t = 0; t < args.numthreads; t++) - thr[t] = new thread(SubgraphThread, t, &graph_data, &graph_vector, &graphnum, &list_mtx, args.graphfilepath + "/"); + thr[t] = new thread(SubgraphThread, t, &graph_data, &graph_vector, &graphnum, &list_mtx, args.graphfilepath + "/", &traveler_lists); for (unsigned int t = 0; t < args.numthreads; t++) thr[t]->join(); for (unsigned int t = 0; t < args.numthreads; t++) delete thr[t]; #else while (graphnum < graph_vector.size()) - { graph_data.write_subgraphs_tmg(graph_vector, args.graphfilepath + "/", graphnum, 0); - graphnum += 2; + { graph_data.write_subgraphs_tmg(graph_vector, args.graphfilepath + "/", graphnum, 0, &traveler_lists); + graphnum += 3; } #endif graph_types.push_back({"country", "Routes Within a Single Multi-Region Country", @@ -336,6 +342,7 @@ else { list *regions; // add entries to graph_vector for (pair &c : continents) { regions = new list; + // deleted on termination of program for (Region &r : all_regions) // does it match this continent and have routes? if (&c == r.continent && r.active_preview_mileage) @@ -346,21 +353,23 @@ else { list *regions; 's', 'C', regions, (list*)0, (PlaceRadius*)0); graph_vector.emplace_back(c.first + "-continent", c.second + " All Routes on Continent", 'c', 'C', (list*)0, (list*)0, (PlaceRadius*)0); + graph_vector.emplace_back(c.first + "-continent", c.second + " All Routes on Continent", + 't', 'C', (list*)0, (list*)0, (PlaceRadius*)0); } } // write new graph_vector entries to disk #ifdef threading_enabled // set up for threaded subgraph generation for (unsigned int t = 0; t < args.numthreads; t++) - thr[t] = new thread(SubgraphThread, t, &graph_data, &graph_vector, &graphnum, &list_mtx, args.graphfilepath + "/"); + thr[t] = new thread(SubgraphThread, t, &graph_data, &graph_vector, &graphnum, &list_mtx, args.graphfilepath + "/", &traveler_lists); for (unsigned int t = 0; t < args.numthreads; t++) thr[t]->join(); for (unsigned int t = 0; t < args.numthreads; t++) delete thr[t]; #else while (graphnum < graph_vector.size()) - { graph_data.write_subgraphs_tmg(graph_vector, args.graphfilepath + "/", graphnum, 0); - graphnum += 2; + { graph_data.write_subgraphs_tmg(graph_vector, args.graphfilepath + "/", graphnum, 0, &traveler_lists); + graphnum += 3; } #endif graph_types.push_back({"continent", "Routes Within a Continent", "These graphs contain the routes on a continent."}); diff --git a/siteupdate/cplusplus/siteupdate.cpp b/siteupdate/cplusplus/siteupdate.cpp index 930964f8..caa4539e 100644 --- a/siteupdate/cplusplus/siteupdate.cpp +++ b/siteupdate/cplusplus/siteupdate.cpp @@ -23,9 +23,8 @@ class TravelerList; class Region; class DatacheckEntryList; class HighwayGraph; -class HighwayGraphVertexInfo; -class HighwayGraphEdgeInfo; -class HighwayGraphCollapsedEdgeInfo; +class HGVertex; +class HGEdge; #include #include #include @@ -69,14 +68,12 @@ class HighwayGraphCollapsedEdgeInfo; #include "classes/ConnectedRoute.cpp" #include "classes/TravelerList/TravelerList.cpp" #include "classes/HighwaySegment/HighwaySegment.cpp" -#include "classes/GraphGeneration/HighwayGraphEdgeInfo.h" -#include "classes/GraphGeneration/HighwayGraphCollapsedEdgeInfo.h" -#include "classes/GraphGeneration/HighwayGraphVertexInfo.cpp" +#include "classes/GraphGeneration/HGEdge.h" +#include "classes/GraphGeneration/HGVertex.cpp" #include "classes/GraphGeneration/PlaceRadius.cpp" #include "classes/GraphGeneration/GraphListEntry.cpp" #include "classes/GraphGeneration/HighwayGraph.cpp" -#include "classes/GraphGeneration/HighwayGraphEdgeInfo.cpp" -#include "classes/GraphGeneration/HighwayGraphCollapsedEdgeInfo.cpp" +#include "classes/GraphGeneration/HGEdge.cpp" #include "threads/ReadWptThread.cpp" #include "threads/NmpMergedThread.cpp" #include "threads/ReadListThread.cpp" @@ -202,6 +199,7 @@ int main(int argc, char *argv[]) continue; } HighwaySystem *hs = new HighwaySystem(line, el, args.highwaydatapath+"/hwy_data/_systems", args.systemsfile, countries, all_regions); + // deleted on termination of program if (hs->is_valid()) highway_systems.push_back(hs); else delete hs; cout << hs->systemname << '.' << std::flush; @@ -214,6 +212,7 @@ int main(int argc, char *argv[]) file.close(); DatacheckEntryList *datacheckerrors = new DatacheckEntryList; + // deleted on termination of program // check for duplicate .list names // and duplicate root entries among Route and ConnectedRoute @@ -276,8 +275,8 @@ int main(int argc, char *argv[]) // that do not have a .csv file entry that causes them to be // read into the data cout << et.et() << "Finding all .wpt files. " << flush; - unordered_set all_wpt_files; - crawl_hwy_data(args.highwaydatapath+"/hwy_data", all_wpt_files); + unordered_set all_wpt_files, splitsystems; + crawl_hwy_data(args.highwaydatapath+"/hwy_data", all_wpt_files, splitsystems, args.splitregion, 0); cout << all_wpt_files.size() << " files found." << endl; // For finding colocated Waypoints and concurrent segments, we have @@ -383,6 +382,8 @@ int main(int argc, char *argv[]) #endif } + #include "functions/concurrency_detection.cpp" + // Create hash table for faster lookup of routes by list file name cout << et.et() << "Creating route hash table for list processing:" << endl; unordered_map route_hash; @@ -413,6 +414,12 @@ int main(int argc, char *argv[]) #endif cout << " processed " << traveler_lists.size() << " traveler list files." << endl; traveler_lists.sort(sort_travelers_by_name); + // assign traveler numbers + unsigned int travnum = 0; + for (TravelerList *t : traveler_lists) + { t->traveler_num = travnum; + travnum++; + } //#include "debug/highway_segment_log.cpp" //#include "debug/pioneers.cpp" @@ -575,52 +582,6 @@ int main(int argc, char *argv[]) unusedfile.close(); - // concurrency detection -- will augment our structure with list of concurrent - // segments with each segment (that has a concurrency) - cout << et.et() << "Concurrent segment detection." << flush; - filename = args.logfilepath+"/concurrencies.log"; - ofstream concurrencyfile(filename.data()); - timestamp = time(0); - concurrencyfile << "Log file created at: " << ctime(×tamp); - for (HighwaySystem *h : highway_systems) - { cout << '.' << flush; - for (Route &r : h->route_list) - for (HighwaySegment *s : r.segment_list) - if (s->waypoint1->colocated && s->waypoint2->colocated) - for ( Waypoint *w1 : *(s->waypoint1->colocated) ) - if (w1->route->root != r.root) //FIXME: compare route objects directly, using pointers. - // Route is the odd one out anyway, being accessed by value, with copy ctors... Speed increase? - for ( Waypoint *w2 : *(s->waypoint2->colocated) ) - if (w1->route->root == w2->route->root) //FIXME, same thing - { HighwaySegment *other = w1->route->find_segment_by_waypoints(w1,w2); - if (other) - if (!s->concurrent) - { s->concurrent = new list; - other->concurrent = s->concurrent; - s->concurrent->push_back(s); - s->concurrent->push_back(other); - concurrencyfile << "New concurrency [" << s->str() << "][" << other->str() << "] (" << s->concurrent->size() << ")\n"; - } - else - { other->concurrent = s->concurrent; - std::list::iterator it = s->concurrent->begin(); //FIXME - while (it != s->concurrent->end() && *it != other) it++; //see HighwaySegment.h - if (it == s->concurrent->end()) - { s->concurrent->push_back(other); - //concurrencyfile << "Added concurrency [" << s->str() << "]-[" \ - << other->str() << "] (" << s->concurrent->size() << ")\n"; - concurrencyfile << "Extended concurrency "; - for (HighwaySegment *x : *(s->concurrent)) - concurrencyfile << '[' << x->str() << ']'; - concurrencyfile << " (" << s->concurrent->size() << ")\n"; - } - } - } - // see https://github.com/TravelMapping/DataProcessing/issues/137 - // changes not yet implemented in either the original Python or this C++ version. - } - cout << "!\n"; - // now augment any traveler clinched segments for concurrencies cout << et.et() << "Augmenting travelers for detected concurrent segments." << flush; //#include "debug/concurrency_augments.cpp" @@ -648,6 +609,14 @@ int main(int argc, char *argv[]) cout << "!\n"; concurrencyfile.close(); + /*filename = args.logfilepath+"/concurrent_travelers_sanity_check.log"; + ofstream sanetravfile(filename.data()); + for (HighwaySystem *h : highway_systems) + for (Route &r : h->route_list) + for (HighwaySegment *s : r.segment_list) + sanetravfile << s->concurrent_travelers_sanity_check(); + sanetravfile.close(); //*/ + // compute lots of stats, first total mileage by route, system, overall, where // system and overall are stored in unordered_maps by region cout << et.et() << "Computing stats." << flush; @@ -948,7 +917,6 @@ int main(int argc, char *argv[]) { el.add_error("Could not parse datacheckfps.csv line: [" + line + "], expected 6 fields, found more"); continue; } - char *rootstr = new char[line.size()-left]; fields[5] = line.substr(left+1); if (datacheck_always_error.find(fields[4]) != datacheck_always_error.end()) diff --git a/siteupdate/cplusplus/threads/ReadListThread.cpp b/siteupdate/cplusplus/threads/ReadListThread.cpp index b392bcd5..f990922e 100644 --- a/siteupdate/cplusplus/threads/ReadListThread.cpp +++ b/siteupdate/cplusplus/threads/ReadListThread.cpp @@ -13,6 +13,10 @@ void ReadListThread(std::list *traveler_ids, std::listpop_front() successful." << std::endl; std::cout << ' ' << tl << std::flush; tl_mtx->unlock(); - traveler_lists->push_back(new TravelerList(tl, route_hash, args, strtok_mtx)); + TravelerList *t = new TravelerList(tl, route_hash, args, strtok_mtx); + // deleted on termination of program + TravelerList::alltrav_mtx.lock(); + traveler_lists->push_back(t); + TravelerList::alltrav_mtx.unlock(); } } diff --git a/siteupdate/cplusplus/threads/SubgraphThread.cpp b/siteupdate/cplusplus/threads/SubgraphThread.cpp index e9b943f7..d56e96f2 100644 --- a/siteupdate/cplusplus/threads/SubgraphThread.cpp +++ b/siteupdate/cplusplus/threads/SubgraphThread.cpp @@ -1,4 +1,5 @@ -void SubgraphThread(unsigned int id, HighwayGraph *graph_data, std::vector *graph_vector, size_t *index, std::mutex *mtx, std::string path) +void SubgraphThread(unsigned int id, HighwayGraph *graph_data, std::vector *graph_vector, +size_t *index, std::mutex *mtx, std::string path, std::list *traveler_lists) { //std::cout << "Starting SubgraphThread " << id << std::endl; while (*index < graph_vector->size()) { mtx->lock(); @@ -9,8 +10,8 @@ void SubgraphThread(unsigned int id, HighwayGraph *graph_data, std::vectorat(*index).tag() << std::endl; size_t i = *index; - *index += 2; + *index += 3; mtx->unlock(); - graph_data->write_subgraphs_tmg(*graph_vector, path, i, id); + graph_data->write_subgraphs_tmg(*graph_vector, path, i, id, traveler_lists); } }