diff --git a/siteupdate/cplusplus/classes/DatacheckEntry.cpp b/siteupdate/cplusplus/classes/DatacheckEntry.cpp index 99f7cd61..5151cf2d 100644 --- a/siteupdate/cplusplus/classes/DatacheckEntry.cpp +++ b/siteupdate/cplusplus/classes/DatacheckEntry.cpp @@ -17,6 +17,7 @@ class DatacheckEntry DUPLICATE_LABEL | HIDDEN_JUNCTION | number of incident edges in TM master graph HIDDEN_TERMINUS | + INTERSTATE_NO_HYPHEN | INVALID_FINAL_CHAR | final character in label INVALID_FIRST_CHAR | first character in label other than * LABEL_INVALID_CHAR | diff --git a/siteupdate/cplusplus/classes/Route/read_wpt.cpp b/siteupdate/cplusplus/classes/Route/read_wpt.cpp index e2c33867..ba51e96f 100644 --- a/siteupdate/cplusplus/classes/Route/read_wpt.cpp +++ b/siteupdate/cplusplus/classes/Route/read_wpt.cpp @@ -71,16 +71,17 @@ void Route::read_wpt } // checks for visible points if (!w->is_hidden) - { w->visible_distance(datacheckerrors, fstr, vis_dist, last_visible); - const char *slash = strchr(w->label.data(), '/'); - w->label_selfref(datacheckerrors, slash); - w->label_slashes(datacheckerrors, slash); - w->underscore_datachecks(datacheckerrors, slash); - w->label_parens(datacheckerrors); - w->label_invalid_ends(datacheckerrors); + { const char *slash = strchr(w->label.data(), '/'); w->bus_with_i(datacheckerrors); + w->interstate_no_hyphen(datacheckerrors); + w->label_invalid_ends(datacheckerrors); w->label_looks_hidden(datacheckerrors); + w->label_parens(datacheckerrors); + w->label_selfref(datacheckerrors, slash); + w->label_slashes(datacheckerrors, slash); w->lacks_generic(datacheckerrors); + w->underscore_datachecks(datacheckerrors, slash); + w->visible_distance(datacheckerrors, fstr, vis_dist, last_visible); } } delete[] wptdata; diff --git a/siteupdate/cplusplus/classes/Waypoint/Waypoint.cpp b/siteupdate/cplusplus/classes/Waypoint/Waypoint.cpp index e964b9bc..e8d67fe0 100644 --- a/siteupdate/cplusplus/classes/Waypoint/Waypoint.cpp +++ b/siteupdate/cplusplus/classes/Waypoint/Waypoint.cpp @@ -305,6 +305,16 @@ bool Waypoint::label_references_route(Route *r, DatacheckEntryList *datacheckerr /* Datacheck */ +inline void Waypoint::distance_update(DatacheckEntryList *datacheckerrors, char *fstr, double &vis_dist, Waypoint *prev_w) +{ // visible distance update, and last segment length check + double last_distance = distance_to(prev_w); + vis_dist += last_distance; + if (last_distance > 20) + { sprintf(fstr, "%.2f", last_distance); + datacheckerrors->add(route, prev_w->label, label, "", "LONG_SEGMENT", fstr); + } +} + inline void Waypoint::duplicate_coords(DatacheckEntryList *datacheckerrors, std::unordered_set &coords_used, char *fstr) { // duplicate coordinates Waypoint *w; @@ -320,6 +330,25 @@ inline void Waypoint::duplicate_coords(DatacheckEntryList *datacheckerrors, std: } } +inline void Waypoint::label_invalid_char(DatacheckEntryList *datacheckerrors) +{ // look for labels with invalid characters + if (label == "*") + datacheckerrors->add(route, label, "", "", "LABEL_INVALID_CHAR", ""); + else for (const char *c = label.data(); *c; c++) + if ((*c == 42 || *c == 43) && c > label.data() + || (*c < 40) || (*c == 44) || (*c > 57 && *c < 65) + || (*c == 96) || (*c > 122) || (*c > 90 && *c < 95)) + datacheckerrors->add(route, label, "", "", "LABEL_INVALID_CHAR", ""); + for (std::string& lbl : alt_labels) + if (lbl == "*") + datacheckerrors->add(route, lbl, "", "", "LABEL_INVALID_CHAR", ""); + else for (const char *c = lbl.data(); *c; c++) + if (*c == '+' && c > lbl.data() || *c == '*' && (c > lbl.data()+1 || lbl[0] != '+') + || (*c < 40) || (*c == 44) || (*c > 57 && *c < 65) + || (*c == 96) || (*c > 122) || (*c > 90 && *c < 95)) + datacheckerrors->add(route, lbl, "", "", "LABEL_INVALID_CHAR", ""); +} + inline bool Waypoint::label_too_long(DatacheckEntryList *datacheckerrors) { // label longer than the DB can store if (label.size() > DBFieldLength::label) @@ -346,16 +375,6 @@ inline bool Waypoint::label_too_long(DatacheckEntryList *datacheckerrors) return 0; } -inline void Waypoint::lacks_generic(DatacheckEntryList *datacheckerrors) -{ // label lacks generic highway type - const char* c = label[0] == '*' ? label.data()+1 : label.data(); - if ( (*c == 'O' || *c == 'o') - && (*(c+1) == 'l' || *(c+1) == 'L') - && (*(c+2) == 'd' || *(c+2) == 'D') - && *(c+3) >= '0' && *(c+3) <= '9') - datacheckerrors->add(route, label, "", "", "LACKS_GENERIC", ""); -} - inline void Waypoint::out_of_bounds(DatacheckEntryList *datacheckerrors, char *fstr) { // out-of-bounds coords if (lat > 90 || lat < -90 || lng > 180 || lng < -180) @@ -364,34 +383,13 @@ inline void Waypoint::out_of_bounds(DatacheckEntryList *datacheckerrors, char *f } } -inline void Waypoint::distance_update(DatacheckEntryList *datacheckerrors, char *fstr, double &vis_dist, Waypoint *prev_w) -{ // visible distance update, and last segment length check - double last_distance = distance_to(prev_w); - vis_dist += last_distance; - if (last_distance > 20) - { sprintf(fstr, "%.2f", last_distance); - datacheckerrors->add(route, prev_w->label, label, "", "LONG_SEGMENT", fstr); - } -} - /* checks for visible points */ -inline void Waypoint::visible_distance(DatacheckEntryList *datacheckerrors, char *fstr, double &vis_dist, Waypoint *&last_visible) -{ // complete visible distance check, omit report for active - // systems to reduce clutter - if (vis_dist > 10 && !route->system->active()) - { sprintf(fstr, "%.2f", vis_dist); - datacheckerrors->add(route, last_visible->label, label, "", "VISIBLE_DISTANCE", fstr); - } - last_visible = this; - vis_dist = 0; -} - inline void Waypoint::bus_with_i(DatacheckEntryList *datacheckerrors) { // look for I-xx with Bus instead of BL or BS const char *c = label.data(); if (*c == '*') c++; - if (*c++ != 'I' || *c++ != '-' || route->region->country->first != "USA") return; + if (*c++ != 'I' || *c++ != '-' || route->system->country->first != "USA") return; if (*c < '0' || *c > '9') return; while (*c >= '0' && *c <= '9') c++; if ( *c == 'E' || *c == 'W' || *c == 'C' || *c == 'N' || *c == 'S' @@ -402,6 +400,25 @@ inline void Waypoint::bus_with_i(DatacheckEntryList *datacheckerrors) datacheckerrors->add(route, label, "", "", "BUS_WITH_I", ""); } +inline void Waypoint::interstate_no_hyphen(DatacheckEntryList *datacheckerrors) +{ if (route->system->country->first == "USA" && label.size() >= 2) + { const char *c = label.data(); + if (c[0] == 'T' && c[1] == 'o') c += 2; + if (c[0] == 'I' && isdigit(c[1])) + datacheckerrors->add(route, label, "", "", "INTERSTATE_NO_HYPHEN", ""); + } +} + +inline void Waypoint::label_invalid_ends(DatacheckEntryList *datacheckerrors) +{ // look for labels with invalid first or final characters + const char *c = label.data(); + while (*c == '*') c++; + if (*c == '_' || *c == '/' || *c == '(') + datacheckerrors->add(route, label, "", "", "INVALID_FIRST_CHAR", std::string(1, *c)); + if (label.back() == '_' || label.back() == '/') + datacheckerrors->add(route, label, "", "", "INVALID_FINAL_CHAR", std::string(1, label.back())); +} + inline void Waypoint::label_looks_hidden(DatacheckEntryList *datacheckerrors) { // look for labels that look like hidden waypoints but which aren't hidden if (label.size() != 7) return; @@ -415,35 +432,6 @@ inline void Waypoint::label_looks_hidden(DatacheckEntryList *datacheckerrors) datacheckerrors->add(route, label, "", "", "LABEL_LOOKS_HIDDEN", ""); } -inline void Waypoint::label_invalid_char(DatacheckEntryList *datacheckerrors) -{ // look for labels with invalid characters - if (label == "*") - datacheckerrors->add(route, label, "", "", "LABEL_INVALID_CHAR", ""); - else for (const char *c = label.data(); *c; c++) - if ((*c == 42 || *c == 43) && c > label.data() - || (*c < 40) || (*c == 44) || (*c > 57 && *c < 65) - || (*c == 96) || (*c > 122) || (*c > 90 && *c < 95)) - datacheckerrors->add(route, label, "", "", "LABEL_INVALID_CHAR", ""); - for (std::string& lbl : alt_labels) - if (lbl == "*") - datacheckerrors->add(route, lbl, "", "", "LABEL_INVALID_CHAR", ""); - else for (const char *c = lbl.data(); *c; c++) - if (*c == '+' && c > lbl.data() || *c == '*' && (c > lbl.data()+1 || lbl[0] != '+') - || (*c < 40) || (*c == 44) || (*c > 57 && *c < 65) - || (*c == 96) || (*c > 122) || (*c > 90 && *c < 95)) - datacheckerrors->add(route, lbl, "", "", "LABEL_INVALID_CHAR", ""); -} - -inline void Waypoint::label_invalid_ends(DatacheckEntryList *datacheckerrors) -{ // look for labels with invalid first or final characters - const char *c = label.data(); - while (*c == '*') c++; - if (*c == '_' || *c == '/' || *c == '(') - datacheckerrors->add(route, label, "", "", "INVALID_FIRST_CHAR", std::string(1, *c)); - if (label.back() == '_' || label.back() == '/') - datacheckerrors->add(route, label, "", "", "INVALID_FINAL_CHAR", std::string(1, label.back())); -} - inline void Waypoint::label_parens(DatacheckEntryList *datacheckerrors) { // look for parenthesis balance in label int parens = 0; @@ -467,28 +455,6 @@ inline void Waypoint::label_parens(DatacheckEntryList *datacheckerrors) datacheckerrors->add(route, label, "", "", "LABEL_PARENS", ""); } -inline void Waypoint::underscore_datachecks(DatacheckEntryList *datacheckerrors, const char *slash) -{ const char *underscore = strchr(label.data(), '_'); - if (underscore) - { // look for too many underscores in label - if (strchr(underscore+1, '_')) - datacheckerrors->add(route, label, "", "", "LABEL_UNDERSCORES", ""); - // look for too many characters after underscore in label - if (label.data()+label.size() > underscore+4) - if (label.back() > 'Z' || label.back() < 'A' || label.data()+label.size() > underscore+5) - datacheckerrors->add(route, label, "", "", "LONG_UNDERSCORE", ""); - // look for labels with a slash after an underscore - if (slash > underscore) - datacheckerrors->add(route, label, "", "", "NONTERMINAL_UNDERSCORE", ""); - } -} - -inline void Waypoint::label_slashes(DatacheckEntryList *datacheckerrors, const char *slash) -{ // look for too many slashes in label - if (slash && strchr(slash+1, '/')) - datacheckerrors->add(route, label, "", "", "LABEL_SLASHES", ""); -} - inline void Waypoint::label_selfref(DatacheckEntryList *datacheckerrors, const char *slash) { // looking for the route within the label //match_start = w.label.find(r.route) @@ -532,8 +498,51 @@ inline void Waypoint::label_selfref(DatacheckEntryList *datacheckerrors, const c datacheckerrors->add(route, label, "", "", "LABEL_SELFREF", ""); } +inline void Waypoint::label_slashes(DatacheckEntryList *datacheckerrors, const char *slash) +{ // look for too many slashes in label + if (slash && strchr(slash+1, '/')) + datacheckerrors->add(route, label, "", "", "LABEL_SLASHES", ""); +} + +inline void Waypoint::lacks_generic(DatacheckEntryList *datacheckerrors) +{ // label lacks generic highway type + const char* c = label[0] == '*' ? label.data()+1 : label.data(); + if ( (*c == 'O' || *c == 'o') + && (*(c+1) == 'l' || *(c+1) == 'L') + && (*(c+2) == 'd' || *(c+2) == 'D') + && *(c+3) >= '0' && *(c+3) <= '9') + datacheckerrors->add(route, label, "", "", "LACKS_GENERIC", ""); +} + +inline void Waypoint::underscore_datachecks(DatacheckEntryList *datacheckerrors, const char *slash) +{ const char *underscore = strchr(label.data(), '_'); + if (underscore) + { // look for too many underscores in label + if (strchr(underscore+1, '_')) + datacheckerrors->add(route, label, "", "", "LABEL_UNDERSCORES", ""); + // look for too many characters after underscore in label + if (label.data()+label.size() > underscore+4) + if (label.back() > 'Z' || label.back() < 'A' || label.data()+label.size() > underscore+5) + datacheckerrors->add(route, label, "", "", "LONG_UNDERSCORE", ""); + // look for labels with a slash after an underscore + if (slash > underscore) + datacheckerrors->add(route, label, "", "", "NONTERMINAL_UNDERSCORE", ""); + } +} + // look for USxxxA but not USxxxAlt, B/Bus (others?) //if re.fullmatch('US[0-9]+A.*', w.label) and not re.fullmatch('US[0-9]+Alt.*', w.label) or \ // re.fullmatch('US[0-9]+B.*', w.label) and \ // not (re.fullmatch('US[0-9]+Bus.*', w.label) or re.fullmatch('US[0-9]+Byp.*', w.label)): // datacheckerrors.append(DatacheckEntry(r,[w.label],'US_BANNER')) + +inline void Waypoint::visible_distance(DatacheckEntryList *datacheckerrors, char *fstr, double &vis_dist, Waypoint *&last_visible) +{ // complete visible distance check, omit report for active + // systems to reduce clutter + if (vis_dist > 10 && !route->system->active()) + { sprintf(fstr, "%.2f", vis_dist); + datacheckerrors->add(route, last_visible->label, label, "", "VISIBLE_DISTANCE", fstr); + } + last_visible = this; + vis_dist = 0; +} diff --git a/siteupdate/cplusplus/classes/Waypoint/Waypoint.h b/siteupdate/cplusplus/classes/Waypoint/Waypoint.h index f0862cb8..23a5d09f 100644 --- a/siteupdate/cplusplus/classes/Waypoint/Waypoint.h +++ b/siteupdate/cplusplus/classes/Waypoint/Waypoint.h @@ -39,19 +39,20 @@ class Waypoint bool label_references_route(Route *, DatacheckEntryList *); // Datacheck + inline void distance_update(DatacheckEntryList *, char *, double &, Waypoint *); inline void duplicate_coords(DatacheckEntryList *, std::unordered_set &, char *); + inline void label_invalid_char(DatacheckEntryList *); + inline bool label_too_long(DatacheckEntryList *); inline void out_of_bounds(DatacheckEntryList *, char *); - inline void distance_update(DatacheckEntryList *, char *, double &, Waypoint *); // checks for visible points - inline void visible_distance(DatacheckEntryList *, char *, double &, Waypoint *&); inline void bus_with_i(DatacheckEntryList *); - inline void label_looks_hidden(DatacheckEntryList *); - inline void label_invalid_char(DatacheckEntryList *); + inline void interstate_no_hyphen(DatacheckEntryList *); inline void label_invalid_ends(DatacheckEntryList *); + inline void label_looks_hidden(DatacheckEntryList *); inline void label_parens(DatacheckEntryList *); - inline void label_slashes(DatacheckEntryList *, const char *); inline void label_selfref(DatacheckEntryList *, const char *); - inline bool label_too_long(DatacheckEntryList *); + inline void label_slashes(DatacheckEntryList *, const char *); inline void lacks_generic(DatacheckEntryList *); inline void underscore_datachecks(DatacheckEntryList *, const char *); + inline void visible_distance(DatacheckEntryList *, char *, double &, Waypoint *&); }; diff --git a/siteupdate/cplusplus/siteupdate.cpp b/siteupdate/cplusplus/siteupdate.cpp index 33252cf2..fa63f0fc 100644 --- a/siteupdate/cplusplus/siteupdate.cpp +++ b/siteupdate/cplusplus/siteupdate.cpp @@ -930,7 +930,8 @@ int main(int argc, char *argv[]) list> datacheckfps; unordered_set datacheck_always_error ({ "BAD_ANGLE", "DISCONNECTED_ROUTE", "DUPLICATE_LABEL", - "HIDDEN_TERMINUS", "INVALID_FINAL_CHAR", "INVALID_FIRST_CHAR", + "HIDDEN_TERMINUS", "INTERSTATE_NO_HYPHEN", + "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 73f1de2d..a8d99ad9 100755 --- a/siteupdate/python-teresco/siteupdate.py +++ b/siteupdate/python-teresco/siteupdate.py @@ -1559,6 +1559,7 @@ class DatacheckEntry: DUPLICATE_LABEL | HIDDEN_JUNCTION | number of incident edges in TM master graph HIDDEN_TERMINUS | + INTERSTATE_NO_HYPHEN | INVALID_FINAL_CHAR | final character in label INVALID_FIRST_CHAR | first character in label other than * LABEL_INVALID_CHAR | @@ -3543,7 +3544,8 @@ def run(self): lines.pop(0) # ignore header line datacheckfps = [] datacheck_always_error = [ 'BAD_ANGLE', 'DISCONNECTED_ROUTE', 'DUPLICATE_LABEL', - 'HIDDEN_TERMINUS', 'INVALID_FINAL_CHAR', 'INVALID_FIRST_CHAR', + 'HIDDEN_TERMINUS', 'INTERSTATE_NO_HYPHEN', + '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', @@ -3948,10 +3950,6 @@ def run(self): w.label.index('/') > w.label.index('_'): datacheckerrors.append(DatacheckEntry(r,[w.label],'NONTERMINAL_UNDERSCORE')) - # look for I-xx with Bus instead of BL or BS - if re.fullmatch('\*?I\-[0-9]+[EeWwCcNnSs]?[Bb][Uu][Ss].*', w.label) and all_regions[w.route.region][2] == "USA": - datacheckerrors.append(DatacheckEntry(r,[w.label],'BUS_WITH_I')) - # look for labels that look like hidden waypoints but # which aren't hidden if re.fullmatch('X[0-9][0-9][0-9][0-9][0-9][0-9]', w.label): @@ -3961,6 +3959,16 @@ def run(self): if re.fullmatch('^\*?[Oo][lL][dD][0-9].*', w.label): datacheckerrors.append(DatacheckEntry(r,[w.label],'LACKS_GENERIC')) + # USA-only datachecks + if w.route.system.country == "USA" and len(w.label) >= 2: + # look for I-xx with Bus instead of BL or BS + if re.fullmatch('\*?I\-[0-9]+[EeWwCcNnSs]?[Bb][Uu][Ss].*', w.label): + datacheckerrors.append(DatacheckEntry(r,[w.label],'BUS_WITH_I')) + # look for Ixx without hyphen + c = 2 if (w.label.startswith("To") and len(w.label) > 2) else 0 + if w.label[c] == 'I' and w.label[c+1].isdigit(): + datacheckerrors.append(DatacheckEntry(r,[w.label],'INTERSTATE_NO_HYPHEN')) + # look for USxxxA but not USxxxAlt, B/Bus (others?) ##if re.fullmatch('US[0-9]+A.*', w.label) and not re.fullmatch('US[0-9]+Alt.*', w.label) or \ ## re.fullmatch('US[0-9]+B.*', w.label) and \