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()