diff --git a/siteupdate/cplusplus/classes/DatacheckEntry.cpp b/siteupdate/cplusplus/classes/DatacheckEntry.cpp index 8aa0ec0d..99f7cd61 100644 --- a/siteupdate/cplusplus/classes/DatacheckEntry.cpp +++ b/siteupdate/cplusplus/classes/DatacheckEntry.cpp @@ -12,6 +12,7 @@ class DatacheckEntry -----------------------+-------------------------------------------- BAD_ANGLE | BUS_WITH_I | + DISCONNECTED_ROUTE | adjacent root's expected connection point DUPLICATE_COORDS | coordinate pair DUPLICATE_LABEL | HIDDEN_JUNCTION | number of incident edges in TM master graph diff --git a/siteupdate/cplusplus/classes/Route/Route.cpp b/siteupdate/cplusplus/classes/Route/Route.cpp index f11e86fa..609de387 100644 --- a/siteupdate/cplusplus/classes/Route/Route.cpp +++ b/siteupdate/cplusplus/classes/Route/Route.cpp @@ -8,6 +8,7 @@ Route::Route(std::string &line, HighwaySystem *sys, ErrorList &el, std::unordere mileage = 0; rootOrder = -1; // order within connected route region = 0; // if this stays 0, setup has failed due to bad .csv data + is_reversed = 0; // parse chopped routes csv line size_t NumFields = 8; @@ -216,3 +217,11 @@ inline void Route::store_traveled_segments(TravelerList* t, unsigned int beg, un t->clinched_segments.insert(hs); } } + +inline Waypoint* Route::con_beg() +{ return is_reversed ? point_list.back() : point_list.front(); +} + +inline Waypoint* Route::con_end() +{ return is_reversed ? point_list.front() : point_list.back(); +} diff --git a/siteupdate/cplusplus/classes/Route/Route.h b/siteupdate/cplusplus/classes/Route/Route.h index 82f6ed6d..e099d6db 100644 --- a/siteupdate/cplusplus/classes/Route/Route.h +++ b/siteupdate/cplusplus/classes/Route/Route.h @@ -60,6 +60,7 @@ class Route std::vector segment_list; double mileage; int rootOrder; + bool is_reversed; Route(std::string &, HighwaySystem *, ErrorList &, std::unordered_map &); @@ -76,4 +77,6 @@ class Route //std::string list_line(int, int); void write_nmp_merged(std::string); inline void store_traveled_segments(TravelerList*, unsigned int, unsigned int); + inline Waypoint* con_beg(); + inline Waypoint* con_end(); }; diff --git a/siteupdate/cplusplus/classes/TravelerList/mark_connected_route_segments.cpp b/siteupdate/cplusplus/classes/TravelerList/mark_connected_route_segments.cpp index 2ed64ef6..61f70421 100644 --- a/siteupdate/cplusplus/classes/TravelerList/mark_connected_route_segments.cpp +++ b/siteupdate/cplusplus/classes/TravelerList/mark_connected_route_segments.cpp @@ -124,9 +124,13 @@ else { if (r1->rootOrder > r2->rootOrder) reverse = 1; } // mark the beginning chopped route from index1 to its end - r1->store_traveled_segments(this, index1, r1->segment_list.size()); + if (r1->is_reversed) + r1->store_traveled_segments(this, 0, index1); + else r1->store_traveled_segments(this, index1, r1->segment_list.size()); // mark the ending chopped route from its beginning to index2 - r2->store_traveled_segments(this, 0, index2); + if (r2->is_reversed) + r2->store_traveled_segments(this, index2, r2->segment_list.size()); + else r2->store_traveled_segments(this, 0, index2); // mark any intermediate chopped routes in their entirety. for (size_t r = r1->rootOrder+1; r < r2->rootOrder; r++) r1->con_route->roots[r]->store_traveled_segments(this, 0, r1->con_route->roots[r]->segment_list.size()); diff --git a/siteupdate/cplusplus/siteupdate.cpp b/siteupdate/cplusplus/siteupdate.cpp index 79bc8322..91612622 100644 --- a/siteupdate/cplusplus/siteupdate.cpp +++ b/siteupdate/cplusplus/siteupdate.cpp @@ -391,8 +391,39 @@ int main(int argc, char *argv[]) { // check for unconnected chopped routes if (!r.con_route) el.add_error(r.system->systemname + ".csv: root " + r.root + " not matched by any connected route root."); - unsigned int index = 0; - for (Waypoint* w : r.point_list) + + // check for mismatched route endpoints within connected routes + #define q r.con_route->roots[r.rootOrder-1] + if ( r.rootOrder > 0 && q->point_list.size() > 1 && !r.con_beg()->same_coords(q->con_end()) ) + { if ( q->con_beg()->same_coords(r.con_beg()) ) + { //std::cout << "DEBUG: marking only " << q->str() << " reversed" << std::endl; + //if (q->is_reversed) std::cout << "DEBUG: " << q->str() << " already reversed!" << std::endl; + q->is_reversed = 1; + } + else if ( q->con_end()->same_coords(r.con_end()) ) + { //std::cout << "DEBUG: marking only " << r.str() << " reversed" << std::endl; + //if (r.is_reversed) std::cout << "DEBUG: " << r.str() << " already reversed!" << std::endl; + r.is_reversed = 1; + } + else if ( q->con_beg()->same_coords(r.con_end()) ) + { //std::cout << "DEBUG: marking both " << q->str() << " and " << r.str() << " reversed" << std::endl; + //if (q->is_reversed) std::cout << "DEBUG: " << q->str() << " already reversed!" << std::endl; + //if (r.is_reversed) std::cout << "DEBUG: " << r.str() << " already reversed!" << std::endl; + q->is_reversed = 1; + r.is_reversed = 1; + } + else if ( !q->con_end()->same_coords(r.con_beg()) ) + { datacheckerrors->add(&r, r.con_beg()->label, "", "", + "DISCONNECTED_ROUTE", q->con_end()->root_at_label()); + datacheckerrors->add(q, q->con_end()->label, "", "", + "DISCONNECTED_ROUTE", r.con_beg()->root_at_label()); + } + } + #undef q + + // create label hashes and check for duplicates + #define w r.point_list[index] + for (unsigned int index = 0; index < r.point_list.size(); index++) { // ignore case and leading '+' or '*' std::string upper_label = upper(w->label); while (upper_label[0] == '+' || upper_label[0] == '*') @@ -422,8 +453,8 @@ int main(int argc, char *argv[]) r.duplicate_labels.insert(a); } } - index++; } + #undef w } // Create a list of TravelerList objects, one per person @@ -898,8 +929,8 @@ int main(int argc, char *argv[]) getline(file, line); // ignore header line list> datacheckfps; unordered_set datacheck_always_error - ({ "BAD_ANGLE", "DUPLICATE_LABEL", "HIDDEN_TERMINUS", - "INVALID_FINAL_CHAR", "INVALID_FIRST_CHAR", + ({ "BAD_ANGLE", "DISCONNECTED_ROUTE", "DUPLICATE_LABEL", + "HIDDEN_TERMINUS", "INVALID_FINAL_CHAR", "INVALID_FIRST_CHAR", "LABEL_INVALID_CHAR", "LABEL_PARENS", "LABEL_SLASHES", "LABEL_TOO_LONG", "LABEL_UNDERSCORES", "LONG_UNDERSCORE", "MALFORMED_LAT", "MALFORMED_LON", "MALFORMED_URL", diff --git a/siteupdate/python-teresco/siteupdate.py b/siteupdate/python-teresco/siteupdate.py index ef61f481..c6894159 100755 --- a/siteupdate/python-teresco/siteupdate.py +++ b/siteupdate/python-teresco/siteupdate.py @@ -990,6 +990,7 @@ def __init__(self,line,system,el): self.con_route = None self.mileage = 0.0 self.rootOrder = -1 # order within connected route + self.is_reversed = False def __str__(self): """printable version of the object""" @@ -1095,6 +1096,12 @@ def store_traveled_segments(self, t, beg, end): hs.add_clinched_by(t) t.clinched_segments.add(hs) + def con_beg(self): + return self.point_list[-1] if self.is_reversed else self.point_list[0] + + def con_end(self): + return self.point_list[0] if self.is_reversed else self.point_list[-1] + class ConnectedRoute: """This class encapsulates a single 'connected route' as given by a single line of a _con.csv file @@ -1490,9 +1497,15 @@ def __init__(self,travelername,el,path="../../../UserData/list_files"): (r1, r2) = (r2, r1) (index1, index2) = (index2, index1) # mark the beginning chopped route from index1 to its end - r1.store_traveled_segments(self, index1, len(r1.segment_list)) + if r1.is_reversed: + r1.store_traveled_segments(self, 0, index1) + else: + r1.store_traveled_segments(self, index1, len(r1.segment_list)) # mark the ending chopped route from its beginning to index2 - r2.store_traveled_segments(self, 0, index2) + if r2.is_reversed: + r2.store_traveled_segments(self, index2, len(r2.segment_list)) + else: + r2.store_traveled_segments(self, 0, index2) # mark any intermediate chopped routes in their entirety. for r in range(r1.rootOrder+1, r2.rootOrder): r1.con_route.roots[r].store_traveled_segments(self, 0, len(r1.con_route.roots[r].segment_list)) @@ -1545,6 +1558,7 @@ class DatacheckEntry: -----------------------+-------------------------------------------- BAD_ANGLE | BUS_WITH_I | + DISCONNECTED_ROUTE | adjacent root's expected connection point DUPLICATE_COORDS | coordinate pair DUPLICATE_LABEL | HIDDEN_JUNCTION | number of incident edges in TM master graph @@ -2873,6 +2887,24 @@ def run(self): # check for unconnected chopped routes if r.con_route is None: el.add_error(r.system.systemname + ".csv: root " + r.root + " not matched by any connected route root.") + + # check for mismatched route endpoints within connected routes + q = r.con_route.roots[r.rootOrder-1] + if r.rootOrder > 0 and len(q.point_list) > 1 and not r.con_beg().same_coords(q.con_end()): + if q.con_beg().same_coords(r.con_beg()): + q.is_reversed = True + elif q.con_end().same_coords(r.con_end()): + r.is_reversed = True + elif q.con_beg().same_coords(r.con_end()): + q.is_reversed = True + r.is_reversed = True + elif not q.con_end().same_coords(r.con_beg()): + datacheckerrors.append(DatacheckEntry(r, [r.con_beg().label], + "DISCONNECTED_ROUTE", q.root + '@' + q.con_end().label)) + datacheckerrors.append(DatacheckEntry(q, [q.con_end().label], + "DISCONNECTED_ROUTE", r.root + '@' + r.con_beg().label)) + + # create label hashes and check for duplicates index = 0 for w in r.point_list: # ignore case and leading '+' or '*' @@ -3514,8 +3546,8 @@ def run(self): lines.pop(0) # ignore header line datacheckfps = [] -datacheck_always_error = [ 'BAD_ANGLE', 'DUPLICATE_LABEL', 'HIDDEN_TERMINUS', - 'INVALID_FINAL_CHAR', 'INVALID_FIRST_CHAR', +datacheck_always_error = [ 'BAD_ANGLE', 'DISCONNECTED_ROUTE', 'DUPLICATE_LABEL', + 'HIDDEN_TERMINUS', 'INVALID_FINAL_CHAR', 'INVALID_FIRST_CHAR', 'LABEL_INVALID_CHAR', 'LABEL_PARENS', 'LABEL_SLASHES', 'LABEL_TOO_LONG', 'LABEL_UNDERSCORES', 'LONG_UNDERSCORE', 'MALFORMED_LAT', 'MALFORMED_LON', 'MALFORMED_URL',