From 17a3ea880402338420699e03bcb24181e4ff3924 Mon Sep 17 00:00:00 2001 From: Rutger Broekhoff Date: Thu, 2 May 2024 20:27:40 +0200 Subject: Initial commit Based on dc4ba6a --- lib/libtmi8/include/tmi8/kv1_index.hpp | 135 +++ lib/libtmi8/include/tmi8/kv1_lexer.hpp | 46 + lib/libtmi8/include/tmi8/kv1_parser.hpp | 87 ++ lib/libtmi8/include/tmi8/kv1_types.hpp | 1528 ++++++++++++++++++++++++++++++ lib/libtmi8/include/tmi8/kv6_parquet.hpp | 46 + 5 files changed, 1842 insertions(+) create mode 100644 lib/libtmi8/include/tmi8/kv1_index.hpp create mode 100644 lib/libtmi8/include/tmi8/kv1_lexer.hpp create mode 100644 lib/libtmi8/include/tmi8/kv1_parser.hpp create mode 100644 lib/libtmi8/include/tmi8/kv1_types.hpp create mode 100644 lib/libtmi8/include/tmi8/kv6_parquet.hpp (limited to 'lib/libtmi8/include/tmi8') diff --git a/lib/libtmi8/include/tmi8/kv1_index.hpp b/lib/libtmi8/include/tmi8/kv1_index.hpp new file mode 100644 index 0000000..621acf6 --- /dev/null +++ b/lib/libtmi8/include/tmi8/kv1_index.hpp @@ -0,0 +1,135 @@ +// vim:set sw=2 ts=2 sts et: + +#ifndef OEUF_LIBTMI8_KV1_INDEX_HPP +#define OEUF_LIBTMI8_KV1_INDEX_HPP + +#include + +#include + +#include + +struct Kv1Index { + Kv1Records *records; + + explicit Kv1Index(Kv1Records *records); + + std::unordered_map< + Kv1OrganizationalUnit::Key, + Kv1OrganizationalUnit *, + boost::hash> organizational_units; + std::unordered_map< + Kv1HigherOrganizationalUnit::Key, + Kv1HigherOrganizationalUnit *, + boost::hash> higher_organizational_units; + std::unordered_map< + Kv1UserStopPoint::Key, + Kv1UserStopPoint *, + boost::hash> user_stop_points; + std::unordered_map< + Kv1UserStopArea::Key, + Kv1UserStopArea *, + boost::hash> user_stop_areas; + std::unordered_map< + Kv1TimingLink::Key, + Kv1TimingLink *, + boost::hash> timing_links; + std::unordered_map< + Kv1Link::Key, + Kv1Link *, + boost::hash> links; + std::unordered_map< + Kv1Line::Key, + Kv1Line *, + boost::hash> lines; + std::unordered_map< + Kv1Destination::Key, + Kv1Destination *, + boost::hash> destinations; + std::unordered_map< + Kv1JourneyPattern::Key, + Kv1JourneyPattern *, + boost::hash> journey_patterns; + std::unordered_map< + Kv1ConcessionFinancerRelation::Key, + Kv1ConcessionFinancerRelation *, + boost::hash> concession_financer_relations; + std::unordered_map< + Kv1ConcessionArea::Key, + Kv1ConcessionArea *, + boost::hash> concession_areas; + std::unordered_map< + Kv1Financer::Key, + Kv1Financer *, + boost::hash> financers; + std::unordered_map< + Kv1JourneyPatternTimingLink::Key, + Kv1JourneyPatternTimingLink *, + boost::hash> journey_pattern_timing_links; + std::unordered_map< + Kv1Point::Key, + Kv1Point *, + boost::hash> points; + std::unordered_map< + Kv1PointOnLink::Key, + Kv1PointOnLink *, + boost::hash> point_on_links; + std::unordered_map< + Kv1Icon::Key, + Kv1Icon *, + boost::hash> icons; + std::unordered_map< + Kv1Notice::Key, + Kv1Notice *, + boost::hash> notices; + std::unordered_map< + Kv1TimeDemandGroup::Key, + Kv1TimeDemandGroup *, + boost::hash> time_demand_groups; + std::unordered_map< + Kv1TimeDemandGroupRunTime::Key, + Kv1TimeDemandGroupRunTime *, + boost::hash> time_demand_group_run_times; + std::unordered_map< + Kv1PeriodGroup::Key, + Kv1PeriodGroup *, + boost::hash> period_groups; + std::unordered_map< + Kv1SpecificDay::Key, + Kv1SpecificDay *, + boost::hash> specific_days; + std::unordered_map< + Kv1TimetableVersion::Key, + Kv1TimetableVersion *, + boost::hash> timetable_versions; + std::unordered_map< + Kv1PublicJourney::Key, + Kv1PublicJourney *, + boost::hash> public_journeys; + std::unordered_map< + Kv1PeriodGroupValidity::Key, + Kv1PeriodGroupValidity *, + boost::hash> period_group_validities; + std::unordered_map< + Kv1ExceptionalOperatingDay::Key, + Kv1ExceptionalOperatingDay *, + boost::hash> exceptional_operating_days; + std::unordered_map< + Kv1ScheduleVersion::Key, + Kv1ScheduleVersion *, + boost::hash> schedule_versions; + std::unordered_map< + Kv1PublicJourneyPassingTimes::Key, + Kv1PublicJourneyPassingTimes *, + boost::hash> public_journey_passing_times; + std::unordered_map< + Kv1OperatingDay::Key, + Kv1OperatingDay *, + boost::hash> operating_days; + + size_t size() const; +}; + +void kv1LinkRecords(Kv1Index &index); + +#endif // OEUF_LIBTMI8_KV1_INDEX_HPP diff --git a/lib/libtmi8/include/tmi8/kv1_lexer.hpp b/lib/libtmi8/include/tmi8/kv1_lexer.hpp new file mode 100644 index 0000000..df6a57c --- /dev/null +++ b/lib/libtmi8/include/tmi8/kv1_lexer.hpp @@ -0,0 +1,46 @@ +// vim:set sw=2 ts=2 sts et: + +#ifndef OEUF_LIBTMI8_KV1_LEXER_HPP +#define OEUF_LIBTMI8_KV1_LEXER_HPP + +#include +#include +#include +#include +#include +#include + +enum Kv1TokenType { + KV1_TOKEN_CELL, + KV1_TOKEN_ROW_END, +}; +struct Kv1Token { Kv1TokenType type; std::string data; }; + +struct Kv1Lexer { + std::vector errors; + std::vector tokens; + + explicit Kv1Lexer(std::string_view input); + + void lex(); + + private: + // Does not eat newline character. + void eatRestOfLine(); + void lexOptionalHeader(); + void lexOptionalComment(); + + static bool isWhitespace(int c); + + void readQuotedColumn(); + void readUnquotedColumn(); + void lexRow(); + // Returns true when a line ending was consumed. + bool eatWhitespace(); + + std::string_view input; + std::string_view slice; + std::string colbuf; +}; + +#endif // OEUF_LIBTMI8_KV1_LEXER_HPP diff --git a/lib/libtmi8/include/tmi8/kv1_parser.hpp b/lib/libtmi8/include/tmi8/kv1_parser.hpp new file mode 100644 index 0000000..ccd8ec6 --- /dev/null +++ b/lib/libtmi8/include/tmi8/kv1_parser.hpp @@ -0,0 +1,87 @@ +// vim:set sw=2 ts=2 sts et: + +#ifndef OEUF_LIBTMI8_KV1_PARSER_HPP +#define OEUF_LIBTMI8_KV1_PARSER_HPP + +#include +#include +#include +#include +#include + +#include +#include + +struct Kv1Parser { + explicit Kv1Parser(std::vector tokens, Kv1Records &parse_into); + + void parse(); + + private: + // Method pointer to a method of Kv1Parser (i.e. a function that takes + // 'this'; is not static) that takes no arguments and also does not return + // anything. + using ParseFunc = void (Kv1Parser::*)(); + static const std::unordered_map type_parsers; + + bool atEnd() const; + void eatRowEnds(); + const Kv1Token *cur() const; + const std::string *eatCell(std::string_view parsing_what); + std::string parseHeader(); + void eatRestOfRow(); + + void requireString(std::string_view field, bool mandatory, size_t max_length, std::string_view value); + std::optional requireBoolean(std::string_view field, bool mandatory, std::string_view value); + std::optional requireNumber(std::string_view field, bool mandatory, size_t max_digits, std::string_view value); + std::optional requireRgbColor(std::string_view field, bool mandatory, std::string_view value); + std::optional requireRdCoord(std::string_view field, bool mandatory, size_t min_digits, std::string_view value); + + std::string eatString(std::string_view field, bool mandatory, size_t max_length); + std::optional eatBoolean(std::string_view field, bool mandatory); + std::optional eatNumber(std::string_view field, bool mandatory, size_t max_digits); + std::optional eatRgbColor(std::string_view field, bool mandatory); + std::optional eatRdCoord(std::string_view field, bool mandatory, size_t min_digits); + + void parseOrganizationalUnit(); + void parseHigherOrganizationalUnit(); + void parseUserStopPoint(); + void parseUserStopArea(); + void parseTimingLink(); + void parseLink(); + void parseLine(); + void parseDestination(); + void parseJourneyPattern(); + void parseConcessionFinancerRelation(); + void parseConcessionArea(); + void parseFinancer(); + void parseJourneyPatternTimingLink(); + void parsePoint(); + void parsePointOnLink(); + void parseIcon(); + void parseNotice(); + void parseNoticeAssignment(); + void parseTimeDemandGroup(); + void parseTimeDemandGroupRunTime(); + void parsePeriodGroup(); + void parseSpecificDay(); + void parseTimetableVersion(); + void parsePublicJourney(); + void parsePeriodGroupValidity(); + void parseExceptionalOperatingDay(); + void parseScheduleVersion(); + void parsePublicJourneyPassingTimes(); + void parseOperatingDay(); + + size_t pos = 0; + std::vector tokens; + const std::chrono::time_zone *amsterdam = std::chrono::locate_zone("Europe/Amsterdam"); + + public: + std::vector warns; + std::vector global_errors; + std::vector record_errors; + Kv1Records &records; +}; + +#endif // OEUF_LIBTMI8_KV1_PARSER_HPP diff --git a/lib/libtmi8/include/tmi8/kv1_types.hpp b/lib/libtmi8/include/tmi8/kv1_types.hpp new file mode 100644 index 0000000..d4a0760 --- /dev/null +++ b/lib/libtmi8/include/tmi8/kv1_types.hpp @@ -0,0 +1,1528 @@ +// vim:set sw=2 ts=2 sts et: + +#ifndef OEUF_LIBTMI8_KV1_TYPES_HPP +#define OEUF_LIBTMI8_KV1_TYPES_HPP + +#include +#include +#include +#include +#include + +struct Kv1OrganizationalUnit; +struct Kv1HigherOrganizationalUnit; +struct Kv1UserStopPoint; +struct Kv1UserStopArea; +struct Kv1TimingLink; +struct Kv1Link; +struct Kv1Line; +struct Kv1Destination; +struct Kv1JourneyPattern; +struct Kv1ConcessionFinancerRelation; +struct Kv1ConcessionArea; +struct Kv1Financer; +struct Kv1JourneyPatternTimingLink; +struct Kv1Point; +struct Kv1PointOnLink; +struct Kv1Icon; +struct Kv1Notice; +struct Kv1NoticeAssignment; +struct Kv1TimeDemandGroup; +struct Kv1TimeDemandGroupRunTime; +struct Kv1PeriodGroup; +struct Kv1SpecificDay; +struct Kv1TimetableVersion; +struct Kv1PublicJourney; +struct Kv1PeriodGroupValidity; +struct Kv1ExceptionalOperatingDay; +struct Kv1ScheduleVersion; +struct Kv1PublicJourneyPassingTimes; +struct Kv1OperatingDay; + +struct Kv1Records { + std::vector organizational_units; + std::vector higher_organizational_units; + std::vector user_stop_points; + std::vector user_stop_areas; + std::vector timing_links; + std::vector links; + std::vector lines; + std::vector destinations; + std::vector journey_patterns; + std::vector concession_financer_relations; + std::vector concession_areas; + std::vector financers; + std::vector journey_pattern_timing_links; + std::vector points; + std::vector point_on_links; + std::vector icons; + std::vector notices; + std::vector notice_assignments; + std::vector time_demand_groups; + std::vector time_demand_group_run_times; + std::vector period_groups; + std::vector specific_days; + std::vector timetable_versions; + std::vector public_journeys; + std::vector period_group_validities; + std::vector exceptional_operating_days; + std::vector schedule_versions; + std::vector public_journey_passing_times; + std::vector operating_days; + + size_t size() const; +}; + +// These definitions implement TMI8, KV1 Dienstregeling (Timetable) version +// 8.3.0.2 (release), published by BISON on January 8, 2020. +// (Filename: tmi8 dienstregeling (kv 1) v8.3.0.2, release.docx) +// +// This specification and other BISON specifications, as well as other +// supplementary information, can be found on BISON's website: +// https://bison.dova.nu/ +// +// The specification that was used to create these definitions was downloaded +// from the following address: +// https://bison.dova.nu/sites/default/files/bestanden/tmi8_dienstregeling_kv_1_v8.3.0.2_release.pdf +// +// The KV1 table structure and the corresponding documentation describing the +// relevant tables and fields, as presented here, is derived from the original +// specification. Most documentation is a manually translated version of the +// documentation as present in the specification. The specification is licensed +// under CC BY-ND 3.0. The exact text of this license can be found on +// https://creativecommons.org/licenses/by-nd/3.0/nl/. + +// KV1 Table 1: Organizational Unit [ORUN] (MANDATORY) +// +// A collection of trips with the same validity features. An organizational +// unit can be part of a 'higher' unit. +// +// An organizational unit is defined as a unity vor which the planning of trips +// is compiled. When defining the organizational units, it is important that +// all trips within the package have a homogeneous validity (school holidays, +// shopping Sundays, foreign bank holidays). +// +// This table is part of the core data tables, which are common for all KV1 +// variants. +struct Kv1OrganizationalUnit { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. + std::string organizational_unit_code; + + explicit Key(std::string data_owner_code, + std::string organizational_unit_code); + }; + + Key key; + // Mandatory, at most 50 characters. + std::string name; + // Mandatory, at most 10 characters. + std::string organizational_unit_type; + // Optional, at most 255 characters. + std::string description; +}; + +// KV1 Table 2: Higher Organizational Unit [ORUNORUN] (OPTIONAL) +// +// An in the hierarchy higher-ordered organizational unit for the purpose of +// (among others) recording of (deviating) validities on the high level. +// +// This table is part of the core data tables, which are common for all KV1 +// variants. +struct Kv1HigherOrganizationalUnit { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. Parent, higher organizational unit + // that is referred to. + std::string organizational_unit_code_parent; + // Mandatory (key), at most 10 characters. Child, lower organizational unit. + std::string organizational_unit_code_child; + // Mandatory (key), at most 10 characters. [YYYY-MM-DD] Starting date of the + // hierarchical relation (can be a fixed value, e.g. 2006-12-31). + std::chrono::year_month_day valid_from; + + explicit Key(std::string data_owner_code, + std::string organizational_unit_code_parent, + std::string organizational_unit_code_child, + std::chrono::year_month_day valid_from); + }; + + Key key; + + Kv1OrganizationalUnit *p_organizational_unit_parent = nullptr; + Kv1OrganizationalUnit *p_organizational_unit_child = nullptr; +}; + +// KV1 Table 3: User Stop Point [USRSTOP] +// +// Stop or other point (e.g. Bridge, functioning as info for the bridge keeper) +// for which times are recorded in the planning system of the transit operator. +// +// Coordinates of a UserStopPoint are recorded as Point. When defining +// UserStopPoints, it is important that the coordinates can be unambiguously +// and verifiably recorded. For a stop, the coordinates of the stop sign are +// recorded. If there is no stop sign, the end of the bus stop (where the bus +// normally halts) is recorded as the coordinate of the stop. +// +// This table is part of the core data tables, which are common for all KV1 +// variants. +struct Kv1UserStopPoint { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. Stop number in domain of operator. + std::string user_stop_code; + + explicit Key(std::string data_owner_code, + std::string user_stop_code); + }; + + Key key; + // Optional, at most 10 characters. Stop number in domain of integrator, + // (initially) equal to UserStopCode. + std::string timing_point_code; + // Mandatory, at most 5 characters. Boolean indicator whether USRSTOP is used + // as boarding stop, true by default. False for e.g. dummy stop for bridge + // keeper. + bool get_in = true; + // Mandatory, at most 5 characters. Boolean indicator whether USRSTOP is used + // as alighting stop. + bool get_out = false; + // Mandatory, at most 50 characters. Stop name. + std::string name; + // Mandatory, at most 50 characters. Town name. + std::string town; + // Optional, at most 10 characters. Reference to StopArea of which the + // UserStop is part. + std::string user_stop_area_code; + // Mandatory, at most 10 characters. Platform indication/letter. The '-' + // value is used to indication that this is not applicable. + std::string stop_side_code; + // Mandatory, at most 5 digits. Minimal stop duration for boarding and + // alighting, zero by default. In seconds. + double minimal_stop_time_s = 0; + // Optional, at most 3 digits. Length of stop platform. + std::optional stop_side_length; + // Optional, at most 255 characters. + std::string description; + // Mandatory, at most 10 characters. USRSTOPTYPE. Indicates the stop kind. + std::string user_stop_type; + // Optional, at most 30 characters. Nationally unique stop number. + std::string quay_code; + + Kv1UserStopArea *p_user_stop_area = nullptr; + Kv1Point *p_point = nullptr; +}; + +// KV1 Table 4: User Stop Area [USRSTAR] +// +// A StopArea is a collection of stops, which have the same name for passengers +// and logically belong together. (E.g. a bus station of transfer point.) Stops +// lying opposite each other can also form a StopArea. +// +// Used for display of all stops in a stop area on an overview display and for +// announcement of stop names (stops on both sides of the street share the same +// name). +// +// This table is part of the core data tables, which are common for all KV1 +// variants. +struct Kv1UserStopArea { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. Code of StopArea following coding + // of operator, e.g. PlaceCode. + std::string user_stop_area_code; + + explicit Key(std::string data_owner_code, + std::string user_stop_area_code); + }; + + Key key; + // Mandatory, at most 50 characters. + std::string name; + // Mandatory, at most 50 characters. + std::string town; + // Mandatory, at most 255 characters. + std::string description; +}; + +// KV1 Table 5: Timing Link [TILI] +// +// Link between two points which have the feature 'stop' or 'timing point'. A +// Timing Link is set between all stops and other timing points (e.g. for the +// bridge) which make part of a journey pattern. +// +// This table is part of the core data tables, which are common for all KV1 +// variants. +struct Kv1TimingLink { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. Stop number in the domain of + // DataOwner (here: the operator). + std::string user_stop_code_begin; + // Mandatory (key), at most 10 characters. Stop number in the domain of + // DataOwner (here: the operator). + std::string user_stop_code_end; + + explicit Key(std::string data_owner_code, + std::string user_stop_code_begin, + std::string user_stop_code_end); + }; + + Key key; + // Optional, at most 5 digits. Minimal trip time (in seconds). + std::optional minimal_drive_time_s; + // Optional, at most 255 characters. + std::string description; + + Kv1UserStopPoint *p_user_stop_begin = nullptr; + Kv1UserStopPoint *p_user_stop_end = nullptr; +}; + +// KV1 Table 6: Link [LINK] +// +// A route link describes the connection between to points on the physical path +// of a route. +// +// This table is part of the core data tables, which are common for all KV1 +// variants. +struct Kv1Link { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. Stop code in the domain of + // DataOwner (here: the operator). + std::string user_stop_code_begin; + // Mandatory (key), at most 10 characters. Stop code in the domain of + // DataOwner (here: the operator). + std::string user_stop_code_end; + // Mandatory (key), at most 5 characters. Modality for which the distance + // applies, see BISON enumeration E9. + // TODO: Check if BISON enumeration E9 can be put into an enum. + std::string transport_type; + + explicit Key(std::string data_owner_code, + std::string user_stop_code_begin, + std::string user_stop_code_end, + std::string transport_type); + }; + + Key key; + // Mandatory, at most 6 digits. Length of the link (in meters). + double distance = 0; + // Optional, at most 255 characters. + std::string description; + + Kv1UserStopPoint *p_user_stop_begin = nullptr; + Kv1UserStopPoint *p_user_stop_end = nullptr; +}; + +struct RgbColor { + uint8_t r, g, b = 0; +}; + +// KV1 Table 7: Line [LINE] +// +// A line is a collection of routes/journey patterns which is publically known +// under a shared number. +// +// This table is part of the core data tables, which are common for all KV1 +// variants. +struct Kv1Line { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. Unique system line number in the + // domain of DataOwner. + std::string line_planning_number; + + explicit Key(std::string data_owner_code, + std::string line_planning_number); + }; + + Key key; + // Mandatory, at most 4 characters. Line number for the public, incl. S/N + // indications. + std::string line_public_number; + // Mandatory, at most 50 characters. + std::string line_name; + // Mandatory, at most three digits. Should be in the range [0, 400). + // Only processing Connexxion's KV1 export, however, shows us that this range + // constrained is not honored in practice. That is why we also don't care. + short line_ve_tag_number = 0; + // Optional, at most 255 characters. + std::string description; + // Mandatory, at most 5 characters. Modality, see BISON enumeration E9. + // TODO: Check if BISON enumeration E9 can be put into an enum. + std::string transport_type; + // Optional, at most 4 digits. Symbol / image for the line. Reference to ICON + // table. + std::optional line_icon; + // Optional, at most four characters. Background color for the line. + // Hexadecimal representation following RGB coding. Always six characters + // (RRGGBB), only numbers and/or capital letters. + std::optional line_color; + // Optional, at most four characters. Foreground color for the line. + // Hexadecimal representation following RGB coding. Always six characters + // (RRGGBB), only numbers and/or capital letters. + std::optional line_text_color; + + Kv1Icon *p_line_icon = nullptr; +}; + +// KV1 Table 8: Destination [DEST] +// +// A destination shows the place/district/description of the route for the +// passenger. Intermediate and detail destinations of a journey pattern are +// shown under a single desination code, together with the primary destination. +// +// This table is part of the core data tables, which are common for all KV1 +// variants. +struct Kv1Destination { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. + std::string dest_code; + + explicit Key(std::string data_owner_code, + std::string dest_code); + }; + + Key key; + // Mandatory, at most 50 characters. Full destination (e.g. compiled from + // primary, detail or intermediate destination). + std::string dest_name_full; + // Mandatory, at most 24 characters. Primary / intermediate destination in + // enumeration / final destination if 1 line is used. + std::string dest_name_main; + // Optional, at most 24 characters. Detail/secondary or intermediate + // destination for primary desination, final destination (for intermediate + // destination on line 1). + std::string dest_name_detail; + // Mandatory, at most 5 characters. Boolean which indcates whether + // DestNameDetail must always be shown (e.g. because this contains an + // important intermediate destination.) + bool relevant_dest_name_detail = false; + // Mandatory, at most 21 characters. Primary destination in 21 characters. + std::string dest_name_main_21; + // Optional, at most 21 characters. Detail/secondary/intermediate destination + // in 21 characters. + std::string dest_name_detail_21; + // Mandatory, at most 19 characters. Primary destination in 19 characters. + std::string dest_name_main_19; + // Optional, at most 19 characters. Detail/secondary/intermediate destination + // in 19 characters. + std::string dest_name_detail_19; + // Mandatory, at most 16 characters. Primary destination in 16 characters. + std::string dest_name_main_16; + // Optional, at most 16 characters. Detail/secondary/intermediate destination + // in 16 characters. + std::string dest_name_detail_16; + // Optional, at most 4 digits. Symbol/image for the destination. Reference to + // the ICON table. + std::optional dest_icon; + // Optional, at most 6 characters. Background color for the destination. + // Hexadecimal representation following RGB coding. Always six characters + // (RRGGBB), only six digits and/or capital letters. + std::optional dest_color; + // Optional, at most 30 characters (WTF?). Foreground color for the + // destination. Hexadecimal representation following RGB coding. Always six + // characters (RRGGBB), only six digits and/or capital letters. + std::optional dest_text_color; +}; + +// KV1 Table 9: Journey Pattern [JOPA] +// +// The journey pattern describes the route from start to end point as a ordered +// list of stops and links between stops/timing points. +// +// This table is part of the core data tables, which are common for all KV1 +// variants. +struct Kv1JourneyPattern { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. + std::string line_planning_number; + // Mandatory (key), at most 10 characters. + std::string journey_pattern_code; + + explicit Key(std::string data_owner_code, + std::string line_planning_number, + std::string journey_pattern_code); + }; + + Key key; + // Mandatory, at most 10 characters. Refers to a journey pattern type + // (JOPATYPE). + std::string journey_pattern_type; + // Mandatory, at most 1 character. One of [1, 2, A, B]. + char direction = 0; + // Optional, at most 255 characters. + std::string description; + + Kv1Line *p_line = nullptr; +}; + +// KV1 Table 10: Concession Financer Relation [CONFINREL] +// +// Concession financer relation (mainly parcel). Smallest unit for which data +// about a concession can be captured in relation to a financer and/or +// concession. +// +// This table is part of the core data tables, which are common for all KV1 +// variants. +struct Kv1ConcessionFinancerRelation { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. Parcel code. + std::string con_fin_rel_code; + + explicit Key(std::string data_owner_code, + std::string con_fin_rel_code); + }; + + Key key; + // Mandatory, at most 10 characters. Concession code. + std::string concession_area_code; + // Optional, at most 10 characters. Code of financer/client of the parcel. + std::string financer_code; + + Kv1ConcessionArea *p_concession_area = nullptr; + Kv1Financer *p_financer = nullptr; +}; + +// KV1 Table 11: Concession Area [CONAREA] +// +// Concession (area). +// +// This table is part of the core data tables, which are common for all KV1 +// variants. +struct Kv1ConcessionArea { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. Code of the concession. + std::string concession_area_code; + + explicit Key(std::string data_owner_code, + std::string concession_area_code); + }; + + Key key; + // Mandatory, at most 255 characters. + std::string description; +}; + +// KV1 Table 12: Financer [FINANCER] (OPTIONAL) +// +// Financer of a parcel. +// +// This table is part of the core data tables, which are common for all KV1 +// variants. +struct Kv1Financer { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. + std::string financer_code; + + explicit Key(std::string data_owner_code, + std::string financer_code); + }; + + Key key; + // Mandatory, at most 255 characters. + std::string description; +}; + +// KV1 Table 13: Journey Pattern Timing Link [JOPATILI] +// +// Compilation of journey pattern from logical links (between pairs of +// stops/timing points). Features such as the destination code, the public line +// number, the concession financer relation (parcel) and product formula are +// set per connection. Moreover, a color and/or image linked to the line +// destination and the use of the (first) stop as boarding/alighting stop can +// be set per link. +// +// Timing Link: A timing link is a stop, set by the transit operator, where a +// bus / public transit vehicle may never depart earlier than set in the +// timetable. +// +// A logical link may never occur more than once in a journey pattern. +// Therefore, the combination of LinePlanningNumber, JourneyPatternCode, +// UserStopCodeBegin and UserStopCodeEnd must be unique in JOPATILI. +// +// The value of GetIn and GetOut are normally copied from the corresponding +// stop in the USRSTOP table, but can be overruled per journey pattern if so +// desired. +// +// A Icon or (Text)Color set here overrules the general value of the +// corresponding line (Line) or destination (Destination). +// +// A value of ShowFlexibleTrip or ProductFormulaType in PUJO or PUJOPASS +// overrules the value in JOPATILI. +// +// This table is part of the core data tables, which are common for all KV1 +// variants. +struct Kv1JourneyPatternTimingLink { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. + std::string line_planning_number; + // Mandatory (key), at most 10 characters. + std::string journey_pattern_code; + // Mandatory (key), at most 3 digits. + short timing_link_order = 0; + + explicit Key(std::string data_owner_code, + std::string line_planning_number, + std::string journey_pattern_code, + short timing_link_order); + }; + + Key key; + // Mandatory, at most 10 characters. Stop number in the domain of the + // DataOwner (here: the transit operator). + std::string user_stop_code_begin; + // Mandatory, at most 10 characters. Stop number in the domain of the + // DataOwner (here: the transit operator). + std::string user_stop_code_end; + // Mandatory, at most 10 characters. Concession financer relation / parcel + // (smallest unit). + std::string con_fin_rel_code; + // Mandatory, at most 10 characters. The destination (incl. intermediat + // destinations) as these are shown at the first stop of the journey pattern + // link. + std::string dest_code; + // Mandatory, at most 5 characters. Boolean which indicates whether the first + // stop of the connection is a timing stop. Indicator is at least "true" at + // first stop of a line and at waiting stops. + bool is_timing_stop = false; + // Optional, at most 4 characters. Public line number which must be shown on + // displays from the first stop of the journey pattern link (e.g. Line number + // + S). This is important when a deviating public line number applies from a + // certain point on forward. Normally, the public line number of the + // corresponding line is shown. + std::string display_public_line; + // Optional, at most 4 digits. Enumeration E10 (see section 2.5). A public + // transit service which distinguishes itself by a set of unique features, + // that is offered to the passenger as distinct (a marketing aspect). + // TODO: Check if we can turn BISON enumeration E10 into an enum + std::optional product_formula_type; + // Mandatory, at most 5 characters. Boolean indicator whether UserStopBegin + // is used as a boarding stop in this journey pattern. Usually equal to the + // value of the corresponding USRSTOP. + bool get_in = false; + // Mandatory, at most 5 characters. Boolean indicator whether UserStopBegin + // is used as an alighting stop in this journey pattern. Usually equal to the + // value of the corresponding USRSTOP. + bool get_out = false; + // Optional, at most 8 characters. Indicates whether the transit operator + // wants a not explicitly planned trip (i.e. a trip that only operates after + // reservation such as a 'call bus' (belbus), 'line taxi' (lijntaxi) etc.) to + // be shown on displays. Values according enumeration E21: TRUE (always), + // FALSE (never), REALTIME (only when tracking trip). + // TODO: Check if we can turn BISON enumeration E21 into an enum + std::string show_flexible_trip; + // Optional, at most 4 digits. Symbol / image for display of the line + // destination at the journey stop passing. Reference to the ICON table. + std::optional line_dest_icon; + // Optional, at most 6 characters. Background color for display of the line + // destination at a journey stop passing. Hexadecimal representation + // following RGB coding. Always six characters (RRGGBB), only numbers and/or + // capital letters. + std::optional line_dest_color; + // Optional, at most 6 characters. Foreground color for display of the line + // destination at a journey stop passing. Hexadecimal representation + // following RGB coding. Always six characters (RRGGBB), only numbers and/or + // capital letters. + std::optional line_dest_text_color; + + Kv1Line *p_line = nullptr; + Kv1JourneyPattern *p_journey_pattern = nullptr; + Kv1UserStopPoint *p_user_stop_begin = nullptr; + Kv1UserStopPoint *p_user_stop_end = nullptr; + Kv1ConcessionFinancerRelation *p_con_fin_rel = nullptr; + Kv1Destination *p_dest = nullptr; + Kv1Icon *p_line_dest_icon = nullptr; +}; + +// KV1 Table 14: Point [POINT] +// +// A point is the smallest location which can be reffered to within the public +// transit network. Every stop (USRSTOP) is a point. +// +// This table is part of the core data tables, which are common for all KV1 +// variants. +struct Kv1Point { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. + std::string point_code; + + explicit Key(std::string data_owner_code, + std::string point_code); + }; + + Key key; + // Mandatory, at most 10 characters. Refers to the POINTTYPE table. + std::string point_type; + // Mandatory, at most 10 characters. Refers to the GEOSYSTYPE table. Only + // allowed to have the value "RD" (rijkdsdriehoekstelsel; the national Dutch + // coordinate system). + std::string coordinate_system_type; + // Mandatory, at most 15 characters. X position in the RD coordinate system, + // in meters (at least 6 digits). + double location_x_ew = 0; + // Mandatory, at most 15 characters. Y position in the RD coordinate system, + // in meters (at least 6 digits). + double location_y_ns = 0; + // Optional, at most 15 characters. + // NOTE: the standart (presumeably wrongly) indicates this field as having + // alphanumeric contents. + std::optional location_z; + // Optional, at most 255 characters. + std::string description; +}; + +// KV1 Table 15: Point on Link [POOL] +// +// A point that is used to geographically describe the trajectory between two +// stops. +// +// This table is part of the core data tables, which are common for all KV1 +// variants. +struct Kv1PointOnLink { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. Stop number in the domain of the + // DataOwner (here: transit operator). + std::string user_stop_code_begin; + // Mandatory (key), at most 10 characters. Stop number in the domain of the + // DataOwner (here: transit operator). + std::string user_stop_code_end; + // Mandatory (key), at most 10 characters. Code from the road manager for KAR + // points. For curve points of the DataOwner (often the transit operator). + std::string point_data_owner_code; + // Mandatory (key), at most 10 charcters. + std::string point_code; + // Mandatory (key), at most 5 characters. Modality for which the distance + // applies, see BISON enumeration E9. + std::string transport_type; + + explicit Key(std::string data_owner_code, + std::string user_stop_code_begin, + std::string user_stop_code_end, + std::string point_data_owner_code, + std::string point_code, + std::string transport_type); + }; + + Key key; + // Mandatory, at most 5 digits. Distance in meters relative to the start of + // the link. + double distance_since_start_of_link = 0; + // Optional, at most 4 digits. Crossing speed for a public transit vehicle + // from the previous point (on a link) in m/s. + std::optional segment_speed_mps = 0; + // Optional, at most 4 digits. Comfort speed for a public transit vehicle on + // the curve point. + std::optional local_point_speed_mps = 0; + // Optional, at most 255 characters. + std::string description; + + Kv1UserStopPoint *p_user_stop_begin = nullptr; + Kv1UserStopPoint *p_user_stop_end = nullptr; + Kv1Point *p_point = nullptr; +}; + +// KV1 Table 16: Icon [ICON] +// +// Table with images which can be referred to from DEST.DestIcon, LINE.LineIcon +// and JOPATILI.LineDestIcon to load the correct image. +// +// This table is part of the core data tables, which are common for all KV1 +// variants. +struct Kv1Icon { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 4 digits. Reference from other tables for the + // requested image. + short icon_number = 0; + + explicit Key(std::string data_owner_code, + short icon_number); + }; + + Key key; + // Mandatory, at most 1024 characters. Absolute URI to a publically available + // location from which the image can be loaded. The extension of the file + // indicates the image type. + // Supported file types are: GIF (.gif), JPEG (.jpg, .jpeg), + // PNG (.png), SVG (.svg) + // Supported protocols are: HTTP, HTTPS, FTP + // Prefer to not use any capital letters. Examples: + // - http://bison.dova.nu/images/logo.png + // - https://bison.dova.nu/images/logo.png + // - ftp://ftp.dova.nu/images/logo.png + std::string icon_uri; +}; + +// KV1 Table 17: Notice [NOTICE] (OPTIONAL) +// +// A (reusable) text with supplementary information about exceptions / +// clarifications for a line, journey pattern etc. +// +// Usage is optional; when there are no clarifying texts, the NOTICE table does +// not need to be provided in a KV1 set. +// +// This table is part of the core data tables, which are common for all KV1 +// variants. +struct Kv1Notice { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 20 characters. Identification of Notice (remark, + // clarifying text). + std::string notice_code; + + explicit Key(std::string data_owner_code, + std::string notice_code); + }; + + Key key; + // Mandatory, at most 1024 characters. Content, text. Contains contact + // information such as telephone number, web address and reservation time for + // 'call buses' (belbussen) and other demand-based transit. + std::string notice_content; +}; + +// KV1 Table 18: Notice Assignment [NTCASSGNM] (OPTIONAL) +// +// Linking table in which Notice (remark, clarfiying text) is assigned to a +// line, journey pattern, stops within a journey pattern, journey etc. Notice +// Assignment contains all logical key elements of the corresponding objects to +// which a Notice can be assigned. +// +// Different attributes are required for the Notice Assignment, depending on +// the type object to which the Notice is assigned. In the following table +// structure, this is indicated as 'Only relevant for ...'. This means that +// fields for other object types in the Notice Assignment can be ignored. +// +// Moreover, it can also occur that not all key fields of the linked table are +// of interest (content-wise) for recording the Notice. +// +// Both matters are summarised in this overview: +// +// -------------------------------------------------------- +// AssignedObject PUJO PUJOPASS LINE JOPATILI +// -------------------------------------------------------- +// DataOwnerCode........... x ...... x ...... x ..... x ... +// TimetableVersionCode ... o ............................. +// OrganizationalUnitCode . o ...... o .................... +// ScheduleCode .................... o .................... +// ScheduleTypeCode ................ o .................... +// PeriodGroupCode ........ o ............................. +// SpecificDayCode ........ o ............................. +// DayType ................ o ............................. +// LinePlanningNumber ..... x ...... x ...... x ..... x ... +// JourneyNumber .......... x ...... x .................... +// StopOrder ....................... o .............. o ... +// JourneyPatternCode ............................... x ... +// TimingLinkOrder .................................. o ... +// UserStopCode .................... o .............. o ... +// -------------------------------------------------------- +// +// Legend: +// x - Mandatory. The Notice for this object type is always depndent on the +// value of the attribute. +// o - Optional. The Notice can be independent of the value of this +// attribute for this object type. +// - Attribute is no key field for this object type and can be +// ignored when processed. +// +// Usage of Notice Assignment is optional in KV1. If there are no clarifying +// texts, then the Notice Assignment table is not required to be present in the +// provided KV1 set. +// +// This table is part of the core data tables, which are common for all KV1 +// variants. +struct Kv1NoticeAssignment { + // Mandatory, at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory, at most 20 characters. Notice that is assigned. + std::string notice_code; + // Mandatory, at most 8 characters. Object type to which Notice is assigned. + std::string assigned_object; + // Optional, at most 10 characters. Only relevant for PUJO. + std::string timetable_version_code; + // Optional, at most 10 characters. Only relevant for PUJO and PUJOPASS. + std::string organizational_unit_code; + // Optional, at most 10 characters. Only relevant for PUJOPASS. + std::string schedule_code; + // Optional, at most 10 characters. Only relevant for PUJOPASS. + std::string schedule_type_code; + // Optional, at most 10 characters. Only relevant for PUJO. + std::string period_group_code; + // Optional, at most 10 characters. Only relevant for PUJO. + std::string specific_day_code; + // Optional, at most 10 characters. Only relevant for PUJO. + // [0|1][0|2][0|3][0|4][0|5][0|6][0|7] for Mon, Tue, Wed, Thu, Fri, Sat, Sun. + // E.g. 1234500 means Mon, Tue, Wed, Thu, Fri but not Sat, Sun. + std::string day_type; + // Mandatory, at most 10 characters. Mandatory for all object types. + std::string line_planning_number; + // Optional (for all object types except PUJO and PUJOPASS), at most 6 + // digits. Only relevant for PUJO and PUJOPASS. Must be in the range + // [0-1000000). + std::optional journey_number; + // Optional, at most 4 digits. Only relevant for PUJOPASS and JOPATILI. + std::optional stop_order; + // Optional (for all object types except JOPATILI), at most 4 digits. Only + // relevant for JOPATILI. + std::string journey_pattern_code; + // Optional (at most 3 digits). Only relevant for JOPATILI. + std::optional timing_link_order; + // Optional (at most 10 characters). Only relevant for PUJOPASS and JOPATILI. + // For JOPATILI, this correspond to the first stop of the link. + std::string user_stop_code; + + Kv1Notice *p_notice = nullptr; +}; + +// KV1 Table 19: Time Demand Group [TIMDEMGRP] +// +// A time demand group is a grouping of the run time distribution from stop to +// stop, for a journey pattern (from start to end point). +// +// This table is part of the KV1 variant "validities and time demand groups". +struct Kv1TimeDemandGroup { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. + std::string line_planning_number; + // Mandatory (key), at most 10 characters. Refers to the JOPATILI table. + std::string journey_pattern_code; + // Mandatory (key), at most 10 characters. Defines the code for the time + // demand group. (NOTE: this is not entirely made clear by the specification. + // This claim must be verified.) + std::string time_demand_group_code; + + explicit Key(std::string data_owner_code, + std::string line_planning_number, + std::string journey_pattern_code, + std::string time_demand_group_code); + }; + + Key key; + + Kv1Line *p_line = nullptr; + Kv1JourneyPattern *p_journey_pattern = nullptr; +}; + +// KV1 Table 20: Time Demand Group Run Time [TIMDEMRNT] +// +// The run time structure/distribution for all timing links of a journey +// pattern or a time demand group. +// +// Optional run time elements are, when these are present, used to more +// accurately calculate expected departure times based on punctuality +// deviations. +// +// This table is part of the KV1 variant "validities and time demand groups". +struct Kv1TimeDemandGroupRunTime { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. + std::string line_planning_number; + // Mandatory (key), at most 10 characters. Refers to the JOPATILI table. + std::string journey_pattern_code; + // Mandatory (key), at most 10 characters. Refers to the TIMDEMGRP table. + std::string time_demand_group_code; + // Mandatory (key), at most 3 digits. Reference number of a link within the + // journey pattern (a link can occur more than once within a journey + // pattern). + short timing_link_order = 0; + + explicit Key(std::string data_owner_code, + std::string line_planning_number, + std::string journey_pattern_code, + std::string time_demand_group_code, + short timing_link_order); + }; + + Key key; + // Mandatory, at most 10 characters. Refers to the first stop of the link. + std::string user_stop_code_begin; + // Mandatory, at most 10 characters. Refers to the last stop of the link. + std::string user_stop_code_end; + // Mandatory, at most 5 digits. Planned total run time on link for time + // demand group: (Departure time end stop - departure time begin stop) + // corresponding to the time demand group. In seconds. + double total_drive_time_s = 0; + // Mandatory, at most 5 digits. Planned minimal run time on link for time + // demand group. Often calculated as: (Arrival time end stop - arrival time + // begin stop) corresponding to the time demand group. In seconds. + double drive_time_s = 0; + // Optional, at most 5 digits. Expected/planned delay/congestion on link for + // time demand group. In seconds. + std::optional expected_delay_s; + // Optional, at most 5 digits. Layover/catch-up time. Gives play in the + // timetable. In seconds. + // LayOverTime = TotDriveTime - DriveTime + ExpectedDelay - StopWaitTime. + std::optional layover_time; + // Mandatory, at most 5 digits. Planned stop waiting time at the final stop + // of the link for the time demand group. Determined based on the difference + // between the departure time and arrival time at this stop. Is zero when no + // waiting time is planned for this stop. In seconds. + double stop_wait_time = 0; + // Optional, at most 5 digits. Planned minimal stop time for + // boarding/alighting of passengers at the final stop of the link for the + // time demand group. Application: at hub stops with a planned waiting time, + // the difference between the planned waiting time and the minimum stop time + // is the layover/catch-up time. In seconds. + std::optional minimum_stop_time; + + Kv1Line *p_line = nullptr; + Kv1UserStopPoint *p_user_stop_begin = nullptr; + Kv1UserStopPoint *p_user_stop_end = nullptr; + Kv1JourneyPattern *p_journey_pattern = nullptr; + Kv1TimeDemandGroup *p_time_demand_group = nullptr; + Kv1JourneyPatternTimingLink *p_journey_pattern_timing_link = nullptr; +}; + +// KV1 Table 21: Period Group [PEGR] +// +// Period group is an indication of a 'homogeneous period' during the year, +// i.e. a period in which the schedule has the same composition w.r.t. +// frequencies and run times. +// +// This table is part of the KV1 variant "validities and time demand groups". +struct Kv1PeriodGroup { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. + std::string period_group_code; + + explicit Key(std::string data_owner_code, + std::string period_group_code); + }; + + Key key; + // Optional, at most 255 characters. + std::string description; +}; + +// KV1 Table 22: Specific Day [SPECDAY] +// +// A specific day is a feature of a day for which a deviating service level is +// provided, respective to a normal day of the week. +// +// E.g. shopping Sundays (koopzondagen, if not every Sunday), New Year's Eve +// (oudejaarsdag), foreign bank holidays (as applicable). +// +// This table is part of the KV1 variant "validities and time demand groups". +struct Kv1SpecificDay { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. Default: "NORMAL". + std::string specific_day_code; + + explicit Key(std::string data_owner_code, + std::string specific_day_code); + }; + + Key key; + // Mandatory, at most 50 characters. + std::string name; + // Optional, at most 255 characters. + std::string description; +}; + +// KV1 Table 23: Timetable Version [TIVE] +// +// A timetable version budles all planned activities for an organizational +// unit. For the public schedule, these are trips, routes, run times etc. +// +// When processing a new Timetable Version, it is checked if another TIVE with +// the same key has already been processed. If this is the case, ValidFrom must +// be equal to the starting date of the previously provided set. The new set +// replaces the older one. A package with a new starting date is only processed +// if another TimetableVersionCode is used. +// +// This table is part of the KV1 variant "validities and time demand groups". +struct Kv1TimetableVersion { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. + std::string organizational_unit_code; + // Mandatory (key), at most 10 characters. + std::string timetable_version_code; + // Mandatory (key), at most 10 charactes. + std::string period_group_code; + // Mandatory (key), at most 10 characters. Default: "NORMAL". + std::string specific_day_code; + + explicit Key(std::string data_owner_code, + std::string organizational_unit_code, + std::string timetable_version_code, + std::string period_group_code, + std::string specific_day_code); + }; + + Key key; + // Mandatory, at most 10 characters. Datum on which the timetable goes into + // effect, following the YYYY-MM-DD format. + std::chrono::year_month_day valid_from; + // Mandatory, at most 10 characters. Value: "PUBT". + std::string timetable_version_type; + // Optional, at most 10 characters. Datum on which the timetable goes out of + // effect, following the YYYY-MM-DD format. + std::optional valid_thru; + // Optional, at most 255 characters. Should be null/empty. + std::string description; + + Kv1OrganizationalUnit *p_organizational_unit = nullptr; + Kv1PeriodGroup *p_period_group = nullptr; + Kv1SpecificDay *p_specific_day = nullptr; +}; + +// KV1 Table 24: Public Journey [PUJO] +// +// Public journeys are journeys that are operated by a public transit +// organization and are accessible to the passenger. +// +// Business rules: +// - If ShowFlexibleTrip or ProductFormulaType is set in a record of this +// table, this takes precedence over the value as in the corresponding +// JOPATILI entry. +// +// This table is part of the KV1 variant "validities and time demand groups". +struct Kv1PublicJourney { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. + std::string timetable_version_code; + // Mandatory (key), at most 10 characters. + std::string organizational_unit_code; + // Mandatory (key), at most 10 characters. + std::string period_group_code; + // Mandatory (key), at most 10 characters. + std::string specific_day_code; + // Mandatory (key), at most 7 characters. + // [0|1][0|2][0|3][0|4][0|5][0|6][0|7] for Mon, Tue, Wed, Thu, Fri, Sat, Sun. + // E.g. 1234500 means Mon, Tue, Wed, Thu, Fri but not Sat, Sun. + // TODO: See if we can make this into a more concrete type + std::string day_type; + // Mandatory (key), at most 10 characters. + std::string line_planning_number; + // Mandatory (key), at most 6 digits. Must be in the range [0-1000000). + int journey_number = 0; + + explicit Key(std::string data_owner_code, + std::string timetable_version_code, + std::string organizational_unit_code, + std::string period_group_code, + std::string specific_day_code, + std::string day_type, + std::string line_planning_number, + int journey_number); + }; + + Key key; + // Mandatory, at most 10 characters. + std::string time_demand_group_code; + // Mandatory, at most 10 characters. + std::string journey_pattern_code; + // Mandatory, at most 8 characters. Format: "HH:MM:SS". + std::chrono::hh_mm_ss departure_time; + // Mandatory, at most 13 characters. Values as in BISON enumeration E3. + // Allowed are: "ACCESSIBLE", "NOTACCESSIBLE" and "UNKNOWN". + // TODO: See if we can fit BISON enumeration E3 into an enum + std::string wheelchair_accessible; + // Mandatory, at most 5 characters. Boolean. Value "true": journey is + // operator by DataOwner. Value "false": journey is operator by a different + // DataOwner. Indicator is meant for a line that is operated jointly by + // multiple transit operators. The indicator is used to be able to match the + // journey operation (KV6, KV19 etc.); only journeys for which the indicator + // is "true" can be expected to have corresponding current/real-time + // information, although "true" doesn't necessarily mean that this + // current/real-time information will (always) become available. + bool data_owner_is_operator = false; + // Mandatory, at most 5 characters. Boolean. Indicates whether + // current/real-time journey information may be expected for the + // corresponding journey ("true" or "false"). + bool planned_monitored = false; + // Optional, at most 4 digits. BISON enumeration E10. Intended to allow + // capturing transit mode features at the journey level. + // TODO: See if we can make BISON enumeration E10 into an enum + std::optional product_formula_type; + // Optional, at most 8 characters. Indicates whether the transit operator + // wants that a not-explicitly planned trip (i.e. a journey that only runs on + // reservation, e.g. 'call bus' (belbus), 'line taxi' (lijntaxi) etc.) to be + // shown on displays. Values following BISON enumeration E21: TRUE (always), + // FALSE (never), REALTIME (only when journey is tracked). + // TODO: See if we can make BISON enumeration E21 into an enum + std::string show_flexible_trip; + + Kv1TimetableVersion *p_timetable_version = nullptr; + Kv1OrganizationalUnit *p_organizational_unit = nullptr; + Kv1PeriodGroup *p_period_group = nullptr; + Kv1SpecificDay *p_specific_day = nullptr; + Kv1Line *p_line = nullptr; + Kv1TimeDemandGroup *p_time_demand_group = nullptr; + Kv1JourneyPattern *p_journey_pattern = nullptr; +}; + +// KV1 Table 25: Period Group Validity [PEGRVAL] +// +// Validities (multiple from-thru data) of a period group. +// +// This table is part of the KV1 variant "validities and time demand groups". +struct Kv1PeriodGroupValidity { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. + std::string organizational_unit_code; + // Mandatory (key), at most 10 characters. + std::string period_group_code; + // Mandatory (key), at most 10 characters. Date of the start of the validity + // period. Format: "YYYY-MM-DD". + std::chrono::year_month_day valid_from; + + explicit Key(std::string data_owner_code, + std::string organizational_unit_code, + std::string period_group_code, + std::chrono::year_month_day valid_from); + }; + + Key key; + // Mandatory, at most 10 characters. Date of the end of the validity period. + // Format: "YYYY-MM-DD". + std::chrono::year_month_day valid_thru; + + Kv1OrganizationalUnit *p_organizational_unit = nullptr; + Kv1PeriodGroup *p_period_group = nullptr; +}; + +// KV1 Table 26: Exceptional Operating Day [EXCOPDAY] +// +// Contains exceptional validity dates, for which the service runs following a +// different day type (such as another day of the week or a different period). +// +// This table is part of the KV1 variant "validities and time demand groups". +struct Kv1ExceptionalOperatingDay { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. Organization unit for which an + // exceptional day validity applies. + std::string organizational_unit_code; + // Mandatory (key), at most 23 characters. Date (+ time) for which the + // exceptional validity applies. Format: "YYYYMMDDThh:mm:ssTZD". + std::chrono::sys_seconds valid_date; + + explicit Key(std::string data_owner_code, + std::string organizational_unit_code, + std::chrono::sys_seconds valid_date); + }; + + Key key; + // Mandatory, at most 7 characters. The exceptional day type that applies on + // a calendar day: [0|1][0|2][0|3][0|4][0|5][0|6][0|7] for Mon, Tue, Wed, + // Thu, Fri, Sat. + // E.g. 1234500 means Mon, Tue, Wed, Thu, Fri but not Sat, Sun. + // TODO: See if we can make this into a more concrete type + std::string day_type_as_on; + // Mandatory, at most 10 characters. Specific day service level to which the + // exceptional day validity refers. + std::string specific_day_code; + // Optional, at most 10 characters. An exceptional day validity can be + // related to the service level of another period (e.g. the school holiday + // schedule). This exceptional period reference is set here. + // + // E.g. on Good Friday or the day after Ascension day, transit runs according + // to the holiday season schedule, while transit runs following the winter + // package in the surrounding days. + std::string period_group_code; + // Optional, at most 255 characters. + std::string description; + + Kv1OrganizationalUnit *p_organizational_unit = nullptr; + Kv1SpecificDay *p_specific_day = nullptr; + Kv1PeriodGroup *p_period_group = nullptr; +}; + +// KV1 Table 27: Schedule Version [SCHEDVERS] +// +// A schedule version bundles the planned activities for an organisation unit +// per day type. The journeys with passing times and corresponding routes are +// for the public timetable. +// +// When processing a new Schedule Version, it is checked if another SCHEDVERS +// with the same key has already been processed. If this is the case, ValidFrom +// must be equal to the starting date of the previously provided set. The new +// set replaces the older one. A package with a new starting date is only +// processed if another Schedule Code is used. +// +// This table is part of the KV1 variant "schedules and passing times". +struct Kv1ScheduleVersion { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. + std::string organizational_unit_code; + // Mandatory (key), at most 10 characters. A unique code in combination with + // the ScheduleTypeCode of the package within the ORUN. + std::string schedule_code; + // Mandatory (key), at most 10 characters. Code for the Schedule Type (Day Type). + std::string schedule_type_code; + + explicit Key(std::string data_owner_code, + std::string organizational_unit_code, + std::string schedule_code, + std::string schedule_type_code); + }; + + Key key; + // Mandatory, at most 10 characters. Date on which the schedule goes into + // effect. Format: "YYYY-MM-DD". + std::chrono::year_month_day valid_from; + // Optional, at most 10 characters. Date on which the schedule goes out of + // effect. Format: "YYYY-MM-DD". + std::optional valid_thru; + // Optional, at most 255 characters. Should be empty/null. + std::string description; + + Kv1OrganizationalUnit *p_organizational_unit = nullptr; +}; + +// KV1 Table 28: Public Journey Passing Times [PUJOPASS] +// +// Public journey with arrival and departure times at all stops (and other +// timing points). +// +// Business rules: +// - If ShowFlexibleTrip or ProductFormulaType is set here, then this takes +// precedence over the value in the corresponding JOPATILI record. +// - All stop passings of a public journey refer to the same journey pattern +// (JOPA)! +// +// This table is part of the KV1 variant "schedules and passing times". +struct Kv1PublicJourneyPassingTimes { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. + std::string organizational_unit_code; + // Mandatory (key), at most 10 characters. A unique code in combination with + // the ScheduleTypeCode of the package within the ORUN. + std::string schedule_code; + // Mandatory (key), at most 10 characters. Code for the Schedule Type (e.g. + // Day Type). + std::string schedule_type_code; + // Mandatory (key), at most 10 characters. + std::string line_planning_number; + // Mandatory (key), at most 6 digits. Must be in the range [0-1000000). + int journey_number = 0; + // Mandatory (key), at most 4 digits. + short stop_order = 0; + + explicit Key(std::string data_owner_code, + std::string organizational_unit_code, + std::string schedule_code, + std::string schedule_type_code, + std::string line_planning_number, + int journey_number, + short stop_order); + }; + + Key key; + // Mandatory, at most 10 characters. + std::string journey_pattern_code; + // Mandatory, at most 10 characters. + std::string user_stop_code; + // Mandatory (except for the first stop of a journey), at most 8 digits. Not + // compulsory for the first stop of a journey. Format: "HH:MM:SS". + std::optional> target_arrival_time; + // Mandatory (expect for the last stop of a journey), at most 8 digits. Not + // compulsory for the last stop of a journey. Format: "HH:MM:SS". + std::optional> target_departure_time; + // Mandatory, at most 13 characters. Values as in BISON enumeration E3. + // Allowed are: "ACCESSIBLE", "NOTACCESSIBLE" and "UNKNOWN". + // TODO: See if we can fit BISON enumeration E3 into an enum + std::string wheelchair_accessible; + // Mandatory, at most 5 characters. Boolean. Value "true": journey is + // operator by DataOwner. Value "false": journey is operator by a different + // DataOwner. Indicator is meant for a line that is operated jointly by + // multiple transit operators. The indicator is used to be able to match the + // journey operation (KV6, KV19 etc.); only journeys for which the indicator + // is "true" can be expected to have corresponding current/real-time + // information, although "true" doesn't necessarily mean that this + // current/real-time information will (always) become available. + bool data_owner_is_operator = false; + // Mandatory, at most 5 characters. Boolean. Indicates whether + // current/real-time journey information may be expected for the + // corresponding journey ("true" or "false"). + bool planned_monitored = false; + // Optional, at most 4 digits. BISON enumeration E10. Intended to allow + // capturing transit mode features at the journey level. + // TODO: See if we can make BISON enumeration E10 into an enum + std::optional product_formula_type; + // Optional, at most 8 characters. Indicates whether the transit operator + // wants that a not-explicitly planned trip (i.e. a journey that only runs on + // reservation, e.g. 'call bus' (belbus), 'line taxi' (lijntaxi) etc.) to be + // shown on displays. Values following BISON enumeration E21: TRUE (always), + // FALSE (never), REALTIME (only when journey is tracked). + // TODO: See if we can make BISON enumeration E21 into an enum + std::string show_flexible_trip; + + Kv1OrganizationalUnit *p_organizational_unit = nullptr; + Kv1ScheduleVersion *p_schedule_version = nullptr; + Kv1Line *p_line = nullptr; + Kv1JourneyPattern *p_journey_pattern = nullptr; + Kv1UserStopPoint *p_user_stop = nullptr; +}; + +// KV1 Table 29: Operating Day [OPERDAY] +// +// Contains the operational calendar. Which package (schedule version) applies +// is specified per day, per organisation unit. +// +// This table is part of the KV1 variant "schedules and passing times". +struct Kv1OperatingDay { + struct Key { + // Mandatory (key), at most 10 characters. Transport operator (from list as + // defined in BISON enumeration E1). + std::string data_owner_code; + // Mandatory (key), at most 10 characters. + std::string organizational_unit_code; + // Mandatory (key), at most 10 characters. + std::string schedule_code; + // Mandatory (key), at most 10 characters. + std::string schedule_type_code; + // Mandatory (key), at most 10 characters. Date on which the package + // (schedule version) applies. Format: "YYYY-MM-DD". + std::chrono::year_month_day valid_date; + + explicit Key(std::string data_owner_code, + std::string organizational_unit_code, + std::string schedule_code, + std::string schedule_type_code, + std::chrono::year_month_day valid_date); + }; + + Key key; + // Optional, at most 255 characters. + std::string description; + + Kv1OrganizationalUnit *p_organizational_unit = nullptr; + Kv1ScheduleVersion *p_schedule_version = nullptr; +}; + +bool operator==(const Kv1OrganizationalUnit::Key &a, const Kv1OrganizationalUnit::Key &b); +bool operator==(const Kv1HigherOrganizationalUnit::Key &a, const Kv1HigherOrganizationalUnit::Key &b); +bool operator==(const Kv1UserStopPoint::Key &a, const Kv1UserStopPoint::Key &b); +bool operator==(const Kv1UserStopArea::Key &a, const Kv1UserStopArea::Key &b); +bool operator==(const Kv1TimingLink::Key &a, const Kv1TimingLink::Key &b); +bool operator==(const Kv1Link::Key &a, const Kv1Link::Key &b); +bool operator==(const Kv1Line::Key &a, const Kv1Line::Key &b); +bool operator==(const Kv1Destination::Key &a, const Kv1Destination::Key &b); +bool operator==(const Kv1JourneyPattern::Key &a, const Kv1JourneyPattern::Key &b); +bool operator==(const Kv1ConcessionFinancerRelation::Key &a, const Kv1ConcessionFinancerRelation::Key &b); +bool operator==(const Kv1ConcessionArea::Key &a, const Kv1ConcessionArea::Key &b); +bool operator==(const Kv1Financer::Key &a, const Kv1Financer::Key &b); +bool operator==(const Kv1JourneyPatternTimingLink::Key &a, const Kv1JourneyPatternTimingLink::Key &b); +bool operator==(const Kv1Point::Key &a, const Kv1Point::Key &b); +bool operator==(const Kv1PointOnLink::Key &a, const Kv1PointOnLink::Key &b); +bool operator==(const Kv1Icon::Key &a, const Kv1Icon::Key &b); +bool operator==(const Kv1Notice::Key &a, const Kv1Notice::Key &b); +bool operator==(const Kv1TimeDemandGroup::Key &a, const Kv1TimeDemandGroup::Key &b); +bool operator==(const Kv1TimeDemandGroupRunTime::Key &a, const Kv1TimeDemandGroupRunTime::Key &b); +bool operator==(const Kv1PeriodGroup::Key &a, const Kv1PeriodGroup::Key &b); +bool operator==(const Kv1SpecificDay::Key &a, const Kv1SpecificDay::Key &b); +bool operator==(const Kv1TimetableVersion::Key &a, const Kv1TimetableVersion::Key &b); +bool operator==(const Kv1PublicJourney::Key &a, const Kv1PublicJourney::Key &b); +bool operator==(const Kv1PeriodGroupValidity::Key &a, const Kv1PeriodGroupValidity::Key &b); +bool operator==(const Kv1ExceptionalOperatingDay::Key &a, const Kv1ExceptionalOperatingDay::Key &b); +bool operator==(const Kv1ScheduleVersion::Key &a, const Kv1ScheduleVersion::Key &b); +bool operator==(const Kv1PublicJourneyPassingTimes::Key &a, const Kv1PublicJourneyPassingTimes::Key &b); +bool operator==(const Kv1OperatingDay::Key &a, const Kv1OperatingDay::Key &b); + +size_t hash_value(const Kv1OrganizationalUnit::Key &k); +size_t hash_value(const Kv1HigherOrganizationalUnit::Key &k); +size_t hash_value(const Kv1UserStopPoint::Key &k); +size_t hash_value(const Kv1UserStopArea::Key &k); +size_t hash_value(const Kv1TimingLink::Key &k); +size_t hash_value(const Kv1Link::Key &k); +size_t hash_value(const Kv1Line::Key &k); +size_t hash_value(const Kv1Destination::Key &k); +size_t hash_value(const Kv1JourneyPattern::Key &k); +size_t hash_value(const Kv1ConcessionFinancerRelation::Key &k); +size_t hash_value(const Kv1ConcessionArea::Key &k); +size_t hash_value(const Kv1Financer::Key &k); +size_t hash_value(const Kv1JourneyPatternTimingLink::Key &k); +size_t hash_value(const Kv1Point::Key &k); +size_t hash_value(const Kv1PointOnLink::Key &k); +size_t hash_value(const Kv1Icon::Key &k); +size_t hash_value(const Kv1Notice::Key &k); +size_t hash_value(const Kv1TimeDemandGroup::Key &k); +size_t hash_value(const Kv1TimeDemandGroupRunTime::Key &k); +size_t hash_value(const Kv1PeriodGroup::Key &k); +size_t hash_value(const Kv1SpecificDay::Key &k); +size_t hash_value(const Kv1TimetableVersion::Key &k); +size_t hash_value(const Kv1PublicJourney::Key &k); +size_t hash_value(const Kv1PeriodGroupValidity::Key &k); +size_t hash_value(const Kv1ExceptionalOperatingDay::Key &k); +size_t hash_value(const Kv1ScheduleVersion::Key &k); +size_t hash_value(const Kv1PublicJourneyPassingTimes::Key &k); +size_t hash_value(const Kv1OperatingDay::Key &k); + +#endif // OEUF_LIBTMI8_KV1_TYPES_HPP diff --git a/lib/libtmi8/include/tmi8/kv6_parquet.hpp b/lib/libtmi8/include/tmi8/kv6_parquet.hpp new file mode 100644 index 0000000..33b57ca --- /dev/null +++ b/lib/libtmi8/include/tmi8/kv6_parquet.hpp @@ -0,0 +1,46 @@ +// vim:set sw=2 ts=2 sts et: + +#ifndef OEUF_LIBTMI8_KV6_PARQUET_HPP +#define OEUF_LIBTMI8_KV6_PARQUET_HPP + +#include + +#include +#include +#include + +static const size_t MAX_PARQUET_CHUNK = 10000; + +struct ParquetBuilder { + ParquetBuilder(); + arrow::Result> getTable(); + + std::shared_ptr schema; + + arrow::StringBuilder types; + arrow::StringBuilder data_owner_codes; + arrow::StringBuilder line_planning_numbers; + arrow::Date32Builder operating_days; + arrow::UInt32Builder journey_numbers; + arrow::UInt8Builder reinforcement_numbers; + arrow::TimestampBuilder timestamps{arrow::timestamp(arrow::TimeUnit::SECOND), arrow::default_memory_pool()}; + arrow::StringBuilder sources; + arrow::Int16Builder punctualities; + arrow::StringBuilder user_stop_codes; + arrow::UInt16Builder passage_sequence_numbers; + arrow::UInt32Builder vehicle_numbers; + arrow::UInt32Builder block_codes; + arrow::StringBuilder wheelchair_accessibles; + arrow::UInt8Builder number_of_coaches; + arrow::Int32Builder rd_ys; + arrow::Int32Builder rd_xs; + arrow::UInt32Builder distance_since_last_user_stops; +}; + +[[nodiscard]] +arrow::Status writeArrowRecordsAsParquetFile(arrow::RecordBatchReader &rbr, std::filesystem::path filename); + +[[nodiscard]] +arrow::Status writeArrowTableAsParquetFile(const arrow::Table &table, std::filesystem::path filename); + +#endif // OEUF_LIBTMI8_KV6_PARQUET_HPP -- cgit v1.2.3