diff --git a/README.md b/README.md index 01b3007..a806724 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,11 @@ as an application. pip install asam-qc-opendrive@git+https://github.com/asam-ev/qc-opendrive@main ``` -**Note**: To install from different sources, you can replace `@main` with -your desired target. For example, `develop` branch as `@develop`. +**Note:** The above command will install `asam-qc-opendrive` from the `main` branch. If you want to install `asam-qc-opendrive` from another branch or tag, replace `@main` with the desired branch or tag. It is also possible to install from a local directory. + +```bash +pip install /home/user/qc-opendrive +``` #### To use as a library @@ -163,10 +166,29 @@ An example configuration file for using this Checker Bundle within the ASAM Qual - - - - + + + + + + + + + + + + + + + + + + + + + + + @@ -231,3 +253,9 @@ You need to have pre-commit installed and install the hooks: ``` pre-commit install ``` + +[This folder](tests/data/not_implemented_yet/) contains the valid and invalid sample OpenDrive files of the +rules that need to be implemented in the future. It can be used as a reference for anyone who +wants to contribute to the implementation of the rules. + +Contributions of valid and invalid OpenDrive sample files are also welcome. New sample files can be added to [the same folder](tests/data/not_implemented_yet/). diff --git a/checker_bundle_doc.md b/checker_bundle_doc.md index be742f9..e845108 100644 --- a/checker_bundle_doc.md +++ b/checker_bundle_doc.md @@ -5,44 +5,144 @@ ## Parameters -* InputFile: The path of the input file. +* InputFile ## Checkers -### semantic_xodr +### check_asam_xodr_xml_valid_xml_document -* Description: Evaluates elements in the file and their semantics to guarantee they are in conformity with the standard. +* Description: The input file must be a valid XML document. +* Addressed rules: + * asam.net:xodr:1.0.0:xml.valid_xml_document + +### check_asam_xodr_xml_root_tag_is_opendrive + +* Description: The root element of a valid XML document must be OpenSCENARIO. +* Addressed rules: + * asam.net:xodr:1.0.0:xml.root_tag_is_opendrive + +### check_asam_xodr_xml_fileheader_is_present + +* Description: Below the root element a tag with FileHeader must be defined. +* Addressed rules: + * asam.net:xodr:1.0.0:xml.fileheader_is_present + +### check_asam_xodr_xml_version_is_defined + +* Description: The FileHeader tag must have the attributes revMajor and revMinor and of type unsignedShort. +* Addressed rules: + * asam.net:xodr:1.0.0:xml.version_is_defined + +### check_asam_xodr_xml_valid_schema + +* Description: Input xml file must be valid according to the schema. +* Addressed rules: + * asam.net:xodr:1.0.0:xml.valid_schema + +### check_asam_xodr_road_lane_level_true_one_side + +* Description: Check if there is any @Level=False after being True until the lane border. * Addressed rules: * asam.net:xodr:1.7.0:road.lane.level_true_one_side + +### check_asam_xodr_road_lane_access_no_mix_of_deny_or_allow + +* Description: Check if there is mixed content on access rules for the same sOffset on lanes. +* Addressed rules: * asam.net:xodr:1.7.0:road.lane.access.no_mix_of_deny_or_allow + +### check_asam_xodr_road_lane_link_lanes_across_lane_sections + +* Description: Lanes that continues across the lane sections shall be connected in both directions. +* Addressed rules: * asam.net:xodr:1.4.0:road.lane.link.lanes_across_lane_sections + +### check_asam_xodr_road_linkage_is_junction_needed + +* Description: Two roads shall only be linked directly, if the linkage is clear. If the relationship to successor or predecessor is ambiguous, junctions shall be used. +* Addressed rules: * asam.net:xodr:1.4.0:road.linkage.is_junction_needed + +### check_asam_xodr_road_lane_link_zero_width_at_start + +* Description: Lanes that have a width of zero at the beginning of the lane section shall have no predecessor element. +* Addressed rules: * asam.net:xodr:1.7.0:road.lane.link.zero_width_at_start + +### check_asam_xodr_road_lane_link_zero_width_at_end + +* Description: Lanes that have a width of zero at the end of the lane section shall have no successor element. +* Addressed rules: * asam.net:xodr:1.7.0:road.lane.link.zero_width_at_end + +### check_asam_xodr_road_lane_link_new_lane_appear + +* Description: If a new lane appears besides, only the continuing lane shall be connected to the original lane, not the appearing lane. +* Addressed rules: * asam.net:xodr:1.4.0:road.lane.link.new_lane_appear + +### check_asam_xodr_junctions_connection_connect_road_no_incoming_road + +* Description: Connecting roads shall not be incoming roads. +* Addressed rules: * asam.net:xodr:1.4.0:junctions.connection.connect_road_no_incoming_road + +### check_asam_xodr_junctions_connection_one_connection_element + +* Description: Each connecting road shall be represented by exactly one element. A connecting road may contain as many lanes as required. +* Addressed rules: * asam.net:xodr:1.7.0:junctions.connection.one_connection_element + +### check_asam_xodr_junctions_connection_one_link_to_incoming + +* Description: Each connecting road shall be associated with at most one element per incoming road. A connecting road shall only have the element for that direction. +* Addressed rules: * asam.net:xodr:1.8.0:junctions.connection.one_link_to_incoming + +### check_asam_xodr_junctions_connection_start_along_linkage + +* Description: The value "start" shall be used to indicate that the connecting road runs along the linkage indicated in the element. +* Addressed rules: * asam.net:xodr:1.7.0:junctions.connection.start_along_linkage + +### check_asam_xodr_junctions_connection_end_opposite_linkage + +* Description: The value "end" shall be used to indicate that the connectingroad runs along the opposite direction of the linkage indicated in the element. +* Addressed rules: * asam.net:xodr:1.7.0:junctions.connection.end_opposite_linkage -### geometry_xodr +### check_asam_xodr_road_geometry_parampoly3_length_match -* Description: Evaluates elements in the file and their geometrys to guarantee they are in conformity with the standard. +* Description: The actual curve length, as determined by numerical integration over the parameter range, should match '@Length'. * Addressed rules: * asam.net:xodr:1.7.0:road.geometry.parampoly3.length_match + +### check_asam_xodr_road_lane_border_overlap_with_inner_lanes + +* Description: Lane borders shall not intersect inner lanes. +* Addressed rules: * asam.net:xodr:1.4.0:road.lane.border.overlap_with_inner_lanes + +### check_asam_xodr_road_geometry_parampoly3_arclength_range + +* Description: If @prange='arcLength', p shall be chosen in [0, @Length from geometry]. +* Addressed rules: * asam.net:xodr:1.7.0:road.geometry.parampoly3.arclength_range + +### check_asam_xodr_road_geometry_parampoly3_normalized_range + +* Description: If @prange='normalized', p shall be chosen in [0, 1]. +* Addressed rules: * asam.net:xodr:1.7.0:road.geometry.parampoly3.normalized_range -### performance_xodr +### check_asam_xodr_performance_avoid_redundant_info -* Description: Evaluates elements in the file to guarantee they are optimized. +* Description: Redundant elements should be avoided. * Addressed rules: * asam.net:xodr:1.7.0:performance.avoid_redundant_info -### smoothness_xodr +### check_asam_xodr_lane_smoothness_contact_point_no_horizontal_gaps -* Description: Evaluates elements in the file and their geometries to guarantee they are in conformity with the standard definition of smoothness. +* Description: Two connected drivable lanes shall have no horizontal gaps. * Addressed rules: * asam.net:xodr:1.7.0:lane_smoothness.contact_point_no_horizontal_gaps diff --git a/poetry.lock b/poetry.lock index 04c17ff..a7fee80 100644 --- a/poetry.lock +++ b/poetry.lock @@ -29,7 +29,7 @@ pydantic-xml = "^2.11.0" type = "git" url = "https://github.com/asam-ev/qc-baselib-py.git" reference = "develop" -resolved_reference = "a893b7b14c7b6251ba17af619a0022d8ea9c8ea9" +resolved_reference = "bfed12fc6f1bbd2cab97f74a6c9aa0e0faaef7b3" [[package]] name = "black" @@ -375,13 +375,13 @@ files = [ [[package]] name = "platformdirs" -version = "4.3.2" +version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.3.2-py3-none-any.whl", hash = "sha256:eb1c8582560b34ed4ba105009a4badf7f6f85768b30126f351328507b2beb617"}, - {file = "platformdirs-4.3.2.tar.gz", hash = "sha256:9e5e27a08aa095dd127b9f2e764d74254f482fef22b0970773bfba79d091ab8c"}, + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, ] [package.extras] @@ -406,17 +406,17 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pybind11" -version = "2.13.5" +version = "2.13.6" description = "Seamless operability between C++11 and Python" optional = false python-versions = ">=3.7" files = [ - {file = "pybind11-2.13.5-py3-none-any.whl", hash = "sha256:dc35a98b61a0d23ee8599b317664f5be7e259fdc369a3b810b1ebbc3f5674d27"}, - {file = "pybind11-2.13.5.tar.gz", hash = "sha256:ae33f635322f9d9741abde0c5f348bf9373f6c22298883395e586cb43c55574e"}, + {file = "pybind11-2.13.6-py3-none-any.whl", hash = "sha256:237c41e29157b962835d356b370ededd57594a26d5894a795960f0047cb5caf5"}, + {file = "pybind11-2.13.6.tar.gz", hash = "sha256:ba6af10348c12b24e92fa086b39cfba0eff619b61ac77c406167d813b096d39a"}, ] [package.extras] -global = ["pybind11-global (==2.13.5)"] +global = ["pybind11-global (==2.13.6)"] [[package]] name = "pyclothoids" @@ -487,18 +487,18 @@ pybind11 = ">=2.4" [[package]] name = "pydantic" -version = "2.9.1" +version = "2.9.2" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.9.1-py3-none-any.whl", hash = "sha256:7aff4db5fdf3cf573d4b3c30926a510a10e19a0774d38fc4967f78beb6deb612"}, - {file = "pydantic-2.9.1.tar.gz", hash = "sha256:1363c7d975c7036df0db2b4a61f2e062fbc0aa5ab5f2772e0ffc7191a4f4bce2"}, + {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, + {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, ] [package.dependencies] annotated-types = ">=0.6.0" -pydantic-core = "2.23.3" +pydantic-core = "2.23.4" typing-extensions = [ {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, {version = ">=4.6.1", markers = "python_version < \"3.13\""}, @@ -510,100 +510,100 @@ timezone = ["tzdata"] [[package]] name = "pydantic-core" -version = "2.23.3" +version = "2.23.4" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.23.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7f10a5d1b9281392f1bf507d16ac720e78285dfd635b05737c3911637601bae6"}, - {file = "pydantic_core-2.23.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3c09a7885dd33ee8c65266e5aa7fb7e2f23d49d8043f089989726391dd7350c5"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6470b5a1ec4d1c2e9afe928c6cb37eb33381cab99292a708b8cb9aa89e62429b"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9172d2088e27d9a185ea0a6c8cebe227a9139fd90295221d7d495944d2367700"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86fc6c762ca7ac8fbbdff80d61b2c59fb6b7d144aa46e2d54d9e1b7b0e780e01"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0cb80fd5c2df4898693aa841425ea1727b1b6d2167448253077d2a49003e0ed"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03667cec5daf43ac4995cefa8aaf58f99de036204a37b889c24a80927b629cec"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:047531242f8e9c2db733599f1c612925de095e93c9cc0e599e96cf536aaf56ba"}, - {file = "pydantic_core-2.23.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5499798317fff7f25dbef9347f4451b91ac2a4330c6669821c8202fd354c7bee"}, - {file = "pydantic_core-2.23.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bbb5e45eab7624440516ee3722a3044b83fff4c0372efe183fd6ba678ff681fe"}, - {file = "pydantic_core-2.23.3-cp310-none-win32.whl", hash = "sha256:8b5b3ed73abb147704a6e9f556d8c5cb078f8c095be4588e669d315e0d11893b"}, - {file = "pydantic_core-2.23.3-cp310-none-win_amd64.whl", hash = "sha256:2b603cde285322758a0279995b5796d64b63060bfbe214b50a3ca23b5cee3e83"}, - {file = "pydantic_core-2.23.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c889fd87e1f1bbeb877c2ee56b63bb297de4636661cc9bbfcf4b34e5e925bc27"}, - {file = "pydantic_core-2.23.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea85bda3189fb27503af4c45273735bcde3dd31c1ab17d11f37b04877859ef45"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7f7f72f721223f33d3dc98a791666ebc6a91fa023ce63733709f4894a7dc611"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b2b55b0448e9da68f56b696f313949cda1039e8ec7b5d294285335b53104b61"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c24574c7e92e2c56379706b9a3f07c1e0c7f2f87a41b6ee86653100c4ce343e5"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2b05e6ccbee333a8f4b8f4d7c244fdb7a979e90977ad9c51ea31261e2085ce0"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2c409ce1c219c091e47cb03feb3c4ed8c2b8e004efc940da0166aaee8f9d6c8"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d965e8b325f443ed3196db890d85dfebbb09f7384486a77461347f4adb1fa7f8"}, - {file = "pydantic_core-2.23.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f56af3a420fb1ffaf43ece3ea09c2d27c444e7c40dcb7c6e7cf57aae764f2b48"}, - {file = "pydantic_core-2.23.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5b01a078dd4f9a52494370af21aa52964e0a96d4862ac64ff7cea06e0f12d2c5"}, - {file = "pydantic_core-2.23.3-cp311-none-win32.whl", hash = "sha256:560e32f0df04ac69b3dd818f71339983f6d1f70eb99d4d1f8e9705fb6c34a5c1"}, - {file = "pydantic_core-2.23.3-cp311-none-win_amd64.whl", hash = "sha256:c744fa100fdea0d000d8bcddee95213d2de2e95b9c12be083370b2072333a0fa"}, - {file = "pydantic_core-2.23.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e0ec50663feedf64d21bad0809f5857bac1ce91deded203efc4a84b31b2e4305"}, - {file = "pydantic_core-2.23.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:db6e6afcb95edbe6b357786684b71008499836e91f2a4a1e55b840955b341dbb"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ccd69edcf49f0875d86942f4418a4e83eb3047f20eb897bffa62a5d419c8fa"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a678c1ac5c5ec5685af0133262103defb427114e62eafeda12f1357a12140162"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01491d8b4d8db9f3391d93b0df60701e644ff0894352947f31fff3e52bd5c801"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fcf31facf2796a2d3b7fe338fe8640aa0166e4e55b4cb108dbfd1058049bf4cb"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7200fd561fb3be06827340da066df4311d0b6b8eb0c2116a110be5245dceb326"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc1636770a809dee2bd44dd74b89cc80eb41172bcad8af75dd0bc182c2666d4c"}, - {file = "pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:67a5def279309f2e23014b608c4150b0c2d323bd7bccd27ff07b001c12c2415c"}, - {file = "pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:748bdf985014c6dd3e1e4cc3db90f1c3ecc7246ff5a3cd4ddab20c768b2f1dab"}, - {file = "pydantic_core-2.23.3-cp312-none-win32.whl", hash = "sha256:255ec6dcb899c115f1e2a64bc9ebc24cc0e3ab097775755244f77360d1f3c06c"}, - {file = "pydantic_core-2.23.3-cp312-none-win_amd64.whl", hash = "sha256:40b8441be16c1e940abebed83cd006ddb9e3737a279e339dbd6d31578b802f7b"}, - {file = "pydantic_core-2.23.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6daaf5b1ba1369a22c8b050b643250e3e5efc6a78366d323294aee54953a4d5f"}, - {file = "pydantic_core-2.23.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d015e63b985a78a3d4ccffd3bdf22b7c20b3bbd4b8227809b3e8e75bc37f9cb2"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3fc572d9b5b5cfe13f8e8a6e26271d5d13f80173724b738557a8c7f3a8a3791"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f6bd91345b5163ee7448bee201ed7dd601ca24f43f439109b0212e296eb5b423"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc379c73fd66606628b866f661e8785088afe2adaba78e6bbe80796baf708a63"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbdce4b47592f9e296e19ac31667daed8753c8367ebb34b9a9bd89dacaa299c9"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc3cf31edf405a161a0adad83246568647c54404739b614b1ff43dad2b02e6d5"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8e22b477bf90db71c156f89a55bfe4d25177b81fce4aa09294d9e805eec13855"}, - {file = "pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0a0137ddf462575d9bce863c4c95bac3493ba8e22f8c28ca94634b4a1d3e2bb4"}, - {file = "pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:203171e48946c3164fe7691fc349c79241ff8f28306abd4cad5f4f75ed80bc8d"}, - {file = "pydantic_core-2.23.3-cp313-none-win32.whl", hash = "sha256:76bdab0de4acb3f119c2a4bff740e0c7dc2e6de7692774620f7452ce11ca76c8"}, - {file = "pydantic_core-2.23.3-cp313-none-win_amd64.whl", hash = "sha256:37ba321ac2a46100c578a92e9a6aa33afe9ec99ffa084424291d84e456f490c1"}, - {file = "pydantic_core-2.23.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d063c6b9fed7d992bcbebfc9133f4c24b7a7f215d6b102f3e082b1117cddb72c"}, - {file = "pydantic_core-2.23.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6cb968da9a0746a0cf521b2b5ef25fc5a0bee9b9a1a8214e0a1cfaea5be7e8a4"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edbefe079a520c5984e30e1f1f29325054b59534729c25b874a16a5048028d16"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cbaaf2ef20d282659093913da9d402108203f7cb5955020bd8d1ae5a2325d1c4"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb539d7e5dc4aac345846f290cf504d2fd3c1be26ac4e8b5e4c2b688069ff4cf"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e6f33503c5495059148cc486867e1d24ca35df5fc064686e631e314d959ad5b"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:04b07490bc2f6f2717b10c3969e1b830f5720b632f8ae2f3b8b1542394c47a8e"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:03795b9e8a5d7fda05f3873efc3f59105e2dcff14231680296b87b80bb327295"}, - {file = "pydantic_core-2.23.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c483dab0f14b8d3f0df0c6c18d70b21b086f74c87ab03c59250dbf6d3c89baba"}, - {file = "pydantic_core-2.23.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b2682038e255e94baf2c473dca914a7460069171ff5cdd4080be18ab8a7fd6e"}, - {file = "pydantic_core-2.23.3-cp38-none-win32.whl", hash = "sha256:f4a57db8966b3a1d1a350012839c6a0099f0898c56512dfade8a1fe5fb278710"}, - {file = "pydantic_core-2.23.3-cp38-none-win_amd64.whl", hash = "sha256:13dd45ba2561603681a2676ca56006d6dee94493f03d5cadc055d2055615c3ea"}, - {file = "pydantic_core-2.23.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:82da2f4703894134a9f000e24965df73cc103e31e8c31906cc1ee89fde72cbd8"}, - {file = "pydantic_core-2.23.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dd9be0a42de08f4b58a3cc73a123f124f65c24698b95a54c1543065baca8cf0e"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89b731f25c80830c76fdb13705c68fef6a2b6dc494402987c7ea9584fe189f5d"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c6de1ec30c4bb94f3a69c9f5f2182baeda5b809f806676675e9ef6b8dc936f28"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb68b41c3fa64587412b104294b9cbb027509dc2f6958446c502638d481525ef"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c3980f2843de5184656aab58698011b42763ccba11c4a8c35936c8dd6c7068c"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94f85614f2cba13f62c3c6481716e4adeae48e1eaa7e8bac379b9d177d93947a"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:510b7fb0a86dc8f10a8bb43bd2f97beb63cffad1203071dc434dac26453955cd"}, - {file = "pydantic_core-2.23.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1eba2f7ce3e30ee2170410e2171867ea73dbd692433b81a93758ab2de6c64835"}, - {file = "pydantic_core-2.23.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4b259fd8409ab84b4041b7b3f24dcc41e4696f180b775961ca8142b5b21d0e70"}, - {file = "pydantic_core-2.23.3-cp39-none-win32.whl", hash = "sha256:40d9bd259538dba2f40963286009bf7caf18b5112b19d2b55b09c14dde6db6a7"}, - {file = "pydantic_core-2.23.3-cp39-none-win_amd64.whl", hash = "sha256:5a8cd3074a98ee70173a8633ad3c10e00dcb991ecec57263aacb4095c5efb958"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f399e8657c67313476a121a6944311fab377085ca7f490648c9af97fc732732d"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:6b5547d098c76e1694ba85f05b595720d7c60d342f24d5aad32c3049131fa5c4"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0dda0290a6f608504882d9f7650975b4651ff91c85673341789a476b1159f211"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b6e5da855e9c55a0c67f4db8a492bf13d8d3316a59999cfbaf98cc6e401961"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:09e926397f392059ce0afdcac920df29d9c833256354d0c55f1584b0b70cf07e"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:87cfa0ed6b8c5bd6ae8b66de941cece179281239d482f363814d2b986b79cedc"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e61328920154b6a44d98cabcb709f10e8b74276bc709c9a513a8c37a18786cc4"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ce3317d155628301d649fe5e16a99528d5680af4ec7aa70b90b8dacd2d725c9b"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e89513f014c6be0d17b00a9a7c81b1c426f4eb9224b15433f3d98c1a071f8433"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:4f62c1c953d7ee375df5eb2e44ad50ce2f5aff931723b398b8bc6f0ac159791a"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2718443bc671c7ac331de4eef9b673063b10af32a0bb385019ad61dcf2cc8f6c"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0d90e08b2727c5d01af1b5ef4121d2f0c99fbee692c762f4d9d0409c9da6541"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b676583fc459c64146debea14ba3af54e540b61762dfc0613dc4e98c3f66eeb"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:50e4661f3337977740fdbfbae084ae5693e505ca2b3130a6d4eb0f2281dc43b8"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:68f4cf373f0de6abfe599a38307f4417c1c867ca381c03df27c873a9069cda25"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:59d52cf01854cb26c46958552a21acb10dd78a52aa34c86f284e66b209db8cab"}, - {file = "pydantic_core-2.23.3.tar.gz", hash = "sha256:3cb0f65d8b4121c1b015c60104a685feb929a29d7cf204387c7f2688c7974690"}, + {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, + {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"}, + {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"}, + {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"}, + {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"}, + {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"}, + {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"}, + {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"}, + {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"}, + {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"}, + {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"}, + {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"}, + {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"}, + {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"}, + {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"}, + {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"}, + {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"}, + {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"}, + {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"}, + {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"}, + {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"}, + {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"}, + {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"}, + {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"}, + {file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"}, + {file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"}, + {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"}, + {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"}, + {file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"}, + {file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"}, + {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"}, + {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"}, + {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"}, + {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"}, + {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"}, + {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"}, + {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"}, ] [package.dependencies] @@ -630,13 +630,13 @@ lxml = ["lxml (>=4.9.0)"] [[package]] name = "pytest" -version = "8.3.2" +version = "8.3.3" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"}, - {file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"}, + {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, + {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, ] [package.dependencies] @@ -738,13 +738,13 @@ files = [ [[package]] name = "xmlschema" -version = "3.3.2" +version = "3.4.2" description = "An XML Schema validator and decoder" optional = false python-versions = ">=3.8" files = [ - {file = "xmlschema-3.3.2-py3-none-any.whl", hash = "sha256:e9b9663aaa313904b8bf3d7487fc8123920e199662bc09bb32e0d62d9548f1ec"}, - {file = "xmlschema-3.3.2.tar.gz", hash = "sha256:a2f021f21d0b5ab371e9bcb5a1d5c34b9ba2c74ad3e32854474c4159bf94e158"}, + {file = "xmlschema-3.4.2-py3-none-any.whl", hash = "sha256:c6b4de5f8aadeb45e74229f09a2129342b446456efc5e5a27388050afdfedec8"}, + {file = "xmlschema-3.4.2.tar.gz", hash = "sha256:d35023ea504ea46127302d1297b046d023b96fec5fe4b4b690534ea85b5e9bf8"}, ] [package.dependencies] @@ -758,4 +758,4 @@ docs = ["Sphinx", "elementpath (>=4.4.0,<5.0.0)", "jinja2", "sphinx-rtd-theme"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "b77641d76ca1c5c09781e94ff01cf8a04f2f4b2789806a56ea37b743bcb61922" +content-hash = "3a048b66fad82cdc6da151947aef4285f7d4f4b11af6cc3f2155c1f1d4fb71e7" diff --git a/pyproject.toml b/pyproject.toml index 5455e88..1e7ac8e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ numpy = "^1.26.0" scipy = "^1.14.0" pyclothoids = "^0.1.5" transforms3d = "^0.4.2" -xmlschema = "^3.3.2" +xmlschema = "^3.3.1" [tool.poetry.group.dev.dependencies] pytest = "^8.2.2" diff --git a/qc_opendrive/__init__.py b/qc_opendrive/__init__.py index 137b3cc..0dc6a13 100644 --- a/qc_opendrive/__init__.py +++ b/qc_opendrive/__init__.py @@ -1,2 +1,3 @@ from . import constants as constants from . import checks as checks +from . import basic_preconditions as basic_preconditions diff --git a/qc_opendrive/base/models.py b/qc_opendrive/base/models.py index 5e4a65c..ba9ae73 100644 --- a/qc_opendrive/base/models.py +++ b/qc_opendrive/base/models.py @@ -1,17 +1,18 @@ from dataclasses import dataclass from enum import Enum -from typing import Union from lxml import etree +from typing import Union from qc_baselib import Configuration, Result @dataclass class CheckerData: - input_file_xml_root: etree._ElementTree + xml_file_path: str + input_file_xml_root: Union[None, etree._ElementTree] config: Configuration result: Result - schema_version: str + schema_version: Union[None, str] class LinkageTag(str, Enum): diff --git a/qc_opendrive/base/utils.py b/qc_opendrive/base/utils.py index c79d013..e993026 100644 --- a/qc_opendrive/base/utils.py +++ b/qc_opendrive/base/utils.py @@ -14,6 +14,20 @@ ) +def to_int(s): + try: + return int(s) + except (ValueError, TypeError): + return None + + +def to_float(s): + try: + return float(s) + except (ValueError, TypeError): + return None + + def get_root_without_default_namespace(path: str) -> etree._ElementTree: with open(path, "rb") as raw_file: xml_string = raw_file.read().decode() @@ -77,9 +91,9 @@ def get_road_id_map(root: etree._ElementTree) -> Dict[int, etree._ElementTree]: road_id_map = dict() for road in root.iter("road"): - road_id = road.get("id") + road_id = to_int(road.get("id")) if road_id is not None: - road_id_map[int(road_id)] = road + road_id_map[road_id] = road return road_id_map @@ -94,9 +108,9 @@ def get_junction_id_map(root: etree._ElementTree) -> Dict[int, etree._ElementTre junction_id_map = dict() for junction in root.iter("junction"): - junction_id = junction.get("id") + junction_id = to_int(junction.get("id")) if junction_id is not None: - junction_id_map[int(junction_id)] = junction + junction_id_map[junction_id] = junction return junction_id_map @@ -157,13 +171,13 @@ def get_road_linkage( if linkage is None: return None elif linkage.get("elementType") == "road": - road_id = linkage.get("elementId") + road_id = to_int(linkage.get("elementId")) contact_point = linkage.get("contactPoint") if road_id is None or contact_point is None: return None else: return models.RoadLinkage( - id=int(road_id), contact_point=models.ContactPoint(contact_point) + id=road_id, contact_point=models.ContactPoint(contact_point) ) else: return None @@ -181,11 +195,8 @@ def get_linked_junction_id( if linkage is None: return None elif linkage.get("elementType") == "junction": - junction_id = linkage.get("elementId") - if junction_id is None: - return None - else: - return int(junction_id) + junction_id = to_int(linkage.get("elementId")) + return junction_id else: return None @@ -213,9 +224,9 @@ def get_predecessor_lane_ids(lane: etree._ElementTree) -> List[int]: for link in links: linkages = link.findall("predecessor") for linkage in linkages: - predecessor_id = linkage.get("id") + predecessor_id = to_int(linkage.get("id")) if predecessor_id is not None: - predecessors.append(int(predecessor_id)) + predecessors.append(predecessor_id) return predecessors @@ -227,9 +238,9 @@ def get_successor_lane_ids(lane: etree._ElementTree) -> List[int]: for link in links: linkages = link.findall("successor") for linkage in linkages: - successor_id = linkage.get("id") + successor_id = to_int(linkage.get("id")) if successor_id is not None: - successors.append(int(successor_id)) + successors.append(successor_id) return successors @@ -242,8 +253,8 @@ def get_lane_link_element( for link in links: linkages = link.findall("predecessor") for linkage in linkages: - predecessor_id = linkage.get("id") - if predecessor_id is not None and link_id == int(predecessor_id): + predecessor_id = to_int(linkage.get("id")) + if predecessor_id is not None and link_id == predecessor_id: return linkage return None @@ -251,8 +262,8 @@ def get_lane_link_element( for link in links: linkages = link.findall("successor") for linkage in linkages: - successor_id = linkage.get("id") - if successor_id is not None and link_id == int(successor_id): + successor_id = to_int(linkage.get("id")) + if successor_id is not None and link_id == successor_id: return linkage return None @@ -266,8 +277,8 @@ def get_lane_from_lane_section( lanes = get_left_and_right_lanes_from_lane_section(lane_section) for lane in lanes: - current_id = lane.get("id") - if current_id is not None and int(current_id) == lane_id: + current_id = get_lane_id(lane) + if current_id is not None and current_id == lane_id: return lane return None @@ -298,19 +309,11 @@ def get_connections_from_junction( def get_lane_id(lane: etree._ElementTree) -> Union[None, int]: - lane_id = lane.get("id") - if lane_id is None: - return None - else: - return int(lane_id) + return to_int(lane.get("id")) def get_road_junction_id(road: etree._ElementTree) -> Union[None, int]: - junction_id = road.get("junction") - if junction_id is None: - return None - else: - return int(junction_id) + return to_int(road.get("junction")) def get_road_link_element( @@ -321,8 +324,8 @@ def get_road_link_element( for link in links: linkages = link.findall("predecessor") for linkage in linkages: - predecessor_id = linkage.get("elementId") - if predecessor_id is not None and link_id == int(predecessor_id): + predecessor_id = to_int(linkage.get("elementId")) + if predecessor_id is not None and link_id == predecessor_id: return linkage return None @@ -330,8 +333,8 @@ def get_road_link_element( for link in links: linkages = link.findall("successor") for linkage in linkages: - successor_id = linkage.get("elementId") - if successor_id is not None and link_id == int(successor_id): + successor_id = to_int(linkage.get("elementId")) + if successor_id is not None and link_id == successor_id: return linkage return None @@ -350,21 +353,13 @@ def road_belongs_to_junction(road: etree._Element) -> bool: def get_incoming_road_id_from_connection( connection: etree._Element, ) -> Union[None, int]: - incoming_road_id = connection.get("incomingRoad") - if incoming_road_id is None: - return None - else: - return int(incoming_road_id) + return to_int(connection.get("incomingRoad")) def get_connecting_road_id_from_connection( connection: etree._Element, ) -> Union[None, int]: - connecting_road_id = connection.get("connectingRoad") - if connecting_road_id is None: - return None - else: - return int(connecting_road_id) + return to_int(connection.get("connectingRoad")) def get_contact_point_from_connection( @@ -378,27 +373,34 @@ def get_contact_point_from_connection( def get_from_attribute_from_lane_link(lane_link: etree._Element) -> Union[int, None]: - from_attribute = lane_link.get("from") - if from_attribute is None: - return None - else: - return int(from_attribute) + return to_int(lane_link.get("from")) def get_to_attribute_from_lane_link(lane_link: etree._Element) -> Union[int, None]: - to_attribute = lane_link.get("to") - if to_attribute is None: - return None - else: - return int(to_attribute) + return to_int(lane_link.get("to")) def get_length_from_geometry(geometry: etree._ElementTree) -> Union[None, float]: - length = geometry.get("length") - if length is None: - return None + return to_float(geometry.get("length")) + + +def is_valid_param_poly3(param_poly3: models.ParamPoly3) -> bool: + if any( + value is None + for value in [ + param_poly3.u.a, + param_poly3.u.b, + param_poly3.u.c, + param_poly3.u.d, + param_poly3.v.a, + param_poly3.v.b, + param_poly3.v.c, + param_poly3.v.d, + ] + ): + return False else: - return float(length) + return True def get_normalized_param_poly3_from_geometry( @@ -415,22 +417,27 @@ def get_normalized_param_poly3_from_geometry( if param_poly3.get("pRange") != models.ParamPoly3Range.NORMALIZED: return None - return models.ParamPoly3( + parsed_result = models.ParamPoly3( u=models.Poly3( - a=float(param_poly3.get("aU")), - b=float(param_poly3.get("bU")), - c=float(param_poly3.get("cU")), - d=float(param_poly3.get("dU")), + a=to_float(param_poly3.get("aU")), + b=to_float(param_poly3.get("bU")), + c=to_float(param_poly3.get("cU")), + d=to_float(param_poly3.get("dU")), ), v=models.Poly3( - a=float(param_poly3.get("aV")), - b=float(param_poly3.get("bV")), - c=float(param_poly3.get("cV")), - d=float(param_poly3.get("dV")), + a=to_float(param_poly3.get("aV")), + b=to_float(param_poly3.get("bV")), + c=to_float(param_poly3.get("cV")), + d=to_float(param_poly3.get("dV")), ), range=models.ParamPoly3Range.NORMALIZED, ) + if is_valid_param_poly3(parsed_result): + return parsed_result + else: + return None + def poly3_to_polynomial(poly3: models.Poly3) -> np.polynomial.Polynomial: return np.polynomial.Polynomial([poly3.a, poly3.b, poly3.c, poly3.d]) @@ -450,22 +457,27 @@ def get_arclen_param_poly3_from_geometry( if param_poly3.get("pRange") != models.ParamPoly3Range.ARC_LENGTH: return None - return models.ParamPoly3( + parsed_result = models.ParamPoly3( u=models.Poly3( - a=float(param_poly3.get("aU")), - b=float(param_poly3.get("bU")), - c=float(param_poly3.get("cU")), - d=float(param_poly3.get("dU")), + a=to_float(param_poly3.get("aU")), + b=to_float(param_poly3.get("bU")), + c=to_float(param_poly3.get("cU")), + d=to_float(param_poly3.get("dU")), ), v=models.Poly3( - a=float(param_poly3.get("aV")), - b=float(param_poly3.get("bV")), - c=float(param_poly3.get("cV")), - d=float(param_poly3.get("dV")), + a=to_float(param_poly3.get("aV")), + b=to_float(param_poly3.get("bV")), + c=to_float(param_poly3.get("cV")), + d=to_float(param_poly3.get("dV")), ), range=models.ParamPoly3Range.ARC_LENGTH, ) + if is_valid_param_poly3(parsed_result): + return parsed_result + else: + return None + def arc_length_integrand( t: float, du: np.polynomial.Polynomial, dv: np.polynomial.Polynomial @@ -578,19 +590,40 @@ def get_connecting_lane_ids( return [] +def is_valid_offset_poly3(offset_poly3: models.OffsetPoly3) -> bool: + if any( + value is None + for value in [ + offset_poly3.poly3.a, + offset_poly3.poly3.b, + offset_poly3.poly3.c, + offset_poly3.poly3.d, + offset_poly3.s_offset, + ] + ): + return False + else: + return True + + def get_poly3_from_width( width: etree._ElementTree, ) -> models.OffsetPoly3: - return models.OffsetPoly3( + offset_poly3 = models.OffsetPoly3( poly3=models.Poly3( - a=float(width.get("a")), - b=float(width.get("b")), - c=float(width.get("c")), - d=float(width.get("d")), + a=to_float(width.get("a")), + b=to_float(width.get("b")), + c=to_float(width.get("c")), + d=to_float(width.get("d")), ), - s_offset=float(width.get("sOffset")), + s_offset=to_float(width.get("sOffset")), ) + if is_valid_offset_poly3(offset_poly3): + return offset_poly3 + else: + return None + def get_lane_width_poly3_list(lane: etree._Element) -> List[models.OffsetPoly3]: width_poly3 = [] @@ -742,37 +775,29 @@ def get_traffic_hand_rule_from_road(road: etree._Element) -> models.TrafficHandR def get_road_length(road: etree._ElementTree) -> Union[None, float]: - length = road.get("length") - if length is None: - return None - else: - return float(length) + return to_float(road.get("length")) def get_s_from_lane_section( lane_section: etree._ElementTree, ) -> Union[None, float]: - s_coordinate = lane_section.get("s") - if s_coordinate is None: - return None - else: - return float(s_coordinate) + return to_float(lane_section.get("s")) def get_borders_from_lane(lane: etree._ElementTree) -> List[models.OffsetPoly3]: border_list = [] for border in lane.iter("border"): - border_list.append( - models.OffsetPoly3( - models.Poly3( - a=float(border.get("a")), - b=float(border.get("b")), - c=float(border.get("c")), - d=float(border.get("d")), - ), - s_offset=float(border.get("sOffset")), - ) + offset_poly3 = models.OffsetPoly3( + models.Poly3( + a=to_float(border.get("a")), + b=to_float(border.get("b")), + c=to_float(border.get("c")), + d=to_float(border.get("d")), + ), + s_offset=to_float(border.get("sOffset")), ) + if is_valid_offset_poly3(offset_poly3): + border_list.append(offset_poly3) return border_list @@ -835,18 +860,19 @@ def get_road_elevations(road: etree._ElementTree) -> List[models.OffsetPoly3]: elevation_list = [] for elevation in elevation_profile.iter("elevation"): - elevation_list.append( - models.OffsetPoly3( - models.Poly3( - a=float(elevation.get("a")), - b=float(elevation.get("b")), - c=float(elevation.get("c")), - d=float(elevation.get("d")), - ), - s_offset=float(elevation.get("s")), - ) + offset_poly3 = models.OffsetPoly3( + models.Poly3( + a=to_float(elevation.get("a")), + b=to_float(elevation.get("b")), + c=to_float(elevation.get("c")), + d=to_float(elevation.get("d")), + ), + s_offset=to_float(elevation.get("s")), ) + if is_valid_offset_poly3(offset_poly3): + elevation_list.append(offset_poly3) + return elevation_list @@ -858,18 +884,19 @@ def get_road_superelevations(road: etree._ElementTree) -> List[models.OffsetPoly superelevation_list = [] for superelevation in lateral_profile.iter("superelevation"): - superelevation_list.append( - models.OffsetPoly3( - models.Poly3( - a=float(superelevation.get("a")), - b=float(superelevation.get("b")), - c=float(superelevation.get("c")), - d=float(superelevation.get("d")), - ), - s_offset=float(superelevation.get("s")), - ) + offset_poly3 = models.OffsetPoly3( + models.Poly3( + a=to_float(superelevation.get("a")), + b=to_float(superelevation.get("b")), + c=to_float(superelevation.get("c")), + d=to_float(superelevation.get("d")), + ), + s_offset=to_float(superelevation.get("s")), ) + if is_valid_offset_poly3(offset_poly3): + superelevation_list.append(offset_poly3) + return superelevation_list @@ -881,18 +908,19 @@ def get_lane_offsets_from_road(road: etree._ElementTree) -> List[models.OffsetPo lane_offset_list = [] for lane_offset in lanes.iter("laneOffset"): - lane_offset_list.append( - models.OffsetPoly3( - models.Poly3( - a=float(lane_offset.get("a")), - b=float(lane_offset.get("b")), - c=float(lane_offset.get("c")), - d=float(lane_offset.get("d")), - ), - s_offset=float(lane_offset.get("s")), - ) + offset_poly3 = models.OffsetPoly3( + models.Poly3( + a=to_float(lane_offset.get("a")), + b=to_float(lane_offset.get("b")), + c=to_float(lane_offset.get("c")), + d=to_float(lane_offset.get("d")), + ), + s_offset=to_float(lane_offset.get("s")), ) + if is_valid_offset_poly3(offset_poly3): + lane_offset_list.append(offset_poly3) + return lane_offset_list @@ -977,36 +1005,19 @@ def get_lane_direction(lane: etree._Element) -> Union[models.LaneDirection, None def get_heading_from_geometry(geometry: etree._ElementTree) -> Union[None, float]: - heading = geometry.get("hdg") - - if heading is None: - return None - - return float(heading) + return to_float(geometry.get("hdg")) def get_s_from_geometry(geometry: etree._ElementTree) -> Union[None, float]: - s = geometry.get("s") - if s is None: - return None - else: - return float(s) + return to_float(geometry.get("s")) def get_x_from_geometry(geometry: etree._ElementTree) -> Union[None, float]: - x = geometry.get("x") - if x is None: - return None - else: - return float(x) + return to_float(geometry.get("x")) def get_y_from_geometry(geometry: etree._ElementTree) -> Union[None, float]: - y = geometry.get("y") - if y is None: - return None - else: - return float(y) + return to_float(geometry.get("y")) def get_geometry_from_road_by_s( @@ -1051,11 +1062,7 @@ def calculate_line_point( def get_curvature_from_arc(arc: etree._Element) -> Union[None, float]: - curvature = arc.get("curvature") - if curvature is None: - return None - else: - return float(curvature) + return to_float(arc.get("curvature")) def calculate_arc_point( @@ -1078,19 +1085,11 @@ def calculate_arc_point( def get_curv_start_from_spiral(spiral: etree._Element) -> Union[None, float]: - curvStart = spiral.get("curvStart") - if curvStart is None: - return None - else: - return float(curvStart) + return to_float(spiral.get("curvStart")) def get_curv_end_from_spiral(spiral: etree._Element) -> Union[None, float]: - curvEnd = spiral.get("curvEnd") - if curvEnd is None: - return None - else: - return float(curvEnd) + return to_float(spiral.get("curvEnd")) def calculate_spiral_point( @@ -1308,20 +1307,11 @@ def get_middle_point_xyz_from_road_reference_line( def get_junction_id(junction: etree._ElementTree) -> Union[None, int]: - id = junction.get("id") - if id is None: - return None - else: - return int(id) + return to_int(junction.get("id")) def get_heading_from_geometry(geometry: etree._ElementTree) -> Union[None, float]: - heading = geometry.get("hdg") - - if heading is None: - return None - - return float(heading) + return to_float(geometry.get("hdg")) def calculate_arc_point_heading( @@ -1757,3 +1747,39 @@ def get_middle_point_xyz_at_height_zero_from_lane_by_s( else: point_xyz = get_point_xyz_from_road(road, s, t, 0.0) return point_xyz + + +def get_s_offset_from_access(access: etree._ElementTree) -> Union[None, float]: + return to_float(access.get("sOffset")) + + +def compare_versions(version1: str, version2: str) -> int: + """Compare two version strings like "X.x.x" + This function is to avoid comparing version string basing on lexicographical order + that could cause problem. E.g. + 1.10.0 > 1.2.0 but lexicographical comparison of string would return the opposite + + Args: + version1 (str): First string to compare + version2 (str): Second string to compare + + Returns: + int: 1 if version1 is bigger than version2. 0 if the version are the same. -1 otherwise + """ + v1_components = list(map(int, version1.split("."))) + v2_components = list(map(int, version2.split("."))) + + # Compare each component until one is greater or they are equal + for v1, v2 in zip(v1_components, v2_components): + if v1 < v2: + return -1 + elif v1 > v2: + return 1 + + # If all components are equal, compare based on length + if len(v1_components) < len(v2_components): + return -1 + elif len(v1_components) > len(v2_components): + return 1 + else: + return 0 diff --git a/qc_opendrive/basic_preconditions.py b/qc_opendrive/basic_preconditions.py new file mode 100644 index 0000000..a74d982 --- /dev/null +++ b/qc_opendrive/basic_preconditions.py @@ -0,0 +1,16 @@ +from qc_opendrive.checks.basic import ( + valid_xml_document, + root_tag_is_opendrive, + fileheader_is_present, + version_is_defined, +) + +from qc_opendrive.checks.schema import valid_schema + +CHECKER_PRECONDITIONS = { + valid_xml_document.CHECKER_ID, + root_tag_is_opendrive.CHECKER_ID, + fileheader_is_present.CHECKER_ID, + version_is_defined.CHECKER_ID, + valid_schema.CHECKER_ID, +} diff --git a/qc_opendrive/checks/basic/__init__.py b/qc_opendrive/checks/basic/__init__.py index 4855eb2..15f6244 100644 --- a/qc_opendrive/checks/basic/__init__.py +++ b/qc_opendrive/checks/basic/__init__.py @@ -1,5 +1,3 @@ -from . import basic_constants as basic_constants -from . import basic_checker as basic_checker from . import valid_xml_document as valid_xml_document from . import root_tag_is_opendrive as root_tag_is_opendrive from . import fileheader_is_present as fileheader_is_present diff --git a/qc_opendrive/checks/basic/basic_checker.py b/qc_opendrive/checks/basic/basic_checker.py deleted file mode 100644 index 7c29dcd..0000000 --- a/qc_opendrive/checks/basic/basic_checker.py +++ /dev/null @@ -1,67 +0,0 @@ -import logging -import os - -from lxml import etree - -from qc_baselib import Configuration, Result, StatusType - -from qc_opendrive import constants -from qc_opendrive.base import models, utils - -from qc_opendrive.checks.basic import ( - basic_constants, - valid_xml_document, - root_tag_is_opendrive, - fileheader_is_present, - version_is_defined, -) - - -def run_checks(config: Configuration, result: Result) -> bool: - logging.info("Executing basic checks") - - result.register_checker( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=basic_constants.CHECKER_ID, - description="Check if basic properties of input file are properly set", - summary="", - ) - - xml_file_path = config.get_config_param("InputFile") - - is_xml = valid_xml_document.check_rule(xml_file_path, result) - - basic_rule_list = [ - root_tag_is_opendrive.check_rule, - fileheader_is_present.check_rule, - version_is_defined.check_rule, - ] - - validation_result = is_xml - if validation_result: - root = utils.get_root_without_default_namespace(xml_file_path) - - for rule in basic_rule_list: - validation_result = validation_result and rule(root, result) - if not validation_result: - break - - if not validation_result: - logging.warning( - "There are problems with input file. Error found in basic rules!" - ) - else: - logging.info("Basic rules check successfull. Input file is valid") - - logging.info( - f"Issues found - {result.get_checker_issue_count(checker_bundle_name=constants.BUNDLE_NAME, checker_id=basic_constants.CHECKER_ID)}" - ) - - # TODO: Add logic to deal with error or to skip it - result.set_checker_status( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=basic_constants.CHECKER_ID, - status=StatusType.COMPLETED, - ) - - return validation_result diff --git a/qc_opendrive/checks/basic/basic_constants.py b/qc_opendrive/checks/basic/basic_constants.py deleted file mode 100644 index 76c9d34..0000000 --- a/qc_opendrive/checks/basic/basic_constants.py +++ /dev/null @@ -1 +0,0 @@ -CHECKER_ID = "basic_xodr" diff --git a/qc_opendrive/checks/basic/fileheader_is_present.py b/qc_opendrive/checks/basic/fileheader_is_present.py index f48fc6c..e8f756b 100644 --- a/qc_opendrive/checks/basic/fileheader_is_present.py +++ b/qc_opendrive/checks/basic/fileheader_is_present.py @@ -1,11 +1,19 @@ import logging -from lxml import etree -from qc_baselib import IssueSeverity, Result +from qc_baselib import IssueSeverity, StatusType from qc_opendrive import constants -from qc_opendrive.checks.basic import basic_constants +from qc_opendrive.checks.basic import valid_xml_document, root_tag_is_opendrive +from qc_opendrive.base import models +CHECKER_ID = "check_asam_xodr_xml_fileheader_is_present" +CHECKER_DESCRIPTION = "Below the root element a tag with FileHeader must be defined." +CHECKER_PRECONDITIONS = { + valid_xml_document.CHECKER_ID, + root_tag_is_opendrive.CHECKER_ID, +} +RULE_UID = "asam.net:xodr:1.0.0:xml.fileheader_is_present" -def check_rule(tree: etree._ElementTree, result: Result) -> bool: + +def check_rule(checker_data: models.CheckerData) -> None: """ Below the root element a tag with FileHeader must be defined @@ -14,16 +22,7 @@ def check_rule(tree: etree._ElementTree, result: Result) -> bool: """ logging.info("Executing fileheader_is_present check") - rule_uid = result.register_rule( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=basic_constants.CHECKER_ID, - emanating_entity="asam.net", - standard="xodr", - definition_setting="1.0.0", - rule_full_name="xml.fileheader_is_present", - ) - - root = tree.getroot() + root = checker_data.input_file_xml_root.getroot() is_valid = False # Check if root contains a tag 'header' @@ -37,22 +36,18 @@ def check_rule(tree: etree._ElementTree, result: Result) -> bool: if not is_valid: - issue_id = result.register_issue( + issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=basic_constants.CHECKER_ID, + checker_id=CHECKER_ID, description="Issue flagging when no header is found under root element", level=IssueSeverity.ERROR, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) - result.add_xml_location( + checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=basic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, - xpath=tree.getpath(root), + xpath=checker_data.input_file_xml_root.getpath(root), description=f"No child element header", ) - - return False - - return True diff --git a/qc_opendrive/checks/basic/root_tag_is_opendrive.py b/qc_opendrive/checks/basic/root_tag_is_opendrive.py index aa8028d..c0ae3a3 100644 --- a/qc_opendrive/checks/basic/root_tag_is_opendrive.py +++ b/qc_opendrive/checks/basic/root_tag_is_opendrive.py @@ -1,11 +1,16 @@ import logging -from lxml import etree -from qc_baselib import IssueSeverity, Result +from qc_baselib import IssueSeverity, StatusType from qc_opendrive import constants -from qc_opendrive.checks.basic import basic_constants +from qc_opendrive.checks.basic import valid_xml_document +from qc_opendrive.base import models +CHECKER_ID = "check_asam_xodr_xml_root_tag_is_opendrive" +CHECKER_DESCRIPTION = "The root element of a valid XML document must be OpenSCENARIO." +CHECKER_PRECONDITIONS = {valid_xml_document.CHECKER_ID} +RULE_UID = "asam.net:xodr:1.0.0:xml.root_tag_is_opendrive" -def check_rule(tree: etree._ElementTree, result: Result) -> bool: + +def check_rule(checker_data: models.CheckerData) -> bool: """ The root element of a valid XML document must be OpenDRIVE @@ -14,16 +19,7 @@ def check_rule(tree: etree._ElementTree, result: Result) -> bool: """ logging.info("Executing root_tag_is_opendrive check") - rule_uid = result.register_rule( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=basic_constants.CHECKER_ID, - emanating_entity="asam.net", - standard="xodr", - definition_setting="1.0.0", - rule_full_name="xml.root_tag_is_opendrive", - ) - - root = tree.getroot() + root = checker_data.input_file_xml_root.getroot() is_valid = False if root.tag == "OpenDRIVE": @@ -35,22 +31,18 @@ def check_rule(tree: etree._ElementTree, result: Result) -> bool: if not is_valid: - issue_id = result.register_issue( + issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=basic_constants.CHECKER_ID, + checker_id=CHECKER_ID, description="Issue flagging when root tag is not OpenDRIVE", level=IssueSeverity.ERROR, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) - result.add_xml_location( + checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=basic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, - xpath=tree.getpath(root), + xpath=checker_data.input_file_xml_root.getpath(root), description=f"Root is not OpenDRIVE", ) - - return False - - return True diff --git a/qc_opendrive/checks/basic/valid_xml_document.py b/qc_opendrive/checks/basic/valid_xml_document.py index ccd8b0a..2651fc4 100644 --- a/qc_opendrive/checks/basic/valid_xml_document.py +++ b/qc_opendrive/checks/basic/valid_xml_document.py @@ -1,8 +1,13 @@ import logging from lxml import etree -from qc_baselib import Result, IssueSeverity +from qc_baselib import IssueSeverity from qc_opendrive import constants -from qc_opendrive.checks.basic import basic_constants +from qc_opendrive.base import models + +CHECKER_ID = "check_asam_xodr_xml_valid_xml_document" +CHECKER_DESCRIPTION = "The input file must be a valid XML document." +CHECKER_PRECONDITIONS = set() +RULE_UID = "asam.net:xodr:1.0.0:xml.valid_xml_document" def _is_xml_doc(file_path: str) -> tuple[bool, tuple[int, int]]: @@ -18,7 +23,7 @@ def _is_xml_doc(file_path: str) -> tuple[bool, tuple[int, int]]: return False, (e.lineno, e.offset) -def check_rule(input_xml_file_path: str, result: Result) -> bool: +def check_rule(checker_data: models.CheckerData) -> None: """ Implements a rule to check if input file is a valid xml document @@ -27,36 +32,23 @@ def check_rule(input_xml_file_path: str, result: Result) -> bool: """ logging.info("Executing valid_xml_document check") - rule_uid = result.register_rule( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=basic_constants.CHECKER_ID, - emanating_entity="asam.net", - standard="xodr", - definition_setting="1.0.0", - rule_full_name="xml.valid_xml_document", - ) - - is_valid, error_location = _is_xml_doc(input_xml_file_path) + is_valid, error_location = _is_xml_doc(checker_data.xml_file_path) if not is_valid: - issue_id = result.register_issue( + issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=basic_constants.CHECKER_ID, - description="Issue flagging when input file is not a valid xml document", + checker_id=CHECKER_ID, + description="The input file is not a valid xml document.", level=IssueSeverity.ERROR, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) - result.add_file_location( + checker_data.result.add_file_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=basic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, row=error_location[0], column=error_location[1], - description=f"Invalid xml detected", + description=f"Invalid xml file.", ) - - return False - - return True diff --git a/qc_opendrive/checks/basic/version_is_defined.py b/qc_opendrive/checks/basic/version_is_defined.py index ed632c6..3ef39d7 100644 --- a/qc_opendrive/checks/basic/version_is_defined.py +++ b/qc_opendrive/checks/basic/version_is_defined.py @@ -1,20 +1,34 @@ import logging -from lxml import etree -from qc_baselib import IssueSeverity, Result +from qc_baselib import IssueSeverity, StatusType from qc_opendrive import constants -from qc_opendrive.checks.basic import basic_constants +from qc_opendrive.checks.basic import ( + valid_xml_document, + root_tag_is_opendrive, + fileheader_is_present, +) +from qc_opendrive.base import utils, models + +CHECKER_ID = "check_asam_xodr_xml_version_is_defined" +CHECKER_DESCRIPTION = "The FileHeader tag must have the attributes revMajor and revMinor and of type unsignedShort." +CHECKER_PRECONDITIONS = { + valid_xml_document.CHECKER_ID, + root_tag_is_opendrive.CHECKER_ID, + fileheader_is_present.CHECKER_ID, +} +RULE_UID = "asam.net:xodr:1.0.0:xml.version_is_defined" def is_unsigned_short(value: int) -> bool: """Helper function to check if a value is within the xsd:unsignedShort range (0-65535).""" - try: - num = int(value) - return 0 <= num <= 65535 - except ValueError: + num = utils.to_int(value) + + if num is None: return False + else: + return 0 <= num <= 65535 -def check_rule(tree: etree._ElementTree, result: Result) -> bool: +def check_rule(checker_data: models.CheckerData) -> bool: """ The header tag must have the attributes revMajor and revMinor and of type unsignedShort. @@ -23,16 +37,7 @@ def check_rule(tree: etree._ElementTree, result: Result) -> bool: """ logging.info("Executing version_is_defined check") - rule_uid = result.register_rule( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=basic_constants.CHECKER_ID, - emanating_entity="asam.net", - standard="xodr", - definition_setting="1.0.0", - rule_full_name="xml.version_is_defined", - ) - - root = tree.getroot() + root = checker_data.input_file_xml_root.getroot() is_valid = True # Check if root contains a tag 'header' @@ -40,6 +45,19 @@ def check_rule(tree: etree._ElementTree, result: Result) -> bool: if file_header_tag is None: logging.error("- No header found, cannot check version. Skipping...") + + checker_data.result.set_checker_status( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=CHECKER_ID, + status=StatusType.SKIPPED, + ) + + checker_data.result.add_checker_summary( + constants.BUNDLE_NAME, + CHECKER_ID, + f"The xml file does not contains the 'header' tag.", + ) + return True # Check if 'header' has the attributes 'revMajor' and 'revMinor' @@ -63,23 +81,18 @@ def check_rule(tree: etree._ElementTree, result: Result) -> bool: if not is_valid: - issue_id = result.register_issue( + issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=basic_constants.CHECKER_ID, + checker_id=CHECKER_ID, description="Issue flagging when revMajor revMinor attribute of header are missing or invalid", level=IssueSeverity.ERROR, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) - result.add_xml_location( + checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=basic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, - xpath=tree.getpath(file_header_tag), - description=f"header tag has invalid or missing version info", + xpath=checker_data.input_file_xml_root.getpath(file_header_tag), + description=f"Header tag has invalid or missing version info", ) - - return False - else: - logging.info(f"- header version correctly defined: {rev_major}.{rev_minor}") - return True diff --git a/qc_opendrive/checks/geometry/__init__.py b/qc_opendrive/checks/geometry/__init__.py index e66cf1b..fc4df10 100644 --- a/qc_opendrive/checks/geometry/__init__.py +++ b/qc_opendrive/checks/geometry/__init__.py @@ -1,5 +1,3 @@ -from . import geometry_constants as geometry_constants -from . import geometry_checker as geometry_checker from . import ( road_geometry_parampoly3_length_match as road_geometry_parampoly3_length_match, ) @@ -9,3 +7,6 @@ from . import ( road_geometry_parampoly3_normalized_range as road_geometry_parampoly3_normalized_range, ) +from . import ( + road_lane_border_overlap_with_inner_lanes as road_lane_border_overlap_with_inner_lanes, +) diff --git a/qc_opendrive/checks/geometry/geometry_checker.py b/qc_opendrive/checks/geometry/geometry_checker.py deleted file mode 100644 index 39d7e9c..0000000 --- a/qc_opendrive/checks/geometry/geometry_checker.py +++ /dev/null @@ -1,83 +0,0 @@ -import logging - -from lxml import etree - -from qc_baselib import Configuration, Result, StatusType - -from qc_opendrive import constants -from qc_opendrive.base import models, utils - -from qc_opendrive.checks.geometry import ( - geometry_constants, - road_geometry_parampoly3_length_match, - road_lane_border_overlap_with_inner_lanes, - road_geometry_parampoly3_arclength_range, - road_geometry_parampoly3_normalized_range, -) - - -def skip_checks(result: Result) -> None: - - if geometry_constants.CHECKER_ID not in result.get_checker_ids( - constants.BUNDLE_NAME - ): - result.register_checker( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=geometry_constants.CHECKER_ID, - description="Check if xml properties of input file are properly set", - summary="", - ) - - logging.error( - f"Invalid xml input file. Checker {geometry_constants.CHECKER_ID} skipped" - ) - result.set_checker_status( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=geometry_constants.CHECKER_ID, - status=StatusType.SKIPPED, - ) - - -def run_checks(config: Configuration, result: Result) -> None: - logging.info("Executing geometry checks") - - root = utils.get_root_without_default_namespace( - config.get_config_param("InputFile") - ) - - result.register_checker( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=geometry_constants.CHECKER_ID, - description="Evaluates elements in the file and their geometrys to guarantee they are in conformity with the standard.", - summary="", - ) - - odr_schema_version = utils.get_standard_schema_version(root) - - rule_list = [ - road_geometry_parampoly3_length_match.check_rule, - road_lane_border_overlap_with_inner_lanes.check_rule, - road_geometry_parampoly3_arclength_range.check_rule, - road_geometry_parampoly3_normalized_range.check_rule, - ] - - checker_data = models.CheckerData( - input_file_xml_root=root, - config=config, - result=result, - schema_version=odr_schema_version, - ) - - for rule in rule_list: - rule(checker_data=checker_data) - - logging.info( - f"Issues found - {result.get_checker_issue_count(checker_bundle_name=constants.BUNDLE_NAME, checker_id=geometry_constants.CHECKER_ID)}" - ) - - # TODO: Add logic to deal with error or to skip it - result.set_checker_status( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=geometry_constants.CHECKER_ID, - status=StatusType.COMPLETED, - ) diff --git a/qc_opendrive/checks/geometry/geometry_constants.py b/qc_opendrive/checks/geometry/geometry_constants.py deleted file mode 100644 index 020aeac..0000000 --- a/qc_opendrive/checks/geometry/geometry_constants.py +++ /dev/null @@ -1 +0,0 @@ -CHECKER_ID = "geometry_xodr" diff --git a/qc_opendrive/checks/geometry/road_geometry_parampoly3_arclength_range.py b/qc_opendrive/checks/geometry/road_geometry_parampoly3_arclength_range.py index 08261fc..d2403cb 100644 --- a/qc_opendrive/checks/geometry/road_geometry_parampoly3_arclength_range.py +++ b/qc_opendrive/checks/geometry/road_geometry_parampoly3_arclength_range.py @@ -3,51 +3,22 @@ import numpy as np from scipy.integrate import quad -from qc_baselib import IssueSeverity +from qc_baselib import IssueSeverity, StatusType from qc_opendrive import constants from qc_opendrive.base import models, utils -from qc_opendrive.checks.geometry import geometry_constants - -RULE_INITIAL_SUPPORTED_SCHEMA_VERSION = "1.7.0" +from qc_opendrive import basic_preconditions + +CHECKER_ID = "check_asam_xodr_road_geometry_parampoly3_arclength_range" +CHECKER_DESCRIPTION = ( + "If @prange='arcLength', p shall be chosen in [0, @Length from geometry]." +) +CHECKER_PRECONDITIONS = basic_preconditions.CHECKER_PRECONDITIONS +RULE_UID = "asam.net:xodr:1.7.0:road.geometry.parampoly3.arclength_range" TOLERANCE_THRESHOLD = 0.001 -def check_rule(checker_data: models.CheckerData) -> None: - """ - Rule ID: asam.net:xodr:1.7.0:road.geometry.parampoly3.arclength_range - - Description: If @prange="arcLength", p shall be chosen in [0, @Length from geometry]. - - Severity: ERROR - - Version range: [1.7.0, ) - - Remark: - This check currently relies on the accuracy of the scipy.integrate.quad method. - The estimated absolute error of the numerical integration is included in - the issue description message. - - More info at - - https://github.com/asam-ev/qc-opendrive/issues/38 - """ - logging.info("Executing road.geometry.parampoly3.arclength_range check.") - - rule_uid = checker_data.result.register_rule( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=geometry_constants.CHECKER_ID, - emanating_entity="asam.net", - standard="xodr", - definition_setting=RULE_INITIAL_SUPPORTED_SCHEMA_VERSION, - rule_full_name="road.geometry.parampoly3.arclength_range", - ) - - if checker_data.schema_version < RULE_INITIAL_SUPPORTED_SCHEMA_VERSION: - logging.info( - f"Schema version {checker_data.schema_version} not supported. Skipping rule." - ) - return - +def _check_all_roads(checker_data: models.CheckerData) -> None: roads = utils.get_roads(checker_data.input_file_xml_root) for road in roads: @@ -75,15 +46,15 @@ def check_rule(checker_data: models.CheckerData) -> None: if np.abs(integral_length - length) > TOLERANCE_THRESHOLD: issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=geometry_constants.CHECKER_ID, + checker_id=CHECKER_ID, description=f"Length does not match the actual curve length. The estimated absolute error from numerical integration is {estimated_error}", level=IssueSeverity.ERROR, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=geometry_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(geometry), description=f"", @@ -102,10 +73,33 @@ def check_rule(checker_data: models.CheckerData) -> None: if inertial_point is not None: checker_data.result.add_inertial_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=geometry_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, x=inertial_point.x, y=inertial_point.y, z=inertial_point.z, description=f"Length does not match the actual curve length. The estimated absolute error from numerical integration is {estimated_error}", ) + + +def check_rule(checker_data: models.CheckerData) -> None: + """ + Rule ID: asam.net:xodr:1.7.0:road.geometry.parampoly3.arclength_range + + Description: If @prange="arcLength", p shall be chosen in [0, @Length from geometry]. + + Severity: ERROR + + Version range: [1.7.0, ) + + Remark: + This check currently relies on the accuracy of the scipy.integrate.quad method. + The estimated absolute error of the numerical integration is included in + the issue description message. + + More info at + - https://github.com/asam-ev/qc-opendrive/issues/38 + """ + logging.info("Executing road.geometry.parampoly3.arclength_range check.") + + _check_all_roads(checker_data) diff --git a/qc_opendrive/checks/geometry/road_geometry_parampoly3_length_match.py b/qc_opendrive/checks/geometry/road_geometry_parampoly3_length_match.py index 9800f5a..fd3dfb4 100644 --- a/qc_opendrive/checks/geometry/road_geometry_parampoly3_length_match.py +++ b/qc_opendrive/checks/geometry/road_geometry_parampoly3_length_match.py @@ -3,51 +3,21 @@ import numpy as np from scipy.integrate import quad -from qc_baselib import IssueSeverity +from qc_baselib import IssueSeverity, StatusType from qc_opendrive import constants from qc_opendrive.base import models, utils -from qc_opendrive.checks.geometry import geometry_constants +from qc_opendrive import basic_preconditions -RULE_INITIAL_SUPPORTED_SCHEMA_VERSION = "1.7.0" -TOLERANCE_THRESHOLD = 0.001 - - -def check_rule(checker_data: models.CheckerData) -> None: - """ - Rule ID: asam.net:xodr:1.7.0:road.geometry.parampoly3.length_match - - Description: The actual curve length, as determined by numerical integration over - the parameter range, should match '@Length'. - - Severity: WARNING +CHECKER_ID = "check_asam_xodr_road_geometry_parampoly3_length_match" +CHECKER_DESCRIPTION = "The actual curve length, as determined by numerical integration over the parameter range, should match '@Length'." +CHECKER_PRECONDITIONS = basic_preconditions.CHECKER_PRECONDITIONS +RULE_UID = "asam.net:xodr:1.7.0:road.geometry.parampoly3.length_match" - Version range: [1.7.0, ) - - Remark: - This check currently relies on the accuracy of the scipy.integrate.quad method. - The estimated absolute error of the numerical integration is included in the issue description message. - - More info at - - https://github.com/asam-ev/qc-opendrive/issues/5 - """ - logging.info("Executing road.geometry.parampoly3.length_match check.") +TOLERANCE_THRESHOLD = 0.001 - rule_uid = checker_data.result.register_rule( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=geometry_constants.CHECKER_ID, - emanating_entity="asam.net", - standard="xodr", - definition_setting=RULE_INITIAL_SUPPORTED_SCHEMA_VERSION, - rule_full_name="road.geometry.parampoly3.length_match", - ) - - if checker_data.schema_version < RULE_INITIAL_SUPPORTED_SCHEMA_VERSION: - logging.info( - f"Schema version {checker_data.schema_version} not supported. Skipping rule." - ) - return +def _check_all_roads(checker_data: models.CheckerData) -> None: roads = utils.get_roads(checker_data.input_file_xml_root) for road in roads: @@ -75,15 +45,15 @@ def check_rule(checker_data: models.CheckerData) -> None: if np.abs(integral_length - length) > TOLERANCE_THRESHOLD: issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=geometry_constants.CHECKER_ID, + checker_id=CHECKER_ID, description=f"Length does not match the actual curve length. The estimated absolute error from numerical integration is {estimated_error}", level=IssueSeverity.WARNING, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=geometry_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(geometry), description=f"", @@ -102,10 +72,33 @@ def check_rule(checker_data: models.CheckerData) -> None: if inertial_point is not None: checker_data.result.add_inertial_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=geometry_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, x=inertial_point.x, y=inertial_point.y, z=inertial_point.z, description="Geometry where length doesn't match.", ) + + +def check_rule(checker_data: models.CheckerData) -> None: + """ + Rule ID: asam.net:xodr:1.7.0:road.geometry.parampoly3.length_match + + Description: The actual curve length, as determined by numerical integration over + the parameter range, should match '@Length'. + + Severity: WARNING + + Version range: [1.7.0, ) + + Remark: + This check currently relies on the accuracy of the scipy.integrate.quad method. + The estimated absolute error of the numerical integration is included in the issue description message. + + More info at + - https://github.com/asam-ev/qc-opendrive/issues/5 + """ + logging.info("Executing road.geometry.parampoly3.length_match check.") + + _check_all_roads(checker_data) diff --git a/qc_opendrive/checks/geometry/road_geometry_parampoly3_normalized_range.py b/qc_opendrive/checks/geometry/road_geometry_parampoly3_normalized_range.py index dae7652..5b9fa18 100644 --- a/qc_opendrive/checks/geometry/road_geometry_parampoly3_normalized_range.py +++ b/qc_opendrive/checks/geometry/road_geometry_parampoly3_normalized_range.py @@ -3,51 +3,21 @@ import numpy as np from scipy.integrate import quad -from qc_baselib import IssueSeverity +from qc_baselib import IssueSeverity, StatusType from qc_opendrive import constants from qc_opendrive.base import models, utils -from qc_opendrive.checks.geometry import geometry_constants +from qc_opendrive import basic_preconditions -RULE_INITIAL_SUPPORTED_SCHEMA_VERSION = "1.7.0" -TOLERANCE_THRESHOLD = 0.001 - - -def check_rule(checker_data: models.CheckerData) -> None: - """ - Rule ID: asam.net:xodr:1.7.0:road.geometry.parampoly3.normalized_range - - Description: If @prange="normalized", p shall be chosen in [0, 1]. - - Severity: ERROR - - Version range: [1.7.0, ) - - Remark: - This check currently relies on the accuracy of the scipy.integrate.quad method. - The estimated absolute error of the numerical integration is included in - the issue description message. +CHECKER_ID = "check_asam_xodr_road_geometry_parampoly3_normalized_range" +CHECKER_DESCRIPTION = "If @prange='normalized', p shall be chosen in [0, 1]." +CHECKER_PRECONDITIONS = basic_preconditions.CHECKER_PRECONDITIONS +RULE_UID = "asam.net:xodr:1.7.0:road.geometry.parampoly3.normalized_range" - More info at - - https://github.com/asam-ev/qc-opendrive/issues/39 - """ - logging.info("Executing road.geometry.parampoly3.normalized_range check.") +TOLERANCE_THRESHOLD = 0.001 - rule_uid = checker_data.result.register_rule( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=geometry_constants.CHECKER_ID, - emanating_entity="asam.net", - standard="xodr", - definition_setting=RULE_INITIAL_SUPPORTED_SCHEMA_VERSION, - rule_full_name="road.geometry.parampoly3.normalized_range", - ) - - if checker_data.schema_version < RULE_INITIAL_SUPPORTED_SCHEMA_VERSION: - logging.info( - f"Schema version {checker_data.schema_version} not supported. Skipping rule." - ) - return +def _check_all_roads(checker_data: models.CheckerData) -> None: roads = utils.get_roads(checker_data.input_file_xml_root) for road in roads: @@ -75,15 +45,15 @@ def check_rule(checker_data: models.CheckerData) -> None: if np.abs(integral_length - length) > TOLERANCE_THRESHOLD: issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=geometry_constants.CHECKER_ID, + checker_id=CHECKER_ID, description=f"Length does not match the actual curve length. The estimated absolute error from numerical integration is {estimated_error}", level=IssueSeverity.ERROR, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=geometry_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(geometry), description=f"", @@ -102,10 +72,33 @@ def check_rule(checker_data: models.CheckerData) -> None: if inertial_point is not None: checker_data.result.add_inertial_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=geometry_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, x=inertial_point.x, y=inertial_point.y, z=inertial_point.z, description=f"Length does not match the actual curve length. The estimated absolute error from numerical integration is {estimated_error}", ) + + +def check_rule(checker_data: models.CheckerData) -> None: + """ + Rule ID: asam.net:xodr:1.7.0:road.geometry.parampoly3.normalized_range + + Description: If @prange="normalized", p shall be chosen in [0, 1]. + + Severity: ERROR + + Version range: [1.7.0, ) + + Remark: + This check currently relies on the accuracy of the scipy.integrate.quad method. + The estimated absolute error of the numerical integration is included in + the issue description message. + + More info at + - https://github.com/asam-ev/qc-opendrive/issues/39 + """ + logging.info("Executing road.geometry.parampoly3.normalized_range check.") + + _check_all_roads(checker_data) diff --git a/qc_opendrive/checks/geometry/road_lane_border_overlap_with_inner_lanes.py b/qc_opendrive/checks/geometry/road_lane_border_overlap_with_inner_lanes.py index 063f162..4fd189e 100644 --- a/qc_opendrive/checks/geometry/road_lane_border_overlap_with_inner_lanes.py +++ b/qc_opendrive/checks/geometry/road_lane_border_overlap_with_inner_lanes.py @@ -5,12 +5,16 @@ import numpy as np from lxml import etree -from qc_baselib import IssueSeverity +from qc_baselib import IssueSeverity, StatusType from qc_opendrive import constants from qc_opendrive.base import models, utils -from qc_opendrive.checks.geometry import geometry_constants +from qc_opendrive import basic_preconditions + +CHECKER_ID = "check_asam_xodr_road_lane_border_overlap_with_inner_lanes" +CHECKER_DESCRIPTION = "Lane borders shall not intersect inner lanes." +CHECKER_PRECONDITIONS = basic_preconditions.CHECKER_PRECONDITIONS +RULE_UID = "asam.net:xodr:1.4.0:road.lane.border.overlap_with_inner_lanes" -RULE_INITIAL_SUPPORTED_SCHEMA_VERSION = "1.4.0" TOLERANCE_THRESHOLD = 1e-6 @@ -101,7 +105,6 @@ def _create_border_pairs( def _raise_issue( checker_data: models.CheckerData, - rule_uid: str, lane_section_with_length: models.LaneSectionWithLength, road, left_lane: etree._ElementTree, @@ -109,15 +112,15 @@ def _raise_issue( ) -> None: issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=geometry_constants.CHECKER_ID, + checker_id=CHECKER_ID, description=f"Outer lane border intersects or stays within inner lane border.", level=IssueSeverity.ERROR, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=geometry_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(left_lane), description=f"", @@ -125,7 +128,7 @@ def _raise_issue( checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=geometry_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(right_lane), description=f"", @@ -145,7 +148,7 @@ def _raise_issue( if left_inertial_point is not None: checker_data.result.add_inertial_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=geometry_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, x=left_inertial_point.x, y=left_inertial_point.y, @@ -160,7 +163,7 @@ def _raise_issue( if right_inertial_point is not None: checker_data.result.add_inertial_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=geometry_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, x=right_inertial_point.x, y=right_inertial_point.y, @@ -234,7 +237,6 @@ def _check_overlap( right_lane: etree._ElementTree, lane_section_with_length: models.LaneSectionWithLength, road: etree._ElementTree, - rule_uid: str, checker_data: models.CheckerData, ) -> None: """ @@ -252,7 +254,6 @@ def _check_overlap( if has_issue: _raise_issue( checker_data, - rule_uid, lane_section_with_length, road, left_lane, @@ -265,7 +266,6 @@ def _check_overlap_among_lane_list( lanes: List[etree._ElementTree], lane_section_with_length: models.LaneSectionWithLength, road: etree._ElementTree, - rule_uid: str, checker_data: models.CheckerData, ) -> None: lanes = [lane for lane in lanes if utils.get_lane_id(lane) is not None] @@ -277,28 +277,21 @@ def _check_overlap_among_lane_list( sorted_lanes[right_lane_index], lane_section_with_length, road, - rule_uid, checker_data, ) -def _check_road( - road: etree._ElementTree, rule_uid: str, checker_data: models.CheckerData -) -> None: +def _check_road(road: etree._ElementTree, checker_data: models.CheckerData) -> None: sorted_lane_sections_with_length = ( utils.get_sorted_lane_sections_with_length_from_road(road) ) for lane_section in sorted_lane_sections_with_length: left_lanes = utils.get_left_lanes_from_lane_section(lane_section.lane_section) - _check_overlap_among_lane_list( - left_lanes, lane_section, road, rule_uid, checker_data - ) + _check_overlap_among_lane_list(left_lanes, lane_section, road, checker_data) right_lanes = utils.get_right_lanes_from_lane_section(lane_section.lane_section) - _check_overlap_among_lane_list( - right_lanes, lane_section, road, rule_uid, checker_data - ) + _check_overlap_among_lane_list(right_lanes, lane_section, road, checker_data) def check_rule(checker_data: models.CheckerData) -> None: @@ -316,22 +309,7 @@ def check_rule(checker_data: models.CheckerData) -> None: """ logging.info("Executing road.lane.border.overlap_with_inner_lanes check.") - rule_uid = checker_data.result.register_rule( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=geometry_constants.CHECKER_ID, - emanating_entity="asam.net", - standard="xodr", - definition_setting=RULE_INITIAL_SUPPORTED_SCHEMA_VERSION, - rule_full_name="road.lane.border.overlap_with_inner_lanes", - ) - - if checker_data.schema_version < RULE_INITIAL_SUPPORTED_SCHEMA_VERSION: - logging.info( - f"Schema version {checker_data.schema_version} not supported. Skipping rule." - ) - return - road_list = utils.get_roads(checker_data.input_file_xml_root) for road in road_list: - _check_road(road, rule_uid, checker_data) + _check_road(road, checker_data) diff --git a/qc_opendrive/checks/performance/__init__.py b/qc_opendrive/checks/performance/__init__.py index cf5e017..e677a81 100644 --- a/qc_opendrive/checks/performance/__init__.py +++ b/qc_opendrive/checks/performance/__init__.py @@ -1,5 +1,3 @@ -from . import performance_constants as performance_constants -from . import performance_checker as performance_checker from . import ( performance_avoid_redundant_info as performance_avoid_redundant_info, ) diff --git a/qc_opendrive/checks/performance/performance_avoid_redundant_info.py b/qc_opendrive/checks/performance/performance_avoid_redundant_info.py index 7fc897e..217d33f 100644 --- a/qc_opendrive/checks/performance/performance_avoid_redundant_info.py +++ b/qc_opendrive/checks/performance/performance_avoid_redundant_info.py @@ -2,18 +2,22 @@ from lxml import etree -from qc_baselib import IssueSeverity +from qc_baselib import IssueSeverity, StatusType from qc_opendrive import constants from qc_opendrive.base import models, utils -from qc_opendrive.checks.performance import performance_constants +from qc_opendrive import basic_preconditions + +CHECKER_ID = "check_asam_xodr_performance_avoid_redundant_info" +CHECKER_DESCRIPTION = "Redundant elements should be avoided." +CHECKER_PRECONDITIONS = basic_preconditions.CHECKER_PRECONDITIONS +RULE_UID = "asam.net:xodr:1.7.0:performance.avoid_redundant_info" -RULE_INITIAL_SUPPORTED_SCHEMA_VERSION = "1.7.0" FLOAT_TOLERANCE = 1e-6 def _check_road_superelevations( - checker_data: models.CheckerData, road: etree._ElementTree, rule_uid: str + checker_data: models.CheckerData, road: etree._ElementTree ) -> None: superelevation_list = utils.get_road_superelevations(road) for i in range(len(superelevation_list) - 1): @@ -22,15 +26,15 @@ def _check_road_superelevations( if utils.are_same_equations(current_superelevation, next_superelevation): issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=performance_constants.CHECKER_ID, + checker_id=CHECKER_ID, description=f"Redudant superelevation declaration.", level=IssueSeverity.WARNING, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=performance_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(road), description=f"", @@ -42,7 +46,7 @@ def _check_road_superelevations( if inertial_point is not None: checker_data.result.add_inertial_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=performance_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, x=inertial_point.x, y=inertial_point.y, @@ -52,7 +56,7 @@ def _check_road_superelevations( def _check_road_elevations( - checker_data: models.CheckerData, road: etree._ElementTree, rule_uid: str + checker_data: models.CheckerData, road: etree._ElementTree ) -> None: elevation_list = utils.get_road_elevations(road) for i in range(len(elevation_list) - 1): @@ -61,15 +65,15 @@ def _check_road_elevations( if utils.are_same_equations(current_elevation, next_elevation): issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=performance_constants.CHECKER_ID, + checker_id=CHECKER_ID, description=f"Redudant elevation declaration.", level=IssueSeverity.WARNING, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=performance_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(road), description=f"", @@ -81,7 +85,7 @@ def _check_road_elevations( if inertial_point is not None: checker_data.result.add_inertial_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=performance_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, x=inertial_point.x, y=inertial_point.y, @@ -91,7 +95,7 @@ def _check_road_elevations( def _check_lane_offsets( - checker_data: models.CheckerData, road: etree._ElementTree, rule_uid: str + checker_data: models.CheckerData, road: etree._ElementTree ) -> None: lane_offset_list = utils.get_lane_offsets_from_road(road) for i in range(len(lane_offset_list) - 1): @@ -100,15 +104,15 @@ def _check_lane_offsets( if utils.are_same_equations(current_lane_offset, next_lane_offset): issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=performance_constants.CHECKER_ID, + checker_id=CHECKER_ID, description=f"Redudant lane offset declaration.", level=IssueSeverity.WARNING, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=performance_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(road), description=f"", @@ -124,7 +128,7 @@ def _check_lane_offsets( if inertial_point is not None: checker_data.result.add_inertial_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=performance_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, x=inertial_point.x, y=inertial_point.y, @@ -134,7 +138,7 @@ def _check_lane_offsets( def _check_road_plan_view( - checker_data: models.CheckerData, road: etree._ElementTree, rule_uid: str + checker_data: models.CheckerData, road: etree._ElementTree ) -> None: geometry_list = utils.get_road_plan_view_geometry_list(road) for i in range(len(geometry_list) - 1): @@ -154,15 +158,15 @@ def _check_road_plan_view( ): issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=performance_constants.CHECKER_ID, + checker_id=CHECKER_ID, description=f"Redudant line geometry declaration.", level=IssueSeverity.WARNING, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=performance_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(road), description=f"", @@ -176,7 +180,7 @@ def _check_road_plan_view( if inertial_point is not None: checker_data.result.add_inertial_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=performance_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, x=inertial_point.x, y=inertial_point.y, @@ -190,7 +194,6 @@ def _check_lane_widths( road: etree._ElementTree, lane_section: etree._ElementTree, lane: etree._ElementTree, - rule_uid: str, ) -> None: widths = utils.get_lane_width_poly3_list(lane) for i in range(len(widths) - 1): @@ -199,15 +202,15 @@ def _check_lane_widths( if utils.are_same_equations(current_width, next_width): issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=performance_constants.CHECKER_ID, + checker_id=CHECKER_ID, description=f"Redudant lane width declaration.", level=IssueSeverity.WARNING, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=performance_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(lane), description=f"", @@ -227,7 +230,7 @@ def _check_lane_widths( if inertial_point is not None: checker_data.result.add_inertial_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=performance_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, x=inertial_point.x, y=inertial_point.y, @@ -241,7 +244,6 @@ def _check_lane_borders( road: etree._ElementTree, lane_section: etree._ElementTree, lane: etree._ElementTree, - rule_uid: str, ) -> None: borders = utils.get_borders_from_lane(lane) for i in range(len(borders) - 1): @@ -250,15 +252,15 @@ def _check_lane_borders( if utils.are_same_equations(current_border, next_border): issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=performance_constants.CHECKER_ID, + checker_id=CHECKER_ID, description=f"Redudant lane border declaration.", level=IssueSeverity.WARNING, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=performance_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(lane), description=f"", @@ -278,7 +280,7 @@ def _check_lane_borders( if inertial_point is not None: checker_data.result.add_inertial_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=performance_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, x=inertial_point.x, y=inertial_point.y, @@ -309,32 +311,17 @@ def check_rule(checker_data: models.CheckerData) -> None: """ logging.info("Executing performance.avoid_redundant_info check.") - rule_uid = checker_data.result.register_rule( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=performance_constants.CHECKER_ID, - emanating_entity="asam.net", - standard="xodr", - definition_setting=RULE_INITIAL_SUPPORTED_SCHEMA_VERSION, - rule_full_name="performance.avoid_redundant_info", - ) - - if checker_data.schema_version < RULE_INITIAL_SUPPORTED_SCHEMA_VERSION: - logging.info( - f"Schema version {checker_data.schema_version} not supported. Skipping rule." - ) - return - road_list = utils.get_roads(checker_data.input_file_xml_root) for road in road_list: - _check_road_elevations(checker_data, road, rule_uid) - _check_road_superelevations(checker_data, road, rule_uid) - _check_lane_offsets(checker_data, road, rule_uid) - _check_road_plan_view(checker_data, road, rule_uid) + _check_road_elevations(checker_data, road) + _check_road_superelevations(checker_data, road) + _check_lane_offsets(checker_data, road) + _check_road_plan_view(checker_data, road) lane_sections = utils.get_lane_sections(road) for lane_section in lane_sections: lanes = utils.get_left_and_right_lanes_from_lane_section(lane_section) for lane in lanes: - _check_lane_widths(checker_data, road, lane_section, lane, rule_uid) - _check_lane_borders(checker_data, road, lane_section, lane, rule_uid) + _check_lane_widths(checker_data, road, lane_section, lane) + _check_lane_borders(checker_data, road, lane_section, lane) diff --git a/qc_opendrive/checks/performance/performance_checker.py b/qc_opendrive/checks/performance/performance_checker.py deleted file mode 100644 index 4563fca..0000000 --- a/qc_opendrive/checks/performance/performance_checker.py +++ /dev/null @@ -1,77 +0,0 @@ -import logging - -from lxml import etree - -from qc_baselib import Configuration, Result, StatusType - -from qc_opendrive import constants -from qc_opendrive.base import models, utils - -from qc_opendrive.checks.performance import ( - performance_constants, - performance_avoid_redundant_info, -) - - -def skip_checks(result: Result) -> None: - - if performance_constants.CHECKER_ID not in result.get_checker_ids( - constants.BUNDLE_NAME - ): - result.register_checker( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=performance_constants.CHECKER_ID, - description="Check if xml properties of input file are properly set", - summary="", - ) - - logging.error( - f"Invalid xml input file. Checker {performance_constants.CHECKER_ID} skipped" - ) - result.set_checker_status( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=performance_constants.CHECKER_ID, - status=StatusType.SKIPPED, - ) - - -def run_checks(config: Configuration, result: Result) -> None: - logging.info("Executing performance checks") - - root = utils.get_root_without_default_namespace( - config.get_config_param("InputFile") - ) - - result.register_checker( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=performance_constants.CHECKER_ID, - description="Evaluates elements in the file to guarantee they are optimized.", - summary="", - ) - - odr_schema_version = utils.get_standard_schema_version(root) - - rule_list = [ - performance_avoid_redundant_info.check_rule, - ] - - checker_data = models.CheckerData( - input_file_xml_root=root, - config=config, - result=result, - schema_version=odr_schema_version, - ) - - for rule in rule_list: - rule(checker_data=checker_data) - - logging.info( - f"Issues found - {result.get_checker_issue_count(checker_bundle_name=constants.BUNDLE_NAME, checker_id=performance_constants.CHECKER_ID)}" - ) - - # TODO: Add logic to deal with error or to skip it - result.set_checker_status( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=performance_constants.CHECKER_ID, - status=StatusType.COMPLETED, - ) diff --git a/qc_opendrive/checks/performance/performance_constants.py b/qc_opendrive/checks/performance/performance_constants.py deleted file mode 100644 index 92a3bdb..0000000 --- a/qc_opendrive/checks/performance/performance_constants.py +++ /dev/null @@ -1 +0,0 @@ -CHECKER_ID = "performance_xodr" diff --git a/qc_opendrive/checks/schema/__init__.py b/qc_opendrive/checks/schema/__init__.py index 9a52ec3..660fc3a 100644 --- a/qc_opendrive/checks/schema/__init__.py +++ b/qc_opendrive/checks/schema/__init__.py @@ -1,3 +1 @@ -from . import schema_constants as schema_constants -from . import schema_checker as schema_checker from . import valid_schema as valid_schema diff --git a/qc_opendrive/checks/schema/schema_checker.py b/qc_opendrive/checks/schema/schema_checker.py deleted file mode 100644 index 69634b8..0000000 --- a/qc_opendrive/checks/schema/schema_checker.py +++ /dev/null @@ -1,83 +0,0 @@ -import logging - -from lxml import etree - -from qc_baselib import Configuration, Result, StatusType - -from qc_opendrive import constants -from qc_opendrive.base import models, utils -from qc_opendrive.schema import schema_files - -from qc_opendrive.checks.schema import ( - schema_constants, - valid_schema, -) - - -def skip_checks(result: Result) -> None: - - if schema_constants.CHECKER_ID not in result.get_checker_ids(constants.BUNDLE_NAME): - result.register_checker( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=schema_constants.CHECKER_ID, - description="Check if xml properties of input file are properly set", - summary="", - ) - - logging.error( - f"Invalid xml input file. Checker {schema_constants.CHECKER_ID} skipped" - ) - result.set_checker_status( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=schema_constants.CHECKER_ID, - status=StatusType.SKIPPED, - ) - - -def run_checks(config: Configuration, result: Result) -> None: - logging.info("Executing schema checks") - - root = utils.get_root_without_default_namespace( - config.get_config_param("InputFile") - ) - result.register_checker( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=schema_constants.CHECKER_ID, - description="Check if xml properties of input file are properly set", - summary="", - ) - odr_schema_version = utils.get_standard_schema_version(root) - - checker_data = models.CheckerData( - input_file_xml_root=root, - config=config, - result=result, - schema_version=odr_schema_version, - ) - if checker_data.schema_version not in schema_files.SCHEMA_FILES: - - logging.error( - f"Version {checker_data.schema_version} unsupported. Checker {schema_constants.CHECKER_ID} skipped" - ) - result.set_checker_status( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=schema_constants.CHECKER_ID, - status=StatusType.SKIPPED, - ) - return - - rule_list = [valid_schema.check_rule] - - for rule in rule_list: - rule(checker_data=checker_data) - - logging.info( - f"Issues found - {checker_data.result.get_checker_issue_count(checker_bundle_name=constants.BUNDLE_NAME, checker_id=schema_constants.CHECKER_ID)}" - ) - - # TODO: Add logic to deal with error or to skip it - checker_data.result.set_checker_status( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=schema_constants.CHECKER_ID, - status=StatusType.COMPLETED, - ) diff --git a/qc_opendrive/checks/schema/schema_constants.py b/qc_opendrive/checks/schema/schema_constants.py deleted file mode 100644 index cefccf1..0000000 --- a/qc_opendrive/checks/schema/schema_constants.py +++ /dev/null @@ -1 +0,0 @@ -CHECKER_ID = "schema_xodr" diff --git a/qc_opendrive/checks/schema/valid_schema.py b/qc_opendrive/checks/schema/valid_schema.py index dc43167..557f16f 100644 --- a/qc_opendrive/checks/schema/valid_schema.py +++ b/qc_opendrive/checks/schema/valid_schema.py @@ -1,59 +1,47 @@ import importlib.resources -import os, logging +import logging from dataclasses import dataclass -from typing import List, Tuple -from qc_baselib import Configuration, Result, IssueSeverity +from typing import List + +from qc_baselib import IssueSeverity, StatusType from qc_opendrive import constants from qc_opendrive.schema import schema_files from qc_opendrive.base import models, utils -from qc_opendrive.checks.schema import schema_constants import xmlschema from lxml import etree +from qc_opendrive.checks.basic import ( + valid_xml_document, + root_tag_is_opendrive, + fileheader_is_present, + version_is_defined, +) + +CHECKER_ID = "check_asam_xodr_xml_valid_schema" +CHECKER_DESCRIPTION = "Input xml file must be valid according to the schema." +CHECKER_PRECONDITIONS = { + valid_xml_document.CHECKER_ID, + root_tag_is_opendrive.CHECKER_ID, + fileheader_is_present.CHECKER_ID, + version_is_defined.CHECKER_ID, +} +RULE_UID = "asam.net:xodr:1.0.0:xml.valid_schema" + + +@dataclass +class SchemaError: + message: str + line: int + column: int + -def find_xpath_from_position(xml_tree, target_line, target_column): - - # Function to build XPath from the element - def build_xpath(element): - path = [] - while element is not None: - siblings = list(element.itersiblings(preceding=True)) - index = len(siblings) + 1 - path.insert(0, f"{element.tag}[{index}]") - element = element.getparent() - return "/" + "/".join(path) - - # Find element by searching for position - def find_element_by_position(element): - if hasattr(element, "sourceline") and element.sourceline == target_line: - # Rough estimation of column position - # 'position' attribute might not always be available, so this is approximate - if hasattr(element, "position") and element.position[1] == target_column: - return element - for child in element: - found = find_element_by_position(child) - if found: - return found - return None - - # Start from the root element - root = xml_tree.getroot() - element = find_element_by_position(root) - - # If element is found, build its XPath - if element is not None: - return build_xpath(element) - else: - return "Element not found" - - -def _is_schema_compliant( +def _get_schema_errors( xml_file: str, schema_file: str, schema_version: str -) -> tuple[bool, List[Tuple]]: +) -> List[SchemaError]: """Check if input xml tree is valid against the input schema file (.xsd) Args: @@ -65,39 +53,40 @@ def _is_schema_compliant( """ split_result = schema_version.split(".") - major = int(split_result[0]) - minor = int(split_result[1]) + major = utils.to_int(split_result[0]) + minor = utils.to_int(split_result[1]) errors = [] + if major is None or minor is None: + return False, errors + # use LXML for XSD 1.0 with better error level -> OpenDRIVE 1.7 and lower if major <= 1 and minor <= 7: schema = etree.XMLSchema(etree.parse(schema_file)) xml_tree = etree.parse(xml_file) - result = schema.validate(xml_tree) + schema.validate(xml_tree) for error in schema.error_log: errors.append( - ( - find_xpath_from_position(xml_tree, error.line, error.column), - error.message, + SchemaError( + message=error.message, + line=error.line, + column=error.column, ) ) else: # use xmlschema to support XSD schema 1.1 -> OpenDRIVE 1.8 and higher schema = xmlschema.XMLSchema11(schema_file) # Iterate over all validation errors - for error in schema.iter_errors(xml_file): - errors.append((error.path, error.message)) - - # Return True and None if there are no errors, otherwise False and the list of errors - if not errors: - logging.info("- XML is valid.") - return True, None - else: - logging.error("- XML is invalid!") - for error in errors: - logging.error(f"- Error: {error[1]}") - logging.error(f"- Path: {error[0]}") + xml_doc = etree.parse(xml_file) + for error in schema.iter_errors(xml_doc): + errors.append( + SchemaError( + message=error.reason, + line=error.sourceline, + column=0, + ) + ) - return False, errors + return errors def check_rule(checker_data: models.CheckerData) -> None: @@ -110,43 +99,45 @@ def check_rule(checker_data: models.CheckerData) -> None: logging.info("Executing valid_schema check") schema_version = checker_data.schema_version - if schema_version is None: - logging.info(f"- Version not found in the file. Skipping check") - return - rule_uid = checker_data.result.register_rule( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=schema_constants.CHECKER_ID, - emanating_entity="asam.net", - standard="xodr", - definition_setting="1.0.0", - rule_full_name="xml.valid_schema", - ) + xsd_file = schema_files.SCHEMA_FILES.get(schema_version) - schema_files_dict = schema_files.SCHEMA_FILES + if xsd_file is None: + checker_data.result.set_checker_status( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=CHECKER_ID, + status=StatusType.SKIPPED, + ) + + checker_data.result.add_checker_summary( + constants.BUNDLE_NAME, + CHECKER_ID, + f"Cannot find the schema file for ASAM OpenDrive version {schema_version}.", + ) + + return - xsd_file = schema_files_dict[schema_version] xsd_file_path = str( importlib.resources.files("qc_opendrive.schema").joinpath(xsd_file) ) - schema_compliant, errors = _is_schema_compliant( + errors = _get_schema_errors( checker_data.config.get_config_param("InputFile"), xsd_file_path, schema_version ) - if not schema_compliant: - - for error in errors: - issue_id = checker_data.result.register_issue( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=schema_constants.CHECKER_ID, - description="Issue flagging when input file does not follow its version schema", - level=IssueSeverity.ERROR, - rule_uid=rule_uid, - ) - checker_data.result.add_xml_location( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=schema_constants.CHECKER_ID, - issue_id=issue_id, - xpath=error[0], - description=error[1], - ) + for error in errors: + issue_id = checker_data.result.register_issue( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=CHECKER_ID, + description="Issue flagging when input file does not follow its version schema", + level=IssueSeverity.ERROR, + rule_uid=RULE_UID, + ) + + checker_data.result.add_file_location( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=CHECKER_ID, + issue_id=issue_id, + row=error.line, + column=error.column, + description=error.message, + ) diff --git a/qc_opendrive/checks/semantic/__init__.py b/qc_opendrive/checks/semantic/__init__.py index 8163e9a..477279f 100644 --- a/qc_opendrive/checks/semantic/__init__.py +++ b/qc_opendrive/checks/semantic/__init__.py @@ -1,5 +1,3 @@ -from . import semantic_constants as semantic_constants -from . import semantic_checker as semantic_checker from . import ( road_lane_access_no_mix_of_deny_or_allow as road_lane_access_no_mix_of_deny_or_allow, ) @@ -25,3 +23,6 @@ from . import ( junctions_connection_end_opposite_linkage as junctions_connection_end_opposite_linkage, ) +from . import ( + junctions_connection_connect_road_no_incoming_road as junctions_connection_connect_road_no_incoming_road, +) diff --git a/qc_opendrive/checks/semantic/junctions_connection_connect_road_no_incoming_road.py b/qc_opendrive/checks/semantic/junctions_connection_connect_road_no_incoming_road.py index 806b40b..08849fb 100644 --- a/qc_opendrive/checks/semantic/junctions_connection_connect_road_no_incoming_road.py +++ b/qc_opendrive/checks/semantic/junctions_connection_connect_road_no_incoming_road.py @@ -1,19 +1,19 @@ import logging -from typing import Dict, List -from lxml import etree - -from qc_baselib import IssueSeverity +from qc_baselib import IssueSeverity, StatusType from qc_opendrive import constants from qc_opendrive.base import models, utils -from qc_opendrive.checks.semantic import semantic_constants +from qc_opendrive import basic_preconditions -RULE_INITIAL_SUPPORTED_SCHEMA_VERSION = "1.4.0" +CHECKER_ID = "check_asam_xodr_junctions_connection_connect_road_no_incoming_road" +CHECKER_DESCRIPTION = "Connecting roads shall not be incoming roads." +CHECKER_PRECONDITIONS = basic_preconditions.CHECKER_PRECONDITIONS +RULE_UID = "asam.net:xodr:1.4.0:junctions.connection.connect_road_no_incoming_road" def _check_junctions_connection_connect_road_no_incoming_road( - checker_data: models.CheckerData, rule_uid: str + checker_data: models.CheckerData, ) -> None: junctions = utils.get_junctions(checker_data.input_file_xml_root) road_id_map = utils.get_road_id_map(checker_data.input_file_xml_root) @@ -23,20 +23,25 @@ def _check_junctions_connection_connect_road_no_incoming_road( for connection in connections: incoming_road_id = utils.get_incoming_road_id_from_connection(connection) - incoming_road = road_id_map[incoming_road_id] + if incoming_road_id is None: + continue + + incoming_road = road_id_map.get(incoming_road_id) + if incoming_road is None: + continue if utils.road_belongs_to_junction(incoming_road): issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, description=f"Connecting roads shall not be incoming roads.", level=IssueSeverity.ERROR, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(connection), description="Connection with connecting road found as incoming road.", @@ -73,7 +78,7 @@ def _check_junctions_connection_connect_road_no_incoming_road( if inertial_point is not None: checker_data.result.add_inertial_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, x=inertial_point.x, y=inertial_point.y, @@ -92,19 +97,4 @@ def check_rule(checker_data: models.CheckerData) -> None: """ logging.info("Executing junctions.connection.connect_road_no_incoming_road check") - rule_uid = checker_data.result.register_rule( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, - emanating_entity="asam.net", - standard="xodr", - definition_setting=RULE_INITIAL_SUPPORTED_SCHEMA_VERSION, - rule_full_name="junctions.connection.connect_road_no_incoming_road", - ) - - if checker_data.schema_version < RULE_INITIAL_SUPPORTED_SCHEMA_VERSION: - logging.info( - f"Schema version {checker_data.schema_version} not supported. Skipping rule." - ) - return - - _check_junctions_connection_connect_road_no_incoming_road(checker_data, rule_uid) + _check_junctions_connection_connect_road_no_incoming_road(checker_data) diff --git a/qc_opendrive/checks/semantic/junctions_connection_end_opposite_linkage.py b/qc_opendrive/checks/semantic/junctions_connection_end_opposite_linkage.py index f15dd30..98aea6e 100644 --- a/qc_opendrive/checks/semantic/junctions_connection_end_opposite_linkage.py +++ b/qc_opendrive/checks/semantic/junctions_connection_end_opposite_linkage.py @@ -2,31 +2,33 @@ from lxml import etree -from qc_baselib import IssueSeverity +from qc_baselib import IssueSeverity, StatusType from qc_opendrive import constants from qc_opendrive.base import models, utils -from qc_opendrive.checks.semantic import semantic_constants +from qc_opendrive import basic_preconditions -RULE_INITIAL_SUPPORTED_SCHEMA_VERSION = "1.7.0" +CHECKER_ID = "check_asam_xodr_junctions_connection_end_opposite_linkage" +CHECKER_DESCRIPTION = 'The value "end" shall be used to indicate that the connectingroad runs along the opposite direction of the linkage indicated in the element.' +CHECKER_PRECONDITIONS = basic_preconditions.CHECKER_PRECONDITIONS +RULE_UID = "asam.net:xodr:1.7.0:junctions.connection.end_opposite_linkage" def _raise_issue( checker_data: models.CheckerData, - rule_uid: str, connection: etree._Element, connection_road: etree._Element, ): issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, description=f"The value 'end' shall be used to indicate that the connecting road runs along the opposite direction of the linkage indicated in the element.", level=IssueSeverity.ERROR, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(connection), description=f"Contact point 'end' not used on successor road connection.", @@ -36,7 +38,7 @@ def _raise_issue( if inertial_point is not None: checker_data.result.add_inertial_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, x=inertial_point.x, y=inertial_point.y, @@ -46,7 +48,7 @@ def _raise_issue( def _check_junction_connection_end_opposite_linkage( - checker_data: models.CheckerData, rule_uid: str + checker_data: models.CheckerData, ) -> None: junctions = utils.get_junctions(checker_data.input_file_xml_root) road_id_map = utils.get_road_id_map(checker_data.input_file_xml_root) @@ -84,7 +86,7 @@ def _check_junction_connection_end_opposite_linkage( continue if successor_linkage.id != incoming_road_id: - _raise_issue(checker_data, rule_uid, connection, connection_road) + _raise_issue(checker_data, connection, connection_road) def check_rule(checker_data: models.CheckerData) -> None: @@ -106,19 +108,4 @@ def check_rule(checker_data: models.CheckerData) -> None: """ logging.info("Executing junctions.connection.end_opposite_linkage check") - rule_uid = checker_data.result.register_rule( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, - emanating_entity="asam.net", - standard="xodr", - definition_setting=RULE_INITIAL_SUPPORTED_SCHEMA_VERSION, - rule_full_name="junctions.connection.end_opposite_linkage", - ) - - if checker_data.schema_version < RULE_INITIAL_SUPPORTED_SCHEMA_VERSION: - logging.info( - f"Schema version {checker_data.schema_version} not supported. Skipping rule." - ) - return - - _check_junction_connection_end_opposite_linkage(checker_data, rule_uid) + _check_junction_connection_end_opposite_linkage(checker_data) diff --git a/qc_opendrive/checks/semantic/junctions_connection_one_connection_element.py b/qc_opendrive/checks/semantic/junctions_connection_one_connection_element.py index dca45ca..dacabf7 100644 --- a/qc_opendrive/checks/semantic/junctions_connection_one_connection_element.py +++ b/qc_opendrive/checks/semantic/junctions_connection_one_connection_element.py @@ -3,17 +3,20 @@ from typing import Dict, List from lxml import etree -from qc_baselib import IssueSeverity +from qc_baselib import IssueSeverity, StatusType from qc_opendrive import constants from qc_opendrive.base import models, utils -from qc_opendrive.checks.semantic import semantic_constants +from qc_opendrive import basic_preconditions -RULE_INITIAL_SUPPORTED_SCHEMA_VERSION = "1.7.0" +CHECKER_ID = "check_asam_xodr_junctions_connection_one_connection_element" +CHECKER_DESCRIPTION = "Each connecting road shall be represented by exactly one element. A connecting road may contain as many lanes as required." +CHECKER_PRECONDITIONS = basic_preconditions.CHECKER_PRECONDITIONS +RULE_UID = "asam.net:xodr:1.7.0:junctions.connection.one_connection_element" def _check_junctions_connection_one_connection_element( - checker_data: models.CheckerData, rule_uid: str + checker_data: models.CheckerData, ) -> None: junctions = utils.get_junctions(checker_data.input_file_xml_root) @@ -43,16 +46,16 @@ def _check_junctions_connection_one_connection_element( # we raise 1 issue with all repeated locations for each repeated id issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, description=f"Connecting road {connecting_road_id} shall be represented by only one element.", level=IssueSeverity.ERROR, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) for connection in connections: checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(connection), description="Connection with reused connecting road id.", @@ -67,7 +70,7 @@ def _check_junctions_connection_one_connection_element( if inertial_point is not None: checker_data.result.add_inertial_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, x=inertial_point.x, y=inertial_point.y, @@ -97,19 +100,4 @@ def check_rule(checker_data: models.CheckerData) -> None: """ logging.info("Executing junctions.connection.one_connection_element check") - rule_uid = checker_data.result.register_rule( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, - emanating_entity="asam.net", - standard="xodr", - definition_setting=RULE_INITIAL_SUPPORTED_SCHEMA_VERSION, - rule_full_name="junctions.connection.one_connection_element", - ) - - if checker_data.schema_version != RULE_INITIAL_SUPPORTED_SCHEMA_VERSION: - logging.info( - f"Schema version {checker_data.schema_version} not supported. Skipping rule." - ) - return - - _check_junctions_connection_one_connection_element(checker_data, rule_uid) + _check_junctions_connection_one_connection_element(checker_data) diff --git a/qc_opendrive/checks/semantic/junctions_connection_one_link_to_incoming.py b/qc_opendrive/checks/semantic/junctions_connection_one_link_to_incoming.py index 2bb0096..edb3993 100644 --- a/qc_opendrive/checks/semantic/junctions_connection_one_link_to_incoming.py +++ b/qc_opendrive/checks/semantic/junctions_connection_one_link_to_incoming.py @@ -3,18 +3,20 @@ from typing import Dict, List from lxml import etree -from qc_baselib import IssueSeverity +from qc_baselib import IssueSeverity, StatusType from qc_opendrive import constants from qc_opendrive.base import models, utils -from qc_opendrive.checks.semantic import semantic_constants +from qc_opendrive import basic_preconditions -RULE_INITIAL_SUPPORTED_SCHEMA_VERSION = "1.8.0" +CHECKER_ID = "check_asam_xodr_junctions_connection_one_link_to_incoming" +CHECKER_DESCRIPTION = "Each connecting road shall be associated with at most one element per incoming road. A connecting road shall only have the element for that direction." +CHECKER_PRECONDITIONS = basic_preconditions.CHECKER_PRECONDITIONS +RULE_UID = "asam.net:xodr:1.8.0:junctions.connection.one_link_to_incoming" def _raise_lane_linkage_issue( checker_data: models.CheckerData, - rule_uid: str, lane_link: etree._Element, connecting_road: etree._Element, connecting_lane_section: etree._Element, @@ -25,14 +27,14 @@ def _raise_lane_linkage_issue( # of the connecting road issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, description=f"A connecting road shall only have the element for that direction.", level=IssueSeverity.ERROR, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(lane_link), description=f"Lane link in opposite direction.", @@ -53,7 +55,7 @@ def _raise_lane_linkage_issue( if inertial_point is not None: checker_data.result.add_inertial_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, x=inertial_point.x, y=inertial_point.y, @@ -364,7 +366,6 @@ def _check_connection_lane_link_same_direction( checker_data: models.CheckerData, road_id_map: Dict[int, etree._ElementTree], connection: etree._Element, - rule_uid: str, ) -> None: connection_contact_point = utils.get_contact_point_from_connection(connection) @@ -439,7 +440,6 @@ def _check_connection_lane_link_same_direction( ): _raise_lane_linkage_issue( checker_data, - rule_uid, lane_link, connecting_road, contacting_lane_sections.connection, @@ -458,7 +458,6 @@ def _check_connection_lane_link_same_direction( ): _raise_lane_linkage_issue( checker_data, - rule_uid, lane_link, connecting_road, contacting_lane_sections.connection, @@ -479,7 +478,6 @@ def _check_connection_lane_link_same_direction( ): _raise_lane_linkage_issue( checker_data, - rule_uid, lane_link, connecting_road, contacting_lane_sections.connection, @@ -498,7 +496,6 @@ def _check_connection_lane_link_same_direction( ): _raise_lane_linkage_issue( checker_data, - rule_uid, lane_link, connecting_road, contacting_lane_sections.connection, @@ -508,7 +505,7 @@ def _check_connection_lane_link_same_direction( def _check_junctions_connection_one_link_to_incoming( - checker_data: models.CheckerData, rule_uid: str + checker_data: models.CheckerData, ) -> None: junctions = utils.get_junctions(checker_data.input_file_xml_root) road_id_map = utils.get_road_id_map(checker_data.input_file_xml_root) @@ -537,7 +534,7 @@ def _check_junctions_connection_one_link_to_incoming( ) _check_connection_lane_link_same_direction( - checker_data, road_id_map, connection, rule_uid + checker_data, road_id_map, connection ) for incoming_road_id, connecting_road_map in connection_road_link_map.items(): @@ -547,15 +544,15 @@ def _check_junctions_connection_one_link_to_incoming( # appears in more than one connection. issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, description=f"Connecting road {connecting_road_id} shall be represented by at most one element per incoming road id.", level=IssueSeverity.ERROR, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) for connection in connections: checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(connection), description=f"Connection with reused (incoming_road_id, connecting_road_id) = ({incoming_road_id}, {connecting_road_id}) pair.", @@ -587,7 +584,7 @@ def _check_junctions_connection_one_link_to_incoming( if inertial_point is not None: checker_data.result.add_inertial_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, x=inertial_point.x, y=inertial_point.y, @@ -602,7 +599,7 @@ def _check_junctions_connection_one_link_to_incoming( if inertial_point is not None: checker_data.result.add_inertial_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, x=inertial_point.x, y=inertial_point.y, @@ -633,19 +630,4 @@ def check_rule(checker_data: models.CheckerData) -> None: """ logging.info("Executing junctions.connection.one_link_to_incoming check") - rule_uid = checker_data.result.register_rule( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, - emanating_entity="asam.net", - standard="xodr", - definition_setting=RULE_INITIAL_SUPPORTED_SCHEMA_VERSION, - rule_full_name="junctions.connection.one_link_to_incoming", - ) - - if checker_data.schema_version < RULE_INITIAL_SUPPORTED_SCHEMA_VERSION: - logging.info( - f"Schema version {checker_data.schema_version} not supported. Skipping rule." - ) - return - - _check_junctions_connection_one_link_to_incoming(checker_data, rule_uid) + _check_junctions_connection_one_link_to_incoming(checker_data) diff --git a/qc_opendrive/checks/semantic/junctions_connection_start_along_linkage.py b/qc_opendrive/checks/semantic/junctions_connection_start_along_linkage.py index 4be38e5..56dcdae 100644 --- a/qc_opendrive/checks/semantic/junctions_connection_start_along_linkage.py +++ b/qc_opendrive/checks/semantic/junctions_connection_start_along_linkage.py @@ -2,31 +2,33 @@ from lxml import etree -from qc_baselib import IssueSeverity +from qc_baselib import IssueSeverity, StatusType from qc_opendrive import constants from qc_opendrive.base import models, utils -from qc_opendrive.checks.semantic import semantic_constants +from qc_opendrive import basic_preconditions -RULE_INITIAL_SUPPORTED_SCHEMA_VERSION = "1.7.0" +CHECKER_ID = "check_asam_xodr_junctions_connection_start_along_linkage" +CHECKER_DESCRIPTION = 'The value "start" shall be used to indicate that the connecting road runs along the linkage indicated in the element.' +CHECKER_PRECONDITIONS = basic_preconditions.CHECKER_PRECONDITIONS +RULE_UID = "asam.net:xodr:1.7.0:junctions.connection.start_along_linkage" def _raise_issue( checker_data: models.CheckerData, - rule_uid: str, connection: etree._Element, connection_road: etree._Element, ): issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, description=f"The value 'start' shall be used to indicate that the connecting road runs along the linkage indicated in the element.", level=IssueSeverity.ERROR, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(connection), description=f"Contact point 'start' not used on predecessor road connection.", @@ -36,7 +38,7 @@ def _raise_issue( if inertial_point is not None: checker_data.result.add_inertial_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, x=inertial_point.x, y=inertial_point.y, @@ -46,7 +48,7 @@ def _raise_issue( def _check_junction_connection_start_along_linkage( - checker_data: models.CheckerData, rule_uid: str + checker_data: models.CheckerData, ) -> None: junctions = utils.get_junctions(checker_data.input_file_xml_root) road_id_map = utils.get_road_id_map(checker_data.input_file_xml_root) @@ -84,7 +86,7 @@ def _check_junction_connection_start_along_linkage( continue if predecessor_linkage.id != incoming_road_id: - _raise_issue(checker_data, rule_uid, connection, connection_road) + _raise_issue(checker_data, connection, connection_road) def check_rule(checker_data: models.CheckerData) -> None: @@ -106,19 +108,4 @@ def check_rule(checker_data: models.CheckerData) -> None: """ logging.info("Executing junctions.connection.start_along_linkage check") - rule_uid = checker_data.result.register_rule( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, - emanating_entity="asam.net", - standard="xodr", - definition_setting=RULE_INITIAL_SUPPORTED_SCHEMA_VERSION, - rule_full_name="junctions.connection.start_along_linkage", - ) - - if checker_data.schema_version < RULE_INITIAL_SUPPORTED_SCHEMA_VERSION: - logging.info( - f"Schema version {checker_data.schema_version} not supported. Skipping rule." - ) - return - - _check_junction_connection_start_along_linkage(checker_data, rule_uid) + _check_junction_connection_start_along_linkage(checker_data) diff --git a/qc_opendrive/checks/semantic/road_lane_access_no_mix_of_deny_or_allow.py b/qc_opendrive/checks/semantic/road_lane_access_no_mix_of_deny_or_allow.py index 49acba1..f753808 100644 --- a/qc_opendrive/checks/semantic/road_lane_access_no_mix_of_deny_or_allow.py +++ b/qc_opendrive/checks/semantic/road_lane_access_no_mix_of_deny_or_allow.py @@ -5,12 +5,20 @@ from lxml import etree -from qc_baselib import Configuration, Result, IssueSeverity +from qc_baselib import IssueSeverity, StatusType from qc_opendrive import constants from qc_opendrive.base import models, utils -from qc_opendrive.checks.semantic import semantic_constants +from qc_opendrive import basic_preconditions + + +CHECKER_ID = "check_asam_xodr_road_lane_access_no_mix_of_deny_or_allow" +CHECKER_DESCRIPTION = ( + "Check if there is mixed content on access rules for the same sOffset on lanes." +) +CHECKER_PRECONDITIONS = basic_preconditions.CHECKER_PRECONDITIONS +RULE_UID = "asam.net:xodr:1.7.0:road.lane.access.no_mix_of_deny_or_allow" @dataclass @@ -19,34 +27,7 @@ class SOffsetInfo: rule: str -RULE_INITIAL_SUPPORTED_SCHEMA_VERSION = "1.7.0" - - -def check_rule(checker_data: models.CheckerData) -> None: - """ - Implements a rule to check if there is mixed content on access rules for - the same sOffset on lanes. - - More info at - - https://github.com/asam-ev/qc-opendrive/issues/1 - """ - logging.info("Executing road.lane.access.no_mix_of_deny_or_allow check") - - if checker_data.schema_version < RULE_INITIAL_SUPPORTED_SCHEMA_VERSION: - logging.info( - f"Schema version {checker_data.schema_version} not supported. Skipping rule." - ) - return - - rule_uid = checker_data.result.register_rule( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, - emanating_entity="asam.net", - standard="xodr", - definition_setting=RULE_INITIAL_SUPPORTED_SCHEMA_VERSION, - rule_full_name="road.lane.access.no_mix_of_deny_or_allow", - ) - +def _check_all_roads(checker_data: models.CheckerData) -> None: roads = utils.get_roads(checker_data.input_file_xml_root) for road in roads: @@ -65,66 +46,81 @@ def check_rule(checker_data: models.CheckerData) -> None: access: etree._Element for access in lane.iter("access"): - access_attr = access.attrib - - if "rule" in access_attr: - s_offset = float(access_attr["sOffset"]) - rule = access_attr["rule"] - - for s_offset_info in access_s_offset_info: - if ( - abs(s_offset_info.s_offset - s_offset) <= 1e-6 - and rule != s_offset_info.rule - ): - issue_id = checker_data.result.register_issue( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, - description="At a given s-position, either only deny or only allow values shall be given, not mixed.", - level=IssueSeverity.ERROR, - rule_uid=rule_uid, - ) + rule = access.get("rule") + if rule is None: + continue + + s_offset = utils.get_s_offset_from_access(access) + if s_offset is None: + continue + + for s_offset_info in access_s_offset_info: + if ( + abs(s_offset_info.s_offset - s_offset) <= 1e-6 + and rule != s_offset_info.rule + ): + issue_id = checker_data.result.register_issue( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=CHECKER_ID, + description="At a given s-position, either only deny or only allow values shall be given, not mixed.", + level=IssueSeverity.ERROR, + rule_uid=RULE_UID, + ) - path = checker_data.input_file_xml_root.getpath(access) + path = checker_data.input_file_xml_root.getpath(access) - previous_rule = s_offset_info.rule - current_rule = access_attr["rule"] + previous_rule = s_offset_info.rule + current_rule = rule - checker_data.result.add_xml_location( + checker_data.result.add_xml_location( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=CHECKER_ID, + issue_id=issue_id, + xpath=path, + description=f"First encounter of {current_rule} having {previous_rule} before.", + ) + + if s_section is None: + continue + + s = s_section + s_offset + (length - s_offset) / 2.0 + t = utils.get_t_middle_point_from_lane_by_s( + road, lane_section, lane, s + ) + + if t is None: + continue + + inertial_point = utils.get_point_xyz_from_road( + road, s, t, 0.0 + ) + if inertial_point is not None: + checker_data.result.add_inertial_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, - xpath=path, - description=f"First encounter of {current_rule} having {previous_rule} before.", + x=inertial_point.x, + y=inertial_point.y, + z=inertial_point.z, + description="Mixed access point.", ) - if s_section is None: - continue + access_s_offset_info.append( + SOffsetInfo( + s_offset=s_offset, + rule=rule, + ) + ) - s = s_section + s_offset + (length - s_offset) / 2.0 - t = utils.get_t_middle_point_from_lane_by_s( - road, lane_section, lane, s - ) - if t is None: - continue +def check_rule(checker_data: models.CheckerData) -> None: + """ + Implements a rule to check if there is mixed content on access rules for + the same sOffset on lanes. + + More info at + - https://github.com/asam-ev/qc-opendrive/issues/1 + """ + logging.info("Executing road.lane.access.no_mix_of_deny_or_allow check") - inertial_point = utils.get_point_xyz_from_road( - road, s, t, 0.0 - ) - if inertial_point is not None: - checker_data.result.add_inertial_location( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, - issue_id=issue_id, - x=inertial_point.x, - y=inertial_point.y, - z=inertial_point.z, - description="Mixed access point.", - ) - - access_s_offset_info.append( - SOffsetInfo( - s_offset=float(access_attr["sOffset"]), - rule=access_attr["rule"], - ) - ) + _check_all_roads(checker_data) diff --git a/qc_opendrive/checks/semantic/road_lane_level_true_one_side.py b/qc_opendrive/checks/semantic/road_lane_level_true_one_side.py index 7263aef..c8e56b1 100644 --- a/qc_opendrive/checks/semantic/road_lane_level_true_one_side.py +++ b/qc_opendrive/checks/semantic/road_lane_level_true_one_side.py @@ -1,18 +1,21 @@ -from dataclasses import dataclass import logging -from typing import Union, List, Dict, Set -from enum import Enum +from typing import List, Dict, Set from lxml import etree -from qc_baselib import Configuration, Result, IssueSeverity +from qc_baselib import Result, IssueSeverity, StatusType from qc_opendrive import constants from qc_opendrive.base import models, utils -from qc_opendrive.checks.semantic import semantic_constants +from qc_opendrive import basic_preconditions -RULE_INITIAL_SUPPORTED_SCHEMA_VERSION = "1.7.0" +CHECKER_ID = "check_asam_xodr_road_lane_level_true_one_side" +CHECKER_DESCRIPTION = ( + "Check if there is any @Level=False after being True until the lane border." +) +CHECKER_PRECONDITIONS = basic_preconditions.CHECKER_PRECONDITIONS +RULE_UID = "asam.net:xodr:1.7.0:road.lane.level_true_one_side" def _check_true_level_on_side( @@ -21,7 +24,6 @@ def _check_true_level_on_side( road: etree._ElementTree, lane_section_with_length: models.LaneSectionWithLength, result: Result, - rule_uid: str, ) -> None: """ Check on a sorted list of lanes if any false level occurs after a true. @@ -33,58 +35,53 @@ def _check_true_level_on_side( found_true_level = False for index, lane in enumerate(side_lanes): - lane_attrib = lane.attrib + lane_level = utils.get_lane_level_from_lane(lane) - if "level" in lane_attrib: - lane_level = utils.xml_string_to_bool(lane_attrib["level"]) + if lane_level == True: + found_true_level = True + + elif lane_level == False and found_true_level == True: + # lane_level is False when previous lane_level was True before + issue_id = result.register_issue( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=CHECKER_ID, + description="Lane level False encountered on same side after True set.", + level=IssueSeverity.ERROR, + rule_uid=RULE_UID, + ) - if lane_level == True: - found_true_level = True + path = root.getpath(lane) - elif lane_level == False and found_true_level == True: - # lane_level is False when previous lane_level was True before - issue_id = result.register_issue( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, - description="Lane level False encountered on same side after True set.", - level=IssueSeverity.ERROR, - rule_uid=rule_uid, - ) + result.add_xml_location( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=CHECKER_ID, + issue_id=issue_id, + xpath=path, + description=f"Lane id {index} @level=False where previous lane id @level=True.", + ) - path = root.getpath(lane) + s_section = utils.get_s_from_lane_section( + lane_section_with_length.lane_section + ) + if s_section is None: + continue - result.add_xml_location( + s = s_section + lane_section_with_length.length / 2.0 + + inertial_point = utils.get_middle_point_xyz_at_height_zero_from_lane_by_s( + road, lane_section_with_length.lane_section, lane, s + ) + if inertial_point is not None: + result.add_inertial_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, - xpath=path, - description=f"Lane id {index} @level=False where previous lane id @level=True.", + x=inertial_point.x, + y=inertial_point.y, + z=inertial_point.z, + description="Lane level false when previous lane level is True.", ) - s_section = utils.get_s_from_lane_section( - lane_section_with_length.lane_section - ) - if s_section is None: - continue - - s = s_section + lane_section_with_length.length / 2.0 - - inertial_point = ( - utils.get_middle_point_xyz_at_height_zero_from_lane_by_s( - road, lane_section_with_length.lane_section, lane, s - ) - ) - if inertial_point is not None: - result.add_inertial_location( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, - issue_id=issue_id, - x=inertial_point.x, - y=inertial_point.y, - z=inertial_point.z, - description="Lane level false when previous lane level is True.", - ) - def _get_linkage_level_warnings( root: etree._ElementTree, @@ -99,10 +96,9 @@ def _get_linkage_level_warnings( for link in lane.findall("link"): for linkage in link.findall(linkage_tag): - linkage_id = linkage.get("id") + linkage_id = utils.to_int(linkage.get("id")) if linkage_id is None: continue - linkage_id = int(linkage_id) linkage_lane = utils.get_lane_from_lane_section( target_lane_section, linkage_id ) @@ -122,7 +118,6 @@ def _check_level_change_between_lane_sections( current_lane_section: etree._ElementTree, previous_lane_section: etree._ElementTree, result: Result, - rule_uid: str, ) -> None: """ Check two consecutive lane section from a road if a false level occurs @@ -154,14 +149,14 @@ def _check_level_change_between_lane_sections( for warning in warnings: issue_id = result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, description="Lane levels are not the same in two consecutive lane sections", level=IssueSeverity.WARNING, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=warning, description="", @@ -173,7 +168,6 @@ def _check_level_change_linkage_roads( road: etree._ElementTree, road_id_map: Dict[int, etree._ElementTree], result: Result, - rule_uid: str, root: etree._ElementTree, ) -> None: if linkage_tag == models.LinkageTag.PREDECESSOR: @@ -218,15 +212,15 @@ def _check_level_change_linkage_roads( if other_lane_level is not None and other_lane_level != lane_level: issue_id = result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, description="Lane levels are not the same between two connected roads.", level=IssueSeverity.WARNING, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=root.getpath(lane), description="", @@ -250,7 +244,7 @@ def _check_level_change_linkage_roads( if inertial_point is not None: result.add_inertial_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, x=inertial_point.x, y=inertial_point.y, @@ -261,7 +255,6 @@ def _check_level_change_linkage_roads( def _check_level_among_lane_sections( checker_data: models.CheckerData, - rule_uid: str, ) -> None: roads = utils.get_roads(checker_data.input_file_xml_root) for road in roads: @@ -274,14 +267,12 @@ def _check_level_among_lane_sections( current_lane_section=lane_sections[i], previous_lane_section=lane_sections[i - 1], result=checker_data.result, - rule_uid=rule_uid, ) def _check_level_among_roads( checker_data: models.CheckerData, road_id_map: Dict[int, etree._ElementTree], - rule_uid: str, ) -> None: roads = utils.get_roads(checker_data.input_file_xml_root) for road in roads: @@ -290,7 +281,6 @@ def _check_level_among_roads( road=road, road_id_map=road_id_map, result=checker_data.result, - rule_uid=rule_uid, root=checker_data.input_file_xml_root, ) _check_level_change_linkage_roads( @@ -298,14 +288,12 @@ def _check_level_among_roads( road=road, road_id_map=road_id_map, result=checker_data.result, - rule_uid=rule_uid, root=checker_data.input_file_xml_root, ) def _check_level_in_lane_section( checker_data: models.CheckerData, - rule_uid: str, ) -> None: roads = utils.get_roads(checker_data.input_file_xml_root) for road in roads: @@ -318,10 +306,18 @@ def _check_level_in_lane_section( left_lanes_list = utils.get_left_lanes_from_lane_section(lane_section) right_lanes_list = utils.get_right_lanes_from_lane_section(lane_section) + left_lanes_list = [ + lane for lane in left_lanes_list if utils.get_lane_id(lane) is not None + ] + + right_lanes_list = [ + lane for lane in right_lanes_list if utils.get_lane_id(lane) is not None + ] + # sort by lane id to guarantee order while checking level # left ids goes monotonic increasing from 1 sorted_left_lane = sorted( - left_lanes_list, key=lambda lane: int(lane.attrib["id"]) + left_lanes_list, key=lambda lane: int(utils.get_lane_id(lane)) ) _check_true_level_on_side( @@ -330,13 +326,12 @@ def _check_level_in_lane_section( road, lane_section_with_length, checker_data.result, - rule_uid, ) # sort by lane abs(id) to guarantee order while checking level # right ids goes monotonic decreasing from -1 sorted_right_lane = sorted( - right_lanes_list, key=lambda lane: abs(int(lane.attrib["id"])) + right_lanes_list, key=lambda lane: abs(utils.get_lane_id(lane)) ) _check_true_level_on_side( @@ -345,14 +340,12 @@ def _check_level_in_lane_section( road, lane_section_with_length, checker_data.result, - rule_uid, ) def _check_level_among_junctions( checker_data: models.CheckerData, road_id_map: Dict[int, etree._ElementTree], - rule_uid: str, ) -> None: for junction in utils.get_junctions(checker_data.input_file_xml_root): for connection in utils.get_connections_from_junction(junction): @@ -388,15 +381,15 @@ def _check_level_among_junctions( if incoming_level != connection_level: issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, description="Lane levels are not the same between incoming road and junction.", level=IssueSeverity.WARNING, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(incoming_lane), description="", @@ -404,15 +397,15 @@ def _check_level_among_junctions( issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, description="Lane levels are not the same between junction and incoming road.", level=IssueSeverity.WARNING, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(connection_lane), description="", @@ -429,24 +422,9 @@ def check_rule(checker_data: models.CheckerData) -> None: """ logging.info("Executing road.lane.level.true.one_side check") - rule_uid = checker_data.result.register_rule( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, - emanating_entity="asam.net", - standard="xodr", - definition_setting=RULE_INITIAL_SUPPORTED_SCHEMA_VERSION, - rule_full_name="road.lane.level_true_one_side", - ) - - if checker_data.schema_version < RULE_INITIAL_SUPPORTED_SCHEMA_VERSION: - logging.info( - f"Schema version {checker_data.schema_version} not supported. Skipping rule." - ) - return - road_id_map = utils.get_road_id_map(checker_data.input_file_xml_root) - _check_level_in_lane_section(checker_data, rule_uid) - _check_level_among_lane_sections(checker_data, rule_uid) - _check_level_among_roads(checker_data, road_id_map, rule_uid) - _check_level_among_junctions(checker_data, road_id_map, rule_uid) + _check_level_in_lane_section(checker_data) + _check_level_among_lane_sections(checker_data) + _check_level_among_roads(checker_data, road_id_map) + _check_level_among_junctions(checker_data, road_id_map) diff --git a/qc_opendrive/checks/semantic/road_lane_link_lanes_across_lane_sections.py b/qc_opendrive/checks/semantic/road_lane_link_lanes_across_lane_sections.py index 6427f87..825914c 100644 --- a/qc_opendrive/checks/semantic/road_lane_link_lanes_across_lane_sections.py +++ b/qc_opendrive/checks/semantic/road_lane_link_lanes_across_lane_sections.py @@ -4,18 +4,20 @@ from lxml import etree -from qc_baselib import IssueSeverity +from qc_baselib import IssueSeverity, StatusType from qc_opendrive import constants from qc_opendrive.base import models, utils -from qc_opendrive.checks.semantic import semantic_constants +from qc_opendrive import basic_preconditions -RULE_INITIAL_SUPPORTED_SCHEMA_VERSION = "1.4.0" +CHECKER_ID = "check_asam_xodr_road_lane_link_lanes_across_lane_sections" +CHECKER_DESCRIPTION = "Lanes that continues across the lane sections shall be connected in both directions." +CHECKER_PRECONDITIONS = basic_preconditions.CHECKER_PRECONDITIONS +RULE_UID = "asam.net:xodr:1.4.0:road.lane.link.lanes_across_lane_sections" def _check_two_lane_sections_one_direction( checker_data: models.CheckerData, - rule_uid: str, first_lane_section: models.ContactingLaneSection, second_lane_section: models.ContactingLaneSection, ) -> None: @@ -44,15 +46,15 @@ def _check_two_lane_sections_one_direction( if current_lane_id not in connecting_lane_ids_of_second_lane: issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, description="Missing lane link.", level=IssueSeverity.ERROR, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(connecting_lane), description="", @@ -61,20 +63,19 @@ def _check_two_lane_sections_one_direction( def _check_two_lane_sections( checker_data: models.CheckerData, - rule_uid: str, first_lane_section: models.ContactingLaneSection, second_lane_section: models.ContactingLaneSection, ) -> None: _check_two_lane_sections_one_direction( - checker_data, rule_uid, first_lane_section, second_lane_section + checker_data, first_lane_section, second_lane_section ) _check_two_lane_sections_one_direction( - checker_data, rule_uid, second_lane_section, first_lane_section + checker_data, second_lane_section, first_lane_section ) def _check_middle_lane_sections( - checker_data: models.CheckerData, road: etree._ElementTree, rule_uid: str + checker_data: models.CheckerData, road: etree._ElementTree ) -> None: lane_sections = utils.get_lane_sections(road) @@ -86,7 +87,6 @@ def _check_middle_lane_sections( previous_lane_section = lane_sections[i - 1] _check_two_lane_sections( checker_data, - rule_uid, models.ContactingLaneSection( lane_section=previous_lane_section, linkage_tag=models.LinkageTag.SUCCESSOR, @@ -102,7 +102,6 @@ def _check_first_lane_section( checker_data: models.CheckerData, road: etree._ElementTree, road_id_map: Dict[int, etree._ElementTree], - rule_uid: str, ) -> None: first_lane_section = utils.get_first_lane_section(road) if first_lane_section is None: @@ -124,7 +123,6 @@ def _check_first_lane_section( _check_two_lane_sections( checker_data, - rule_uid, first_contacting_lane_section, other_contacting_lane_section, ) @@ -134,7 +132,6 @@ def _check_last_lane_section( checker_data: models.CheckerData, road: etree._ElementTree, road_id_map: Dict[int, etree._ElementTree], - rule_uid: str, ) -> None: last_lane_section = utils.get_last_lane_section(road) if last_lane_section is None: @@ -157,7 +154,6 @@ def _check_last_lane_section( _check_two_lane_sections( checker_data, - rule_uid, last_contacting_lane_section, other_contacting_lane_section, ) @@ -178,32 +174,17 @@ def check_rule(checker_data: models.CheckerData) -> None: """ logging.info("Executing road.lane.link.lanes_across_lane_sections check.") - rule_uid = checker_data.result.register_rule( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, - emanating_entity="asam.net", - standard="xodr", - definition_setting=RULE_INITIAL_SUPPORTED_SCHEMA_VERSION, - rule_full_name="road.lane.link.lanes_across_lane_sections", - ) - - if checker_data.schema_version < RULE_INITIAL_SUPPORTED_SCHEMA_VERSION: - logging.info( - f"Schema version {checker_data.schema_version} not supported. Skipping rule." - ) - return - road_id_map = utils.get_road_id_map(checker_data.input_file_xml_root) for road in utils.get_roads(checker_data.input_file_xml_root): # For all roads, no matter whether they belong to a junction or not, middle lane sections # shall always be connected - _check_middle_lane_sections(checker_data, road, rule_uid) + _check_middle_lane_sections(checker_data, road) # For all roads not belonging to a junction, the first and the last lane sections shall be checked. # For roads belonging to a junction, ignore this rule for the first and the last lane section # due to the following statement from the standard: # "The element shall be omitted if the lane starts or ends in a junction or has no link." if not utils.road_belongs_to_junction(road): - _check_first_lane_section(checker_data, road, road_id_map, rule_uid) - _check_last_lane_section(checker_data, road, road_id_map, rule_uid) + _check_first_lane_section(checker_data, road, road_id_map) + _check_last_lane_section(checker_data, road, road_id_map) diff --git a/qc_opendrive/checks/semantic/road_lane_link_new_lane_appear.py b/qc_opendrive/checks/semantic/road_lane_link_new_lane_appear.py index ed92c24..391ff1d 100644 --- a/qc_opendrive/checks/semantic/road_lane_link_new_lane_appear.py +++ b/qc_opendrive/checks/semantic/road_lane_link_new_lane_appear.py @@ -1,54 +1,56 @@ import logging -from typing import Dict, List +from typing import Dict from lxml import etree -from qc_baselib import IssueSeverity +from qc_baselib import IssueSeverity, StatusType from qc_opendrive import constants from qc_opendrive.base import models, utils -from qc_opendrive.checks.semantic import semantic_constants +from qc_opendrive import basic_preconditions -RULE_INITIAL_SUPPORTED_SCHEMA_VERSION = "1.4.0" +CHECKER_ID = "check_asam_xodr_road_lane_link_new_lane_appear" +CHECKER_DESCRIPTION = "If a new lane appears besides, only the continuing lane shall be connected to the original lane, not the appearing lane." +CHECKER_PRECONDITIONS = basic_preconditions.CHECKER_PRECONDITIONS +RULE_UID = "asam.net:xodr:1.4.0:road.lane.link.new_lane_appear" FLOAT_COMPARISON_THRESHOLD = 1e-6 def _raise_issue( checker_data: models.CheckerData, - rule_uid: str, lane: etree._Element, - successor_width_zero_lane: etree._Element, + width_zero_lane: etree._Element, issue_severity: IssueSeverity, + linkage_tag: models.LinkageTag, ) -> None: issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, description=f"If a new lane appears besides, only the continuing lane shall be connected to the original lane, not the appearing lane.", level=issue_severity, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(lane), - description="Lane with successors with width zero.", + description=f"Lane with {linkage_tag.value} with width zero.", ) checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, - xpath=checker_data.input_file_xml_root.getpath(successor_width_zero_lane), - description="Successor lane with width zero.", + xpath=checker_data.input_file_xml_root.getpath(width_zero_lane), + description=f"{linkage_tag.value.capitalize()} lane with width zero.", ) def _check_successor_with_width_zero_between_lane_sections( checker_data: models.CheckerData, - rule_uid: str, current_lane_section: etree._ElementTree, next_lane_section: etree._ElementTree, contact_point: models.ContactPoint, @@ -88,16 +90,15 @@ def _check_successor_with_width_zero_between_lane_sections( ): _raise_issue( checker_data, - rule_uid, lane, successor_lane, IssueSeverity.ERROR, + models.LinkageTag.SUCCESSOR, ) def _check_predecessor_with_width_zero_between_lane_sections( checker_data: models.CheckerData, - rule_uid: str, current_lane_section: etree._ElementTree, next_lane_section: etree._ElementTree, contact_point: models.ContactPoint, @@ -137,15 +138,15 @@ def _check_predecessor_with_width_zero_between_lane_sections( ): _raise_issue( checker_data, - rule_uid, lane, predecessor_lane, IssueSeverity.ERROR, + models.LinkageTag.PREDECESSOR, ) def _check_appearing_successor_with_width_zero_on_road( - checker_data: models.CheckerData, rule_uid: str, road: etree._ElementTree + checker_data: models.CheckerData, road: etree._ElementTree ) -> None: lane_sections = utils.get_sorted_lane_sections_with_length_from_road(road) @@ -158,7 +159,6 @@ def _check_appearing_successor_with_width_zero_on_road( _check_successor_with_width_zero_between_lane_sections( checker_data, - rule_uid, current_lane_section, next_lane_section, models.ContactPoint.START, @@ -168,7 +168,6 @@ def _check_appearing_successor_with_width_zero_on_road( def _check_appearing_successor_road( checker_data: models.CheckerData, - rule_uid: str, road_id_map: Dict[int, etree._ElementTree], current_road_id: int, successor_road_id: int, @@ -205,7 +204,6 @@ def _check_appearing_successor_road( _check_successor_with_width_zero_between_lane_sections( checker_data, - rule_uid, current_road_last_lane_section, successor_road_target_lane_section.lane_section, successor_linkage.contact_point, @@ -215,7 +213,6 @@ def _check_appearing_successor_road( def _check_appearing_predecessor_road( checker_data: models.CheckerData, - rule_uid: str, road_id_map: Dict[int, etree._ElementTree], current_road_id: int, predecessor_road_id: int, @@ -256,7 +253,6 @@ def _check_appearing_predecessor_road( _check_predecessor_with_width_zero_between_lane_sections( checker_data, - rule_uid, current_road_last_lane_section, predecessor_road_target_lane_section.lane_section, predecessor_linkage.contact_point, @@ -266,7 +262,6 @@ def _check_appearing_predecessor_road( def _check_appearing_successor_junction( checker_data: models.CheckerData, - rule_uid: str, junction_id_map: Dict[int, etree._ElementTree], road_id_map: Dict[int, etree._ElementTree], road_id: int, @@ -345,16 +340,15 @@ def _check_appearing_successor_junction( continue _raise_issue( checker_data, - rule_uid, current_road_lane, connection_lane, IssueSeverity.ERROR, + models.LinkageTag.SUCCESSOR, ) def _check_appearing_predecessor_junction( checker_data: models.CheckerData, - rule_uid: str, junction_id_map: Dict[int, etree._ElementTree], road_id_map: Dict[int, etree._ElementTree], road_id: int, @@ -430,34 +424,32 @@ def _check_appearing_predecessor_junction( continue _raise_issue( checker_data, - rule_uid, current_road_lane, connection_lane, IssueSeverity.ERROR, + models.LinkageTag.PREDECESSOR, ) -def _check_road_lane_link_new_lane_appear( - checker_data: models.CheckerData, rule_uid: str -) -> None: +def _check_road_lane_link_new_lane_appear(checker_data: models.CheckerData) -> None: road_id_map = utils.get_road_id_map(checker_data.input_file_xml_root) junction_id_map = utils.get_junction_id_map(checker_data.input_file_xml_root) for road_id, road in road_id_map.items(): - _check_appearing_successor_with_width_zero_on_road(checker_data, rule_uid, road) + _check_appearing_successor_with_width_zero_on_road(checker_data, road) successor_road_id = utils.get_successor_road_id(road) if successor_road_id is not None: _check_appearing_successor_road( - checker_data, rule_uid, road_id_map, road_id, successor_road_id + checker_data, road_id_map, road_id, successor_road_id ) predecessor_road_id = utils.get_predecessor_road_id(road) if predecessor_road_id is not None: _check_appearing_predecessor_road( - checker_data, rule_uid, road_id_map, road_id, predecessor_road_id + checker_data, road_id_map, road_id, predecessor_road_id ) successor_junction_id = utils.get_linked_junction_id( @@ -466,7 +458,6 @@ def _check_road_lane_link_new_lane_appear( if successor_junction_id is not None: _check_appearing_successor_junction( checker_data, - rule_uid, junction_id_map, road_id_map, road_id, @@ -480,7 +471,6 @@ def _check_road_lane_link_new_lane_appear( if predecessor_junction_id is not None: _check_appearing_predecessor_junction( checker_data, - rule_uid, junction_id_map, road_id_map, road_id, @@ -507,19 +497,4 @@ def check_rule(checker_data: models.CheckerData) -> None: """ logging.info("Executing road.lane.link.new_lane_appear check") - rule_uid = checker_data.result.register_rule( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, - emanating_entity="asam.net", - standard="xodr", - definition_setting=RULE_INITIAL_SUPPORTED_SCHEMA_VERSION, - rule_full_name="road.lane.link.new_lane_appear", - ) - - if checker_data.schema_version < RULE_INITIAL_SUPPORTED_SCHEMA_VERSION: - logging.info( - f"Schema version {checker_data.schema_version} not supported. Skipping rule." - ) - return - - _check_road_lane_link_new_lane_appear(checker_data, rule_uid) + _check_road_lane_link_new_lane_appear(checker_data) diff --git a/qc_opendrive/checks/semantic/road_lane_link_zero_width_at_end.py b/qc_opendrive/checks/semantic/road_lane_link_zero_width_at_end.py index c219503..eb31288 100644 --- a/qc_opendrive/checks/semantic/road_lane_link_zero_width_at_end.py +++ b/qc_opendrive/checks/semantic/road_lane_link_zero_width_at_end.py @@ -1,22 +1,24 @@ import logging -from typing import Dict, List +from typing import Dict from lxml import etree -from qc_baselib import IssueSeverity +from qc_baselib import IssueSeverity, StatusType from qc_opendrive import constants from qc_opendrive.base import models, utils -from qc_opendrive.checks.semantic import semantic_constants +from qc_opendrive import basic_preconditions -RULE_INITIAL_SUPPORTED_SCHEMA_VERSION = "1.7.0" +CHECKER_ID = "check_asam_xodr_road_lane_link_zero_width_at_end" +CHECKER_DESCRIPTION = "Lanes that have a width of zero at the end of the lane section shall have no successor element." +CHECKER_PRECONDITIONS = basic_preconditions.CHECKER_PRECONDITIONS +RULE_UID = "asam.net:xodr:1.7.0:road.lane.link.zero_width_at_end" FLOAT_COMPARISON_THRESHOLD = 1e-6 def _raise_issue( checker_data: models.CheckerData, - rule_uid: str, road: etree._ElementTree, lane_section_with_length: models.LaneSectionWithLength, lane: etree._Element, @@ -24,15 +26,15 @@ def _raise_issue( ) -> None: issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, description=f" Lanes that have a width of zero at the end of the lane section shall have no successor element.", level=issue_severity, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(lane), description="Lane with width zero and successors.", @@ -51,7 +53,7 @@ def _raise_issue( if inertial_point is not None: checker_data.result.add_inertial_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, x=inertial_point.x, y=inertial_point.y, @@ -66,7 +68,6 @@ def _raise_issue_based_on_lane_id( road: etree._ElementTree, lane_section_with_length: models.LaneSectionWithLength, checker_data: models.CheckerData, - rule_uid: str, ) -> None: if lane_id == 0: # Because of backward compatibility, this rule does @@ -76,7 +77,6 @@ def _raise_issue_based_on_lane_id( # to "WARNING" _raise_issue( checker_data, - rule_uid, road, lane_section_with_length, lane, @@ -85,7 +85,6 @@ def _raise_issue_based_on_lane_id( else: _raise_issue( checker_data, - rule_uid, road, lane_section_with_length, lane, @@ -93,9 +92,7 @@ def _raise_issue_based_on_lane_id( ) -def _check_road_lane_link_zero_width_at_end( - checker_data: models.CheckerData, rule_uid: str -) -> None: +def _check_road_lane_link_zero_width_at_end(checker_data: models.CheckerData) -> None: roads = utils.get_roads(checker_data.input_file_xml_root) for road in roads: @@ -127,13 +124,11 @@ def _check_road_lane_link_zero_width_at_end( road, lane_section, checker_data, - rule_uid, ) def _check_incoming_road_junction_successor_lane_width_zero( checker_data: models.CheckerData, - rule_uid: str, road: etree._Element, road_id: int, road_id_map: Dict[int, etree._ElementTree], @@ -196,13 +191,11 @@ def _check_incoming_road_junction_successor_lane_width_zero( road, last_lane_section, checker_data, - rule_uid, ) def _check_connecting_road_lane_width_zero_with_successor( checker_data: models.CheckerData, - rule_uid: str, road: etree._Element, road_id: int, junction_id_map: Dict[int, etree._ElementTree], @@ -262,12 +255,11 @@ def _check_connecting_road_lane_width_zero_with_successor( road, last_lane_section, checker_data, - rule_uid, ) def _check_junction_road_lane_link_zero_width_at_end( - checker_data: models.CheckerData, rule_uid: str + checker_data: models.CheckerData, ) -> None: road_id_map = utils.get_road_id_map(checker_data.input_file_xml_root) junction_id_map = utils.get_junction_id_map(checker_data.input_file_xml_root) @@ -275,11 +267,11 @@ def _check_junction_road_lane_link_zero_width_at_end( for road_id, road in road_id_map.items(): if utils.road_belongs_to_junction(road): _check_connecting_road_lane_width_zero_with_successor( - checker_data, rule_uid, road, road_id, junction_id_map + checker_data, road, road_id, junction_id_map ) else: _check_incoming_road_junction_successor_lane_width_zero( - checker_data, rule_uid, road, road_id, road_id_map, junction_id_map + checker_data, road, road_id, road_id_map, junction_id_map ) @@ -305,20 +297,5 @@ def check_rule(checker_data: models.CheckerData) -> None: """ logging.info("Executing road.lane.link.zero_width_at_end check") - rule_uid = checker_data.result.register_rule( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, - emanating_entity="asam.net", - standard="xodr", - definition_setting=RULE_INITIAL_SUPPORTED_SCHEMA_VERSION, - rule_full_name="road.lane.link.zero_width_at_end", - ) - - if checker_data.schema_version < RULE_INITIAL_SUPPORTED_SCHEMA_VERSION: - logging.info( - f"Schema version {checker_data.schema_version} not supported. Skipping rule." - ) - return - - _check_road_lane_link_zero_width_at_end(checker_data, rule_uid) - _check_junction_road_lane_link_zero_width_at_end(checker_data, rule_uid) + _check_road_lane_link_zero_width_at_end(checker_data) + _check_junction_road_lane_link_zero_width_at_end(checker_data) diff --git a/qc_opendrive/checks/semantic/road_lane_link_zero_width_at_start.py b/qc_opendrive/checks/semantic/road_lane_link_zero_width_at_start.py index ddd4f31..2d25eb6 100644 --- a/qc_opendrive/checks/semantic/road_lane_link_zero_width_at_start.py +++ b/qc_opendrive/checks/semantic/road_lane_link_zero_width_at_start.py @@ -1,22 +1,24 @@ import logging -from typing import Dict, List +from typing import Dict from lxml import etree -from qc_baselib import IssueSeverity +from qc_baselib import IssueSeverity, StatusType from qc_opendrive import constants from qc_opendrive.base import models, utils -from qc_opendrive.checks.semantic import semantic_constants +from qc_opendrive import basic_preconditions -RULE_INITIAL_SUPPORTED_SCHEMA_VERSION = "1.7.0" +CHECKER_ID = "check_asam_xodr_road_lane_link_zero_width_at_start" +CHECKER_DESCRIPTION = "Lanes that have a width of zero at the beginning of the lane section shall have no predecessor element." +CHECKER_PRECONDITIONS = basic_preconditions.CHECKER_PRECONDITIONS +RULE_UID = "asam.net:xodr:1.7.0:road.lane.link.zero_width_at_start" FLOAT_COMPARISON_THRESHOLD = 1e-6 def _raise_issue( checker_data: models.CheckerData, - rule_uid: str, road: etree._ElementTree, lane_section: etree._ElementTree, lane: etree._Element, @@ -24,15 +26,15 @@ def _raise_issue( ) -> None: issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, description=f" Lanes that have a width of zero at the beginning of the lane section shall have no predecessor element.", level=issue_severity, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(lane), description="Lane with width zero and predecessors.", @@ -50,7 +52,7 @@ def _raise_issue( if inertial_point is not None: checker_data.result.add_inertial_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, x=inertial_point.x, y=inertial_point.y, @@ -65,7 +67,6 @@ def _raise_issue_based_on_lane_id( lane: etree._Element, lane_id: int, checker_data: models.CheckerData, - rule_uid: str, ) -> None: if lane_id == 0: # Because of backward compatibility, this rule does @@ -73,18 +74,12 @@ def _raise_issue_based_on_lane_id( # a width (see Rule 77) but it might have a predecessor). # In this case the severity level should be changed # to "WARNING" - _raise_issue( - checker_data, rule_uid, road, lane_section, lane, IssueSeverity.WARNING - ) + _raise_issue(checker_data, road, lane_section, lane, IssueSeverity.WARNING) else: - _raise_issue( - checker_data, rule_uid, road, lane_section, lane, IssueSeverity.ERROR - ) + _raise_issue(checker_data, road, lane_section, lane, IssueSeverity.ERROR) -def _check_road_lane_link_zero_width_at_start( - checker_data: models.CheckerData, rule_uid: str -) -> None: +def _check_road_lane_link_zero_width_at_start(checker_data: models.CheckerData) -> None: roads = utils.get_roads(checker_data.input_file_xml_root) for road in roads: @@ -112,13 +107,11 @@ def _check_road_lane_link_zero_width_at_start( lane, lane_id, checker_data, - rule_uid, ) def _check_incoming_road_junction_predecessor_lane_width_zero( checker_data: models.CheckerData, - rule_uid: str, road: etree._Element, road_id: int, road_id_map: Dict[int, etree._ElementTree], @@ -171,13 +164,11 @@ def _check_incoming_road_junction_predecessor_lane_width_zero( lane, lane_id, checker_data, - rule_uid, ) def _check_connecting_road_lane_width_zero_with_predecessor( checker_data: models.CheckerData, - rule_uid: str, road: etree._Element, road_id: int, junction_id_map: Dict[int, etree._ElementTree], @@ -228,12 +219,11 @@ def _check_connecting_road_lane_width_zero_with_predecessor( lane, lane_id, checker_data, - rule_uid, ) def _check_junction_road_lane_link_zero_width_at_start( - checker_data: models.CheckerData, rule_uid: str + checker_data: models.CheckerData, ) -> None: road_id_map = utils.get_road_id_map(checker_data.input_file_xml_root) junction_id_map = utils.get_junction_id_map(checker_data.input_file_xml_root) @@ -241,11 +231,11 @@ def _check_junction_road_lane_link_zero_width_at_start( for road_id, road in road_id_map.items(): if utils.road_belongs_to_junction(road): _check_connecting_road_lane_width_zero_with_predecessor( - checker_data, rule_uid, road, road_id, junction_id_map + checker_data, road, road_id, junction_id_map ) else: _check_incoming_road_junction_predecessor_lane_width_zero( - checker_data, rule_uid, road, road_id, road_id_map, junction_id_map + checker_data, road, road_id, road_id_map, junction_id_map ) @@ -271,20 +261,5 @@ def check_rule(checker_data: models.CheckerData) -> None: """ logging.info("Executing road.lane.link.zero_width_at_start check") - rule_uid = checker_data.result.register_rule( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, - emanating_entity="asam.net", - standard="xodr", - definition_setting=RULE_INITIAL_SUPPORTED_SCHEMA_VERSION, - rule_full_name="road.lane.link.zero_width_at_start", - ) - - if checker_data.schema_version < RULE_INITIAL_SUPPORTED_SCHEMA_VERSION: - logging.info( - f"Schema version {checker_data.schema_version} not supported. Skipping rule." - ) - return - - _check_road_lane_link_zero_width_at_start(checker_data, rule_uid) - _check_junction_road_lane_link_zero_width_at_start(checker_data, rule_uid) + _check_road_lane_link_zero_width_at_start(checker_data) + _check_junction_road_lane_link_zero_width_at_start(checker_data) diff --git a/qc_opendrive/checks/semantic/road_linkage_is_junction_needed.py b/qc_opendrive/checks/semantic/road_linkage_is_junction_needed.py index acd2574..bee0e59 100644 --- a/qc_opendrive/checks/semantic/road_linkage_is_junction_needed.py +++ b/qc_opendrive/checks/semantic/road_linkage_is_junction_needed.py @@ -1,37 +1,39 @@ import logging -from typing import Dict, Set, List +from typing import Dict, List from lxml import etree from typing import Union -from qc_baselib import IssueSeverity +from qc_baselib import IssueSeverity, StatusType from qc_opendrive import constants from qc_opendrive.base import models, utils -from qc_opendrive.checks.semantic import semantic_constants +from qc_opendrive import basic_preconditions -RULE_INITIAL_SUPPORTED_SCHEMA_VERSION = "1.4.0" +CHECKER_ID = "check_asam_xodr_road_linkage_is_junction_needed" +CHECKER_DESCRIPTION = "Two roads shall only be linked directly, if the linkage is clear. If the relationship to successor or predecessor is ambiguous, junctions shall be used." +CHECKER_PRECONDITIONS = basic_preconditions.CHECKER_PRECONDITIONS +RULE_UID = "asam.net:xodr:1.4.0:road.linkage.is_junction_needed" def _raise_road_linkage_is_junction_needed_issue( checker_data: models.CheckerData, - rule_uid: str, road_linkage_elements: List[etree._Element], linkage_tag: models.LinkageTag, problematic_road: Union[None, etree._ElementTree], ) -> None: issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, description=f"Road cannot have ambiguous {linkage_tag.value}, a junction is needed.", level=IssueSeverity.ERROR, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) for element in road_linkage_elements: checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(element), description="", @@ -51,7 +53,7 @@ def _raise_road_linkage_is_junction_needed_issue( if inertial_point is not None: checker_data.result.add_inertial_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, x=inertial_point.x, y=inertial_point.y, @@ -74,9 +76,7 @@ def _get_road_linkage_from_contact_point_id( ) -def _check_road_linkage_is_junction_needed( - checker_data: models.CheckerData, rule_uid: str -) -> None: +def _check_road_linkage_is_junction_needed(checker_data: models.CheckerData) -> None: road_id_map = utils.get_road_id_map(checker_data.input_file_xml_root) if len(road_id_map) < 2: @@ -140,7 +140,7 @@ def _check_road_linkage_is_junction_needed( problematic_road = road_id_map.get(road_linkage.id) _raise_road_linkage_is_junction_needed_issue( - checker_data, rule_uid, elements, linkage_tag, problematic_road + checker_data, elements, linkage_tag, problematic_road ) @@ -164,19 +164,4 @@ def check_rule(checker_data: models.CheckerData) -> None: """ logging.info("Executing road.linkage.is_junction_needed check") - rule_uid = checker_data.result.register_rule( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, - emanating_entity="asam.net", - standard="xodr", - definition_setting=RULE_INITIAL_SUPPORTED_SCHEMA_VERSION, - rule_full_name="road.linkage.is_junction_needed", - ) - - if checker_data.schema_version < RULE_INITIAL_SUPPORTED_SCHEMA_VERSION: - logging.info( - f"Schema version {checker_data.schema_version} not supported. Skipping rule." - ) - return - - _check_road_linkage_is_junction_needed(checker_data, rule_uid) + _check_road_linkage_is_junction_needed(checker_data) diff --git a/qc_opendrive/checks/semantic/semantic_checker.py b/qc_opendrive/checks/semantic/semantic_checker.py deleted file mode 100644 index 8eac1ab..0000000 --- a/qc_opendrive/checks/semantic/semantic_checker.py +++ /dev/null @@ -1,99 +0,0 @@ -import logging - -from lxml import etree - -from qc_baselib import Configuration, Result, StatusType - -from qc_opendrive import constants -from qc_opendrive.base import models, utils - -from qc_opendrive.checks.semantic import ( - semantic_constants, - road_lane_level_true_one_side, - road_lane_access_no_mix_of_deny_or_allow, - road_lane_link_lanes_across_lane_sections, - road_linkage_is_junction_needed, - road_lane_link_zero_width_at_start, - road_lane_link_zero_width_at_end, - road_lane_link_new_lane_appear, - junctions_connection_connect_road_no_incoming_road, - junctions_connection_one_connection_element, - junctions_connection_one_link_to_incoming, - junctions_connection_start_along_linkage, - junctions_connection_end_opposite_linkage, -) - - -def skip_checks(result: Result) -> None: - - if semantic_constants.CHECKER_ID not in result.get_checker_ids( - constants.BUNDLE_NAME - ): - result.register_checker( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, - description="Check if xml properties of input file are properly set", - summary="", - ) - - logging.error( - f"Invalid xml input file. Checker {semantic_constants.CHECKER_ID} skipped" - ) - result.set_checker_status( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, - status=StatusType.SKIPPED, - ) - - -def run_checks(config: Configuration, result: Result) -> None: - logging.info("Executing semantic checks") - - root = utils.get_root_without_default_namespace( - config.get_config_param("InputFile") - ) - - result.register_checker( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, - description="Evaluates elements in the file and their semantics to guarantee they are in conformity with the standard.", - summary="", - ) - - odr_schema_version = utils.get_standard_schema_version(root) - - rule_list = [ - road_lane_level_true_one_side.check_rule, - road_lane_access_no_mix_of_deny_or_allow.check_rule, - road_lane_link_lanes_across_lane_sections.check_rule, - road_linkage_is_junction_needed.check_rule, - road_lane_link_zero_width_at_start.check_rule, - road_lane_link_zero_width_at_end.check_rule, - road_lane_link_new_lane_appear.check_rule, - junctions_connection_connect_road_no_incoming_road.check_rule, - junctions_connection_one_connection_element.check_rule, - junctions_connection_one_link_to_incoming.check_rule, - junctions_connection_start_along_linkage.check_rule, - junctions_connection_end_opposite_linkage.check_rule, - ] - - checker_data = models.CheckerData( - input_file_xml_root=root, - config=config, - result=result, - schema_version=odr_schema_version, - ) - - for rule in rule_list: - rule(checker_data=checker_data) - - logging.info( - f"Issues found - {result.get_checker_issue_count(checker_bundle_name=constants.BUNDLE_NAME, checker_id=semantic_constants.CHECKER_ID)}" - ) - - # TODO: Add logic to deal with error or to skip it - result.set_checker_status( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=semantic_constants.CHECKER_ID, - status=StatusType.COMPLETED, - ) diff --git a/qc_opendrive/checks/semantic/semantic_constants.py b/qc_opendrive/checks/semantic/semantic_constants.py deleted file mode 100644 index 6f163b5..0000000 --- a/qc_opendrive/checks/semantic/semantic_constants.py +++ /dev/null @@ -1 +0,0 @@ -CHECKER_ID = "semantic_xodr" diff --git a/qc_opendrive/checks/smoothness/__init__.py b/qc_opendrive/checks/smoothness/__init__.py index 2d15ca8..5de89fd 100644 --- a/qc_opendrive/checks/smoothness/__init__.py +++ b/qc_opendrive/checks/smoothness/__init__.py @@ -1,5 +1,3 @@ -from . import smoothness_constants as smoothness_constants -from . import smoothness_checker as smoothness_checker from . import ( lane_smoothness_contact_point_no_horizontal_gaps as lane_smoothness_contact_point_no_horizontal_gaps, ) diff --git a/qc_opendrive/checks/smoothness/lane_smoothness_contact_point_no_horizontal_gaps.py b/qc_opendrive/checks/smoothness/lane_smoothness_contact_point_no_horizontal_gaps.py index 5c86321..f472162 100644 --- a/qc_opendrive/checks/smoothness/lane_smoothness_contact_point_no_horizontal_gaps.py +++ b/qc_opendrive/checks/smoothness/lane_smoothness_contact_point_no_horizontal_gaps.py @@ -4,14 +4,18 @@ from lxml import etree from scipy.spatial import distance -from qc_baselib import IssueSeverity +from qc_baselib import IssueSeverity, StatusType from qc_opendrive import constants from qc_opendrive.base import utils, models -from qc_opendrive.checks.smoothness import smoothness_constants +from qc_opendrive import basic_preconditions -RULE_INITIAL_SUPPORTED_SCHEMA_VERSION = "1.7.0" +CHECKER_ID = "check_asam_xodr_lane_smoothness_contact_point_no_horizontal_gaps" +CHECKER_DESCRIPTION = "Two connected drivable lanes shall have no horizontal gaps." +CHECKER_PRECONDITIONS = basic_preconditions.CHECKER_PRECONDITIONS +RULE_UID = "asam.net:xodr:1.7.0:lane_smoothness.contact_point_no_horizontal_gaps" + # This parameter needs to be configurable later TOLERANCE_THRESHOLD = 0.01 # meters @@ -33,7 +37,6 @@ def _raise_geometry_gap_issue( checker_data: models.CheckerData, - rule_uid: str, previous_geometry: etree._Element, geometry: etree._Element, distance: float, @@ -41,22 +44,22 @@ def _raise_geometry_gap_issue( ) -> None: issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=smoothness_constants.CHECKER_ID, + checker_id=CHECKER_ID, description=f"The transition between geometry elements should be defined with no gaps. A gap of {distance} meters has been found.", level=IssueSeverity.ERROR, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=smoothness_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(previous_geometry), description=f"First geometry element", ) checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=smoothness_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(geometry), description=f"Second geometry element", @@ -65,7 +68,7 @@ def _raise_geometry_gap_issue( if inertial_point is not None: checker_data.result.add_inertial_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=smoothness_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, x=inertial_point.x, y=inertial_point.y, @@ -76,40 +79,50 @@ def _raise_geometry_gap_issue( def _raise_lane_linkage_gap_issue( checker_data: models.CheckerData, - rule_uid: str, previous_lane: etree._Element, current_lane: etree._Element, + inertial_point: Union[None, models.Point3D], ) -> None: issue_id = checker_data.result.register_issue( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=smoothness_constants.CHECKER_ID, + checker_id=CHECKER_ID, description=f"The transition between lane elements should be defined with no gaps.", level=IssueSeverity.ERROR, - rule_uid=rule_uid, + rule_uid=RULE_UID, ) checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=smoothness_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(previous_lane), description=f"First lane element", ) checker_data.result.add_xml_location( checker_bundle_name=constants.BUNDLE_NAME, - checker_id=smoothness_constants.CHECKER_ID, + checker_id=CHECKER_ID, issue_id=issue_id, xpath=checker_data.input_file_xml_root.getpath(current_lane), description=f"Next lane element", ) + if inertial_point is not None: + checker_data.result.add_inertial_location( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=CHECKER_ID, + issue_id=issue_id, + x=inertial_point.x, + y=inertial_point.y, + z=inertial_point.z, + description="Next lane middle reference point", + ) + def _check_geometries_gap( road: etree._ElementTree, previous_geometry: etree._Element, current_geometry: etree._Element, checker_data: models.CheckerData, - rule_uid: str, ) -> None: x0 = utils.get_x_from_geometry(current_geometry) y0 = utils.get_y_from_geometry(current_geometry) @@ -144,7 +157,6 @@ def _check_geometries_gap( _raise_geometry_gap_issue( checker_data, - rule_uid, previous_geometry, current_geometry, gap_size, @@ -156,7 +168,6 @@ def _check_plan_view_gaps( road: etree._ElementTree, geometries: etree._ElementTree, checker_data: models.CheckerData, - rule_uid: str, ) -> None: # we are assuming geometries is a sorted list on s position previous_geometry: Union[etree._Element, None] = None @@ -167,7 +178,6 @@ def _check_plan_view_gaps( previous_geometry=previous_geometry, current_geometry=geometry, checker_data=checker_data, - rule_uid=rule_uid, ) previous_geometry = geometry @@ -180,7 +190,7 @@ def _compute_inner_point( road_s: float, ) -> Union[None, models.Point3D]: sign = -1 if lane_id < 0 else 1 - current_lane_t = current_lane_t = lanes_outer_points.get(lane_id - 1 * sign) + current_lane_t = lanes_outer_points.get(lane_id - 1 * sign) if current_lane_t is None: return None return utils.get_point_xyz_from_road(road=road, s=road_s, t=current_lane_t, h=0) @@ -198,6 +208,26 @@ def _compute_outer_point( return utils.get_point_xyz_from_road(road=road, s=road_s, t=current_lane_t, h=0) +def _compute_middle_point( + lanes_outer_points: Dict[int, float], + lane_id: int, + road: etree._Element, + road_s: float, +) -> Union[None, models.Point3D]: + outer_point = _compute_outer_point(lanes_outer_points, lane_id, road, road_s) + + inner_point = _compute_inner_point(lanes_outer_points, lane_id, road, road_s) + + if inner_point is not None and outer_point is not None: + return models.Point3D( + x=(inner_point.x + outer_point.x) / 2, + y=(inner_point.y + outer_point.y) / 2, + z=(inner_point.z + outer_point.z) / 2, + ) + else: + return None + + def _equal_outer_border_points( road: etree._Element, lane_id: int, @@ -261,7 +291,6 @@ def _validate_same_road_lane_successors( successor_road_s: float, successor_lanes: List[etree._Element], checker_data: models.CheckerData, - rule_uid: str, ) -> None: successors = utils.get_successor_lane_ids(lane) lane_id = utils.get_lane_id(lane) @@ -292,11 +321,17 @@ def _validate_same_road_lane_successors( if utils.get_lane_id(next_lane) == next_lane_id ] if len(next_lane) == 1: + inertial_point = _compute_middle_point( + successor_outer_points, + next_lane_id, + road, + successor_road_s, + ) _raise_lane_linkage_gap_issue( checker_data, - rule_uid, lane, next_lane[0], + inertial_point, ) elif len(successors) == 2: @@ -320,11 +355,17 @@ def _validate_same_road_lane_successors( if utils.get_lane_id(next_lane) == bottom_successor_id ] if len(next_lane) == 1: + inertial_point = _compute_middle_point( + successor_outer_points, + bottom_successor_id, + road, + successor_road_s, + ) _raise_lane_linkage_gap_issue( checker_data, - rule_uid, lane, next_lane[0], + inertial_point, ) if not _equal_inner_border_points( @@ -342,11 +383,17 @@ def _validate_same_road_lane_successors( if utils.get_lane_id(next_lane) == upper_successor_id ] if len(next_lane) == 1: + inertial_point = _compute_middle_point( + successor_outer_points, + upper_successor_id, + road, + successor_road_s, + ) _raise_lane_linkage_gap_issue( checker_data, - rule_uid, lane, next_lane[0], + inertial_point, ) else: @@ -357,11 +404,17 @@ def _validate_same_road_lane_successors( if utils.get_lane_id(next_lane) == extra_lane_id ] if len(next_lane) == 1: + inertial_point = _compute_middle_point( + successor_outer_points, + extra_lane_id, + road, + successor_road_s, + ) _raise_lane_linkage_gap_issue( checker_data, - rule_uid, lane, next_lane[0], + inertial_point, ) @@ -374,7 +427,6 @@ def _validate_same_road_lane_predecessors( current_road_s: float, prev_lanes: List[etree._Element], checker_data: models.CheckerData, - rule_uid: str, ) -> None: lane_id = utils.get_lane_id(lane) predecessors = utils.get_predecessor_lane_ids(lane) @@ -405,11 +457,17 @@ def _validate_same_road_lane_predecessors( if utils.get_lane_id(prev_lane) == prev_lane_id ] if len(prev_lane) == 1: + inertial_point = _compute_middle_point( + current_outer_points, + lane_id, + road, + current_road_s, + ) _raise_lane_linkage_gap_issue( checker_data, - rule_uid, prev_lane[0], lane, + inertial_point, ) elif len(predecessors) == 2: @@ -433,11 +491,16 @@ def _validate_same_road_lane_predecessors( if utils.get_lane_id(prev_lane) == upper_prev_id ] if len(prev_lane) == 1: + inertial_point = _compute_middle_point( + current_outer_points, + lane_id, + road, + current_road_s, + ) _raise_lane_linkage_gap_issue( checker_data, - rule_uid, prev_lane[0], - lane, + inertial_point, ) if not _equal_inner_border_points( @@ -455,11 +518,17 @@ def _validate_same_road_lane_predecessors( if utils.get_lane_id(prev_lane) == bottom_prev_id ] if len(prev_lane) == 1: + inertial_point = _compute_middle_point( + current_outer_points, + lane_id, + road, + current_road_s, + ) _raise_lane_linkage_gap_issue( checker_data, - rule_uid, prev_lane[0], lane, + inertial_point, ) else: @@ -470,18 +539,23 @@ def _validate_same_road_lane_predecessors( if utils.get_lane_id(prev_lane) == extra_lane_id ] if len(prev_lane) == 1: + inertial_point = _compute_middle_point( + current_outer_points, + lane_id, + road, + current_road_s, + ) _raise_lane_linkage_gap_issue( checker_data, - rule_uid, prev_lane[0], lane, + inertial_point, ) def _check_road_lane_sections_gaps( road: etree._ElementTree, checker_data: models.CheckerData, - rule_uid: str, ) -> None: lane_sections = utils.get_sorted_lane_sections_with_length_from_road(road) @@ -540,7 +614,6 @@ def _check_road_lane_sections_gaps( next_lane_section_s, next_lanes, checker_data, - rule_uid, ) for lane in next_lanes: @@ -555,13 +628,10 @@ def _check_road_lane_sections_gaps( next_lane_section_s, current_lanes, checker_data, - rule_uid, ) -def _check_roads_internal_smoothness( - checker_data: models.CheckerData, rule_uid: str -) -> None: +def _check_roads_internal_smoothness(checker_data: models.CheckerData) -> None: road_id_map = utils.get_road_id_map(checker_data.input_file_xml_root) for road in road_id_map.values(): @@ -569,9 +639,9 @@ def _check_roads_internal_smoothness( # we can only calculate gaps with 2 or more geometries if len(geometries) > 2: - _check_plan_view_gaps(road, geometries, checker_data, rule_uid) + _check_plan_view_gaps(road, geometries, checker_data) - _check_road_lane_sections_gaps(road, checker_data, rule_uid) + _check_road_lane_sections_gaps(road, checker_data) def _validate_inter_road_smoothness( @@ -582,7 +652,6 @@ def _validate_inter_road_smoothness( road_s: float, road_id_map: Dict[int, etree._ElementTree], checker_data: models.CheckerData, - rule_uid: str, ): lane_section = road_lane_section @@ -719,19 +788,33 @@ def _validate_inter_road_smoothness( for target_lane in target_lanes if utils.get_lane_id(target_lane) == conn_lane_id ) + if road_relation == models.LinkageTag.PREDECESSOR: + inertial_point = _compute_middle_point( + lanes_outer_points, + lane_id, + road, + road_s, + ) _raise_lane_linkage_gap_issue( checker_data, - rule_uid, target_lane, lane, + inertial_point, ) + elif road_relation == models.LinkageTag.SUCCESSOR: + inertial_point = _compute_middle_point( + target_lanes_outer_points, + conn_lane_id, + target_road, + target_s, + ) _raise_lane_linkage_gap_issue( checker_data, - rule_uid, lane, target_lane, + inertial_point, ) @@ -743,7 +826,6 @@ def _validate_junction_connection_gaps( road_relation: models.ContactPoint, road_id_map: Dict[int, etree._ElementTree], checker_data: models.CheckerData, - rule_uid: str, ): connection_contact_point = utils.get_contact_point_from_connection(connection) connection_road_id = utils.get_connecting_road_id_from_connection(connection) @@ -888,25 +970,36 @@ def _validate_junction_connection_gaps( for target_lane in target_lanes if utils.get_lane_id(target_lane) == to_id ) + if road_relation == models.LinkageTag.PREDECESSOR: + inertial_point = _compute_middle_point( + lanes_outer_points, + from_id, + incoming_road, + incoming_road_s, + ) _raise_lane_linkage_gap_issue( checker_data, - rule_uid, from_lane, target_lane, + inertial_point, ) elif road_relation == models.LinkageTag.SUCCESSOR: + inertial_point = _compute_middle_point( + target_lanes_outer_points, + to_id, + target_road, + target_s, + ) _raise_lane_linkage_gap_issue( checker_data, - rule_uid, from_lane, target_lane, + inertial_point, ) -def _check_inter_roads_smoothness( - checker_data: models.CheckerData, rule_uid: str -) -> None: +def _check_inter_roads_smoothness(checker_data: models.CheckerData) -> None: road_id_map = utils.get_road_id_map(checker_data.input_file_xml_root) junction_id_map = utils.get_junction_id_map(checker_data.input_file_xml_root) @@ -926,7 +1019,6 @@ def _check_inter_roads_smoothness( road_s=road_length, road_id_map=road_id_map, checker_data=checker_data, - rule_uid=rule_uid, ) if predecessor is not None: @@ -938,7 +1030,6 @@ def _check_inter_roads_smoothness( road_s=0.0, road_id_map=road_id_map, checker_data=checker_data, - rule_uid=rule_uid, ) successor_junction_id = utils.get_linked_junction_id( @@ -968,7 +1059,6 @@ def _check_inter_roads_smoothness( road_relation=models.LinkageTag.SUCCESSOR, road_id_map=road_id_map, checker_data=checker_data, - rule_uid=rule_uid, ) if predecessor_junction_id is not None: @@ -991,7 +1081,6 @@ def _check_inter_roads_smoothness( road_relation=models.LinkageTag.PREDECESSOR, road_id_map=road_id_map, checker_data=checker_data, - rule_uid=rule_uid, ) @@ -1019,20 +1108,5 @@ def check_rule(checker_data: models.CheckerData) -> None: """ logging.info("Executing lane_smoothness.contact_point_no_horizontal_gaps check.") - rule_uid = checker_data.result.register_rule( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=smoothness_constants.CHECKER_ID, - emanating_entity="asam.net", - standard="xodr", - definition_setting=RULE_INITIAL_SUPPORTED_SCHEMA_VERSION, - rule_full_name="lane_smoothness.contact_point_no_horizontal_gaps", - ) - - if checker_data.schema_version < RULE_INITIAL_SUPPORTED_SCHEMA_VERSION: - logging.info( - f"Schema version {checker_data.schema_version} not supported. Skipping rule." - ) - return - - _check_roads_internal_smoothness(checker_data=checker_data, rule_uid=rule_uid) - _check_inter_roads_smoothness(checker_data=checker_data, rule_uid=rule_uid) + _check_roads_internal_smoothness(checker_data=checker_data) + _check_inter_roads_smoothness(checker_data=checker_data) diff --git a/qc_opendrive/checks/smoothness/smoothness_checker.py b/qc_opendrive/checks/smoothness/smoothness_checker.py deleted file mode 100644 index f08572f..0000000 --- a/qc_opendrive/checks/smoothness/smoothness_checker.py +++ /dev/null @@ -1,75 +0,0 @@ -import logging - -from lxml import etree - -from qc_baselib import Configuration, Result, StatusType - -from qc_opendrive import constants -from qc_opendrive.base import utils, models - -from qc_opendrive.checks.smoothness import ( - lane_smoothness_contact_point_no_horizontal_gaps, - smoothness_constants, -) - - -def skip_checks(result: Result) -> None: - - if smoothness_constants.CHECKER_ID not in result.get_checker_ids( - constants.BUNDLE_NAME - ): - result.register_checker( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=smoothness_constants.CHECKER_ID, - description="Check if xml properties of input file are properly set", - summary="", - ) - - logging.error( - f"Invalid xml input file. Checker {smoothness_constants.CHECKER_ID} skipped" - ) - result.set_checker_status( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=smoothness_constants.CHECKER_ID, - status=StatusType.SKIPPED, - ) - - -def run_checks(config: Configuration, result: Result) -> None: - logging.info("Executing smoothness checks") - - root = etree.parse(config.get_config_param("InputFile")) - - result.register_checker( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=smoothness_constants.CHECKER_ID, - description="Evaluates elements in the file and their geometries to guarantee they are in conformity with the standard definition of smoothness.", - summary="", - ) - - odr_schema_version = utils.get_standard_schema_version(root) - - rule_list = [ - lane_smoothness_contact_point_no_horizontal_gaps.check_rule, - ] - - checker_data = models.CheckerData( - input_file_xml_root=root, - config=config, - result=result, - schema_version=odr_schema_version, - ) - - for rule in rule_list: - rule(checker_data=checker_data) - - logging.info( - f"Issues found - {result.get_checker_issue_count(checker_bundle_name=constants.BUNDLE_NAME, checker_id=smoothness_constants.CHECKER_ID)}" - ) - - # TODO: Add logic to deal with error or to skip it - result.set_checker_status( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=smoothness_constants.CHECKER_ID, - status=StatusType.COMPLETED, - ) diff --git a/qc_opendrive/checks/smoothness/smoothness_constants.py b/qc_opendrive/checks/smoothness/smoothness_constants.py deleted file mode 100644 index e94a96a..0000000 --- a/qc_opendrive/checks/smoothness/smoothness_constants.py +++ /dev/null @@ -1 +0,0 @@ -CHECKER_ID = "smoothness_xodr" diff --git a/qc_opendrive/main.py b/qc_opendrive/main.py index b04fdc9..64740ff 100644 --- a/qc_opendrive/main.py +++ b/qc_opendrive/main.py @@ -1,16 +1,18 @@ import argparse import logging +import types -from qc_baselib import Configuration, Result +from qc_baselib import Configuration, Result, StatusType from qc_baselib.models.common import ParamType from qc_opendrive import constants -from qc_opendrive.checks.semantic import semantic_checker -from qc_opendrive.checks.geometry import geometry_checker -from qc_opendrive.checks.performance import performance_checker -from qc_opendrive.checks.smoothness import smoothness_checker -from qc_opendrive.checks.basic import basic_checker -from qc_opendrive.checks.schema import schema_checker +from qc_opendrive.checks import semantic +from qc_opendrive.checks import geometry +from qc_opendrive.checks import performance +from qc_opendrive.checks import smoothness +from qc_opendrive.checks import basic +from qc_opendrive.checks import schema +from qc_opendrive.base import models, utils logging.basicConfig(format="%(asctime)s - %(message)s", level=logging.INFO) @@ -22,65 +24,215 @@ def args_entrypoint() -> argparse.Namespace: ) group = parser.add_mutually_exclusive_group(required=True) - group.add_argument("-d", "--default_config", action="store_true") group.add_argument("-c", "--config_path") - parser.add_argument("-g", "--generate_markdown", action="store_true") return parser.parse_args() -def main(): - args = args_entrypoint() +def execute_checker( + checker: types.ModuleType, + checker_data: models.CheckerData, + required_definition_setting: bool = True, +) -> None: + # Register checker + checker_data.result.register_checker( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=checker.CHECKER_ID, + description=checker.CHECKER_DESCRIPTION, + ) - logging.info("Initializing checks") + # Register rule uid + checker_data.result.register_rule_by_uid( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=checker.CHECKER_ID, + rule_uid=checker.RULE_UID, + ) - if args.default_config: - raise RuntimeError("Not implemented.") - else: - config = Configuration() - config.load_from_file(xml_file_path=args.config_path) - - result = Result() - result.register_checker_bundle( - name=constants.BUNDLE_NAME, - build_date="2024-06-05", - description="OpenDrive checker bundle", - version=constants.BUNDLE_VERSION, - summary="", + # Check preconditions. If not satisfied then set status as SKIPPED and return + if not checker_data.result.all_checkers_completed_without_issue( + checker.CHECKER_PRECONDITIONS + ): + checker_data.result.set_checker_status( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=checker.CHECKER_ID, + status=StatusType.SKIPPED, ) - result.set_result_version(version=constants.BUNDLE_VERSION) - input_file_path = config.get_config_param("InputFile") - input_param = ParamType(name="InputFile", value=input_file_path) - result.get_checker_bundle_result(constants.BUNDLE_NAME).params.append( - input_param + checker_data.result.add_checker_summary( + constants.BUNDLE_NAME, + checker.CHECKER_ID, + "Preconditions are not satisfied. Skip the check.", ) - # 1. Run basic checks - valid_document = basic_checker.run_checks(config=config, result=result) - - if valid_document: - schema_checker.run_checks(config=config, result=result) - semantic_checker.run_checks(config=config, result=result) - geometry_checker.run_checks(config=config, result=result) - performance_checker.run_checks(config=config, result=result) - smoothness_checker.run_checks(config=config, result=result) - else: - schema_checker.skip_checks(result=result) - semantic_checker.skip_checks(result=result) - geometry_checker.skip_checks(result=result) - performance_checker.skip_checks(result=result) - smoothness_checker.skip_checks(result=result) - - result.write_to_file( - config.get_checker_bundle_param( - checker_bundle_name=constants.BUNDLE_NAME, param_name="resultFile" + return + + # Checker definition setting. If not satisfied then set status as SKIPPED and return + if required_definition_setting: + schema_version = checker_data.schema_version + + splitted_rule_uid = checker.RULE_UID.split(":") + if len(splitted_rule_uid) != 4: + raise RuntimeError(f"Invalid rule uid: {checker.RULE_UID}") + + definition_setting = splitted_rule_uid[2] + if ( + schema_version is None + or utils.compare_versions(schema_version, definition_setting) < 0 + ): + checker_data.result.set_checker_status( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=checker.CHECKER_ID, + status=StatusType.SKIPPED, ) + + checker_data.result.add_checker_summary( + constants.BUNDLE_NAME, + checker.CHECKER_ID, + f"Version {schema_version} is lower than definition setting {definition_setting}. Skip the check.", + ) + + return + + # Execute checker + try: + checker.check_rule(checker_data) + + # If checker is not explicitly set as SKIPPED, then set it as COMPLETED + if ( + checker_data.result.get_checker_status(checker.CHECKER_ID) + != StatusType.SKIPPED + ): + checker_data.result.set_checker_status( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=checker.CHECKER_ID, + status=StatusType.COMPLETED, + ) + except Exception as e: + # If any exception occurs during the check, set the status as ERROR + checker_data.result.set_checker_status( + checker_bundle_name=constants.BUNDLE_NAME, + checker_id=checker.CHECKER_ID, + status=StatusType.ERROR, + ) + + checker_data.result.add_checker_summary( + constants.BUNDLE_NAME, checker.CHECKER_ID, f"Error: {str(e)}." + ) + + logging.exception(f"An error occur in {checker.CHECKER_ID}.") + + +def run_checks(config: Configuration, result: Result) -> None: + checker_data = models.CheckerData( + xml_file_path=config.get_config_param("InputFile"), + input_file_xml_root=None, + config=config, + result=result, + schema_version=None, + ) + + # 1. Run basic checks + execute_checker( + basic.valid_xml_document, checker_data, required_definition_setting=False + ) + + # Get xml root if the input file is a valid xml doc + if result.all_checkers_completed_without_issue( + {basic.valid_xml_document.CHECKER_ID} + ): + checker_data.input_file_xml_root = utils.get_root_without_default_namespace( + checker_data.xml_file_path + ) + + execute_checker( + basic.root_tag_is_opendrive, checker_data, required_definition_setting=False + ) + execute_checker( + basic.fileheader_is_present, checker_data, required_definition_setting=False + ) + execute_checker( + basic.version_is_defined, checker_data, required_definition_setting=False + ) + + # Get schema version if it exists + if result.all_checkers_completed_without_issue( + { + basic.valid_xml_document.CHECKER_ID, + basic.root_tag_is_opendrive.CHECKER_ID, + basic.fileheader_is_present.CHECKER_ID, + basic.version_is_defined.CHECKER_ID, + } + ): + checker_data.schema_version = utils.get_standard_schema_version( + checker_data.input_file_xml_root ) - if args.generate_markdown: - result.write_markdown_doc("generated_checker_bundle_doc.md") + # 2. Run schema check + execute_checker(schema.valid_schema, checker_data) + + # 3. Run semantic checks + execute_checker(semantic.road_lane_level_true_one_side, checker_data) + execute_checker(semantic.road_lane_access_no_mix_of_deny_or_allow, checker_data) + execute_checker(semantic.road_lane_link_lanes_across_lane_sections, checker_data) + execute_checker(semantic.road_linkage_is_junction_needed, checker_data) + execute_checker(semantic.road_lane_link_zero_width_at_start, checker_data) + execute_checker(semantic.road_lane_link_zero_width_at_end, checker_data) + execute_checker(semantic.road_lane_link_new_lane_appear, checker_data) + execute_checker( + semantic.junctions_connection_connect_road_no_incoming_road, checker_data + ) + execute_checker(semantic.junctions_connection_one_connection_element, checker_data) + execute_checker(semantic.junctions_connection_one_link_to_incoming, checker_data) + execute_checker(semantic.junctions_connection_start_along_linkage, checker_data) + execute_checker(semantic.junctions_connection_end_opposite_linkage, checker_data) + + # 4. Run geometry checks + execute_checker(geometry.road_geometry_parampoly3_length_match, checker_data) + execute_checker(geometry.road_lane_border_overlap_with_inner_lanes, checker_data) + execute_checker(geometry.road_geometry_parampoly3_arclength_range, checker_data) + execute_checker(geometry.road_geometry_parampoly3_normalized_range, checker_data) + + # 5. Run performance checks + execute_checker(performance.performance_avoid_redundant_info, checker_data) + + # 6. Run smoothness checks + execute_checker( + smoothness.lane_smoothness_contact_point_no_horizontal_gaps, checker_data + ) + + +def main(): + args = args_entrypoint() + + logging.info("Initializing checks") + + config = Configuration() + config.load_from_file(xml_file_path=args.config_path) + + result = Result() + result.register_checker_bundle( + name=constants.BUNDLE_NAME, + build_date="2024-06-05", + description="OpenDrive checker bundle", + version=constants.BUNDLE_VERSION, + summary="", + ) + result.set_result_version(version=constants.BUNDLE_VERSION) + + run_checks(config, result) + + result.copy_param_from_config(config) + + result.write_to_file( + config.get_checker_bundle_param( + checker_bundle_name=constants.BUNDLE_NAME, param_name="resultFile" + ), + generate_summary=True, + ) + + if args.generate_markdown: + result.write_markdown_doc("generated_checker_bundle_doc.md") logging.info("Done") diff --git a/tests/data/not_implemented_yet/rule_104_road_lanes_lane_offset_no_offset_if_border_defined_invalid.xodr b/tests/data/not_implemented_yet/rule_104_road_lanes_lane_offset_no_offset_if_border_defined_invalid.xodr new file mode 100644 index 0000000..63593a8 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_104_road_lanes_lane_offset_no_offset_if_border_defined_invalid.xodr @@ -0,0 +1,114 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_104_road_lanes_lane_offset_no_offset_if_border_defined_valid.xodr b/tests/data/not_implemented_yet/rule_104_road_lanes_lane_offset_no_offset_if_border_defined_valid.xodr new file mode 100644 index 0000000..ec0e208 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_104_road_lanes_lane_offset_no_offset_if_border_defined_valid.xodr @@ -0,0 +1,113 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_178_junctions_priority_high_and_low_attrib_invalid.xodr b/tests/data/not_implemented_yet/rule_178_junctions_priority_high_and_low_attrib_invalid.xodr new file mode 100644 index 0000000..ba3d8af --- /dev/null +++ b/tests/data/not_implemented_yet/rule_178_junctions_priority_high_and_low_attrib_invalid.xodr @@ -0,0 +1,314 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + +
+
+ + + + + + +
+ + + + + + + +
diff --git a/tests/data/not_implemented_yet/rule_178_junctions_priority_high_and_low_attrib_valid.xodr b/tests/data/not_implemented_yet/rule_178_junctions_priority_high_and_low_attrib_valid.xodr new file mode 100644 index 0000000..7f687d2 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_178_junctions_priority_high_and_low_attrib_valid.xodr @@ -0,0 +1,314 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + +
+
+ + + + + + +
+ + + + + + + +
diff --git a/tests/data/not_implemented_yet/rule_242_road_object_orientation_invalid.xodr b/tests/data/not_implemented_yet/rule_242_road_object_orientation_invalid.xodr new file mode 100644 index 0000000..253b7d0 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_242_road_object_orientation_invalid.xodr @@ -0,0 +1,172 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_242_road_object_orientation_valid.xodr b/tests/data/not_implemented_yet/rule_242_road_object_orientation_valid.xodr new file mode 100644 index 0000000..e3461c0 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_242_road_object_orientation_valid.xodr @@ -0,0 +1,172 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_247_road_object_outline_outline_followed_by_corner_invalid.xodr b/tests/data/not_implemented_yet/rule_247_road_object_outline_outline_followed_by_corner_invalid.xodr new file mode 100644 index 0000000..089244f --- /dev/null +++ b/tests/data/not_implemented_yet/rule_247_road_object_outline_outline_followed_by_corner_invalid.xodr @@ -0,0 +1,192 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_247_road_object_outline_outline_followed_by_corner_valid.xodr b/tests/data/not_implemented_yet/rule_247_road_object_outline_outline_followed_by_corner_valid.xodr new file mode 100644 index 0000000..35c1c64 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_247_road_object_outline_outline_followed_by_corner_valid.xodr @@ -0,0 +1,200 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_253_road_corner_road_element_min_amount_invalid.xodr b/tests/data/not_implemented_yet/rule_253_road_corner_road_element_min_amount_invalid.xodr new file mode 100644 index 0000000..5ad9f2a --- /dev/null +++ b/tests/data/not_implemented_yet/rule_253_road_corner_road_element_min_amount_invalid.xodr @@ -0,0 +1,175 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_253_road_corner_road_element_min_amount_valid.xodr b/tests/data/not_implemented_yet/rule_253_road_corner_road_element_min_amount_valid.xodr new file mode 100644 index 0000000..e7aecbe --- /dev/null +++ b/tests/data/not_implemented_yet/rule_253_road_corner_road_element_min_amount_valid.xodr @@ -0,0 +1,178 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_255_road_corner_local_element_min_amount_invalid.xodr b/tests/data/not_implemented_yet/rule_255_road_corner_local_element_min_amount_invalid.xodr new file mode 100644 index 0000000..33ba04c --- /dev/null +++ b/tests/data/not_implemented_yet/rule_255_road_corner_local_element_min_amount_invalid.xodr @@ -0,0 +1,175 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_255_road_corner_local_element_min_amount_valid.xodr b/tests/data/not_implemented_yet/rule_255_road_corner_local_element_min_amount_valid.xodr new file mode 100644 index 0000000..6d7cd4a --- /dev/null +++ b/tests/data/not_implemented_yet/rule_255_road_corner_local_element_min_amount_valid.xodr @@ -0,0 +1,178 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_257_road_object_skeleton_polyline_followed_by_vertex_invalid.xodr b/tests/data/not_implemented_yet/rule_257_road_object_skeleton_polyline_followed_by_vertex_invalid.xodr new file mode 100644 index 0000000..87a7e00 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_257_road_object_skeleton_polyline_followed_by_vertex_invalid.xodr @@ -0,0 +1,216 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_257_road_object_skeleton_polyline_followed_by_vertex_valid.xodr b/tests/data/not_implemented_yet/rule_257_road_object_skeleton_polyline_followed_by_vertex_valid.xodr new file mode 100644 index 0000000..258619a --- /dev/null +++ b/tests/data/not_implemented_yet/rule_257_road_object_skeleton_polyline_followed_by_vertex_valid.xodr @@ -0,0 +1,240 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_263_road_object_skeleton_vertex_road_element_min_amount_invalid.xodr b/tests/data/not_implemented_yet/rule_263_road_object_skeleton_vertex_road_element_min_amount_invalid.xodr new file mode 100644 index 0000000..696b3d6 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_263_road_object_skeleton_vertex_road_element_min_amount_invalid.xodr @@ -0,0 +1,192 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_263_road_object_skeleton_vertex_road_element_min_amount_valid.xodr b/tests/data/not_implemented_yet/rule_263_road_object_skeleton_vertex_road_element_min_amount_valid.xodr new file mode 100644 index 0000000..46570cb --- /dev/null +++ b/tests/data/not_implemented_yet/rule_263_road_object_skeleton_vertex_road_element_min_amount_valid.xodr @@ -0,0 +1,208 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_267_road_object_skeleton_vertex_local_element_min_amount_invalid.xodr b/tests/data/not_implemented_yet/rule_267_road_object_skeleton_vertex_local_element_min_amount_invalid.xodr new file mode 100644 index 0000000..a52efc6 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_267_road_object_skeleton_vertex_local_element_min_amount_invalid.xodr @@ -0,0 +1,197 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_267_road_object_skeleton_vertex_local_element_min_amount_valid.xodr b/tests/data/not_implemented_yet/rule_267_road_object_skeleton_vertex_local_element_min_amount_valid.xodr new file mode 100644 index 0000000..b49aca3 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_267_road_object_skeleton_vertex_local_element_min_amount_valid.xodr @@ -0,0 +1,198 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_284_road_object_no_cornerreference_if_no_outline_invalid.xodr b/tests/data/not_implemented_yet/rule_284_road_object_no_cornerreference_if_no_outline_invalid.xodr new file mode 100644 index 0000000..c8a333b --- /dev/null +++ b/tests/data/not_implemented_yet/rule_284_road_object_no_cornerreference_if_no_outline_invalid.xodr @@ -0,0 +1,188 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_284_road_object_no_cornerreference_if_no_outline_valid.xodr b/tests/data/not_implemented_yet/rule_284_road_object_no_cornerreference_if_no_outline_valid.xodr new file mode 100644 index 0000000..da33f9a --- /dev/null +++ b/tests/data/not_implemented_yet/rule_284_road_object_no_cornerreference_if_no_outline_valid.xodr @@ -0,0 +1,188 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_285_road_object_border_useCompleteOutline_true_invalid.xodr b/tests/data/not_implemented_yet/rule_285_road_object_border_useCompleteOutline_true_invalid.xodr new file mode 100644 index 0000000..6b7b3c5 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_285_road_object_border_useCompleteOutline_true_invalid.xodr @@ -0,0 +1,220 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_285_road_object_border_useCompleteOutline_true_valid.xodr b/tests/data/not_implemented_yet/rule_285_road_object_border_useCompleteOutline_true_valid.xodr new file mode 100644 index 0000000..a05f2fd --- /dev/null +++ b/tests/data/not_implemented_yet/rule_285_road_object_border_useCompleteOutline_true_valid.xodr @@ -0,0 +1,217 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_286_road_object_border_useCompleteOutline_false_invalid.xodr b/tests/data/not_implemented_yet/rule_286_road_object_border_useCompleteOutline_false_invalid.xodr new file mode 100644 index 0000000..30f6a12 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_286_road_object_border_useCompleteOutline_false_invalid.xodr @@ -0,0 +1,216 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_286_road_object_border_useCompleteOutline_false_valid.xodr b/tests/data/not_implemented_yet/rule_286_road_object_border_useCompleteOutline_false_valid.xodr new file mode 100644 index 0000000..a05f2fd --- /dev/null +++ b/tests/data/not_implemented_yet/rule_286_road_object_border_useCompleteOutline_false_valid.xodr @@ -0,0 +1,217 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_305_road.object.attributes_barrier_invalid.xodr b/tests/data/not_implemented_yet/rule_305_road.object.attributes_barrier_invalid.xodr new file mode 100644 index 0000000..a007d14 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_305_road.object.attributes_barrier_invalid.xodr @@ -0,0 +1,351 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_305_road.object.attributes_barrier_valid.xodr b/tests/data/not_implemented_yet/rule_305_road.object.attributes_barrier_valid.xodr new file mode 100644 index 0000000..2282b1a --- /dev/null +++ b/tests/data/not_implemented_yet/rule_305_road.object.attributes_barrier_valid.xodr @@ -0,0 +1,308 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_305_road.object.attributes_building_invalid.xodr b/tests/data/not_implemented_yet/rule_305_road.object.attributes_building_invalid.xodr new file mode 100644 index 0000000..66868c0 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_305_road.object.attributes_building_invalid.xodr @@ -0,0 +1,351 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_305_road.object.attributes_building_valid.xodr b/tests/data/not_implemented_yet/rule_305_road.object.attributes_building_valid.xodr new file mode 100644 index 0000000..5a0a41a --- /dev/null +++ b/tests/data/not_implemented_yet/rule_305_road.object.attributes_building_valid.xodr @@ -0,0 +1,168 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_305_road.object.attributes_crosswalk_invalid.xodr b/tests/data/not_implemented_yet/rule_305_road.object.attributes_crosswalk_invalid.xodr new file mode 100644 index 0000000..87f5085 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_305_road.object.attributes_crosswalk_invalid.xodr @@ -0,0 +1,208 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_305_road.object.attributes_crosswalk_valid.xodr b/tests/data/not_implemented_yet/rule_305_road.object.attributes_crosswalk_valid.xodr new file mode 100644 index 0000000..579da49 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_305_road.object.attributes_crosswalk_valid.xodr @@ -0,0 +1,186 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_305_road.object.attributes_gantry_invalid.xodr b/tests/data/not_implemented_yet/rule_305_road.object.attributes_gantry_invalid.xodr new file mode 100644 index 0000000..0a91be6 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_305_road.object.attributes_gantry_invalid.xodr @@ -0,0 +1,213 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_305_road.object.attributes_gantry_valid.xodr b/tests/data/not_implemented_yet/rule_305_road.object.attributes_gantry_valid.xodr new file mode 100644 index 0000000..61bd819 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_305_road.object.attributes_gantry_valid.xodr @@ -0,0 +1,194 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_305_road.object.attributes_obstacle_invalid.xodr b/tests/data/not_implemented_yet/rule_305_road.object.attributes_obstacle_invalid.xodr new file mode 100644 index 0000000..b66acff --- /dev/null +++ b/tests/data/not_implemented_yet/rule_305_road.object.attributes_obstacle_invalid.xodr @@ -0,0 +1,213 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_305_road.object.attributes_obstacle_valid.xodr b/tests/data/not_implemented_yet/rule_305_road.object.attributes_obstacle_valid.xodr new file mode 100644 index 0000000..a89f7d8 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_305_road.object.attributes_obstacle_valid.xodr @@ -0,0 +1,168 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_305_road.object.attributes_parkingSpace_invalid.xodr b/tests/data/not_implemented_yet/rule_305_road.object.attributes_parkingSpace_invalid.xodr new file mode 100644 index 0000000..f816ddd --- /dev/null +++ b/tests/data/not_implemented_yet/rule_305_road.object.attributes_parkingSpace_invalid.xodr @@ -0,0 +1,217 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_305_road.object.attributes_parkingSpace_valid.xodr b/tests/data/not_implemented_yet/rule_305_road.object.attributes_parkingSpace_valid.xodr new file mode 100644 index 0000000..46f81ad --- /dev/null +++ b/tests/data/not_implemented_yet/rule_305_road.object.attributes_parkingSpace_valid.xodr @@ -0,0 +1,188 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_305_road.object.attributes_pole_invalid.xodr b/tests/data/not_implemented_yet/rule_305_road.object.attributes_pole_invalid.xodr new file mode 100644 index 0000000..c3ad809 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_305_road.object.attributes_pole_invalid.xodr @@ -0,0 +1,187 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_305_road.object.attributes_pole_valid.xodr b/tests/data/not_implemented_yet/rule_305_road.object.attributes_pole_valid.xodr new file mode 100644 index 0000000..004e880 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_305_road.object.attributes_pole_valid.xodr @@ -0,0 +1,168 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_305_road.object.attributes_roadMark_invalid.xodr b/tests/data/not_implemented_yet/rule_305_road.object.attributes_roadMark_invalid.xodr new file mode 100644 index 0000000..79f5916 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_305_road.object.attributes_roadMark_invalid.xodr @@ -0,0 +1,211 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_305_road.object.attributes_roadMark_valid.xodr b/tests/data/not_implemented_yet/rule_305_road.object.attributes_roadMark_valid.xodr new file mode 100644 index 0000000..a8a4b60 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_305_road.object.attributes_roadMark_valid.xodr @@ -0,0 +1,180 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_305_road.object.attributes_roadSurface_invalid.xodr b/tests/data/not_implemented_yet/rule_305_road.object.attributes_roadSurface_invalid.xodr new file mode 100644 index 0000000..3b274e7 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_305_road.object.attributes_roadSurface_invalid.xodr @@ -0,0 +1,212 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_305_road.object.attributes_roadSurface_valid.xodr b/tests/data/not_implemented_yet/rule_305_road.object.attributes_roadSurface_valid.xodr new file mode 100644 index 0000000..0dd634b --- /dev/null +++ b/tests/data/not_implemented_yet/rule_305_road.object.attributes_roadSurface_valid.xodr @@ -0,0 +1,171 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_305_road.object.attributes_trafficIsland_invalid.xodr b/tests/data/not_implemented_yet/rule_305_road.object.attributes_trafficIsland_invalid.xodr new file mode 100644 index 0000000..26b53b0 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_305_road.object.attributes_trafficIsland_invalid.xodr @@ -0,0 +1,236 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_305_road.object.attributes_trafficIsland_valid.xodr b/tests/data/not_implemented_yet/rule_305_road.object.attributes_trafficIsland_valid.xodr new file mode 100644 index 0000000..b2720b8 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_305_road.object.attributes_trafficIsland_valid.xodr @@ -0,0 +1,205 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_305_road.object.attributes_tree_invalid.xodr b/tests/data/not_implemented_yet/rule_305_road.object.attributes_tree_invalid.xodr new file mode 100644 index 0000000..9b0729a --- /dev/null +++ b/tests/data/not_implemented_yet/rule_305_road.object.attributes_tree_invalid.xodr @@ -0,0 +1,217 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_305_road.object.attributes_tree_valid.xodr b/tests/data/not_implemented_yet/rule_305_road.object.attributes_tree_valid.xodr new file mode 100644 index 0000000..b89ca4c --- /dev/null +++ b/tests/data/not_implemented_yet/rule_305_road.object.attributes_tree_valid.xodr @@ -0,0 +1,198 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_305_road.object.attributes_vegetation_invalid.xodr b/tests/data/not_implemented_yet/rule_305_road.object.attributes_vegetation_invalid.xodr new file mode 100644 index 0000000..ef95a12 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_305_road.object.attributes_vegetation_invalid.xodr @@ -0,0 +1,193 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_305_road.object.attributes_vegetation_valid.xodr b/tests/data/not_implemented_yet/rule_305_road.object.attributes_vegetation_valid.xodr new file mode 100644 index 0000000..ca14725 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_305_road.object.attributes_vegetation_valid.xodr @@ -0,0 +1,168 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_325_road_signal_reference_used_for_signals_only_invalid.xodr b/tests/data/not_implemented_yet/rule_325_road_signal_reference_used_for_signals_only_invalid.xodr new file mode 100644 index 0000000..06ca466 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_325_road_signal_reference_used_for_signals_only_invalid.xodr @@ -0,0 +1,170 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_325_road_signal_reference_used_for_signals_only_valid.xodr b/tests/data/not_implemented_yet/rule_325_road_signal_reference_used_for_signals_only_valid.xodr new file mode 100644 index 0000000..fc73bec --- /dev/null +++ b/tests/data/not_implemented_yet/rule_325_road_signal_reference_used_for_signals_only_valid.xodr @@ -0,0 +1,170 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_341_road_signal_boards_multi_board_have_sub_boards_no_static_invalid.xodr b/tests/data/not_implemented_yet/rule_341_road_signal_boards_multi_board_have_sub_boards_no_static_invalid.xodr new file mode 100644 index 0000000..ab024fd --- /dev/null +++ b/tests/data/not_implemented_yet/rule_341_road_signal_boards_multi_board_have_sub_boards_no_static_invalid.xodr @@ -0,0 +1,226 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_341_road_signal_boards_multi_board_have_sub_boards_no_vms_invalid.xodr b/tests/data/not_implemented_yet/rule_341_road_signal_boards_multi_board_have_sub_boards_no_vms_invalid.xodr new file mode 100644 index 0000000..8beb800 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_341_road_signal_boards_multi_board_have_sub_boards_no_vms_invalid.xodr @@ -0,0 +1,226 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_341_road_signal_boards_multi_board_have_sub_boards_valid.xodr b/tests/data/not_implemented_yet/rule_341_road_signal_boards_multi_board_have_sub_boards_valid.xodr new file mode 100644 index 0000000..b66072d --- /dev/null +++ b/tests/data/not_implemented_yet/rule_341_road_signal_boards_multi_board_have_sub_boards_valid.xodr @@ -0,0 +1,237 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_343_road_signal_boards_multi_board_use_dynamic_true_invalid.xodr b/tests/data/not_implemented_yet/rule_343_road_signal_boards_multi_board_use_dynamic_true_invalid.xodr new file mode 100644 index 0000000..cdc054b --- /dev/null +++ b/tests/data/not_implemented_yet/rule_343_road_signal_boards_multi_board_use_dynamic_true_invalid.xodr @@ -0,0 +1,237 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_343_road_signal_boards_multi_board_use_dynamic_true_valid.xodr b/tests/data/not_implemented_yet/rule_343_road_signal_boards_multi_board_use_dynamic_true_valid.xodr new file mode 100644 index 0000000..b66072d --- /dev/null +++ b/tests/data/not_implemented_yet/rule_343_road_signal_boards_multi_board_use_dynamic_true_valid.xodr @@ -0,0 +1,237 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_371_general_optional_param_used_invalid.xodr b/tests/data/not_implemented_yet/rule_371_general_optional_param_used_invalid.xodr new file mode 100644 index 0000000..63f09ea --- /dev/null +++ b/tests/data/not_implemented_yet/rule_371_general_optional_param_used_invalid.xodr @@ -0,0 +1,170 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_371_general_optional_param_used_valid.xodr b/tests/data/not_implemented_yet/rule_371_general_optional_param_used_valid.xodr new file mode 100644 index 0000000..0c0cccc --- /dev/null +++ b/tests/data/not_implemented_yet/rule_371_general_optional_param_used_valid.xodr @@ -0,0 +1,170 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_64_road_cross_section_surface_no_shape_superelevation_invalid.xodr b/tests/data/not_implemented_yet/rule_64_road_cross_section_surface_no_shape_superelevation_invalid.xodr new file mode 100644 index 0000000..8927566 --- /dev/null +++ b/tests/data/not_implemented_yet/rule_64_road_cross_section_surface_no_shape_superelevation_invalid.xodr @@ -0,0 +1,79 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + +
+
+ + + + + + +
+
diff --git a/tests/data/not_implemented_yet/rule_64_road_cross_section_surface_no_shape_superelevation_valid.xodr b/tests/data/not_implemented_yet/rule_64_road_cross_section_surface_no_shape_superelevation_valid.xodr new file mode 100644 index 0000000..78a308b --- /dev/null +++ b/tests/data/not_implemented_yet/rule_64_road_cross_section_surface_no_shape_superelevation_valid.xodr @@ -0,0 +1,78 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + +
+
+ + + + + + +
+
diff --git a/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_elevation_invalid_1.xodr b/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_elevation_invalid_1.xodr index 1382462..7130122 100644 --- a/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_elevation_invalid_1.xodr +++ b/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_elevation_invalid_1.xodr @@ -5,8 +5,12 @@ west="0.0000000000000000e+00"> + + + - + @@ -16,5 +20,36 @@ + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + +
+
diff --git a/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_elevation_invalid_2.xodr b/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_elevation_invalid_2.xodr index eac39dd..78f2a10 100644 --- a/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_elevation_invalid_2.xodr +++ b/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_elevation_invalid_2.xodr @@ -5,8 +5,12 @@ west="0.0000000000000000e+00"> + + + - + @@ -16,5 +20,36 @@ + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + +
+
diff --git a/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_elevation_valid.xodr b/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_elevation_valid.xodr index 2b18f28..504afac 100644 --- a/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_elevation_valid.xodr +++ b/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_elevation_valid.xodr @@ -5,11 +5,51 @@ west="0.0000000000000000e+00"> + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + +
+
diff --git a/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_lane_offset_invalid_1.xodr b/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_lane_offset_invalid_1.xodr index 991bd46..75d5f06 100644 --- a/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_lane_offset_invalid_1.xodr +++ b/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_lane_offset_invalid_1.xodr @@ -5,11 +5,19 @@ west="0.0000000000000000e+00"> + + + - + + + + + @@ -17,6 +25,39 @@ c="0.0000000000000000e+00" d="0.0000000000000000e+00" /> + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + +
diff --git a/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_lane_offset_invalid_2.xodr b/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_lane_offset_invalid_2.xodr index 0f6daf2..5c31d40 100644 --- a/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_lane_offset_invalid_2.xodr +++ b/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_lane_offset_invalid_2.xodr @@ -5,11 +5,19 @@ west="0.0000000000000000e+00"> + + + - + + + + + @@ -17,6 +25,39 @@ c="0.0000000000000000e+00" d="0.0000000000000000e+00" /> + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + +
diff --git a/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_lane_offset_valid.xodr b/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_lane_offset_valid.xodr index 09b6bd6..3dd1f1b 100644 --- a/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_lane_offset_valid.xodr +++ b/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_lane_offset_valid.xodr @@ -5,11 +5,57 @@ west="0.0000000000000000e+00"> + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + +
diff --git a/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_lane_width_valid.xodr b/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_lane_width_valid.xodr index 4d1b7f8..84ec987 100644 --- a/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_lane_width_valid.xodr +++ b/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_lane_width_valid.xodr @@ -5,6 +5,19 @@ west="0.0000000000000000e+00"> + + + + + + + + + + + + diff --git a/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_line_geometry_invalid.xodr b/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_line_geometry_invalid.xodr index 835bee0..c403528 100644 --- a/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_line_geometry_invalid.xodr +++ b/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_line_geometry_invalid.xodr @@ -1,10 +1,13 @@ -
- + + + + @@ -19,5 +22,42 @@ + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + +
+
diff --git a/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_line_geometry_valid.xodr b/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_line_geometry_valid.xodr index ac3a7dc..e8717fc 100644 --- a/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_line_geometry_valid.xodr +++ b/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_line_geometry_valid.xodr @@ -1,10 +1,13 @@ -
- + + + + @@ -15,5 +18,42 @@ + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + +
+
diff --git a/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_superelevation_invalid.xodr b/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_superelevation_invalid.xodr index a175bd5..a19feb4 100644 --- a/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_superelevation_invalid.xodr +++ b/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_superelevation_invalid.xodr @@ -5,11 +5,17 @@ west="0.0000000000000000e+00"> + + + - + + + @@ -18,5 +24,40 @@ + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + +
+
diff --git a/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_superelevation_valid.xodr b/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_superelevation_valid.xodr index 1f05ccb..a939ddc 100644 --- a/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_superelevation_valid.xodr +++ b/tests/data/performance_avoid_redundant_info/performance_avoid_redundant_info_superelevation_valid.xodr @@ -5,6 +5,17 @@ west="0.0000000000000000e+00"> + + + + + + + + + + @@ -13,5 +24,40 @@ + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + +
+
diff --git a/tests/data/road_geometry_parampoly3_arclength_range/road_geometry_parampoly3_arclength_range_valid.xodr b/tests/data/road_geometry_parampoly3_arclength_range/road_geometry_parampoly3_arclength_range_valid.xodr index 734dc40..024bc1c 100644 --- a/tests/data/road_geometry_parampoly3_arclength_range/road_geometry_parampoly3_arclength_range_valid.xodr +++ b/tests/data/road_geometry_parampoly3_arclength_range/road_geometry_parampoly3_arclength_range_valid.xodr @@ -2,7 +2,7 @@
- + @@ -40,9 +40,29 @@ + + - + + + + + + + + + + + + + + + + + + +
diff --git a/tests/data/smoothness_example/multiple_successor_invalid.xodr b/tests/data/smoothness_example/multiple_successor_invalid.xodr index 8fe9498..2fba757 100644 --- a/tests/data/smoothness_example/multiple_successor_invalid.xodr +++ b/tests/data/smoothness_example/multiple_successor_invalid.xodr @@ -85,7 +85,4 @@
- - - diff --git a/tests/data/smoothness_example/multiple_successor_valid.xodr b/tests/data/smoothness_example/multiple_successor_valid.xodr index b5ec8b1..2ff9f9a 100644 --- a/tests/data/smoothness_example/multiple_successor_valid.xodr +++ b/tests/data/smoothness_example/multiple_successor_valid.xodr @@ -86,6 +86,4 @@ - - diff --git a/tests/test_basic_checks.py b/tests/test_basic_checks.py index 71f2e92..d4f7922 100644 --- a/tests/test_basic_checks.py +++ b/tests/test_basic_checks.py @@ -1,9 +1,8 @@ import os import pytest from test_setup import * -from qc_opendrive import constants -from qc_opendrive.checks.basic import basic_constants -from qc_baselib import Result, IssueSeverity +from qc_baselib import Result, IssueSeverity, StatusType +from qc_opendrive.checks import basic def test_valid_xml_document_positive( @@ -20,6 +19,11 @@ def test_valid_xml_document_positive( result = Result() result.load_from_file(REPORT_FILE_PATH) + assert ( + result.get_checker_status(basic.valid_xml_document.CHECKER_ID) + == StatusType.COMPLETED + ) + assert ( len(result.get_issues_by_rule_uid("asam.net:xodr:1.0.0:xml.valid_xml_document")) == 0 @@ -42,6 +46,11 @@ def test_valid_xml_document_negative( result = Result() result.load_from_file(REPORT_FILE_PATH) + assert ( + result.get_checker_status(basic.valid_xml_document.CHECKER_ID) + == StatusType.COMPLETED + ) + xml_doc_issues = result.get_issues_by_rule_uid( "asam.net:xodr:1.0.0:xml.valid_xml_document" ) @@ -64,6 +73,11 @@ def test_root_tag_is_opendrive_positive( result = Result() result.load_from_file(REPORT_FILE_PATH) + assert ( + result.get_checker_status(basic.root_tag_is_opendrive.CHECKER_ID) + == StatusType.COMPLETED + ) + assert ( len( result.get_issues_by_rule_uid( @@ -90,6 +104,11 @@ def test_root_tag_is_opendrive_negative( result = Result() result.load_from_file(REPORT_FILE_PATH) + assert ( + result.get_checker_status(basic.root_tag_is_opendrive.CHECKER_ID) + == StatusType.COMPLETED + ) + xml_doc_issues = result.get_issues_by_rule_uid( "asam.net:xodr:1.0.0:xml.root_tag_is_opendrive" ) @@ -112,6 +131,11 @@ def test_fileheader_is_present_positive( result = Result() result.load_from_file(REPORT_FILE_PATH) + assert ( + result.get_checker_status(basic.fileheader_is_present.CHECKER_ID) + == StatusType.COMPLETED + ) + assert ( len( result.get_issues_by_rule_uid( @@ -138,6 +162,11 @@ def test_fileheader_is_present_negative( result = Result() result.load_from_file(REPORT_FILE_PATH) + assert ( + result.get_checker_status(basic.fileheader_is_present.CHECKER_ID) + == StatusType.COMPLETED + ) + xml_doc_issues = result.get_issues_by_rule_uid( "asam.net:xodr:1.0.0:xml.fileheader_is_present" ) @@ -160,6 +189,11 @@ def test_version_is_defined__positive( result = Result() result.load_from_file(REPORT_FILE_PATH) + assert ( + result.get_checker_status(basic.version_is_defined.CHECKER_ID) + == StatusType.COMPLETED + ) + assert ( len(result.get_issues_by_rule_uid("asam.net:xodr:1.0.0:xml.version_is_defined")) == 0 @@ -182,6 +216,11 @@ def test_version_is_defined_negative_attr( result = Result() result.load_from_file(REPORT_FILE_PATH) + assert ( + result.get_checker_status(basic.version_is_defined.CHECKER_ID) + == StatusType.COMPLETED + ) + xml_doc_issues = result.get_issues_by_rule_uid( "asam.net:xodr:1.0.0:xml.version_is_defined" ) @@ -204,6 +243,11 @@ def test_version_is_defined_negative_type( result = Result() result.load_from_file(REPORT_FILE_PATH) + assert ( + result.get_checker_status(basic.version_is_defined.CHECKER_ID) + == StatusType.COMPLETED + ) + xml_doc_issues = result.get_issues_by_rule_uid( "asam.net:xodr:1.0.0:xml.version_is_defined" ) diff --git a/tests/test_geometry_checks.py b/tests/test_geometry_checks.py index e3562ed..ec15eb0 100644 --- a/tests/test_geometry_checks.py +++ b/tests/test_geometry_checks.py @@ -1,10 +1,10 @@ import os -import sys import pytest from typing import List from qc_baselib import IssueSeverity +from qc_opendrive.checks import geometry from test_setup import * @@ -49,7 +49,13 @@ def test_road_geometry_param_poly3_length_match( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + geometry.road_geometry_parampoly3_length_match.CHECKER_ID, + ) cleanup_files() @@ -100,7 +106,13 @@ def test_road_lane_border_overlap_with_inner_lanes( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + geometry.road_lane_border_overlap_with_inner_lanes.CHECKER_ID, + ) cleanup_files() @@ -137,7 +149,13 @@ def test_road_geometry_parampoly3_arclength_range( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + geometry.road_geometry_parampoly3_arclength_range.CHECKER_ID, + ) cleanup_files() @@ -172,5 +190,11 @@ def test_road_geometry_param_poly3_normalized_range( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + geometry.road_geometry_parampoly3_normalized_range.CHECKER_ID, + ) cleanup_files() diff --git a/tests/test_performance_checks.py b/tests/test_performance_checks.py index 18f37ef..f11641f 100644 --- a/tests/test_performance_checks.py +++ b/tests/test_performance_checks.py @@ -4,6 +4,7 @@ from typing import List from qc_baselib import IssueSeverity +from qc_opendrive.checks import performance from test_setup import * @@ -113,5 +114,11 @@ def test_performance_avoid_redundant_info( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + performance.performance_avoid_redundant_info.CHECKER_ID, + ) cleanup_files() diff --git a/tests/test_schema_checks.py b/tests/test_schema_checks.py index 66c1f0d..3f1f904 100644 --- a/tests/test_schema_checks.py +++ b/tests/test_schema_checks.py @@ -1,9 +1,8 @@ import os import pytest from test_setup import * -from qc_opendrive import constants -from qc_opendrive.checks.schema import schema_constants from qc_baselib import Result, IssueSeverity, StatusType +from qc_opendrive.checks.schema import valid_schema def test_valid_schema_positive17( @@ -125,14 +124,9 @@ def test_unsupported_schema_version( result = Result() result.load_from_file(REPORT_FILE_PATH) - schema_result = result.get_checker_result( - checker_bundle_name=constants.BUNDLE_NAME, - checker_id=schema_constants.CHECKER_ID, - ) - schema_issues = result.get_issues_by_rule_uid( "asam.net:xodr:1.0.0:xml.valid_schema" ) assert len(schema_issues) == 0 - assert schema_result.status == StatusType.SKIPPED + assert result.get_checker_status(valid_schema.CHECKER_ID) == StatusType.SKIPPED cleanup_files() diff --git a/tests/test_semantic_checks.py b/tests/test_semantic_checks.py index a6a5c68..fbaf7e8 100644 --- a/tests/test_semantic_checks.py +++ b/tests/test_semantic_checks.py @@ -1,10 +1,10 @@ import os -import sys import pytest from typing import List from qc_baselib import IssueSeverity +from qc_opendrive.checks import semantic from test_setup import * @@ -23,7 +23,6 @@ ("17_valid", 0, []), ("18_invalid", 1, ["/OpenDRIVE/road/lanes/laneSection/left/lane[1]/access[2]"]), ("18_valid", 0, []), - ("17_invalid_older_schema_version", 0, []), ], ) def test_road_lane_access_no_mix_of_deny_or_allow_examples( @@ -40,7 +39,37 @@ def test_road_lane_access_no_mix_of_deny_or_allow_examples( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + semantic.road_lane_access_no_mix_of_deny_or_allow.CHECKER_ID, + ) + cleanup_files() + + +@pytest.mark.parametrize( + "target_file", + [ + ("17_invalid_older_schema_version"), + ], +) +def test_road_lane_access_no_mix_of_deny_or_allow_older_schema( + target_file: str, + monkeypatch, +) -> None: + base_path = "tests/data/road_lane_access_no_mix_of_deny_or_allow/" + target_file_name = f"road_lane_access_no_mix_of_deny_or_allow_{target_file}.xodr" + rule_uid = "asam.net:xodr:1.7.0:road.lane.access.no_mix_of_deny_or_allow" + + target_file_path = os.path.join(base_path, target_file_name) + create_test_config(target_file_path) + launch_main(monkeypatch) + check_skipped( + rule_uid, + semantic.road_lane_access_no_mix_of_deny_or_allow.CHECKER_ID, + ) cleanup_files() @@ -74,7 +103,13 @@ def test_road_lane_access_no_mix_of_deny_or_allow_close_match( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + semantic.road_lane_access_no_mix_of_deny_or_allow.CHECKER_ID, + ) cleanup_files() @@ -90,7 +125,6 @@ def test_road_lane_access_no_mix_of_deny_or_allow_close_match( "/OpenDRIVE/road/lanes/laneSection/right/lane[3]", ], ), - ("invalid_older_schema_version", 0, []), ], ) def test_road_lane_true_level_one_side( @@ -107,7 +141,37 @@ def test_road_lane_true_level_one_side( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + semantic.road_lane_level_true_one_side.CHECKER_ID, + ) + cleanup_files() + + +@pytest.mark.parametrize( + "target_file", + [ + ("invalid_older_schema_version"), + ], +) +def test_road_lane_true_level_one_side_older_schema( + target_file: str, + monkeypatch, +) -> None: + base_path = "tests/data/road_lane_level_true_one_side/" + target_file_name = f"road_lane_level_true_one_side_{target_file}.xodr" + rule_uid = "asam.net:xodr:1.7.0:road.lane.level_true_one_side" + + target_file_path = os.path.join(base_path, target_file_name) + create_test_config(target_file_path) + launch_main(monkeypatch) + check_skipped( + rule_uid, + semantic.road_lane_level_true_one_side.CHECKER_ID, + ) cleanup_files() @@ -154,7 +218,13 @@ def test_road_lane_true_level_one_side_lane_section( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + semantic.road_lane_level_true_one_side.CHECKER_ID, + ) cleanup_files() @@ -188,7 +258,13 @@ def test_road_lane_true_level_one_side_road( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + semantic.road_lane_level_true_one_side.CHECKER_ID, + ) cleanup_files() @@ -220,7 +296,13 @@ def test_road_lane_true_level_one_side_junction( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + semantic.road_lane_level_true_one_side.CHECKER_ID, + ) cleanup_files() @@ -278,7 +360,13 @@ def test_road_lane_link_lanes_across_lane_sections( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + semantic.road_lane_link_lanes_across_lane_sections.CHECKER_ID, + ) cleanup_files() @@ -310,7 +398,13 @@ def test_road_linkage_is_junction_needed( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + semantic.road_linkage_is_junction_needed.CHECKER_ID, + ) cleanup_files() @@ -344,7 +438,13 @@ def test_junctions_connection_road_no_incoming_road( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + semantic.junctions_connection_connect_road_no_incoming_road.CHECKER_ID, + ) cleanup_files() @@ -376,7 +476,13 @@ def test_junctions_connection_one_connection_element( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + semantic.junctions_connection_one_connection_element.CHECKER_ID, + ) cleanup_files() @@ -423,7 +529,13 @@ def test_junctions_connection_one_link_to_incoming( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + semantic.junctions_connection_one_link_to_incoming.CHECKER_ID, + ) cleanup_files() @@ -454,7 +566,13 @@ def test_junctions_connection_one_link_to_incoming_bidirectional( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + semantic.junctions_connection_one_link_to_incoming.CHECKER_ID, + ) cleanup_files() @@ -478,7 +596,13 @@ def test_junctions_connection_one_link_to_incoming_direct( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + semantic.junctions_connection_one_link_to_incoming.CHECKER_ID, + ) cleanup_files() @@ -509,7 +633,13 @@ def test_road_lane_link_zero_width_at_start( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + semantic.road_lane_link_zero_width_at_start.CHECKER_ID, + ) cleanup_files() @@ -540,7 +670,13 @@ def test_road_lane_link_zero_width_at_start_junction( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + semantic.road_lane_link_zero_width_at_start.CHECKER_ID, + ) cleanup_files() @@ -573,7 +709,13 @@ def test_road_lane_link_zero_width_at_start_inside_junction( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + semantic.road_lane_link_zero_width_at_start.CHECKER_ID, + ) cleanup_files() @@ -604,7 +746,13 @@ def test_road_lane_link_zero_width_at_end( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + semantic.road_lane_link_zero_width_at_end.CHECKER_ID, + ) cleanup_files() @@ -635,7 +783,13 @@ def test_road_lane_link_zero_width_at_end_junction( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + semantic.road_lane_link_zero_width_at_end.CHECKER_ID, + ) cleanup_files() @@ -668,7 +822,13 @@ def test_road_lane_link_zero_width_at_end_inside_junction( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + semantic.road_lane_link_zero_width_at_end.CHECKER_ID, + ) cleanup_files() @@ -708,7 +868,13 @@ def test_road_lane_link_new_lane_appear( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + semantic.road_lane_link_new_lane_appear.CHECKER_ID, + ) cleanup_files() @@ -740,7 +906,13 @@ def test_road_lane_link_new_lane_appear_junction( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + semantic.road_lane_link_new_lane_appear.CHECKER_ID, + ) cleanup_files() @@ -785,7 +957,13 @@ def test_road_lane_link_new_lane_appear_inside_junction( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + semantic.road_lane_link_new_lane_appear.CHECKER_ID, + ) cleanup_files() @@ -820,7 +998,13 @@ def test_road_lane_link_new_lane_appear_end_contact_point( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + semantic.road_lane_link_new_lane_appear.CHECKER_ID, + ) cleanup_files() @@ -851,7 +1035,13 @@ def test_junction_connection_start_along_linkage( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + semantic.junctions_connection_start_along_linkage.CHECKER_ID, + ) cleanup_files() @@ -882,5 +1072,11 @@ def test_junction_connection_end_opposite_linkage( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + semantic.junctions_connection_end_opposite_linkage.CHECKER_ID, + ) cleanup_files() diff --git a/tests/test_setup.py b/tests/test_setup.py index fb579ce..861be17 100644 --- a/tests/test_setup.py +++ b/tests/test_setup.py @@ -6,7 +6,7 @@ import qc_opendrive.main as main from qc_opendrive import constants -from qc_baselib import Configuration, Result, IssueSeverity +from qc_baselib import Configuration, Result, IssueSeverity, StatusType CONFIG_FILE_PATH = "bundle_config.xml" REPORT_FILE_PATH = "xodr_bundle_report.xqar" @@ -26,11 +26,17 @@ def create_test_config(target_file_path: str): def check_issues( - rule_uid: str, issue_count: int, issue_xpath: List[str], severity: IssueSeverity + rule_uid: str, + issue_count: int, + issue_xpath: List[str], + severity: IssueSeverity, + checker_id: str, ): result = Result() result.load_from_file(REPORT_FILE_PATH) + assert result.get_checker_status(checker_id) == StatusType.COMPLETED + issues = result.get_issues_by_rule_uid(rule_uid) assert len(issues) == issue_count @@ -48,6 +54,20 @@ def check_issues( assert issue.level == severity +def check_skipped( + rule_uid: str, + checker_id: str, +): + result = Result() + result.load_from_file(REPORT_FILE_PATH) + + assert result.get_checker_status(checker_id) == StatusType.SKIPPED + + issues = result.get_issues_by_rule_uid(rule_uid) + + assert len(issues) == 0 + + def launch_main(monkeypatch): monkeypatch.setattr( sys, diff --git a/tests/test_smoothness_checks.py b/tests/test_smoothness_checks.py index ee19b8f..2b4885c 100644 --- a/tests/test_smoothness_checks.py +++ b/tests/test_smoothness_checks.py @@ -1,10 +1,10 @@ import os -import sys import pytest from typing import List from qc_baselib import IssueSeverity +from qc_opendrive.checks import smoothness from test_setup import * @@ -97,5 +97,11 @@ def test_road_lane_access_no_mix_of_deny_or_allow_examples( target_file_path = os.path.join(base_path, target_file_name) create_test_config(target_file_path) launch_main(monkeypatch) - check_issues(rule_uid, issue_count, issue_xpath, issue_severity) + check_issues( + rule_uid, + issue_count, + issue_xpath, + issue_severity, + smoothness.lane_smoothness_contact_point_no_horizontal_gaps.CHECKER_ID, + ) cleanup_files()