├── .gitignore ├── CITATION.cff ├── LICENSE.txt ├── MANIFEST.in ├── README.md ├── docs └── images │ ├── OSC-Generator_Demo.gif │ └── OSC-Generator_Interfaces.png ├── example.py ├── osc_generator ├── __init__.py ├── osc_generator.py ├── tools │ ├── __init__.py │ ├── converter.py │ ├── coord_calculations.py │ ├── man_helpers.py │ ├── osi_transformer.py │ ├── rulebased.py │ ├── scenario_writer.py │ ├── user_config.py │ └── utils.py └── version.py ├── requirements.txt ├── requirements_dev.txt ├── setup.cfg ├── setup.py └── tests ├── __init__.py ├── test_data ├── TestTrack.xodr ├── accelerate_array.npy ├── cleaned_df.csv ├── decelerate_array.npy ├── df33.csv ├── df_lanes.csv ├── df_lanes_llc.csv ├── ego_maneuver_array_0.npy ├── expected_llc.xosc ├── expected_rlc.xosc ├── expected_scenario.xosc ├── expected_scenario_osi.xosc ├── expected_straight.xosc ├── keep_velocity_array.npy ├── lane_change_left_array.npy ├── lane_change_right_array.npy ├── model_speed.csv ├── model_speed.npy ├── prepared_df.csv ├── temp_ego_maneuver_array.csv ├── testfile_llc.csv ├── testfile_llc.osi ├── testfile_rlc.csv ├── testfile_rlc.osi ├── testfile_straight.csv ├── testfile_straight.osi ├── trajectories_file.csv └── trajectories_file.osi └── test_tools ├── __init__.py ├── test_converter.py ├── test_coord_calculations.py ├── test_man_helpers.py ├── test_osc_generator.py ├── test_rulebased.py └── test_utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | authors: 2 | - 3 | family-names: Montanari 4 | given-names: Francesco 5 | - 6 | family-names: Akkaya 7 | given-names: Yigit Ali 8 | - 9 | family-names: Boßmann 10 | given-names: Nils 11 | - 12 | family-names: Sichermann 13 | given-names: Jörg 14 | - 15 | family-names: Müller 16 | given-names: Marcel 17 | - 18 | family-names: Aigner 19 | given-names: Axel Jeronimo 20 | - 21 | family-names: D'Sa 22 | given-names: Dave 23 | cff-version: "1.1.0" 24 | license: "Apache-2.0" 25 | version: "0.2.0" 26 | message: "If you use this software, please cite it using these metadata." 27 | abstract: "Python tools to generate open-scenarios files from trajectory data for automated driving." 28 | repository-code: "https://github.com/EFS-OpenSource/OSC-Generator" 29 | title: "OSC-Generator" -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include requirements.txt requirements_dev.txt LICENSE.txt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Description 2 | OSC-Generator is a collection of Python tools to generate [ASAM OpenSCENARIO](https://www.asam.net/standards/detail/openscenario/) files from vehicle data and an [ASAM OpenDRIVE](https://www.asam.net/standards/detail/opendrive/) file. 3 | 4 | ![OSC-Workflow](docs/images/OSC-Generator_Interfaces.png) 5 | 6 | The generated openSCENARIO file (.xosc) can then be used for purposes such as scenario re-simulations, or in further applications, for example, visualised in a tool like [esmini](https://github.com/esmini/esmini). 7 | 8 | ![openSCENARIO_visualisation_demo](docs/images/OSC-Generator_Demo.gif) 9 | 10 | ## Scope of Application 11 | Currently, OpenSCENARIO V1.2 and OpenDRIVE V1.4 are supported. 12 | Intersections may currently cause trouble but will be supported in a future release. 13 | All features are tested in Python 3.7 on Windows 10. 14 | 15 | ## Installation 16 | ### PyPI 17 | - OSC-Generator can be installed using pip 18 | ``` 19 | pip install osc-generator 20 | ``` 21 | 22 | ### Testing 23 | - Additional dependencies for testing are required. 24 | - Required Python packages can be installed via pip: 25 | ``` 26 | pip install -r requirements_dev.txt 27 | ``` 28 | - For testing, an ASAM OpenDRIVE file is needed. The file '_2017-04-04_Testfeld_A9_Nord_offset.xodr_' from [here](https://service.mdm-portal.de/mdm-portal-application/publDetail.do?publicationId=2594000) can be used by downloading a copy to the _tests/test_data_ folder. This file uses ASAM OpenDRIVE V1.4 format. 29 | - Run pytest in the _tests_ folder or a parent folder thereof. 30 | - When everything is set up correctly, all tests should run successfully without raising any warnings. 31 | 32 | ## Usage 33 | - Class: OSC-Generator provides a Python class which can be used to generate a scenario in the OpenSCENARIO format from trajectories and an OpenDRIVE file. The file example.py contains runnable example code for usage of this class. 34 | - CLI: 35 | - OSC-Generator can use arguments provided via Python's commandline interface. For information on how to use this feature, see the output of the help function: 36 | 37 | - When installed via pip, OSC-Generator can directly be called in the console: 38 | ``` 39 | osc_generator -h 40 | ``` 41 | - To use the OSC-Generator through the command line (without installation via pip), navigate to the main project directory and from there the help function can be called: 42 | ``` 43 | python -m osc_generator.osc_generator -h 44 | ``` 45 | 46 | - CLI arguments 47 | - the following table outlines the available input arguments 48 | 49 | | Argument | Type | Default Value | Description | 50 | |-----|----------|--------------|--------------------------------------------------------------------| 51 | | "-t", "--trajectories" | required | N/A | Path to the file containing the object trajectories used as input | 52 | | "-d", "--opendrive" | required | N/A | Path to the opendrive file which describes the road net which the objects are using | 53 | | "-s", "--openscenario" | optional | "None" | Output file path and name. If not specified, a directory and name will be chosen. If the file already exists , it will be overwritten | 54 | | "-v", "--version" | optional | N/A | Show program's version number and exit | 55 | | "-cat", "--catalog" | optional | "None" | Catalog file path and name. If not specified, a default catalog path is used | 56 | | "-oscv", "--oscversion" | optional | "None" | Desired version of the output OpenScenario file. If not specified, default is OSC V1.0 | 57 | 58 | 59 | ## Expected Input Data and Formats 60 | ### Trajectories file 61 | - the input trajectories file can currently be provided in two formats 62 | - .csv 63 | - .osi (Open Simulation Interface - see below) 64 | 65 | 66 | - the following data is required in a .csv for an OpenSCENARIO file to be generated (especially when using lane data relative to the ego vehicle): 67 | - **timestamp** [seconds] - time of each data point, from the beginning of the "scenario" 68 | - **lat** [decimal degree (WGS84)] - latitude of the ego vehicle in the scenario 69 | - **lon** [decimal degree (WGS84)] - longitude of the ego vehicle in the scenario 70 | - **heading** [degrees (starting North, increasing clockwise)] - heading of the ego vehicle in the scenario 71 | - **speed** [kilometers/hour] - speed of the ego vehicle in the scenario 72 | - **lin_left_typ** [-] - lane type of left lane 73 | - **lin_left_beginn_x** [meters] - beginning left lane detection point, relative to the ego vehicle, in the x direction 74 | - **lin_left_y_abstand** [meters] - distance between the ego vehicle and the left lane, in the y direction 75 | - **lin_left_kruemm** [-] - the curvature of the left lane 76 | - **lin_left_ende_x** [meters] - end left lane detection point, relative to the ego vehicle, in the x direction 77 | - **lin_left_breite** [meters] - width of the lane marking 78 | - **lin_right_typ** [-] - lane type of left lane 79 | - **lin_right_beginn_x** [meters] - beginning right lane detection point, relative to the ego vehicle, in the x direction 80 | - **lin_right_y_abstand** [meters] - distance between the ego vehicle and the right lane, in the y direction 81 | - **lin_right_kruemm** [-] - the curvature of the right lane 82 | - **lin_right_ende_x** [meters] - end right lane detection point, relative to the ego vehicle, in the x direction 83 | - **lin_right_breite** [meters] - lane type of right lane 84 | 85 | 86 | - the following data is optional, depending on how many object vehicles are in the scenario - "#" represents the object number, starting at 1 87 | - **pos_x_#** [meters] - distance between object vehicle and ego vehicle in x direction 88 | - **pos_y_#** [meters] - distance between object vehicle and ego vehicle in y direction 89 | - **speed_x_#** [kilometers/hour] - speed of object vehicle in x direction 90 | - **speed_y_#** [kilometers/hour] - speed of object vehicle in y direction 91 | - **class_#** [-] - class/type of object, e.g. 7 = car class 92 | 93 | - for an example of an input trajectory .csv file, see "tests/test_data/trajectories_file.csv" 94 | 95 | 96 | ### OpenDRIVE file 97 | - the OpenDRIVE file should be compliant with ASAM's standard 98 | - ideally matching the trajectory data too 99 | 100 | 101 | ## Open Simulation Interface (OSI) Format Input 102 | - In order to use OSI format (.osi) input trajectory files with the OSC-Generator, the following steps are required: 103 | - install the Open Simulation Interface (OSI): 104 | - follow the installation instructions: https://github.com/OpenSimulationInterface/open-simulation-interface 105 | - copy the file 'OSITrace.py': 106 | - from "$PATH_TO_OSI_DIRECTORY\open-simulation-interface\format" 107 | - to "$PATH_TO_OSC-GENERATOR_DIRECTORY\OSC-Generator\osc_generator\tools\OSI" 108 | - run tests 109 | 110 | 111 | - Usage of this feature functions as described above. 112 | - if OSI is not installed, the OSC-Generator can still be used with .csv input trajectory files. 113 | 114 | ## Citation 115 | An associated [paper](https://ieeexplore.ieee.org/document/9575441) describes the original use case for which the OSC-Generator was created. 116 | See also [this dissertation](https://opus4.kobv.de/opus4-fau/frontdoor/index/index/searchtype/authorsearch/author/Francesco+Montanari/docId/22788/start/0/rows/20). 117 | 118 | When using this software, please cite the following: 119 | ``` 120 | @software{OSC-Generator, 121 | author = {{Montanari, Francesco}, {Akkaya, Yigit Ali}, {Boßmann, Nils}, {Sichermann, Jörg}, {Müller, Marcel}, {Aigner, Axel Jeronimo}, {D'Sa, Dave}}, 122 | license = {Apache-2.0}, 123 | title = {{OSC-Generator}}, 124 | url = {https://github.com/EFS-OpenSource/OSC-Generator}, 125 | version = {0.2.0} 126 | } 127 | ``` 128 | 129 | ## Acknowledgment 130 | This work is supported by the German Federal Ministry for Digital and Transport (BMDV) within the *Automated and Connected Driving* funding program under grant No. 01MM20012F ([SAVeNoW](https://savenow.de)). 131 | 132 | @copyright 2022 e:fs TechHub GmbH and Audi AG. All rights reserved. 133 | https://www.efs-techhub.com/ 134 | https://www.audi.com/de/company.html 135 | 136 | @license Apache v2.0 137 | 138 | Licensed under the Apache License, Version 2.0 (the "License"); 139 | you may not use this file except in compliance with the License. 140 | You may obtain a copy of the License at 141 | 142 | http://www.apache.org/licenses/LICENSE-2.0 143 | 144 | Unless required by applicable law or agreed to in writing, software 145 | distributed under the License is distributed on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 147 | See the License for the specific language governing permissions and 148 | limitations under the License. 149 | -------------------------------------------------------------------------------- /docs/images/OSC-Generator_Demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFS-OpenSource/OSC-Generator/48177576849e3a600d5d3914447286bf3d1d95e4/docs/images/OSC-Generator_Demo.gif -------------------------------------------------------------------------------- /docs/images/OSC-Generator_Interfaces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFS-OpenSource/OSC-Generator/48177576849e3a600d5d3914447286bf3d1d95e4/docs/images/OSC-Generator_Interfaces.png -------------------------------------------------------------------------------- /example.py: -------------------------------------------------------------------------------- 1 | # **************************************************************************** 2 | # @example.py 3 | # 4 | # @copyright 2022 e:fs TechHub GmbH and Audi AG. All rights reserved. 5 | # 6 | # @license Apache v2.0 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # **************************************************************************** 20 | 21 | 22 | from osc_generator.osc_generator import OSCGenerator 23 | 24 | #trajectories_path = r'tests\test_data\trajectories_file.csv' 25 | trajectories_path = r'tests\test_data\trajectories_file.osi' 26 | opendrive_path = r'tests\test_data\2017-04-04_Testfeld_A9_Nord_offset.xodr' 27 | 28 | OSG = OSCGenerator() 29 | OSG.generate_osc(trajectories_path, opendrive_path) 30 | -------------------------------------------------------------------------------- /osc_generator/__init__.py: -------------------------------------------------------------------------------- 1 | # **************************************************************************** 2 | # @__init__.py 3 | # 4 | # @copyright 2022 e:fs TechHub GmbH and Audi AG. All rights reserved. 5 | # 6 | # @license Apache v2.0 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # **************************************************************************** 20 | 21 | """Top level package. 22 | """ 23 | # __all__ = ["tools", "osc_generator"] 24 | -------------------------------------------------------------------------------- /osc_generator/osc_generator.py: -------------------------------------------------------------------------------- 1 | # **************************************************************************** 2 | # @osc_generator.py 3 | # 4 | # @copyright 2022 e:fs TechHub GmbH and Audi AG. All rights reserved. 5 | # 6 | # @license Apache v2.0 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # **************************************************************************** 20 | 21 | import os 22 | 23 | from .tools.converter import Converter 24 | from .tools.user_config import UserConfig 25 | import sys 26 | from argparse import ArgumentParser 27 | from .version import __version__ 28 | 29 | 30 | class OSCGenerator: 31 | """ 32 | This class provides an easy-to-use API for generating scenarios. 33 | The API is designed with forward-compatibility in mind, 34 | which is the reason why many experimental features of the underlying code are not accessible here. 35 | 36 | """ 37 | def __init__(self): 38 | self.converter: Converter = Converter() 39 | 40 | def generate_osc(self, trajectories_path: str, opendrive_path: str, output_scenario_path: str = None, 41 | **kwargs: str): 42 | """ 43 | This method generates a OpenSCENARIO file based on trajectories and an OpenDRIVE file. 44 | 45 | Args: 46 | trajectories_path: Path to the file containing the object trajectories used as input 47 | opendrive_path: Path to the OpenDRIVE file which describes the road net which the objects are using 48 | output_scenario_path: Output file path and name. If not specified, a directory and name will be chosen. 49 | If the file already exists, it will be overwritten. 50 | keyword arguments: 51 | catalog_path: Path to the catalog file containing vehicle catalog information for the output scenario 52 | osc_version: Desired version of the output OpenScenario file. Default is OSC V1.0 53 | 54 | """ 55 | if "catalog_path" in kwargs: 56 | if kwargs["catalog_path"] is not None: 57 | dir_name = os.path.dirname(trajectories_path) 58 | user_config = UserConfig(dir_name) 59 | user_config.catalogs = kwargs["catalog_path"] 60 | user_config.write_config() 61 | 62 | if "osc_version" in kwargs: 63 | if kwargs["osc_version"] is not None: 64 | self.converter.osc_version = kwargs["osc_version"] 65 | 66 | if output_scenario_path: 67 | self.converter.set_paths(trajectories_path, opendrive_path, output_scenario_path) 68 | else: 69 | self.converter.set_paths(trajectories_path, opendrive_path) 70 | 71 | self.converter.process_trajectories(relative=True) 72 | self.converter.label_maneuvers(acc_threshold=0.2, optimize_acc=False, generate_kml=False) 73 | self.converter.write_scenario(plot=False, 74 | radius_pos_trigger=2.0, 75 | timebased_lon=True, 76 | timebased_lat=False, 77 | output='xosc') 78 | print('Path to OpenSCENARIO file: ' + os.path.abspath(self.converter.outfile)) 79 | 80 | 81 | def main(): 82 | parser = ArgumentParser() 83 | parser.add_argument('-v', '--version', action='version', version=('%(prog)s ' + str(__version__)), 84 | help="Show program's version number and exit.") 85 | parser.add_argument("-t", "--trajectories", dest="trajectories_path", 86 | help="path to the file containing the object trajectories used as input") 87 | parser.add_argument("-d", "--opendrive", dest="opendrive_path", 88 | help="path to the opendrive file which describes the road net which the objects are using") 89 | parser.add_argument("-s", "--openscenario", dest="output_scenario_path", default=None, 90 | help="output file path and name. If not specified, a directory and name will be chosen. " 91 | "If the file already exists , it will be overwritten.") 92 | parser.add_argument("-cat", "--catalog", dest="catalog_path", default=None, 93 | help="catalog file path and name. If not specified, a default catalog path is used. ") 94 | parser.add_argument("-oscv", "--oscversion", dest="osc_version", default=None, 95 | help="Desired version of the output OpenScenario file. If not specified, default is OSC V1.0 ") 96 | 97 | try: 98 | args = parser.parse_args() 99 | except SystemExit as err: 100 | if err.code == 2: 101 | parser.print_help() 102 | sys.exit(0) 103 | if (args.trajectories_path is None) or (args.opendrive_path is None): 104 | parser.print_help() 105 | return 106 | oscg = OSCGenerator() 107 | oscg.generate_osc(args.trajectories_path, args.opendrive_path, args.output_scenario_path, 108 | catalog_path=args.catalog_path, 109 | osc_version=args.osc_version) 110 | 111 | 112 | if __name__ == '__main__': 113 | main() 114 | -------------------------------------------------------------------------------- /osc_generator/tools/__init__.py: -------------------------------------------------------------------------------- 1 | # **************************************************************************** 2 | # @__init__.py 3 | # 4 | # @copyright 2022 e:fs TechHub GmbH and Audi AG. All rights reserved. 5 | # 6 | # @license Apache v2.0 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # **************************************************************************** 20 | 21 | """Subpackage for helper/utility modules. 22 | """ 23 | #__all__ = ["converter", "coord_calculations", "man_helpers", "rulebased", "scenario_writer", "utils", "osi_transformer", "user_config"] 24 | -------------------------------------------------------------------------------- /osc_generator/tools/converter.py: -------------------------------------------------------------------------------- 1 | # **************************************************************************** 2 | # @converter.py 3 | # 4 | # @copyright 2022 e:fs TechHub GmbH and Audi AG. All rights reserved. 5 | # 6 | # @license Apache v2.0 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # **************************************************************************** 20 | 21 | import numpy as np 22 | import pandas as pd 23 | import os 24 | from typing import Union 25 | 26 | from osc_generator.tools import utils 27 | from osc_generator.tools import man_helpers 28 | from osc_generator.tools.coord_calculations import transform_lanes_rel2abs 29 | from osc_generator.tools.scenario_writer import convert_to_osc 30 | from osc_generator.tools.osi_transformer import osi2df 31 | 32 | class Converter: 33 | """ 34 | Main class for converter operations 35 | """ 36 | 37 | def __init__(self): 38 | self.osc_version: str = '1.0' 39 | self.trajectories_path: str = '' 40 | self.opendrive_path: str = '' 41 | self.outfile = None 42 | self.use_folder: bool = True 43 | 44 | self.dir_name: str = '' 45 | self.section_name: str = '' 46 | 47 | self.df = None 48 | self.df_lanes = None 49 | 50 | self.ego_maneuver_array = None 51 | self.inf_maneuver_array = None 52 | self.objlist = None 53 | self.objects = None 54 | self.ego = None 55 | self.movobj_grps_coord = None 56 | 57 | def set_paths(self, trajectories_path: str, opendrive_path: str, output_scenario_path: str = None): 58 | """ 59 | This method should be called before calling other class methods 60 | 61 | Args: 62 | trajectories_path: Path to the file containing the object trajectories used as input 63 | opendrive_path: Path to the OpenDRIVE file which describes the road net which the objects are using 64 | output_scenario_path: Output file path and name. If not specified, a directory and name will be chosen. 65 | If the file already exists, it will be overwritten. 66 | 67 | """ 68 | if os.path.isfile(trajectories_path): 69 | self.trajectories_path = trajectories_path 70 | else: 71 | raise FileNotFoundError("file not found: " + str(trajectories_path)) 72 | 73 | if os.path.isfile(opendrive_path): 74 | self.opendrive_path = opendrive_path 75 | else: 76 | raise FileNotFoundError("file not found: " + str(opendrive_path)) 77 | 78 | if output_scenario_path is not None: 79 | output_dir_path = os.path.dirname(os.path.abspath(output_scenario_path)) 80 | if os.path.isdir(output_dir_path): 81 | self.outfile = output_scenario_path 82 | else: 83 | raise FileNotFoundError("folder not found: " + str(output_dir_path)) 84 | 85 | if self.use_folder: 86 | path_name = self.trajectories_path.rsplit(os.path.sep) 87 | self.section_name = path_name[-1] 88 | self.dir_name = os.path.dirname(self.trajectories_path) 89 | else: 90 | raise NotImplementedError("use_folder flag is going to be removed") 91 | 92 | def process_trajectories(self, relative: bool = True, df_lanes: pd.DataFrame = None): 93 | """ 94 | Process trajectories file and convert it to cleaned main dataframe 95 | and a dataframe for absolute coordination of lanes 96 | 97 | Args: 98 | relative: True -> coordinates of lanes and vehicles are relative to ego. 99 | df_lanes: If absolute coordinates are used, the lane coordinates needs to be passed here. 100 | 101 | """ 102 | data_type = '' 103 | if self.trajectories_path.endswith(".csv"): 104 | df = pd.read_csv(self.trajectories_path) 105 | data_type = 'csv' 106 | elif self.trajectories_path.endswith(".osi"): 107 | df = osi2df(self.trajectories_path) 108 | data_type = 'osi' 109 | 110 | if relative: 111 | # Delete not relevant objects (too far away, not visible long enough, not plausible) 112 | movobj_grps = utils.find_vars('pos_x_|pos_y_|speed_x_|speed_y_|class_', df.columns, reshape=True) 113 | df, del_obj = utils.delete_irrelevant_objects(df, movobj_grps, min_nofcases=20, max_posx_min=50.0, 114 | max_posx_nofcases_ratio=10.0) 115 | # Create absolute lane points from relative 116 | self.df_lanes = transform_lanes_rel2abs(df, data_type) 117 | 118 | # Compute coordinates of Objects 119 | # Find posx-posy movobj_grps and define lat-lon movobj_grps 120 | movobj_grps = utils.find_vars('pos_x_|pos_y_|speed_x_', df.columns, reshape=True) 121 | 122 | movobj_grps_coord = [] 123 | for p in movobj_grps: 124 | movobj_grps_coord.append([p[0].replace('pos_x', 'lat'), p[1].replace('pos_y', 'lon'), 125 | p[2].replace('speed_x', 'speed'), p[2].replace('speed_x', 'class')]) 126 | 127 | # Compute Coordinates and absolute speed 128 | for p, q in zip(movobj_grps, movobj_grps_coord): 129 | coordx = [] 130 | coordy = [] 131 | for k in range(len(df[p])): 132 | if not any(list(pd.isna(df.loc[k, p]))): 133 | n = utils.calc_new_geopos_from_2d_vector_on_spheric_earth(curr_coords=df.loc[k, ["lat", "long"]], 134 | heading=df.loc[k, "heading"], 135 | dist_x=df.loc[k, p[0]], dist_y=df.loc[k, p[1]]) 136 | coordx.append(n[0]) 137 | coordy.append(n[1]) 138 | else: 139 | coordx.append(np.nan) 140 | coordy.append(np.nan) 141 | df[q[0]] = coordx 142 | df[q[1]] = coordy 143 | df[q[2]] = abs(df[p[2]]) 144 | 145 | # Delete and reorder columns 146 | delete_vars = utils.find_vars('speed_x_|speed_y_', df.columns) 147 | df = df.drop(columns=delete_vars) 148 | 149 | reorder_vars1 = ['timestamp', 'lat', 'long', 'heading', 'speed'] 150 | reorder_vars3 = utils.flatten(movobj_grps_coord) 151 | self.df = df[reorder_vars1 + reorder_vars3] 152 | else: 153 | # Delete not relevant objects (too far away, too short seen, not plausible) 154 | movobj_grps = utils.find_vars('lat_|lon_|speed_|class_', df.columns, reshape=True) 155 | df, del_obj = utils.delete_irrelevant_objects(df, movobj_grps, min_nofcases=20, max_posx_min=50.0, 156 | max_posx_nofcases_ratio=10.0) 157 | 158 | if df_lanes is None: 159 | raise ValueError('if absolute coordinates are used, the lane coordinates needs ' 160 | 'to be passed in process_inter func. as a dataframe.') 161 | else: 162 | self.df_lanes = df_lanes 163 | self.df = df 164 | 165 | if self.use_folder: 166 | self.df.to_csv(os.path.join(self.dir_name, 'df33.csv')) 167 | else: 168 | raise NotImplementedError("use_folder flag is going to be removed") 169 | 170 | def label_maneuvers(self, acc_threshold: Union[float, np.ndarray] = 0.2, optimize_acc: bool = False, 171 | generate_kml: bool = False): 172 | """ 173 | Main dataframe and lanes dataframe will be used here to label the maneuvers. 174 | 175 | Args: 176 | acc_threshold: Acceleration threshold for labeling 177 | optimize_acc: Option to get optimal acceleration threshold 178 | generate_kml: Option to create kml files 179 | """ 180 | if optimize_acc: 181 | acc_thres_opt = man_helpers.calc_opt_acc_thresh(self.df, self.df_lanes, self.opendrive_path, 182 | self.use_folder, self.dir_name) 183 | acc_threshold = acc_thres_opt 184 | ego_maneuver_array, inf_maneuver_array, objlist, objects, ego, movobj_grps_coord = man_helpers.label_maneuvers( 185 | self.df, self.df_lanes, acc_threshold, generate_kml, 186 | self.opendrive_path, self.use_folder, self.dir_name) 187 | else: 188 | ego_maneuver_array, inf_maneuver_array, objlist, objects, ego, movobj_grps_coord = man_helpers.label_maneuvers( 189 | self.df, self.df_lanes, acc_threshold, generate_kml, 190 | self.opendrive_path, self.use_folder, self.dir_name) 191 | 192 | self.ego_maneuver_array = ego_maneuver_array 193 | self.inf_maneuver_array = inf_maneuver_array 194 | self.objlist = objlist 195 | self.objects = objects 196 | self.ego = ego 197 | self.movobj_grps_coord = movobj_grps_coord 198 | 199 | def write_scenario(self, plot: bool = False, 200 | radius_pos_trigger: float = 2.0, timebased_lon: bool = True, timebased_lat: bool = False, 201 | output: str = 'xosc'): 202 | """ 203 | Writes the trajectories or maneuvers in selected file formats. 204 | 205 | Args: 206 | plot: Ploting option 207 | radius_pos_trigger: Defines the radius of position trigger 208 | timebased_lon: True -> timebase trigger for longitudinal maneuver will be used. False -> position base 209 | timebased_lat: True -> timebase trigger for latitudinal maneuver will be used. False -> position base 210 | output: Pption for different file formats. To write OpenScenario -> 'xosc'. 211 | """ 212 | if output == 'xosc': 213 | outfile = convert_to_osc(self.df, self.ego, self.objects, self.ego_maneuver_array, self.inf_maneuver_array, 214 | self.movobj_grps_coord, self.objlist, plot, 215 | self.opendrive_path, self.use_folder, timebased_lon, timebased_lat, 216 | self.section_name, radius_pos_trigger, self.dir_name, self.osc_version, self.outfile) 217 | self.outfile = outfile 218 | 219 | else: 220 | raise NotImplementedError('selected output option is not implemented') 221 | -------------------------------------------------------------------------------- /osc_generator/tools/coord_calculations.py: -------------------------------------------------------------------------------- 1 | # **************************************************************************** 2 | # @coord_calculations.py 3 | # 4 | # @copyright 2022 e:fs TechHub GmbH and Audi AG. All rights reserved. 5 | # 6 | # @license Apache v2.0 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # **************************************************************************** 20 | 21 | import pandas as pd 22 | import math 23 | import pyproj 24 | from pyproj import Geod 25 | import numpy as np 26 | import warnings 27 | 28 | 29 | def transform_lanes_rel2abs(df: pd.DataFrame, data_type: str) -> pd.DataFrame: 30 | """ 31 | Transforms lane coordinates in absolut coordinate system 32 | 33 | Args: 34 | df: Input dataframe 35 | data_type: Input file type (csv or osi) 36 | 37 | Returns: 38 | object (pd.DataFrame): Transformed dataframe 39 | """ 40 | if not isinstance(df, pd.DataFrame): 41 | raise TypeError("input must be a pd.DataFrame") 42 | if not isinstance(data_type, str): 43 | raise TypeError("input must be a str") 44 | 45 | def find_curve(begin_x: float, begin_y: float, k: float, end_x: float) -> np.ndarray: 46 | """ 47 | Helper function for rel2abs. Creates an array containing a 2D curve represented by x and y values. 48 | 49 | Args: 50 | begin_x: x coordinate of the curve starting point 51 | begin_y: y coordinate of the curve starting point 52 | k: curvature of the curve 53 | end_x: x coordinate of the curve ending point (may not be represented with a point of the array) 54 | 55 | Returns: 56 | object (np.ndarray): array containing a 2D curve represented by x and y values 57 | """ 58 | points = [] 59 | x = 0 60 | x_increment = 2 61 | while x <= end_x: 62 | if k == 0: 63 | points.append((x, begin_y)) 64 | else: 65 | y = (k * begin_y + 1 - math.sqrt(1 - (k ** 2) * ((x - begin_x) ** 2))) / k 66 | points.append((x, y)) 67 | x += x_increment 68 | return np.array(points) 69 | 70 | def calc_new_geopos_from_2d_vector(lat: float, lon: float, head: float, x: float, y: float, g: Geod) -> tuple: 71 | """ 72 | Helper function for rel2abs. calculates a new geo position based on a start position, heading and a 2d movement 73 | vector from object perspective 74 | 75 | Args: 76 | lat: Latitude of starting position 77 | lon: Longitude of starting position 78 | head: Starting heading 79 | x: X component of movement vector from object starting perspective 80 | y: Y component of movement vector from object starting perspective 81 | g (pyproj.geod): Geodetic 82 | 83 | Returns: 84 | object (tuple): Latitude and longitude of new geo position 85 | """ 86 | d = math.sqrt(x ** 2 + y ** 2) 87 | # When Azimuth to calculate, it is important to consider heading of ego vehicle 88 | q = head - math.degrees(math.atan2(y, x)) 89 | 90 | endlon, endlat, backaz = g.fwd(lon, lat, q, d, radians=False) 91 | return endlat, endlon 92 | 93 | length = len(df.index) - 1 94 | df_crv = df.dropna(subset=['lin_right_beginn_x', 'lin_left_beginn_x']) 95 | length_crv = len(df_crv.index) - 1 96 | if data_type == 'csv': 97 | points_right = find_curve(df_crv['lin_right_beginn_x'][length_crv], 98 | df_crv['lin_right_y_abstand'][length_crv], 99 | df_crv['lin_right_kruemm'][length_crv], 100 | df_crv['lin_right_ende_x'][length_crv]) 101 | 102 | points_left = find_curve(df_crv['lin_left_beginn_x'][length_crv], 103 | df_crv['lin_left_y_abstand'][length_crv], 104 | df_crv['lin_left_kruemm'][length_crv], 105 | df_crv['lin_left_ende_x'][length_crv]) 106 | df_temp_1 = pd.DataFrame(columns=['lin_right_beginn_x']) 107 | df_temp_1['lin_right_beginn_x'] = points_right[:, 0] 108 | df_temp_1['lin_right_y_abstand'] = points_right[:, 1] 109 | df_temp_2 = pd.DataFrame(columns=['lin_left_beginn_x']) 110 | df_temp_2['lin_left_beginn_x'] = points_left[:, 0] 111 | df_temp_2['lin_left_y_abstand'] = points_left[:, 1] 112 | df_temp_1 = pd.concat([df_temp_1, df_temp_2], axis=1) 113 | df_temp_1 = df_temp_1.dropna() 114 | 115 | df = pd.concat([df, df_temp_1], ignore_index=True) 116 | else: 117 | pass 118 | 119 | geodetic = Geod(ellps='WGS84') 120 | r_lane_lat_list = [] 121 | r_lane_lon_list = [] 122 | l_lane_lat_list = [] 123 | l_lane_lon_list = [] 124 | for i in range(len(df.index)): 125 | if i < length: 126 | r_lane_lat, r_lane_lon = calc_new_geopos_from_2d_vector(df['lat'][i], df['long'][i], df['heading'][i], 127 | df['lin_right_beginn_x'][i], 128 | df['lin_right_y_abstand'][i], geodetic) 129 | l_lane_lat, l_lane_lon = calc_new_geopos_from_2d_vector(df['lat'][i], df['long'][i], df['heading'][i], 130 | df['lin_left_beginn_x'][i], 131 | df['lin_left_y_abstand'][i], geodetic) 132 | else: 133 | r_lane_lat, r_lane_lon = calc_new_geopos_from_2d_vector(df['lat'][length], df['long'][length], df['heading'][length], 134 | df['lin_right_beginn_x'][i], 135 | df['lin_right_y_abstand'][i], geodetic) 136 | l_lane_lat, l_lane_lon = calc_new_geopos_from_2d_vector(df['lat'][length], df['long'][length], df['heading'][length], 137 | df['lin_left_beginn_x'][i], 138 | df['lin_left_y_abstand'][i], geodetic) 139 | 140 | r_lane_lat_list.append(r_lane_lat) 141 | r_lane_lon_list.append(r_lane_lon) 142 | l_lane_lat_list.append(l_lane_lat) 143 | l_lane_lon_list.append(l_lane_lon) 144 | 145 | # Give each line an ID: 146 | left = [] 147 | right = [] 148 | # Detect lane change for id change 149 | for i in range(length): 150 | index = i + 1 151 | if df['lin_left_y_abstand'][index] < 0: 152 | left.append(index) 153 | if df['lin_right_y_abstand'][index] > 0: 154 | right.append(index) 155 | 156 | left_index = [] 157 | right_index = [] 158 | # Delete consecutive values 159 | for i in range(len(left) - 1): 160 | if left[i] == left[i + 1] - 1: 161 | left_index.append(left[i]) 162 | for i in range(len(left_index)): 163 | left.remove(left_index[i]) 164 | 165 | for i in range(len(right) - 1): 166 | if right[i] == right[i + 1] - 1: 167 | right_index.append(right[i]) 168 | for i in range(len(right_index)): 169 | right.remove(right_index[i]) 170 | 171 | # Create single list with respective label list 172 | left_right = left + right 173 | left_right = sorted(left_right) 174 | left_right_label = [] 175 | for i in range(len(left_right)): 176 | if left_right[i] in left: 177 | left_right_label.append('left') 178 | if left_right[i] in right: 179 | left_right_label.append('right') 180 | 181 | df_out = pd.DataFrame() 182 | if len(left_right) == 0: 183 | df_out['right_lane_lat'] = r_lane_lat_list 184 | df_out['right_lane_lon'] = r_lane_lon_list 185 | df_out['left_lane_lat'] = l_lane_lat_list 186 | df_out['left_lane_lon'] = l_lane_lon_list 187 | else: 188 | array = np.empty(len(r_lane_lat_list)) 189 | array[:] = np.NaN 190 | df_out['0lat'] = array 191 | df_out['0lon'] = array 192 | df_out['1lat'] = array 193 | df_out['1lon'] = array 194 | 195 | for i in range(len(left_right)): 196 | df_out[str(i + 2) + 'lat'] = array 197 | df_out[str(i + 2) + 'lon'] = array 198 | 199 | df_out.iloc[0:left_right[0] + 1, 0] = r_lane_lat_list[0:left_right[0] + 1] 200 | df_out.iloc[0:left_right[0] + 1, 1] = r_lane_lon_list[0:left_right[0] + 1] 201 | df_out.iloc[0:left_right[0] + 1, 2] = l_lane_lat_list[0:left_right[0] + 1] 202 | df_out.iloc[0:left_right[0] + 1, 3] = l_lane_lon_list[0:left_right[0] + 1] 203 | 204 | counter = 4 205 | id_right = [0, 1] 206 | id_left = [2, 3] 207 | for i in range(len(left_right)): 208 | # Get starting and ending index of next lane slice 209 | current_index = left_right[i] + 1 210 | if len(left_right) - 1 == i: 211 | next_index = df.shape[0] 212 | else: 213 | next_index = left_right[i + 1] + 1 214 | 215 | if left_right_label[i] == 'left': 216 | id_right = id_left 217 | id_left = [counter, counter + 1] 218 | counter = counter + 2 219 | if left_right_label[i] == 'right': 220 | id_left = id_right 221 | id_right = [counter, counter + 1] 222 | counter = counter + 2 223 | 224 | df_out.iloc[current_index:next_index, id_right[0]] = r_lane_lat_list[current_index:next_index] 225 | df_out.iloc[current_index:next_index, id_right[1]] = r_lane_lon_list[current_index:next_index] 226 | df_out.iloc[current_index:next_index, id_left[0]] = l_lane_lat_list[current_index:next_index] 227 | df_out.iloc[current_index:next_index, id_left[1]] = l_lane_lon_list[current_index:next_index] 228 | 229 | return df_out 230 | 231 | 232 | def get_proj_from_open_drive(open_drive_path: str) -> str: 233 | """ 234 | Get Coordinate system infos from OpenDrive file 235 | 236 | Args: 237 | open_drive_path: Path to OpenDRIVE file 238 | 239 | Returns: 240 | object (str): Coordinate system 241 | """ 242 | if not isinstance(open_drive_path, str): 243 | raise TypeError("input must be a str") 244 | 245 | open_drive = open(open_drive_path, 'r') 246 | proj_open_drive = 'unknown' 247 | for line in open_drive: 248 | if line.find("geoReference") >= 0: 249 | proj_open_drive = pyproj.Proj(line[line.find("[CDATA[") + 7:line.find("]]")]) 250 | break 251 | if proj_open_drive == 'unknown': 252 | warnings.warn("no valid coordinate system found in OpenDRIVE -> coordinates won't be correct", UserWarning) 253 | 254 | open_drive.close() 255 | return proj_open_drive 256 | -------------------------------------------------------------------------------- /osc_generator/tools/osi_transformer.py: -------------------------------------------------------------------------------- 1 | import struct 2 | import pandas as pd 3 | import math 4 | import warnings 5 | from osc_generator.tools.user_config import UserConfig 6 | import os 7 | 8 | try: 9 | from osc_generator.tools.OSI.OSITrace import OSITrace 10 | except ImportError: 11 | warnings.warn("Feature OSI Input Data is not available. Download from: https://github.com/OpenSimulationInterface/open-simulation-interface/blob/master/format/OSITrace.py", UserWarning) 12 | 13 | def get_user_defined_attributes(osi_message, path): 14 | """ 15 | Obtain user defined attributes from the OSI message. 16 | Writes to config file 17 | 18 | :param : 19 | osi_message: the initial message from the OSI file 20 | path: for directory in which to write config file 21 | """ 22 | bb_dimension = [] 23 | bb_center = [] 24 | for obj in osi_message.global_ground_truth.moving_object: 25 | if obj.base.HasField('dimension'): 26 | # object bounding box dimensions 27 | bb_dimension.append([obj.base.dimension.width, obj.base.dimension.length, obj.base.dimension.height]) 28 | else: 29 | bb_dimension.append(None) 30 | 31 | if obj.HasField('vehicle_attributes'): 32 | if obj.vehicle_attributes.HasField('bbcenter_to_rear'): 33 | # object bounding box center point, from rear axle 34 | bb_center.append([obj.vehicle_attributes.bbcenter_to_rear.x, 35 | obj.vehicle_attributes.bbcenter_to_rear.y, 36 | obj.vehicle_attributes.bbcenter_to_rear.z]) 37 | else: 38 | bb_center.append(None) 39 | else: 40 | bb_center.append(None) 41 | 42 | dir_name = os.path.dirname(path) 43 | user_config = UserConfig(dir_name) 44 | user_config.read_config() 45 | user_config.object_boundingbox = bb_dimension 46 | user_config.bbcenter_to_rear = bb_center 47 | user_config.write_config() 48 | 49 | 50 | def osi2df(path: str) -> pd.DataFrame: 51 | """ 52 | Transfer osi messages into pandas dataframe. 53 | 54 | :param path: path to osi file 55 | :return: pandas dataframe 56 | """ 57 | if not isinstance(path, str): 58 | raise TypeError("input must be a str") 59 | 60 | trace = OSITrace() 61 | trace.from_file(path=path) 62 | messages = trace.get_messages() 63 | m = trace.get_message_by_index(0) 64 | number_of_vehicles = len(m.global_ground_truth.moving_object) 65 | 66 | get_user_defined_attributes(m, path) 67 | 68 | lists: list = [[] for _ in range(15)] 69 | for i in messages: 70 | timestamp = i.global_ground_truth.timestamp.seconds + i.global_ground_truth.timestamp.nanos / 1000000000 71 | lists[0].append(timestamp) 72 | for v in i.global_ground_truth.moving_object: 73 | lists[1].append(v.base.position.x) 74 | lists[2].append(v.base.position.y) 75 | lists[3].append(v.base.velocity.x) 76 | lists[4].append(v.base.velocity.y) 77 | lists[5].append(v.base.orientation.yaw) 78 | lists[6].append(v.vehicle_classification.type) 79 | 80 | for lane in i.global_ground_truth.lane_boundary: 81 | if lane.id.value == 0: 82 | lists[7].append(lane.classification.type) 83 | for boundary_line in lane.boundary_line: 84 | lists[8].append(boundary_line.position.x) 85 | lists[9].append(boundary_line.position.y) 86 | lists[10].append(boundary_line.width) 87 | elif lane.id.value == 1: 88 | lists[11].append(lane.classification.type) 89 | for boundary_line in lane.boundary_line: 90 | lists[12].append(boundary_line.position.x) 91 | lists[13].append(boundary_line.position.y) 92 | lists[14].append(boundary_line.width) 93 | else: 94 | pass 95 | 96 | df = pd.DataFrame(lists[0], columns=['timestamp']) 97 | df.insert(1, 'lat', lists[1][0::number_of_vehicles]) 98 | df.insert(2, 'long', lists[2][0::number_of_vehicles]) 99 | df.insert(3, 'heading', lists[5][0::number_of_vehicles]) 100 | df.insert(4, 'speed', lists[3][0::number_of_vehicles]) 101 | 102 | df.insert(5, 'lin_right_beginn_x', lists[8]) 103 | df.insert(6, 'lin_right_y_abstand', lists[9]) 104 | df.insert(7, 'lin_right_breite', lists[10]) 105 | df.insert(8, 'lin_right_typ', lists[7]) 106 | df.insert(9, 'lin_left_beginn_x', lists[12]) 107 | df.insert(10, 'lin_left_y_abstand', lists[13]) 108 | df.insert(11, 'lin_left_breite', lists[14]) 109 | df.insert(12, 'lin_left_typ', lists[11]) 110 | 111 | for i in range(number_of_vehicles - 1): 112 | i += 1 113 | df.insert(i * 5 + 8, 'pos_x_' + str(i), lists[1][i::number_of_vehicles]) 114 | df.insert(i * 5 + 9, 'pos_y_' + str(i), lists[2][i::number_of_vehicles]) 115 | df.insert(i * 5 + 10, 'speed_x_' + str(i), lists[3][i::number_of_vehicles]) 116 | df.insert(i * 5 + 11, 'speed_y_' + str(i), lists[4][i::number_of_vehicles]) 117 | df.insert(i * 5 + 12, 'class_' + str(i), lists[6][i::number_of_vehicles]) 118 | 119 | trace.scenario_file.close() 120 | 121 | return df -------------------------------------------------------------------------------- /osc_generator/tools/rulebased.py: -------------------------------------------------------------------------------- 1 | # **************************************************************************** 2 | # @rulebased.py 3 | # 4 | # @copyright 2022 e:fs TechHub GmbH and Audi AG. All rights reserved. 5 | # 6 | # @license Apache v2.0 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # **************************************************************************** 20 | 21 | 22 | import numpy as np 23 | import pandas as pd 24 | import matplotlib.pyplot as plt 25 | from shapely.geometry import MultiPoint, LineString, Point 26 | from scipy.signal import find_peaks 27 | 28 | 29 | def create_longitudinal_maneuver_vectors(speed: pd.Series, acceleration_definition_threshold: float = 0.2, 30 | acceleration_definition_min_length: float = 2.0, 31 | speed_threshold_no_more_start: float = 20.0, plot: bool = False) -> tuple: 32 | """ 33 | Creates vectors for the longitudinal vehicle maneuvers. 34 | 35 | Args: 36 | speed: Vehicle speed information 37 | acceleration_definition_threshold: Due to noise, if acc is bigger --> ego is accelerating 38 | acceleration_definition_min_length: Minimum number in frames, if ego vehicle state is shorter --> ignore 39 | speed_threshold_no_more_start: In kmh, if start is labeled and this velocity is surpassed --> finish labeling 40 | plot: Plotting option 41 | 42 | Returns: 43 | object (tuple): Vectors with vehicle speed maneuvers: 44 | accelerate_array, 45 | start_array, 46 | keep_velocity_array, 47 | standstill_array, 48 | decelerate_array, 49 | stop_array, 50 | reversing_array 51 | """ 52 | if not isinstance(speed, pd.Series): 53 | raise TypeError("input must be a pd.Series") 54 | if not isinstance(acceleration_definition_threshold, float): 55 | raise TypeError("input must be a float") 56 | if not isinstance(acceleration_definition_min_length, float): 57 | raise TypeError("input must be a float") 58 | if not isinstance(speed_threshold_no_more_start, float): 59 | raise TypeError("input must be a float") 60 | if not isinstance(plot, bool): 61 | raise TypeError("input must be a bool") 62 | 63 | new_speed = speed / 3.6 # Conversion km/h --> m/s 64 | speed_gradient = new_speed.diff(periods=1) / 0.1 # Delta_ay/delta_t 65 | speed_gradient = speed_gradient.rolling(window=5, min_periods=0).mean() 66 | speed_gradient = speed_gradient.shift(periods=-2, fill_value=speed_gradient[speed_gradient.shape[0] - 1]) 67 | speed_gradient[speed.isnull()] = np.NaN 68 | acceleration_x = speed_gradient 69 | 70 | accelerate_array = np.zeros(speed.shape[0]) 71 | start_array = np.zeros(speed.shape[0]) 72 | keep_velocity_array = np.zeros(speed.shape[0]) 73 | standstill_array = np.zeros(speed.shape[0]) 74 | decelerate_array = np.zeros(speed.shape[0]) 75 | stop_array = np.zeros(speed.shape[0]) 76 | reversing_array = np.zeros(speed.shape[0]) 77 | 78 | # Initializations 79 | counter_acceleration = 0 80 | acceleration_start = False 81 | 82 | counter_deceleration = 0 83 | deceleration_start = False 84 | 85 | counter_keep = 0 86 | keep_start = False 87 | 88 | counter_start = -1 89 | counter_stop = 0 90 | 91 | counter_buffer = 0 92 | 93 | length_speed_rows = 0 94 | 95 | for i in range(speed.shape[0]): 96 | # Future proofing if breaks are introduced in the loop 97 | length_speed_rows = i 98 | 99 | # Get acceleration ego 100 | if acceleration_x[i] > acceleration_definition_threshold: 101 | acceleration_start = True 102 | counter_acceleration += 1 103 | else: 104 | if acceleration_start & (counter_acceleration >= acceleration_definition_min_length): 105 | if counter_buffer > 0: 106 | counter_acceleration += counter_buffer 107 | accelerate_array[i - counter_acceleration: i] = 1 108 | counter_buffer = 0 109 | else: 110 | counter_buffer += counter_acceleration 111 | counter_acceleration = 0 112 | acceleration_start = False 113 | 114 | # Get deceleration ego 115 | if acceleration_x[i] < -acceleration_definition_threshold: 116 | deceleration_start = True 117 | counter_deceleration += 1 118 | else: 119 | if deceleration_start & (counter_deceleration >= acceleration_definition_min_length): 120 | if counter_buffer > 0: 121 | counter_deceleration += counter_buffer 122 | decelerate_array[i - counter_deceleration: i] = 1 123 | counter_buffer = 0 124 | else: 125 | counter_buffer += counter_deceleration 126 | counter_deceleration = 0 127 | deceleration_start = False 128 | 129 | # Get keep velocity ego 130 | if (acceleration_x[i] < acceleration_definition_threshold) & ( 131 | acceleration_x[i] > -acceleration_definition_threshold) & (speed[i] != 0): 132 | keep_start = True 133 | counter_keep += 1 134 | else: 135 | if keep_start & (counter_keep > acceleration_definition_min_length): 136 | if counter_buffer > 0: 137 | counter_keep += counter_buffer 138 | keep_velocity_array[i - counter_keep: i] = 1 139 | counter_buffer = 0 140 | else: 141 | counter_buffer += counter_keep 142 | counter_keep = 0 143 | keep_start = False 144 | 145 | # Get reversing 146 | if speed[i] < 0: 147 | reversing_array[i] = 1 148 | 149 | # Get standstill 150 | if speed[i] == 0: 151 | standstill_array[i] = 1 152 | 153 | # Get start 154 | # If counter > 0, counter increment (works only after start detection in next if statement) 155 | if (speed[i] > 0) & (counter_start > 0): 156 | counter_start += 1 157 | start_array[(i - counter_start): i] = 1 158 | # Break criteria: 159 | if speed[i] > speed_threshold_no_more_start: 160 | counter_start = -1 161 | if deceleration_start: 162 | counter_start = -1 163 | # If start detected set counter to 1 164 | if (speed[i] == 0) & (counter_start <= 1): 165 | counter_start = 1 166 | if (speed[i] > 0) & (counter_start <= 1): 167 | counter_start = 0 168 | 169 | # Get stop 170 | if (counter_stop > 0) & (speed[i] == 0): 171 | stop_array[i - counter_stop: i] = 1 172 | counter_stop = 0 173 | 174 | if (speed[i] < speed_threshold_no_more_start) & (speed[i] != 0): 175 | counter_stop += 1 176 | else: 177 | counter_stop = 0 178 | 179 | if acceleration_start: 180 | counter_stop = 0 181 | if keep_start: 182 | counter_stop = 0 183 | 184 | counter_acceleration += counter_buffer 185 | counter_deceleration += counter_buffer 186 | counter_keep += counter_buffer 187 | 188 | # Make sure that last maneuver is labeled 189 | if acceleration_start: 190 | accelerate_array[length_speed_rows - counter_acceleration: length_speed_rows] = 1 191 | if deceleration_start: 192 | decelerate_array[length_speed_rows - counter_deceleration: length_speed_rows] = 1 193 | if keep_start: 194 | keep_velocity_array[length_speed_rows - counter_keep: length_speed_rows] = 1 195 | 196 | if plot: 197 | fill_array = accelerate_array.astype('bool') | decelerate_array.astype('bool') | \ 198 | keep_velocity_array.astype('bool') | reversing_array.astype('bool') | \ 199 | standstill_array.astype('bool') | start_array.astype('bool') | stop_array.astype('bool') 200 | plt.subplot(10, 1, 1) 201 | plt.plot(speed) 202 | plt.subplot(10, 1, 2) 203 | plt.plot(acceleration_x) 204 | plt.subplot(10, 1, 3) 205 | plt.plot(accelerate_array) 206 | plt.subplot(10, 1, 4) 207 | plt.plot(decelerate_array) 208 | plt.subplot(10, 1, 5) 209 | plt.plot(keep_velocity_array) 210 | plt.subplot(10, 1, 6) 211 | plt.plot(reversing_array) 212 | plt.subplot(10, 1, 7) 213 | plt.plot(standstill_array) 214 | plt.subplot(10, 1, 8) 215 | plt.plot(start_array) 216 | plt.subplot(10, 1, 9) 217 | plt.plot(stop_array) 218 | plt.subplot(10, 1, 10) 219 | plt.plot(fill_array) 220 | plt.show() 221 | 222 | return accelerate_array, start_array, keep_velocity_array, standstill_array, decelerate_array, stop_array, \ 223 | reversing_array 224 | 225 | 226 | def create_lateral_maneuver_vectors(df_lanes: pd.DataFrame, lat: pd.core.series.Series, 227 | lon: pd.core.series.Series, plot: bool = False) -> tuple: 228 | """ 229 | Get lane change maneuver from lanes with absolute coordinates 230 | 231 | Args: 232 | df_lanes: Lane coordinates 233 | lat: Latitude of the vehicle 234 | lon: Longitude of the vehicle 235 | plot: Plotting option 236 | 237 | Returns: 238 | object (tuple): Vectors with vehicle lane change maneuvers: 239 | """ 240 | if not isinstance(df_lanes, pd.DataFrame): 241 | raise TypeError("input must be a pd.DataFrame") 242 | if not isinstance(lat, pd.core.series.Series): 243 | raise TypeError("input must be a pd.core.series.Series") 244 | if not isinstance(lon, pd.core.series.Series): 245 | raise TypeError("input must be a pd.core.series.Series") 246 | if not isinstance(plot, bool): 247 | raise TypeError("input must be a bool") 248 | 249 | def find_intersection(x_targ_series, y_targ_series, x_ref_series, y_ref_series): 250 | """ 251 | Helper function. detects lanechange 252 | 253 | Args: 254 | x_targ_series (pd.core.series.Series): series containing x-components 255 | y_targ_series (pd.core.series.Series): series containing y-components 256 | x_ref_series (pd.core.series.Series): series containing x-components 257 | y_ref_series (pd.core.series.Series): series containing y-components 258 | 259 | Returns: 260 | object (np.ndarray, np.ndarray): arrays indicating lane change left and right: 261 | left_lane_change, right_lane_change, 262 | left_lane_change, right_lane_change 263 | """ 264 | x_targ_arr = np.asarray(x_targ_series) 265 | y_targ_arr = np.asarray(y_targ_series) 266 | x_ref_arr = np.asarray(x_ref_series) 267 | y_ref_arr = np.asarray(y_ref_series) 268 | 269 | l1 = LineString(list(zip(x_ref_arr[~np.isnan(x_ref_arr)], y_ref_arr[~np.isnan(y_ref_arr)]))) 270 | l2 = LineString(list(zip(x_targ_arr, y_targ_arr))) 271 | 272 | # Find "ideal" intersection point 273 | intersection = l1.intersection(l2) 274 | if intersection.is_empty: 275 | xs = [] 276 | ys = [] 277 | elif isinstance(intersection, MultiPoint): 278 | xs = [point.x for point in intersection.geoms] 279 | ys = [point.y for point in intersection.geoms] 280 | else: 281 | xs = [intersection.x] 282 | ys = [intersection.y] 283 | 284 | dist = [] 285 | dist2 = [] 286 | dist3 = [] 287 | 288 | left_lanechange_array = np.zeros(len(x_targ_arr)) 289 | right_lanechange_array = np.zeros(len(x_targ_arr)) 290 | if len(xs) > 0: 291 | for x_targ_index in range(len(x_targ_arr)): 292 | # Find the nearest trajectory point to "ideal" intersection point 293 | dist.append((x_targ_arr[x_targ_index] - xs[0]) ** 2 + (y_targ_arr[x_targ_index] - ys[0]) ** 2) 294 | 295 | # Compute the distance between trajectory and crossed lane 296 | point = Point(x_targ_arr[x_targ_index], y_targ_arr[x_targ_index]) 297 | dist2.append(point.distance(l1)) 298 | 299 | for x_ref_index in range(len(x_ref_arr)): 300 | dist3.append((x_ref_arr[x_ref_index] - xs[0]) ** 2 + (y_ref_arr[x_ref_index] - ys[0]) ** 2) 301 | 302 | # Min of the nearest trajectory point is the intersection with its respective index 303 | index = dist.index(min(dist)) 304 | # Min if nearest reference point is the intersection with its respective index 305 | index2 = dist3.index(min(dist3)) 306 | 307 | # Find previous and next extrema for 308 | peaks, _ = find_peaks(dist2, height=0) 309 | pos = np.searchsorted(peaks, index) 310 | if pos == 0: 311 | start_index = 0 312 | else: 313 | start_index = peaks[pos - 1] 314 | 315 | if pos == len(peaks): 316 | end_index = len(x_targ_arr) 317 | else: 318 | end_index = peaks[pos] 319 | 320 | if index + 1 == len(x_targ_arr): 321 | point1 = [x_ref_arr[index2 - 1], y_ref_arr[index2 - 1]] 322 | point2 = [x_ref_arr[index2], y_ref_arr[index2]] 323 | point3 = [x_targ_arr[index - 1], y_targ_arr[index - 1]] 324 | point4 = [x_targ_arr[index], y_targ_arr[index]] 325 | elif index == 0: 326 | point1 = [x_ref_arr[index2], y_ref_arr[index2]] 327 | point2 = [x_ref_arr[index2 + 1], y_ref_arr[index2 + 1]] 328 | point3 = [x_targ_arr[index], y_targ_arr[index]] 329 | point4 = [x_targ_arr[index + 1], y_targ_arr[index + 1]] 330 | else: 331 | point1 = [x_ref_arr[index2 - 1], y_ref_arr[index2 - 1]] 332 | point2 = [x_ref_arr[index2 + 1], y_ref_arr[index2 + 1]] 333 | point3 = [x_targ_arr[index - 1], y_targ_arr[index - 1]] 334 | point4 = [x_targ_arr[index + 1], y_targ_arr[index + 1]] 335 | 336 | v0 = np.array(point2) - np.array(point1) 337 | v1 = np.array(point4) - np.array(point3) 338 | 339 | angle = np.math.atan2(np.linalg.det([v0, v1]), np.dot(v0, v1)) 340 | deg = np.degrees(angle) 341 | 342 | # If left lane change 343 | if deg < 0: 344 | left_lanechange_array[start_index:end_index] = 1 345 | else: 346 | # If right lane change 347 | right_lanechange_array[start_index:end_index] = 1 348 | 349 | return left_lanechange_array, right_lanechange_array 350 | 351 | if plot: 352 | for i in range(int(df_lanes.shape[1] / 2)): 353 | plt.scatter(df_lanes.iloc[:, i * 2], df_lanes.iloc[:, i * 2 + 1]) 354 | plt.scatter(lat, lon) 355 | plt.show() 356 | 357 | clean_lat = lat[~np.isnan(lat)] 358 | clean_lon = lon[~np.isnan(lon)] 359 | 360 | # Loop over all available lanes from df_lane 361 | number_of_lanes = int(df_lanes.shape[1] / 2) 362 | left_lane_change_array = np.zeros(len(clean_lat)) 363 | right_lane_change_array = np.zeros(len(clean_lat)) 364 | for i in range(number_of_lanes): 365 | x_ref = df_lanes.iloc[:, (i * 2)] # Lat 366 | y_ref = df_lanes.iloc[:, (i * 2) + 1] # Lon 367 | 368 | left, right = find_intersection(clean_lat, clean_lon, x_ref, y_ref) 369 | 370 | left_lane_change_array = np.logical_or(left_lane_change_array, left) 371 | right_lane_change_array = np.logical_or(right_lane_change_array, right) 372 | 373 | left_lane_change = np.zeros(len(lat)) 374 | left_lane_change[~np.isnan(lat)] = left_lane_change_array 375 | right_lane_change = np.zeros(len(lon)) 376 | right_lane_change[~np.isnan(lon)] = right_lane_change_array 377 | 378 | return left_lane_change, right_lane_change 379 | -------------------------------------------------------------------------------- /osc_generator/tools/scenario_writer.py: -------------------------------------------------------------------------------- 1 | # **************************************************************************** 2 | # @scenario_writer.py 3 | # 4 | # @copyright 2022 e:fs TechHub GmbH and Audi AG. All rights reserved. 5 | # 6 | # @license Apache v2.0 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # **************************************************************************** 20 | 21 | 22 | import pandas as pd 23 | import os 24 | import numpy as np 25 | from xml.etree.ElementTree import Element, SubElement 26 | from xml.etree import ElementTree 27 | from xml.dom import minidom 28 | from scenariogeneration import xosc 29 | from osc_generator.tools.user_config import UserConfig 30 | import datetime 31 | 32 | def write_pretty(elem: Element, output: str, use_folder: bool, timebased_lon: bool, timebased_lat: bool, 33 | dir_name: str, section_name: str, radius_pos_trigger: float, output_path: str = None): 34 | """ 35 | Write a pretty-printed XML string for the Element. 36 | 37 | Args: 38 | elem: Root of the Element tree 39 | output: Output file type xosc 40 | use_folder: Option to use folder structure 41 | timebased_lon: Option to use time based trigger for longitudinal maneuvers 42 | timebased_lat: Option to use time based trigger for lateral maneuvers 43 | dir_name: Name of the directory 44 | section_name: Name of the scenario section 45 | radius_pos_trigger: Radius of the position based trigger 46 | output_path: Path to scenario file 47 | 48 | Returns: 49 | object (str): Path to scenario file 50 | """ 51 | if not isinstance(elem, Element): 52 | raise TypeError("input must be a Element") 53 | if not isinstance(output, str): 54 | raise TypeError("input must be a str") 55 | if not isinstance(use_folder, bool): 56 | raise TypeError("input must be a bool") 57 | if not isinstance(timebased_lon, bool): 58 | raise TypeError("input must be a bool") 59 | if not isinstance(timebased_lat, bool): 60 | raise TypeError("input must be a bool") 61 | if not isinstance(dir_name, str): 62 | raise TypeError("input must be a str") 63 | if not isinstance(section_name, str): 64 | raise TypeError("input must be a str") 65 | 66 | rough_string = ElementTree.tostring(elem, 'utf-8') 67 | reparsed = minidom.parseString(rough_string) 68 | xmlstr = reparsed.toprettyxml(indent="\t") 69 | 70 | if output == 'xosc': 71 | if output_path is not None: 72 | path = output_path 73 | 74 | else: 75 | if use_folder: 76 | if timebased_lon and timebased_lat: 77 | path = os.path.join(dir_name, 'man_export_' + os.path.basename(section_name) + '_time_lon_lat.xosc') 78 | elif timebased_lon and not timebased_lat: 79 | path = os.path.join(dir_name, 'man_export_' + os.path.basename(section_name) + '_time_lon_pos_lat_' + str( 80 | radius_pos_trigger) + '_m.xosc') 81 | elif not timebased_lon and timebased_lat: 82 | path = os.path.join(dir_name, 'man_export_' + os.path.basename(section_name) + '_pos_lon_time_lat.xosc') 83 | 84 | else: 85 | path = os.path.join(dir_name, 'man_export_' + os.path.basename(section_name) + '_pos_lon_lat_' + 86 | str(radius_pos_trigger) + '_m.xosc') 87 | 88 | else: 89 | raise NotImplementedError("use_folder flag is going to be removed") 90 | 91 | else: 92 | raise NotImplementedError("Only xosc output is currently implemented.") 93 | 94 | with open(path, 'w') as f: 95 | f.write(xmlstr) 96 | 97 | return path 98 | 99 | 100 | def convert_to_osc(df: pd.DataFrame, ego: list, objects: dict, ego_maneuver_array: dict, inf_maneuver_array: dict, 101 | movobj_grps_coord: np.ndarray, objlist: list, 102 | plot: bool, opendrive_path: str, use_folder: bool, timebased_lon: bool, timebased_lat: bool, 103 | section_name: str, radius_pos_trigger: float, 104 | dir_name: str, osc_version: str, output_path: str = None) -> str: 105 | """ 106 | Converter for OpenScenario 107 | 108 | Args: 109 | df: Dataframe containing trajectories 110 | ego: Ego position 111 | objects: Object positions 112 | ego_maneuver_array: Dict containing array of ego maneuvers 113 | inf_maneuver_array: Ict containing array of infrastructure specific maneuvers 114 | movobj_grps_coord: Coordinates of groups of detected objects 115 | objlist: Object list 116 | plot: Flag for graphical plotting 117 | opendrive_path: Path to the OpenDRIVE file 118 | use_folder: Iption to use folder structure 119 | timebased_lon: Option to use time based trigger for long maneuvers 120 | timebased_lat: Option to use time based trigger for lat maneuvers 121 | section_name: Name of the scenario section 122 | radius_pos_trigger: Radius of the position based trigger 123 | dir_name: Name of the directory 124 | osc_version: OpenSCENARIO version 125 | output_path: Path to OpenSCENARIO file 126 | 127 | Returns: 128 | object (str): Path to scenario file 129 | """ 130 | if not isinstance(df, pd.DataFrame): 131 | raise TypeError("input must be a pd.DataFrame") 132 | if not isinstance(ego, list): 133 | raise TypeError("input must be a list") 134 | if not isinstance(objects, dict): 135 | raise TypeError("input must be a dict") 136 | if not isinstance(ego_maneuver_array, dict): 137 | raise TypeError("input must be a dict") 138 | if not isinstance(inf_maneuver_array, dict): 139 | raise TypeError("input must be a dict") 140 | if not isinstance(movobj_grps_coord, np.ndarray): 141 | raise TypeError("input must be a np.ndarray") 142 | if not isinstance(objlist, list): 143 | raise TypeError("input must be a list") 144 | if not isinstance(plot, bool): 145 | raise TypeError("input must be a bool") 146 | if not isinstance(opendrive_path, str): 147 | raise TypeError("input must be a str") 148 | if not isinstance(use_folder, bool): 149 | raise TypeError("input must be a bool") 150 | if not isinstance(timebased_lon, bool): 151 | raise TypeError("input must be a bool") 152 | if not isinstance(timebased_lat, bool): 153 | raise TypeError("input must be a bool") 154 | if not isinstance(section_name, str): 155 | raise TypeError("input must be a str") 156 | if not isinstance(radius_pos_trigger, float): 157 | raise TypeError("input must be a float") 158 | if not isinstance(dir_name, str): 159 | raise TypeError("input must be a str") 160 | if not isinstance(osc_version, str): 161 | raise TypeError("input must be a str") 162 | 163 | opendrive_name = opendrive_path.split(os.path.sep)[-1] 164 | osgb_name = opendrive_name[:-4] + 'opt.osgb' 165 | 166 | #Get User-defined parameters 167 | user_param = UserConfig(dir_name) 168 | user_param.read_config() 169 | object_bb, object_bb_center = user_param.object_boundingbox, user_param.bbcenter_to_rear 170 | 171 | # Write Parameters 172 | param = xosc.ParameterDeclarations() 173 | 174 | # Write catalogs 175 | if user_param.catalogs is not None: 176 | catalog_path = user_param.catalogs 177 | else: 178 | catalog_path = "../Catalogs/Vehicles" 179 | catalog = xosc.Catalog() 180 | catalog.add_catalog("VehicleCatalog", catalog_path) 181 | 182 | # Write road network 183 | road = xosc.RoadNetwork( 184 | roadfile=opendrive_name, scenegraph=osgb_name 185 | ) 186 | 187 | # Write entities 188 | entities = xosc.Entities() 189 | # Entity - ego 190 | egoname = "Ego" 191 | 192 | # vehicle 193 | bb_input = [] 194 | if object_bb: 195 | if object_bb[0] is not None and object_bb_center[0] is not None: 196 | bb_input.extend([object_bb[0][0], object_bb[0][1], object_bb[0][2], 197 | object_bb_center[0][0], object_bb_center[0][1], object_bb_center[0][2]]) 198 | elif object_bb[0] is None and object_bb_center[0] is not None: 199 | bb_input.extend([1.872, 4.924, 1.444, 200 | object_bb_center[0][0], object_bb_center[0][1], object_bb_center[0][2]]) 201 | elif object_bb[0] is not None and object_bb_center[0] is None: 202 | bb_input.extend([object_bb[0][0], object_bb[0][1], object_bb[0][2], 203 | 1.376, 0, 0.722]) 204 | else: 205 | # default values 206 | bb_input.extend([1.872, 4.924, 1.444, 1.376, 0, 0.722]) 207 | else: 208 | bb_input.extend([1.872, 4.924, 1.444, 1.376, 0, 0.722]) 209 | 210 | bb = xosc.BoundingBox(*bb_input) # dim(w, l, h), centre(x, y, z) 211 | fa = xosc.Axle(0.48, 0.684, 1.672, 2.91, 0.342) 212 | ra = xosc.Axle(0, 0.684, 1.672, 0, 0.342) 213 | if float(osc_version) < 1.1: 214 | ego_veh = xosc.Vehicle("car_white", xosc.VehicleCategory.car, bb, fa, ra, 67, 10, 9.5) 215 | else: 216 | ego_veh = xosc.Vehicle("car_white", xosc.VehicleCategory.car, bb, fa, ra, 67, 10, 9.5, 1700) 217 | ego_veh.add_property(name="control", value="external") 218 | ego_veh.add_property_file("") 219 | # entity controller 220 | prop = xosc.Properties() 221 | prop.add_property(name="weight", value="60") 222 | prop.add_property(name="height", value="1.8") 223 | prop.add_property(name="eyeDistance", value="0.065") 224 | prop.add_property(name="age", value="28") 225 | prop.add_property(name="sex", value="male") 226 | cont = xosc.Controller("DefaultDriver", prop) 227 | 228 | if user_param.catalogs: 229 | entities.add_scenario_object( 230 | egoname, xosc.CatalogReference("VehicleCatalog", "car_blue") 231 | ) 232 | else: 233 | entities.add_scenario_object(egoname, ego_veh, cont) 234 | 235 | # Entities - objects 236 | bb_obj = [] 237 | for idx, obj in objects.items(): 238 | object_count = idx + 1 239 | objname = f"Player{object_count}" 240 | # vehicle 241 | if len(object_bb) <= 1 or object_bb[object_count] is None and object_bb_center[object_count] is None: 242 | # set default values 243 | bb_obj.extend([1.872, 4.924, 1.444, 1.376, 0, 0.722]) 244 | elif object_bb[object_count] is None and object_bb_center[object_count] is not None: 245 | bb_obj.extend([1.872, 4.924, 1.444, 246 | object_bb_center[object_count][0], 247 | object_bb_center[object_count][1], 248 | object_bb_center[object_count][2]]) 249 | elif object_bb[object_count] is not None and object_bb_center[object_count] is None: 250 | bb_obj.extend([object_bb[object_count][0], object_bb[object_count][1], object_bb[object_count][2], 251 | 1.376, 0, 0.722]) 252 | else: 253 | bb_obj.extend([object_bb[object_count][0], object_bb[object_count][1], object_bb[object_count][2], 254 | object_bb_center[object_count][0], object_bb_center[object_count][1], object_bb_center[object_count][2]]) 255 | 256 | bb = xosc.BoundingBox(*bb_obj) # dim(w, l, h), centre(x, y, z) 257 | bb_obj.clear() 258 | fa = xosc.Axle(0.48, 0.684, 1.672, 2.91, 0.342) 259 | ra = xosc.Axle(0, 0.684, 1.672, 0, 0.342) 260 | if float(osc_version) < 1.1: 261 | obj_veh = xosc.Vehicle(objlist[idx], xosc.VehicleCategory.car, bb, fa, ra, 67, 10, 9.5) 262 | else: 263 | obj_veh = xosc.Vehicle(objlist[idx], xosc.VehicleCategory.car, bb, fa, ra, 67, 10, 9.5, 1700) 264 | obj_veh.add_property(name="control", value="internal") 265 | obj_veh.add_property_file("") 266 | 267 | if user_param.catalogs: 268 | entities.add_scenario_object( 269 | objname, xosc.CatalogReference("VehicleCatalog", "car_white") 270 | ) 271 | else: 272 | entities.add_scenario_object(objname, obj_veh, cont) 273 | 274 | # Determine Priority attribute according to osc version 275 | if float(osc_version) <= 1.1: 276 | xosc_priority = xosc.Priority.overwrite 277 | else: 278 | xosc_priority = xosc.Priority.override 279 | 280 | # Write Init 281 | init = xosc.Init() 282 | step_time = xosc.TransitionDynamics( 283 | xosc.DynamicsShapes.step, xosc.DynamicsDimension.rate, 0 284 | ) 285 | # Start (init) conditions - Ego 286 | egospeed = xosc.AbsoluteSpeedAction(float(f'{ego[2]}'), step_time) 287 | egostart = xosc.TeleportAction(xosc.WorldPosition(x=f'{ego[0]}', y=f'{ego[1]}', z='0', h=f'{ego[3]}', p='0', r='0')) 288 | init.add_init_action(egoname, egostart) 289 | init.add_init_action(egoname, egospeed) 290 | 291 | # init storyboard object 292 | sb = xosc.StoryBoard(init) 293 | 294 | # Start (init) conditions objects 295 | for idx, obj in objects.items(): 296 | object_count = idx + 1 297 | objname = f"Player{object_count}" 298 | objspeed = xosc.AbsoluteSpeedAction(float(f'{obj[2]}'), step_time) 299 | objstart = xosc.TeleportAction(xosc.WorldPosition(x=f'{obj[0]}', y=f'{obj[1]}', 300 | z='0', h=f'{obj[3]}', p='0', r='0')) 301 | init.add_init_action(objname, objstart) 302 | init.add_init_action(objname, objspeed) 303 | 304 | # Write Story 305 | story = xosc.Story("New Story") 306 | 307 | # For each vehicle write a separate Act 308 | # (Act --> for each vehicle; Events inside of Act --> for each of vehicles maneuvers) 309 | for key, maneuver_list in ego_maneuver_array.items(): 310 | if key == 0: 311 | name = 'Ego' 312 | else: 313 | name = 'Player' + str(key) 314 | 315 | eventcounter = 0 316 | standstill = False 317 | 318 | # Create necessary Story Element Objects 319 | man = xosc.Maneuver(f'New Maneuver {key + 1}', parameters=param) 320 | man_lat = xosc.Maneuver(f'New Maneuver {key + 1}', parameters=param) 321 | mangroup = xosc.ManeuverGroup(f'New Sequence {key + 1}') 322 | mangroup.add_actor(name) 323 | # Start trigger for Act 324 | act_trig_cond = xosc.SimulationTimeCondition(value=0, rule=xosc.Rule.greaterThan) 325 | act_starttrigger = xosc.ValueTrigger(name=f'Start Condition of Act {key + 1}', delay=0, 326 | conditionedge=xosc.ConditionEdge.rising, valuecondition=act_trig_cond) 327 | act = xosc.Act(f'New Act {key + 1}', starttrigger=act_starttrigger) 328 | 329 | long_event = False 330 | # Loop ego specific maneuvers (accelerate, decelerate, standstill, ...) 331 | for idx, ego_maneuver in enumerate(maneuver_list): 332 | if str(ego_maneuver[2]) != 'FM_EGO_standstill': 333 | eventcounter += 1 334 | 335 | if standstill & (str(ego_maneuver[2]) == 'FM_EGO_standstill'): 336 | standstill = True 337 | 338 | if not standstill: 339 | ## Long maneuvers without move_in, move_out 340 | event = xosc.Event(f'New Event {eventcounter}', priority=xosc_priority) 341 | 342 | # Starting Condition of long maneuvers 343 | if timebased_lon: 344 | long_event = True 345 | # Time based trigger 346 | trig_cond = xosc.SimulationTimeCondition(value=float(ego_maneuver[0]) / 10, rule=xosc.Rule.greaterThan) 347 | trigger = xosc.ValueTrigger(name=f'Start Condition of Event {eventcounter}', delay=0, 348 | conditionedge=xosc.ConditionEdge.rising, valuecondition=trig_cond) 349 | 350 | elif not timebased_lon: 351 | long_event = True 352 | # Position based absolute position trigger 353 | worldpos = xosc.WorldPosition(x=ego_maneuver[3], y=ego_maneuver[4], 354 | z='0', h='0', p='0', r='0') 355 | trig_cond = xosc.DistanceCondition(value=f'{radius_pos_trigger}', rule=xosc.Rule.lessThan, 356 | position=worldpos, alongroute="0", freespace="0") 357 | trigger = xosc.EntityTrigger(name=f'Start Condition of Event {eventcounter}', delay=0, 358 | conditionedge=xosc.ConditionEdge.rising, entitycondition=trig_cond, 359 | triggerentity=f'{name}') 360 | 361 | event.add_trigger(trigger) 362 | dyn = xosc.TransitionDynamics(shape=xosc.DynamicsShapes.linear, 363 | dimension=xosc.DynamicsDimension.rate, value=ego_maneuver[6]) 364 | action = xosc.AbsoluteSpeedAction(speed=ego_maneuver[5], transition_dynamics=dyn) 365 | event.add_action(actionname=f"{ego_maneuver[2]}", action=action) 366 | 367 | man.add_event(event) 368 | 369 | if standstill & (str(ego_maneuver[2]) != 'FM_EGO_standstill'): 370 | standstill = False 371 | 372 | # Maneuver: change speed by absolute elapsed simulation time trigger 373 | event = xosc.Event(f'New Event {eventcounter}', priority=xosc_priority) 374 | 375 | trig_cond = xosc.SimulationTimeCondition(value=float(ego_maneuver[0]) / 10, rule=xosc.Rule.greaterThan) 376 | trigger = xosc.ValueTrigger(name=f'Start Condition of Event {eventcounter}', delay=0, 377 | conditionedge=xosc.ConditionEdge.rising, valuecondition=trig_cond) 378 | 379 | event.add_trigger(trigger) 380 | dyn = xosc.TransitionDynamics(shape=xosc.DynamicsShapes.linear, 381 | dimension=xosc.DynamicsDimension.rate, value=ego_maneuver[6]) 382 | action = xosc.AbsoluteSpeedAction(speed=ego_maneuver[5], transition_dynamics=dyn) 383 | event.add_action(actionname=f"{ego_maneuver[2]}", action=action) 384 | 385 | man.add_event(event) 386 | 387 | lateral_event = False 388 | # Loop infrastructure specific maneuvers 389 | current_inf_maneuver_array = inf_maneuver_array[key] 390 | for idx, inf_maneuver in enumerate(current_inf_maneuver_array): 391 | eventcounter += 1 392 | 393 | if inf_maneuver[2] == 'FM_INF_lane_change_left': 394 | lane_change = 1 395 | elif inf_maneuver[2] == 'FM_INF_lane_change_right': 396 | lane_change = -1 397 | else: 398 | raise ValueError('Lane change maneuver name is wrong') 399 | 400 | # Lane Change 401 | event = xosc.Event(f'New Event {eventcounter}', priority=xosc_priority) 402 | 403 | # Starting Condition of lane change 404 | if timebased_lat: 405 | # Time based_lat trigger 406 | trig_cond = xosc.SimulationTimeCondition(value=float(inf_maneuver[0]) / 10, 407 | rule=xosc.Rule.greaterThan) 408 | trigger = xosc.ValueTrigger(name=f'Start Condition of Event {eventcounter}', delay=0, 409 | conditionedge=xosc.ConditionEdge.rising, valuecondition=trig_cond) 410 | lateral_event = True 411 | 412 | elif not timebased_lat: 413 | # Position based absolute position trigger 414 | worldpos = xosc.WorldPosition(x=inf_maneuver[3], y=inf_maneuver[4], 415 | z='0', h='0', p='0', r='0') 416 | trig_cond = xosc.DistanceCondition(value=f'{radius_pos_trigger}', rule=xosc.Rule.lessThan, 417 | position=worldpos, alongroute="0", freespace="0") 418 | trigger = xosc.EntityTrigger(name=f'Start Condition of Event {eventcounter}', delay=0, 419 | conditionedge=xosc.ConditionEdge.rising, entitycondition=trig_cond, 420 | triggerentity=f'{name}') 421 | lateral_event = True 422 | 423 | event.add_trigger(trigger) 424 | dyn = xosc.TransitionDynamics(shape=xosc.DynamicsShapes.sinusoidal, 425 | dimension=xosc.DynamicsDimension.time, value=inf_maneuver[5]) 426 | action = xosc.RelativeLaneChangeAction(lane=lane_change, entity=f'{name}', transition_dynamics=dyn, 427 | target_lane_offset=4.26961e-316) 428 | event.add_action(actionname=f"{inf_maneuver[2]}", action=action) 429 | 430 | man_lat.add_event(event) 431 | 432 | # Add maneuver events to act and story 433 | if long_event: 434 | mangroup.add_maneuver(man) 435 | if lateral_event: 436 | mangroup.add_maneuver(man_lat) 437 | act.add_maneuver_group(mangroup) 438 | story.add_act(act) 439 | 440 | # Create Scenario 441 | sb.add_story(story) 442 | 443 | osc_minor_version = int(osc_version.split('.')[-1]) 444 | 445 | scenario = xosc.Scenario( 446 | "", 447 | "OSC Generator", 448 | param, 449 | entities, 450 | sb, 451 | road, 452 | catalog, 453 | creation_date=datetime.datetime(2023, 1, 1, 0, 0, 0, 0), 454 | osc_minor_version=osc_minor_version 455 | ) 456 | 457 | # Create Output Path 458 | if output_path is not None: 459 | path = output_path 460 | 461 | else: 462 | if use_folder: 463 | if timebased_lon and timebased_lat: 464 | path = os.path.join(dir_name, 'man_export_' + os.path.basename(section_name) + '_time_lon_lat.xosc') 465 | elif timebased_lon and not timebased_lat: 466 | path = os.path.join(dir_name, 467 | 'man_export_' + os.path.basename(section_name) + '_time_lon_pos_lat_' + str( 468 | radius_pos_trigger) + '_m.xosc') 469 | elif not timebased_lon and timebased_lat: 470 | path = os.path.join(dir_name, 'man_export_' + os.path.basename(section_name) + '_pos_lon_time_lat.xosc') 471 | 472 | else: 473 | path = os.path.join(dir_name, 'man_export_' + os.path.basename(section_name) + '_pos_lon_lat_' + 474 | str(radius_pos_trigger) + '_m.xosc') 475 | 476 | else: 477 | raise NotImplementedError("use_folder flag is going to be removed") 478 | # Write Scenario to xml 479 | scenario.write_xml(path) 480 | 481 | return path 482 | -------------------------------------------------------------------------------- /osc_generator/tools/user_config.py: -------------------------------------------------------------------------------- 1 | import os 2 | import warnings 3 | import json 4 | 5 | class UserConfig: 6 | """ 7 | class for user defined configurations 8 | """ 9 | 10 | def __init__(self, path): 11 | self.path_to_config: str = path 12 | self.catalogs = None 13 | self.object_boundingbox = None 14 | self.bbcenter_to_rear = None 15 | 16 | 17 | def read_config(self): 18 | """ 19 | read config file and store relevant information 20 | """ 21 | try: 22 | with open(os.path.join(self.path_to_config, 'user_config.json'), 'r') as config_file: 23 | data = json.load(config_file) 24 | 25 | if "moving_objects" in data: 26 | objects = data["moving_objects"] 27 | object_bb = [] 28 | object_bbcenter = [] 29 | for bb in objects: 30 | if "boundingbox" in bb: 31 | object_bb.append(bb["boundingbox"]) 32 | else: 33 | object_bb.append(None) 34 | if "bbcenter_to_rear" in bb: 35 | object_bbcenter.append(bb["bbcenter_to_rear"]) 36 | else: 37 | object_bbcenter.append(None) 38 | 39 | self.object_boundingbox = object_bb 40 | self.bbcenter_to_rear = object_bbcenter 41 | 42 | if "catalogs" in data: 43 | self.catalogs = data["catalogs"] 44 | 45 | except FileNotFoundError: 46 | warnings.warn("User configuration file unavailable. ", UserWarning) 47 | 48 | def write_config(self): 49 | """ 50 | write config file from relevant information 51 | """ 52 | config_data = {} 53 | if self.object_boundingbox is not None and self.bbcenter_to_rear is not None: 54 | config_data["moving_objects"] = [{'boundingbox': self.object_boundingbox[i], 55 | 'bbcenter_to_rear': self.bbcenter_to_rear[i]} 56 | for i in range(len(self.object_boundingbox))] 57 | if self.catalogs is not None: 58 | config_data["catalogs"] = self.catalogs 59 | 60 | if config_data: 61 | try: 62 | with open(os.path.join(self.path_to_config, 'user_config.json'), 'w') as config_file: 63 | json.dump(config_data, config_file, indent=4) 64 | except: 65 | warnings.warn("Unable to write config file", UserWarning) 66 | -------------------------------------------------------------------------------- /osc_generator/tools/utils.py: -------------------------------------------------------------------------------- 1 | # **************************************************************************** 2 | # @utils.py 3 | # 4 | # @copyright 2022 e:fs TechHub GmbH and Audi AG. All rights reserved. 5 | # 6 | # @license Apache v2.0 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # **************************************************************************** 20 | 21 | import math 22 | import decimal 23 | import re 24 | import pandas as pd 25 | import numpy as np 26 | from geographiclib.geodesic import Geodesic 27 | from typing import Union 28 | 29 | 30 | def delete_irrelevant_objects(df: pd.DataFrame, movobj_grps: Union[list, np.ndarray], 31 | min_nofcases: int = 8, max_posx_min: float = 120.0, 32 | max_posx_nofcases_ratio: float = 4.0) -> tuple: 33 | """ 34 | Deletes not relevant objects from input data 35 | 36 | Args: 37 | df: Input dataframe 38 | movobj_grps: Detected objects 39 | min_nofcases: Minimum number of cases (higher value means more dropping) 40 | max_posx_min: Maximum of minimum distance to object (lower value means more dropping) 41 | max_posx_nofcases_ratio: Maximum of ratio between minimum distance and number of cases 42 | (lower value means more dropping) 43 | 44 | Returns: 45 | df: Dataframe containing only objects 46 | count: Number of removed objects 47 | """ 48 | if not isinstance(df, pd.DataFrame): 49 | raise TypeError("input must be a Dataframe") 50 | if not (isinstance(movobj_grps, np.ndarray) or isinstance(movobj_grps, list)): 51 | raise TypeError("input must be a list or np.ndarray") 52 | if not isinstance(min_nofcases, int): 53 | raise TypeError("input must be a integer") 54 | if not isinstance(max_posx_min, float): 55 | print(max_posx_min) 56 | raise TypeError("input must be a float") 57 | if not isinstance(max_posx_nofcases_ratio, float): 58 | print(max_posx_nofcases_ratio) 59 | raise TypeError("input must be a float") 60 | 61 | end = len(df) - 1 62 | count = 0 63 | for p in movobj_grps: 64 | posx_min = df[p[0]].min() 65 | nofcases = sum(~pd.isna(df[p[0]])) 66 | if nofcases < min_nofcases or posx_min > max_posx_min or posx_min / nofcases > max_posx_nofcases_ratio: 67 | df = df.drop(columns=p) 68 | # count += 1 69 | else: 70 | start = True 71 | first_one = end 72 | last_one = end 73 | for i in range(len(df)): 74 | if start and not pd.isna(df.loc[i, p[0]]): 75 | first_one = i 76 | start = False 77 | elif not start and pd.isna(df.loc[i, p[0]]): 78 | last_one = i - 1 79 | break 80 | variance = sum((df.loc[first_one:last_one, "speed"] - df.loc[first_one:last_one, p[2]]) ** 2) 81 | if variance < 50 and nofcases < 50: 82 | df = df.drop(columns=p) 83 | count += 1 84 | else: 85 | for i in range(first_one, last_one): 86 | acceleration = (abs(df.loc[i + 1, p[2]] - df.loc[i, p[2]]) / 3.6) * 10 87 | if acceleration > 250: 88 | df = df.drop(columns=p) 89 | count += 1 90 | break 91 | return df, count 92 | 93 | 94 | def calc_new_geopos_from_2d_vector_on_spheric_earth(curr_coords: pd.Series, heading: float, dist_x: float, dist_y: float) -> list: 95 | """ 96 | Computes the new coordinates of the traced car -- interpolation for only 200ms time intervals 97 | 98 | Args: 99 | curr_coords: Current coordinates of the ego car to get the heading of the car 100 | heading: Tracked heading of the ego car 101 | dist_x: Distances in x directions of the ego car 102 | dist_y: Distances in y directions of the ego car 103 | 104 | Returns: 105 | object (list): List containing latitude and longitude of new position 106 | """ 107 | if not isinstance(curr_coords, pd.Series): 108 | raise TypeError("input must be a pd.Series") 109 | if not isinstance(heading, float): 110 | raise TypeError("input must be a float") 111 | if not isinstance(dist_x, float): 112 | raise TypeError("input must be a float") 113 | if not isinstance(dist_y, float): 114 | raise TypeError("input must be a float") 115 | 116 | earth_rad = 6378137 # Earth radius in meters 117 | 118 | # Take angle from trace 119 | beta = (heading * math.pi) / 180 120 | 121 | # Step one: in x direction (ego view) new coordinates 122 | x_versch = dist_x * math.cos(beta) 123 | y_versch = dist_x * math.sin(beta) 124 | d_lat = x_versch / earth_rad 125 | d_lon = y_versch / (earth_rad * math.cos(math.pi * curr_coords[0] / 180)) 126 | lat_new = curr_coords[0] + d_lat * 180 / math.pi 127 | lon_new = curr_coords[1] + d_lon * 180 / math.pi 128 | first_coords = [lat_new, lon_new] 129 | 130 | # Step two: in y direction (ego view) new coordinates 131 | newbeta = beta - (math.pi / 2) # New heading shift pi/2 for y direction 132 | x_versch_y = dist_y * math.cos(newbeta) 133 | y_versch_y = dist_y * math.sin(newbeta) 134 | d_lat_y = x_versch_y / earth_rad 135 | d_lon_y = y_versch_y / ( 136 | earth_rad * math.cos(math.pi * first_coords[0] / 180)) 137 | lat_new_final = first_coords[0] + d_lat_y * 180 / math.pi 138 | lon_new_final = first_coords[1] + d_lon_y * 180 / math.pi 139 | new_coords = [lat_new_final, lon_new_final] # Final coordinates shifted in x as well as y direction 140 | return new_coords 141 | 142 | 143 | def flatten(x: Union[list, tuple]) -> list: 144 | """ 145 | Flattening function for nested lists and tuples 146 | 147 | Args: 148 | x: List or tuple 149 | 150 | Returns: 151 | object (list): Flat list 152 | """ 153 | if not isinstance(x, list) and isinstance(x, tuple): 154 | raise TypeError("input must be a list or tuple") 155 | 156 | out: list = [] 157 | for item in x: 158 | if isinstance(item, (list, tuple)): 159 | out.extend(flatten(item)) 160 | else: 161 | out.append(item) 162 | return out 163 | 164 | 165 | def find_vars(pattern: str, var_list: pd.Series.index, reshape: bool = False) -> Union[np.ndarray, list]: 166 | """ 167 | Find variables for a given pattern in a list 168 | 169 | Args: 170 | pattern: Search pattern 171 | var_list: List to be searched 172 | reshape: If True pattern will be split by '|' and used to reshape output 173 | 174 | Returns: 175 | object (Union[np.ndarray, list]): Found variables 176 | """ 177 | if not isinstance(pattern, str): 178 | raise TypeError("input must be a str") 179 | if not isinstance(reshape, bool): 180 | raise TypeError("input must be a bool") 181 | 182 | tmp_list: list = [] 183 | for i in var_list: 184 | if len(re.findall(pattern, i)): 185 | tmp_list.append(i) 186 | 187 | if not reshape: 188 | return tmp_list 189 | 190 | else: 191 | order = len(pattern.split('|')) 192 | shape = int(len(tmp_list) / order) 193 | tlist_np = np.array(tmp_list).reshape(shape, order) 194 | return tlist_np 195 | 196 | 197 | def convert_heading(degree: Union[int, float]) -> Union[int, float]: 198 | """ 199 | Convert heading to other convention and unit 200 | 201 | Args: 202 | degree: Starting north increasing clockwise 203 | 204 | Returns: 205 | object (Union[int, float]): Heading angle in rad starting east increasing anti-clockwise and conversion 206 | """ 207 | if not (isinstance(degree, float) or isinstance(degree, int)): 208 | raise TypeError("input must be a float or int") 209 | 210 | float_degree = float(degree) 211 | if float_degree == 0: 212 | temp_degree: float = 0 213 | else: 214 | temp_degree = 360 - float_degree 215 | temp_degree = temp_degree + 90 216 | return math.radians(temp_degree) 217 | 218 | 219 | def calc_heading_from_two_geo_positions(lat1: float, lon1: float, lat2: float, lon2: float) -> float: 220 | """ 221 | Get the heading of the vehicle 222 | 223 | Args: 224 | lat1: Start latitude 225 | lon1: Start longitude 226 | lat2: End latitude 227 | lon2: End longitude 228 | 229 | Returns: 230 | object (float): Heading in rad 231 | """ 232 | if not (isinstance(lat1, float) or isinstance(lat1, int)): 233 | raise TypeError("input must be a float or int") 234 | if not (isinstance(lon1, float) or isinstance(lon1, int)): 235 | raise TypeError("input must be a float or int") 236 | if not (isinstance(lat2, float) or isinstance(lat2, int)): 237 | raise TypeError("input must be a float or int") 238 | if not (isinstance(lon2, float) or isinstance(lon2, int)): 239 | raise TypeError("input must be a float or int") 240 | 241 | brng = Geodesic.WGS84.Inverse(lat1, lon1, lat2, lon2)['azi1'] 242 | return brng 243 | -------------------------------------------------------------------------------- /osc_generator/version.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.2.0' 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyproj 2 | pandas 3 | numpy 4 | simplekml 5 | matplotlib 6 | geographiclib 7 | shapely 8 | scipy 9 | requests 10 | pillow 11 | openpyxl 12 | scenariogeneration -------------------------------------------------------------------------------- /requirements_dev.txt: -------------------------------------------------------------------------------- 1 | -r requirements.txt 2 | 3 | pytest~=6.2.4 4 | xmldiff~=2.4 -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description_file=README.md 3 | license_files=LICENSE.txt 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | import os 3 | 4 | exec(open(os.path.join(os.path.dirname(__file__), 'osc_generator', 'version.py')).read()) 5 | 6 | # with os.path.join(os.path.dirname(__file__), 'README.md') as file_path: 7 | file_path = os.path.join(os.path.dirname(__file__), 'README.md') 8 | if os.path.isfile(file_path): 9 | with open(file_path, 'r+') as file: 10 | lines = file.readlines() 11 | for idx, line in enumerate(lines): 12 | if line.startswith('version = {'): 13 | lines[idx] = 'version = {' + str(__version__) + '}\n' 14 | file.seek(0) 15 | file.writelines(lines) 16 | file.truncate() 17 | 18 | # with os.path.join(os.path.dirname(__file__), 'CITATION.cff') as file_path: 19 | file_path = os.path.join(os.path.dirname(__file__), 'CITATION.cff') 20 | if os.path.isfile(file_path): 21 | with open(file_path, 'r+') as file: 22 | lines = file.readlines() 23 | for idx, line in enumerate(lines): 24 | if line.startswith('version: '): 25 | lines[idx] = 'version: "' + str(__version__) + '"\n' 26 | file.seek(0) 27 | file.writelines(lines) 28 | file.truncate() 29 | 30 | with open("README.md", "r") as fh: 31 | long_description = fh.read() 32 | 33 | CLASSIFIERS = """\ 34 | Development Status :: 3 - Alpha 35 | Intended Audience :: Science/Research 36 | Intended Audience :: Developers 37 | License :: OSI Approved :: Apache Software License 38 | Natural Language :: English 39 | Programming Language :: Python 40 | Programming Language :: Python :: 3 41 | Programming Language :: Python :: 3.7 42 | Programming Language :: Python :: 3.8 43 | Programming Language :: Python :: 3.9 44 | Programming Language :: Python :: 3.10 45 | Programming Language :: Python :: 3 :: Only 46 | Programming Language :: Python :: Implementation :: CPython 47 | Topic :: Scientific/Engineering 48 | Topic :: Scientific/Engineering :: GIS 49 | Topic :: Software Development 50 | Typing :: Stubs Only 51 | Operating System :: Microsoft :: Windows 52 | """ 53 | 54 | with open(os.path.join(os.path.dirname(__file__), 'requirements.txt')) as f: 55 | install_requires = [l.strip() for l in f.readlines()] 56 | 57 | setup(name='osc_generator', 58 | version=str(__version__), 59 | description='OSC-Generator can be used to generate ASAM OpenSCENARIO files from vehicle data and an ASAM OpenDRIVE file.', 60 | long_description=long_description, 61 | long_description_content_type="text/markdown", 62 | url='https://github.com/EFS-OpenSource/OSC-Generator', 63 | project_urls={ 64 | "Bug Tracker": "https://github.com/EFS-OpenSource/OSC-Generator/issues", 65 | "Source Code": "https://github.com/EFS-OpenSource/OSC-Generator"}, 66 | author='Axel Aigner et al.', 67 | author_email='axel.aigner@efs-techhub.com', 68 | packages=find_packages(exclude=('tests',)), 69 | classifiers=[_f for _f in CLASSIFIERS.split('\n') if _f], 70 | python_requires='>=3.7', 71 | entry_points={'console_scripts': ['osc_generator=osc_generator.osc_generator:main']}, 72 | install_requires=install_requires, 73 | 74 | ) 75 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # **************************************************************************** 2 | # @__init__.py 3 | # 4 | # @copyright 2022 e:fs TechHub GmbH and Audi AG. All rights reserved. 5 | # 6 | # @license Apache v2.0 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # **************************************************************************** 20 | 21 | """Subpackage for tests. 22 | """ 23 | # __all__ = ["test_tools"] 24 | -------------------------------------------------------------------------------- /tests/test_data/accelerate_array.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFS-OpenSource/OSC-Generator/48177576849e3a600d5d3914447286bf3d1d95e4/tests/test_data/accelerate_array.npy -------------------------------------------------------------------------------- /tests/test_data/decelerate_array.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFS-OpenSource/OSC-Generator/48177576849e3a600d5d3914447286bf3d1d95e4/tests/test_data/decelerate_array.npy -------------------------------------------------------------------------------- /tests/test_data/df33.csv: -------------------------------------------------------------------------------- 1 | ,timestamp,lat,long,heading,speed,lat_6,lon_6,speed_6,class_6 2 | 0,0.0,48.802719,11.465495,10.9,99.57388888888887,,,, 3 | 1,0.1,48.802719,11.465495,10.9,99.5036111111111,,,, 4 | 2,0.2,48.802719,11.465495,10.9,99.43555555555557,,,, 5 | 3,0.3,48.80275,11.465503625,10.816666666666666,99.37305555555557,,,, 6 | 4,0.4,48.802781,11.46551225,10.733333333333334,99.32666666666668,,,, 7 | 5,0.5,48.802812,11.465520875,10.65,99.28416666666666,,,, 8 | 6,0.6,48.802843,11.4655295,10.566666666666666,99.23222222222222,,,, 9 | 7,0.7,48.802874,11.465538125,10.483333333333334,99.1863888888889,,,, 10 | 8,0.7999999999999999,48.802905,11.46554675,10.4,99.14666666666668,,,, 11 | 9,0.8999999999999999,48.802936,11.465555375,10.316666666666666,99.10805555555557,,,, 12 | 10,1.0,48.802967,11.465564,10.233333333333333,99.0638888888889,,,, 13 | 11,1.1,48.80299283333333,11.465570666666668,10.150000000000002,99.01333333333336,,,, 14 | 12,1.2,48.80301866666667,11.465577333333334,10.075,98.93333333333334,,,, 15 | 13,1.3,48.8030445,11.465584,10.0125,98.85138888888888,,,, 16 | 14,1.4,48.80307033333334,11.465590666666667,9.95,98.77833333333334,,,, 17 | 15,1.5000000000000002,48.80309616666667,11.465597333333337,9.8875,98.6888888888889,,,, 18 | 16,1.6000000000000003,48.803122,11.465604,9.825,98.625,,,, 19 | 17,1.7000000000000004,48.80314783333333,11.465610666666668,9.7625,98.56055555555557,,,, 20 | 18,1.8000000000000005,48.803173666666666,11.465617333333334,9.7,98.50944444444444,,,, 21 | 19,1.9000000000000008,48.803199500000005,11.465624000000002,9.6375,98.4625,,,, 22 | 20,2.0000000000000004,48.80322525,11.4656305,9.575,98.41444444444444,,,, 23 | 21,2.1000000000000005,48.803250875,11.46563675,9.5125,98.35861111111112,,,, 24 | 22,2.2000000000000006,48.8032765,11.465643,9.433333333333334,98.29722222222222,,,, 25 | 23,2.3000000000000007,48.803302125,11.46564925,9.35,98.2302777777778,,,, 26 | 24,2.400000000000001,48.80332775,11.4656555,9.266666666666667,98.16444444444446,,,, 27 | 25,2.500000000000001,48.803353375,11.46566175,9.183333333333334,98.11722222222222,,,, 28 | 26,2.600000000000001,48.803379,11.465668,9.1,98.07055555555556,,,, 29 | 27,2.700000000000001,48.803404625,11.46567425,9.016666666666666,98.02111111111114,,,, 30 | 28,2.800000000000001,48.80343025,11.4656805,8.933333333333332,97.96,,,, 31 | 29,2.900000000000001,48.803455875,11.46568675,8.85,97.90666666666668,,,, 32 | 30,3.0000000000000013,48.8034815,11.465692583333333,8.766666666666666,97.86,,,, 33 | 31,3.1000000000000014,48.803507125,11.4656983125,8.69107142857143,97.83083333333336,,,, 34 | 32,3.200000000000001,48.80353275,11.465704041666667,8.64642857142857,97.79555555555557,,,, 35 | 33,3.3000000000000016,48.803558375,11.465709770833332,8.601785714285715,97.7488888888889,,,, 36 | 34,3.4000000000000017,48.803584,11.4657155,8.557142857142857,97.70000000000002,,,, 37 | 35,3.5000000000000018,48.803609625,11.465721229166668,8.5125,97.63694444444444,,,, 38 | 36,3.600000000000002,48.80363525,11.465726958333333,8.467857142857145,97.54444444444444,,,, 39 | 37,3.700000000000002,48.803660875,11.4657326875,8.423214285714288,97.46472222222224,,,, 40 | 38,3.800000000000002,48.803686500000005,11.465738416666667,8.378571428571428,97.395,,,, 41 | 39,3.900000000000002,48.80371135714286,11.465743910714284,8.333928571428572,97.35638888888889,,,, 42 | 40,4.000000000000002,48.80373314285715,11.465748464285712,8.289285714285715,97.34055555555555,,,, 43 | 41,4.100000000000001,48.80375492857143,11.465753017857145,8.244642857142859,97.30694444444444,,,, 44 | 42,4.200000000000001,48.80377671428572,11.465757571428572,8.200000000000001,97.2577777777778,,,, 45 | 43,4.300000000000001,48.8037985,11.465762125,8.1375,97.20944444444446,,,, 46 | 44,4.4,48.80382028571429,11.465766678571429,8.075000000000001,97.15833333333336,,,, 47 | 45,4.5,48.803842071428576,11.465771232142856,8.0125,97.11111111111111,,,, 48 | 46,4.6,48.80386385714286,11.465775785714284,7.95,97.03,,,, 49 | 47,4.699999999999999,48.80388564285714,11.465780339285716,7.8875,96.93305555555555,,,, 50 | 48,4.799999999999999,48.80390742857143,11.465784892857144,7.825,96.86611111111112,,,, 51 | 49,4.899999999999999,48.80392921428572,11.465789446428571,7.762500000000001,96.82166666666669,,,, 52 | 50,4.999999999999998,48.803951,11.465794,7.7,96.78611111111113,,,, 53 | 51,5.099999999999998,48.80397278571428,11.465798107142856,7.6375,96.76416666666668,,,, 54 | 52,5.1999999999999975,48.80399457142857,11.465802214285716,7.570833333333335,96.75444444444445,,,, 55 | 53,5.299999999999997,48.80401635714286,11.465806321428571,7.497916666666669,96.75722222222224,,,, 56 | 54,5.399999999999997,48.80403814285714,11.465810428571428,7.425000000000002,96.7677777777778,,,, 57 | 55,5.4999999999999964,48.80405992857143,11.465814535714284,7.352083333333336,96.73444444444443,,,, 58 | 56,5.599999999999996,48.80408171428572,11.465818642857144,7.27916666666667,96.7088888888889,,,, 59 | 57,5.699999999999996,48.8041035,11.46582275,7.206250000000003,96.6911111111111,,,, 60 | 58,5.799999999999995,48.804125285714285,11.465826857142858,7.133333333333336,96.68166666666669,,,, 61 | 59,5.899999999999995,48.804147071428574,11.465830964285717,7.06041666666667,96.6588888888889,,,, 62 | 60,5.999999999999995,48.804168857142855,11.465835071428572,6.987500000000004,96.61944444444448,,,, 63 | 61,6.099999999999994,48.80419064285714,11.465839178571429,6.914583333333338,96.5927777777778,,,, 64 | 62,6.199999999999994,48.80421525,11.46584325,6.808333333333341,96.55555555555556,,,, 65 | 63,6.299999999999994,48.8042405625,11.4658473125,6.693750000000008,96.51527777777778,48.80437693633773,11.465818732551098,109.90000000000002,7.0 66 | 64,6.399999999999993,48.804265875,11.465851375,6.579166666666676,96.46555555555555,48.80440442405007,11.465823006918304,109.90000000000002,7.0 67 | 65,6.499999999999993,48.8042911875,11.4658554375,6.464583333333343,96.40861111111111,48.804433046170594,11.465827224624974,110.27499999999998,7.0 68 | 66,6.5999999999999925,48.8043165,11.4658595,6.350000000000009,96.375,48.80446191724186,11.465831464020244,110.5,7.0 69 | 67,6.699999999999992,48.8043418125,11.4658635625,6.235416666666676,96.32583333333336,48.8044906720799,11.465835566959411,110.7,7.0 70 | 68,6.799999999999992,48.804367125,11.465867625,6.120833333333343,96.28333333333332,48.80451953834369,11.46583985743093,110.8,7.0 71 | 69,6.899999999999992,48.8043924375,11.4658716875,6.00625000000001,96.23222222222223,48.80454803028197,11.465844113840655,110.52500000000003,7.0 72 | 70,6.999999999999991,48.80441775,11.46587575,5.891666666666679,96.19722222222222,48.8045765297313,11.465848256612867,110.20000000000005,7.0 73 | 71,7.099999999999991,48.804444075,11.465879825,5.782142857142866,96.14944444444444,48.80460604095396,11.46585246125156,110.0,7.0 74 | 72,7.19999999999999,48.80447445,11.465883949999998,5.692857142857153,96.095,48.80463949143878,11.465856672823389,110.0,7.0 75 | 73,7.29999999999999,48.804504825,11.465888075,5.603571428571438,96.04972222222224,48.8046732529116,11.465860916016949,110.2,7.0 76 | 74,7.39999999999999,48.8045352,11.4658922,5.514285714285725,96.02333333333335,48.804707139208205,11.465865161321476,110.34999999999998,7.0 77 | 75,7.499999999999989,48.804565575,11.465896325,5.42500000000001,96.00444444444446,48.8047410882558,11.465869398898034,110.45000000000002,7.0 78 | 76,7.599999999999989,48.80459595,11.46590045,5.335714285714295,95.99666666666668,48.80477516216871,11.465873637110745,110.4,7.0 79 | 77,7.699999999999989,48.804626325,11.465904575,5.246428571428583,95.9713888888889,48.80480873975794,11.465877788467358,110.4,7.0 80 | 78,7.799999999999988,48.8046567,11.4659087,5.157142857142869,95.91555555555556,48.80484243087468,11.465882130297528,110.25000000000004,7.0 81 | 79,7.899999999999988,48.80468533928571,11.465912482142857,5.067857142857154,95.86694444444444,48.804874260123526,11.465886143952908,110.025,7.0 82 | 80,7.999999999999988,48.80470703571429,11.465914892857144,4.97857142857144,95.80833333333334,48.80489914743373,11.465888771022417,109.90000000000003,7.0 83 | 81,8.099999999999987,48.80472873214286,11.465917303571429,4.889285714285726,95.78361111111111,48.80492416260951,11.465891351886349,109.8,7.0 84 | 82,8.199999999999987,48.80475042857143,11.465919714285716,4.800000000000012,95.75888888888888,48.80494893515618,11.465893790893583,109.8,7.0 85 | 83,8.299999999999986,48.80477212500001,11.465922125,4.706250000000013,95.75638888888888,48.80497401008365,11.465896371885412,109.8,7.0 86 | 84,8.399999999999986,48.804793821428575,11.465924535714286,4.612500000000014,95.7327777777778,48.80499908088386,11.465899030430073,109.8,7.0 87 | 85,8.499999999999986,48.80481551785714,11.46592694642857,4.518750000000013,95.69305555555556,48.805024083188265,11.465901806309933,109.8,7.0 88 | 86,8.599999999999985,48.80483721428571,11.465929357142857,4.425000000000014,95.6477777777778,48.80504909644658,11.465904376823842,109.8,7.0 89 | 87,8.699999999999985,48.80485891071429,11.465931767857144,4.331250000000015,95.60916666666668,48.80507397676646,11.465907105375845,109.8,7.0 90 | 88,8.799999999999985,48.804880607142856,11.465934178571429,4.237500000000016,95.58111111111111,48.80509899197409,11.465909643119744,109.8,7.0 91 | 89,8.899999999999984,48.80490230357143,11.465936589285716,4.143750000000014,95.54666666666668,48.805123936786984,11.465912346401648,109.8,7.0 92 | 90,8.999999999999984,48.804924,11.465939,4.050000000000015,95.52055555555556,48.80514887840047,11.465915127956736,109.8,7.0 93 | 91,9.099999999999984,48.80494920833333,11.465941395833331,3.956250000000015,95.50972222222224,48.80517727537241,11.46591777741123,109.8,7.0 94 | 92,9.199999999999983,48.80497441666667,11.465943791666668,3.8791666666666735,95.50722222222224,48.805205673733695,11.465920607595207,109.8,7.0 95 | 93,9.299999999999985,48.804999625,11.465946187499998,3.8270833333333423,95.50166666666668,48.805234083954694,11.465923485518509,109.925,7.0 96 | 94,9.399999999999984,48.80502483333333,11.465948583333333,3.775000000000009,95.47444444444446,48.8052626153295,11.465926461568275,110.04999999999998,7.0 97 | 95,9.499999999999982,48.80505004166667,11.465950979166667,3.7229166666666753,95.43111111111114,48.805290892508694,11.46592954560284,110.17499999999998,7.0 98 | 96,9.599999999999982,48.80507525,11.465953375,3.670833333333342,95.38,48.80531916671187,11.465932715671352,110.25,7.0 99 | 97,9.699999999999982,48.805100458333335,11.465955770833334,3.61875000000001,95.34083333333334,48.80534768910438,11.465935948436584,110.25,7.0 100 | 98,9.79999999999998,48.80512566666667,11.465958166666669,3.566666666666677,95.3116666666667,48.80537621269786,11.465939172036652,110.25,7.0 101 | 99,9.89999999999998,48.805150875,11.4659605625,3.514583333333344,95.29666666666668,48.805404982722536,11.465942504234242,110.25,7.0 102 | 100,9.99999999999998,48.805166,11.465962,3.4625000000000106,95.28888888888888,48.80542354620092,11.465944856813744,110.25,7.0 103 | 101,10.09999999999998,48.805166,11.465962,3.410416666666677,95.2702777777778,48.80542679545355,11.465945840038112,110.25,7.0 104 | 102,10.19999999999998,48.805166,11.465962,3.4000000000000004,95.2538888888889,48.80542919257142,11.465946743533888,110.25,7.0 105 | -------------------------------------------------------------------------------- /tests/test_data/df_lanes.csv: -------------------------------------------------------------------------------- 1 | right_lane_lat,right_lane_lon,left_lane_lat,left_lane_lon 2 | 48.86554386674132,11.466863912382767,48.865545737447505,11.466816185691394 3 | 48.865543854242105,11.466864231269335,48.86554572390683,11.466816531151869 4 | 48.86554384590929,11.466864443860379,48.8655457166157,11.466816717169046 5 | 48.86557487619595,11.466873018239344,48.865576700087516,11.466825314021117 6 | 48.86560590242058,11.466881698860684,48.86560767558215,11.46683412353457 7 | 48.86563692488916,11.466890485750795,48.86563865529272,11.466842826794899 8 | 48.865667951424456,11.46689916627327,48.86566961448427,11.466852114925302 9 | 48.86569898537662,11.466907634063414,48.865700591403126,11.466860924671494 10 | 48.865730011916256,11.466916314460718,48.86573158111766,11.466869362268062 11 | 48.86576103769697,11.466925021392075,48.86576257565206,11.466877640341597 12 | 48.86579206028265,11.466933834634569,48.865793566171774,11.466886024765085 13 | 48.86581791331275,11.466940795873272,48.865819385504295,11.466892583823697 14 | 48.86584376872122,11.466947597325134,48.865845207955076,11.46689908983687 15 | 48.86586962229527,11.466954318664701,48.865871019689834,11.46690604815856 16 | 48.86589548195317,11.466960827200618,48.865896842355816,11.46691263417763 17 | 48.865921341382375,11.466967335685585,48.86592266783285,11.466919113819305 18 | 48.86594720200786,11.466973790922093,48.86594849161633,11.4669256466636 19 | 48.86597306371472,11.466980192903666,48.86597431798117,11.466932073116967 20 | 48.865998920994386,11.466986754435037,48.86600014265247,11.46693855277428 21 | 48.86602477615659,11.466993395729796,48.86602596115971,11.466945271860395 22 | 48.866050542860634,11.467000083149072,48.86605169386596,11.466951930720752 23 | 48.86607618668392,11.467006440734185,48.86607730920264,11.466958020360813 24 | 48.86610183756485,11.467012745374339,48.866102919498786,11.46696416303624 25 | 48.86612749054079,11.467019023425626,48.86612852528934,11.466970465336079 26 | 48.8661531414033,11.467025407850093,48.866154129015385,11.466976874114344 27 | 48.86617879458612,11.467031685785019,48.866179734078834,11.46698322972732 28 | 48.866204445859665,11.467038070101763,48.86620534130732,11.466989478945845 29 | 48.8662300987843,11.467044374530511,48.8662309466757,11.466995834650648 30 | 48.86625575093354,11.467050732125266,48.86625655400492,11.467002083951568 31 | 48.86628140577083,11.467056929981485,48.866282161333494,11.467008333291446 32 | 48.86630706050697,11.467062711103488,48.86630776555557,11.46701437892072 33 | 48.866332709541496,11.46706860083068,48.866333372884014,11.467020240639892 34 | 48.866358343785954,11.467074809579437,48.86635898824284,11.467025996125907 35 | 48.86638398190035,11.467080752162316,48.8663846056551,11.46703159192615 36 | 48.86640961764164,11.467086907666324,48.86641022033823,11.467037400666648 37 | 48.86643525816592,11.467092690525186,48.866435834172286,11.467043289271677 38 | 48.86646089755847,11.467098579836163,48.866461447577066,11.467049231127001 39 | 48.86648653994531,11.4671042029545,48.86648706176662,11.467055119763357 40 | 48.86651218330012,11.467109719578433,48.86651267719741,11.467060901942059 41 | 48.86653705806782,11.467115054295409,48.866537527893044,11.46706612959258 42 | 48.86655886172681,11.467119395274123,48.866559306402486,11.467070470010022 43 | 48.86658066128665,11.467124188752976,48.866581082638675,11.467075050004992 44 | 48.86660246152211,11.467128928979346,48.86660285933127,11.467079576772809 45 | 48.866624269607804,11.467133509583329,48.86662463365873,11.467083917149997 46 | 48.86664607773207,11.467138090150641,48.866646407327025,11.467088310783467 47 | 48.86666788715175,11.467142457719026,48.866668187677305,11.467091533143286 48 | 48.866689696579314,11.467146772005396,48.86668995893916,11.467096139766715 49 | 48.866711507087665,11.46715076680162,48.866711732653016,11.467100213999524 50 | 48.86673331758858,11.467154548585352,48.86673350428245,11.467104660941946 51 | 48.86675512609332,11.467158623155699,48.86675527495395,11.46710937411806 52 | 48.866776933663196,11.467162804171041,48.86677704634567,11.467113874349948 53 | 48.86679874065456,11.467166565341705,48.866798817660694,11.467117928176764 54 | 48.866820548409734,11.467170432964576,48.86682058771131,11.467122248239345 55 | 48.866842357809375,11.467174247305536,48.866842356652526,11.467125956036885 56 | 48.866864166763236,11.467178114843884,48.8668641249232,11.46712966386711 57 | 48.86688597513341,11.467181875852521,48.866885892614064,11.46713342497466 58 | 48.86690778278702,11.467185583576335,48.86690765985926,11.467137239360914 59 | 48.86692959076036,11.467189584092445,48.86692942661279,11.467141053784454 60 | 48.866951398105215,11.467193478082631,48.866951192874744,11.4671448682467 61 | 48.86697320603543,11.467197558377975,48.86697295945362,11.46714884247335 62 | 48.86699501423492,11.467201691871624,48.86699472486658,11.46715265701565 63 | 48.867016821761084,11.467205718841063,48.867016489429126,11.467156418360414 64 | 48.86704146073714,11.467209709875362,48.86704106724316,11.467160303911083 65 | 48.86706680864102,11.467213824934811,48.867066347492305,11.467164154048119 66 | 48.86709215555543,11.467217833412795,48.867091626051376,11.467167897817065 67 | 48.867117500741394,11.467221708702194,48.86711690408594,11.467171668317343 68 | 48.86714284543449,11.467225583899806,48.86714218346404,11.4671755986309 69 | 48.867168191184945,11.467229565468331,48.86716746520548,11.467179715356409 70 | 48.86719353841404,11.467233653395434,48.867192748779665,11.467183938628251 71 | 48.86721888479607,11.467237687990739,48.8672180286562,11.467187922466394 72 | 48.86724423110607,11.467241722484804,48.86724331325315,11.467192172519347 73 | 48.86727059096495,11.46724590249574,48.86726960746747,11.467196195596163 74 | 48.86730099166604,11.467249973044247,48.867299957067715,11.467200268498052 75 | 48.86733139341881,11.467254096748942,48.86733031120946,11.467204554319459 76 | 48.86736179638728,11.467258273601876,48.86736066933207,11.467208999815997 77 | 48.86739220200735,11.467262556802858,48.867391026204686,11.467213365521973 78 | 48.86742260795412,11.467266839922305,48.86742137826481,11.467217518435012 79 | 48.867453016299294,11.467271202762543,48.867451733143476,11.46722177780528 80 | 48.86748342513505,11.467275565509045,48.86748208111742,11.46722577121963 81 | 48.867512095765775,11.467279478910783,48.86751070801129,11.467229953806111 82 | 48.86753382072043,11.4672819144131,48.86753238823399,11.467232605393317 83 | 48.86755554732781,11.467284403034524,48.867554074550355,11.467235443161217 84 | 48.867577275751906,11.467286944763398,48.86757575843389,11.467238174553053 85 | 48.86759901098993,11.467289645843882,48.86759743760426,11.467240773080245 86 | 48.86762075039597,11.467292453156402,48.867619120717286,11.46724347798721 87 | 48.86764248335312,11.467295047642105,48.867640808117336,11.467246289245802 88 | 48.867664216709464,11.467297642021022,48.86766249449597,11.467249047325026 89 | 48.8676859524065,11.467300289453995,48.86768417482681,11.467251619329975 90 | 48.867707676623105,11.467302617822599,48.867705861501086,11.46725435083255 91 | 48.867729414031295,11.467305291620946,48.86772754475308,11.467256976024048 92 | 48.86775114355517,11.467307752702359,48.867749226352316,11.467259548080108 93 | 48.867776386181085,11.467310225388436,48.86777441151798,11.467261892706496 94 | 48.867801627865234,11.467312804789758,48.86779960476025,11.46726434331022 95 | 48.867826861472814,11.467315384927645,48.86782479693036,11.467266633991478 96 | 48.86785209302039,11.467317911888447,48.86784999581987,11.467269084095506 97 | 48.867877315470665,11.467320226296229,48.867875201715556,11.467271693594316 98 | 48.867902537824286,11.467322540689311,48.86790039848371,11.4672740905833 99 | 48.867927755326065,11.467324748824126,48.867925597630226,11.467276540710111 100 | 48.86795297501321,11.467327010076371,48.86795079925063,11.467279043964936 101 | 48.867978201863984,11.467329430667148,48.867975991233784,11.467281334761184 102 | 48.86799334293901,11.467330839779663,48.86799110223013,11.467282720369154 103 | 48.86799335647604,11.46733075825455,48.86799108440593,11.467282588842915 104 | 48.86799335615818,11.467330678218534,48.86799108082208,11.467282562541653 105 | 48.86799335615818,11.467330678218534,48.86799108082208,11.467282562541653 106 | 48.868011294013854,11.467328717106406,48.86800901867694,11.467280601412423 107 | 48.868029230731466,11.467326731928345,48.86802695539374,11.467278616217248 108 | 48.868047166311,11.46732472268425,48.868044890972435,11.467276606956048 109 | 48.86806510075246,11.467322689373944,48.868062825413055,11.46727457362864 110 | 48.868083034055815,11.467320631997206,48.868080758715564,11.467272516234802 111 | 48.86810096622108,11.467318550553758,48.86809869087997,11.467270434774251 112 | 48.86811889724821,11.467316445043261,48.86811662190623,11.467268329246657 113 | 48.868136827137214,11.467314315465323,48.86813455179435,11.467266199651627 114 | 48.868154755888035,11.467312161819494,48.86815248054428,11.467264045988706 115 | 48.86817268350068,11.467309984105269,48.86817040815603,11.46726186825739 116 | 48.86819060997512,11.467307782322083,48.86818833462956,11.467259666457117 117 | 48.86820853531131,11.467305556469316,48.86820625996483,11.467257440587264 118 | 48.86822645950923,11.467303306546295,48.86822418416182,11.46725519064716 119 | 48.86824438256885,11.467301032552284,48.868242107220475,11.467252916636069 120 | 48.8682623044901,11.467298734486496,48.868260029140785,11.467250618553201 121 | 48.86828022527297,11.467296412348084,48.868277949922685,11.467248296397713 122 | 48.86829814491742,11.467294066136146,48.86829586956617,11.4672459501687 123 | 48.86831606342339,11.467291695849724,48.86831378807115,11.467243579865206 124 | 48.86833398079084,11.4672893014878,48.868331705437605,11.46724118548621 125 | 48.86835189701971,11.467286883049303,48.86834962166548,11.467238767030645 126 | 48.868369812109954,11.467284440533101,48.868367536754704,11.467236324497378 127 | 48.86838772606151,11.467281973938015,48.86838545070524,11.467233857885226 128 | 48.86840563887432,11.467279483262795,48.86840336351703,11.467231367192944 129 | 48.86842355054832,11.467276968506146,48.86842127518999,11.467228852419236 130 | 48.86844146108347,11.46727442966671,48.868439185724085,11.467226313562744 131 | 48.86845937047966,11.467271866743078,48.86845709511922,11.467223750622058 132 | 48.86847727873686,11.467269279733777,48.86847500337534,11.467221163595703 133 | 48.868495185854975,11.467266668637283,48.86849291049237,11.467218552482157 134 | 48.86851309183393,11.467264033452011,48.86851081647024,11.467215917279837 135 | 48.86853099667367,11.467261374176323,48.868528721308856,11.467213257987103 136 | 48.86854890037407,11.467258690808523,48.868546625008165,11.467210574602257 137 | 48.868566802935085,11.467255983346854,48.868564527568054,11.467207867123546 138 | 48.86858470435662,11.46725325178951,48.86858242898844,11.467205135549165 139 | 48.86860260463859,11.467250496134623,48.86860032926928,11.467202379877238 140 | 48.86862050378087,11.467247716380268,48.86861822841041,11.467199600105845 141 | 48.868638401783414,11.467244912524462,48.868636126411786,11.467196796233008 142 | 48.8686562986461,11.467242084565171,48.8686540232733,11.467193968256684 143 | 48.86867419436884,11.467239232500297,48.86867191899484,11.467191116174783 144 | 48.868692088951505,11.46723635632769,48.86868981357633,11.467188239985148 145 | 48.86870998239401,11.467233456045141,48.868707707017634,11.467185339685575 146 | 48.86872787469625,11.467230531650383,48.86872559931867,11.467182415273793 147 | 48.86874576585813,11.467227583141094,48.868743490479304,11.467179466747483 148 | 48.86876365587949,11.467224610514894,48.86876138049946,11.467176494104264 149 | 48.86878154476025,11.467221613769343,48.86877926937896,11.467173497341697 150 | 48.86879943250029,11.467218592901949,48.86879715711775,11.467170476457289 151 | 48.86881731909947,11.46721554791016,48.868815043715664,11.467167431448487 152 | 48.86883520455769,11.467212478791366,48.86883292917261,11.467164362312685 153 | 48.86885308887481,11.467209385542901,48.868850813488436,11.467161269047214 154 | 48.86887097205068,11.467206268162043,48.868868696663036,11.46715815164935 155 | 48.86888885408521,11.467203126646012,48.86888657869624,11.467155010116315 156 | -------------------------------------------------------------------------------- /tests/test_data/ego_maneuver_array_0.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFS-OpenSource/OSC-Generator/48177576849e3a600d5d3914447286bf3d1d95e4/tests/test_data/ego_maneuver_array_0.npy -------------------------------------------------------------------------------- /tests/test_data/expected_straight.xosc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | -------------------------------------------------------------------------------- /tests/test_data/keep_velocity_array.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFS-OpenSource/OSC-Generator/48177576849e3a600d5d3914447286bf3d1d95e4/tests/test_data/keep_velocity_array.npy -------------------------------------------------------------------------------- /tests/test_data/lane_change_left_array.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFS-OpenSource/OSC-Generator/48177576849e3a600d5d3914447286bf3d1d95e4/tests/test_data/lane_change_left_array.npy -------------------------------------------------------------------------------- /tests/test_data/lane_change_right_array.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFS-OpenSource/OSC-Generator/48177576849e3a600d5d3914447286bf3d1d95e4/tests/test_data/lane_change_right_array.npy -------------------------------------------------------------------------------- /tests/test_data/model_speed.csv: -------------------------------------------------------------------------------- 1 | 99.57389 2 | 99.5647788888889 3 | 99.55566777777778 4 | 99.54655666666667 5 | 99.53744555555556 6 | 99.52833444444445 7 | 99.4939063398693 8 | 99.45947823529414 9 | 99.42505013071899 10 | 99.39062202614383 11 | 99.35619392156867 12 | 99.32176581699352 13 | 99.28733771241836 14 | 99.25290960784321 15 | 99.21848150326805 16 | 99.1840533986929 17 | 99.14962529411774 18 | 99.11519718954258 19 | 99.08076908496743 20 | 99.04634098039227 21 | 99.01191287581712 22 | 98.97748477124196 23 | 98.9430566666668 24 | 98.90862856209165 25 | 98.8742004575165 26 | 98.83977235294134 27 | 98.80534424836618 28 | 98.77091614379103 29 | 98.73648803921587 30 | 98.70205993464072 31 | 98.66763183006556 32 | 98.6332037254904 33 | 98.59877562091525 34 | 98.56434751634009 35 | 98.52991941176494 36 | 98.49549130718978 37 | 98.46106320261462 38 | 98.42663509803947 39 | 98.39220699346431 40 | 98.35777888888916 41 | 98.35509370370397 42 | 98.35240851851879 43 | 98.3497233333336 44 | 98.32009370370398 45 | 98.29046407407435 46 | 98.26083444444473 47 | 98.2312048148151 48 | 98.20157518518548 49 | 98.17194555555585 50 | 98.16314925925955 51 | -------------------------------------------------------------------------------- /tests/test_data/model_speed.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFS-OpenSource/OSC-Generator/48177576849e3a600d5d3914447286bf3d1d95e4/tests/test_data/model_speed.npy -------------------------------------------------------------------------------- /tests/test_data/prepared_df.csv: -------------------------------------------------------------------------------- 1 | timestamp,lat,long,heading,speed,lat_6,lon_6,speed_6,class_6 2 | 0.0,48.802719,11.465495,10.9,99.57388888888887,,,, 3 | 0.1,48.802719,11.465495,10.9,99.5036111111111,,,, 4 | 0.2,48.802719,11.465495,10.9,99.43555555555557,,,, 5 | 0.3,48.80275,11.465503625,10.816666666666666,99.37305555555557,,,, 6 | 0.4,48.802781,11.46551225,10.733333333333334,99.32666666666668,,,, 7 | 0.5,48.802812,11.465520875,10.65,99.28416666666666,,,, 8 | 0.6,48.802843,11.4655295,10.566666666666666,99.23222222222222,,,, 9 | 0.7,48.802874,11.465538125,10.483333333333334,99.1863888888889,,,, 10 | 0.7999999999999999,48.802905,11.46554675,10.4,99.14666666666668,,,, 11 | 0.8999999999999999,48.802936,11.465555375,10.316666666666666,99.10805555555557,,,, 12 | 1.0,48.802967,11.465564,10.233333333333333,99.0638888888889,,,, 13 | 1.1,48.80299283333333,11.465570666666668,10.150000000000002,99.01333333333336,,,, 14 | 1.2,48.80301866666667,11.465577333333334,10.075,98.93333333333334,,,, 15 | 1.3,48.8030445,11.465584,10.0125,98.85138888888888,,,, 16 | 1.4,48.80307033333334,11.465590666666667,9.95,98.77833333333334,,,, 17 | 1.5000000000000002,48.80309616666667,11.465597333333337,9.8875,98.6888888888889,,,, 18 | 1.6000000000000003,48.803122,11.465604,9.825,98.625,,,, 19 | 1.7000000000000004,48.80314783333333,11.465610666666668,9.7625,98.56055555555557,,,, 20 | 1.8000000000000005,48.803173666666666,11.465617333333334,9.7,98.50944444444444,,,, 21 | 1.9000000000000008,48.803199500000005,11.465624000000002,9.6375,98.4625,,,, 22 | 2.0000000000000004,48.80322525,11.4656305,9.575,98.41444444444444,,,, 23 | 2.1000000000000005,48.803250875,11.46563675,9.5125,98.35861111111112,,,, 24 | 2.2000000000000006,48.8032765,11.465643,9.433333333333334,98.29722222222222,,,, 25 | 2.3000000000000007,48.803302125,11.46564925,9.35,98.2302777777778,,,, 26 | 2.400000000000001,48.80332775,11.4656555,9.266666666666667,98.16444444444446,,,, 27 | 2.500000000000001,48.803353375,11.46566175,9.183333333333334,98.11722222222222,,,, 28 | 2.600000000000001,48.803379,11.465668,9.1,98.07055555555556,,,, 29 | 2.700000000000001,48.803404625,11.46567425,9.016666666666666,98.02111111111114,,,, 30 | 2.800000000000001,48.80343025,11.4656805,8.933333333333332,97.96,,,, 31 | 2.900000000000001,48.803455875,11.46568675,8.85,97.90666666666668,,,, 32 | 3.0000000000000013,48.8034815,11.465692583333333,8.766666666666666,97.86,,,, 33 | 3.1000000000000014,48.803507125,11.4656983125,8.69107142857143,97.83083333333336,,,, 34 | 3.200000000000001,48.80353275,11.465704041666667,8.64642857142857,97.79555555555557,,,, 35 | 3.3000000000000016,48.803558375,11.465709770833332,8.601785714285715,97.7488888888889,,,, 36 | 3.4000000000000017,48.803584,11.4657155,8.557142857142857,97.70000000000002,,,, 37 | 3.5000000000000018,48.803609625,11.465721229166668,8.5125,97.63694444444444,,,, 38 | 3.600000000000002,48.80363525,11.465726958333333,8.467857142857145,97.54444444444444,,,, 39 | 3.700000000000002,48.803660875,11.4657326875,8.423214285714288,97.46472222222224,,,, 40 | 3.800000000000002,48.803686500000005,11.465738416666667,8.378571428571428,97.395,,,, 41 | 3.900000000000002,48.80371135714286,11.465743910714284,8.333928571428572,97.35638888888889,,,, 42 | 4.000000000000002,48.80373314285715,11.465748464285712,8.289285714285715,97.34055555555555,,,, 43 | 4.100000000000001,48.80375492857143,11.465753017857145,8.244642857142859,97.30694444444444,,,, 44 | 4.200000000000001,48.80377671428572,11.465757571428572,8.200000000000001,97.2577777777778,,,, 45 | 4.300000000000001,48.8037985,11.465762125,8.1375,97.20944444444446,,,, 46 | 4.4,48.80382028571429,11.465766678571429,8.075000000000001,97.15833333333336,,,, 47 | 4.5,48.803842071428576,11.465771232142856,8.0125,97.11111111111111,,,, 48 | 4.6,48.80386385714286,11.465775785714284,7.95,97.03,,,, 49 | 4.699999999999999,48.80388564285714,11.465780339285716,7.8875,96.93305555555555,,,, 50 | 4.799999999999999,48.80390742857143,11.465784892857144,7.825,96.86611111111112,,,, 51 | 4.899999999999999,48.80392921428572,11.465789446428571,7.762500000000001,96.82166666666669,,,, 52 | 4.999999999999998,48.803951,11.465794,7.7,96.78611111111113,,,, 53 | 5.099999999999998,48.80397278571428,11.465798107142856,7.6375,96.76416666666668,,,, 54 | 5.1999999999999975,48.80399457142857,11.465802214285716,7.570833333333335,96.75444444444445,,,, 55 | 5.299999999999997,48.80401635714286,11.465806321428571,7.497916666666669,96.75722222222224,,,, 56 | 5.399999999999997,48.80403814285714,11.465810428571428,7.425000000000002,96.7677777777778,,,, 57 | 5.4999999999999964,48.80405992857143,11.465814535714284,7.352083333333336,96.73444444444443,,,, 58 | 5.599999999999996,48.80408171428572,11.465818642857144,7.27916666666667,96.7088888888889,,,, 59 | 5.699999999999996,48.8041035,11.46582275,7.206250000000003,96.6911111111111,,,, 60 | 5.799999999999995,48.804125285714285,11.465826857142858,7.133333333333336,96.68166666666669,,,, 61 | 5.899999999999995,48.804147071428574,11.465830964285717,7.06041666666667,96.6588888888889,,,, 62 | 5.999999999999995,48.804168857142855,11.465835071428572,6.987500000000004,96.61944444444448,,,, 63 | 6.099999999999994,48.80419064285714,11.465839178571429,6.914583333333338,96.5927777777778,,,, 64 | 6.199999999999994,48.80421525,11.46584325,6.808333333333341,96.55555555555556,,,, 65 | 6.299999999999994,48.8042405625,11.4658473125,6.693750000000008,96.51527777777778,48.80437693633773,11.465818732551098,109.90000000000002,7.0 66 | 6.399999999999993,48.804265875,11.465851375,6.579166666666676,96.46555555555555,48.80440442405007,11.465823006918304,109.90000000000002,7.0 67 | 6.499999999999993,48.8042911875,11.4658554375,6.464583333333343,96.40861111111111,48.804433046170594,11.465827224624974,110.27499999999998,7.0 68 | 6.5999999999999925,48.8043165,11.4658595,6.350000000000009,96.375,48.80446191724186,11.465831464020244,110.5,7.0 69 | 6.699999999999992,48.8043418125,11.4658635625,6.235416666666676,96.32583333333336,48.8044906720799,11.465835566959411,110.7,7.0 70 | 6.799999999999992,48.804367125,11.465867625,6.120833333333343,96.28333333333332,48.80451953834369,11.46583985743093,110.8,7.0 71 | 6.899999999999992,48.8043924375,11.4658716875,6.00625000000001,96.23222222222223,48.80454803028197,11.465844113840655,110.52500000000003,7.0 72 | 6.999999999999991,48.80441775,11.46587575,5.891666666666679,96.19722222222222,48.8045765297313,11.465848256612867,110.20000000000005,7.0 73 | 7.099999999999991,48.804444075,11.465879825,5.782142857142866,96.14944444444444,48.80460604095396,11.46585246125156,110.0,7.0 74 | 7.19999999999999,48.80447445,11.465883949999998,5.692857142857153,96.095,48.80463949143878,11.465856672823389,110.0,7.0 75 | 7.29999999999999,48.804504825,11.465888075,5.603571428571438,96.04972222222224,48.8046732529116,11.465860916016949,110.2,7.0 76 | 7.39999999999999,48.8045352,11.4658922,5.514285714285725,96.02333333333335,48.804707139208205,11.465865161321476,110.34999999999998,7.0 77 | 7.499999999999989,48.804565575,11.465896325,5.42500000000001,96.00444444444446,48.8047410882558,11.465869398898034,110.45000000000002,7.0 78 | 7.599999999999989,48.80459595,11.46590045,5.335714285714295,95.99666666666668,48.80477516216871,11.465873637110745,110.4,7.0 79 | 7.699999999999989,48.804626325,11.465904575,5.246428571428583,95.9713888888889,48.80480873975794,11.465877788467358,110.4,7.0 80 | 7.799999999999988,48.8046567,11.4659087,5.157142857142869,95.91555555555556,48.80484243087468,11.465882130297528,110.25000000000004,7.0 81 | 7.899999999999988,48.80468533928571,11.465912482142857,5.067857142857154,95.86694444444444,48.804874260123526,11.465886143952908,110.025,7.0 82 | 7.999999999999988,48.80470703571429,11.465914892857144,4.97857142857144,95.80833333333334,48.80489914743373,11.465888771022417,109.90000000000003,7.0 83 | 8.099999999999987,48.80472873214286,11.465917303571429,4.889285714285726,95.78361111111111,48.80492416260951,11.465891351886349,109.8,7.0 84 | 8.199999999999987,48.80475042857143,11.465919714285716,4.800000000000012,95.75888888888888,48.80494893515618,11.465893790893583,109.8,7.0 85 | 8.299999999999986,48.80477212500001,11.465922125,4.706250000000013,95.75638888888888,48.80497401008365,11.465896371885412,109.8,7.0 86 | 8.399999999999986,48.804793821428575,11.465924535714286,4.612500000000014,95.7327777777778,48.80499908088386,11.465899030430073,109.8,7.0 87 | 8.499999999999986,48.80481551785714,11.46592694642857,4.518750000000013,95.69305555555556,48.805024083188265,11.465901806309933,109.8,7.0 88 | 8.599999999999985,48.80483721428571,11.465929357142857,4.425000000000014,95.6477777777778,48.80504909644658,11.465904376823842,109.8,7.0 89 | 8.699999999999985,48.80485891071429,11.465931767857144,4.331250000000015,95.60916666666668,48.80507397676646,11.465907105375845,109.8,7.0 90 | 8.799999999999985,48.804880607142856,11.465934178571429,4.237500000000016,95.58111111111111,48.80509899197409,11.465909643119744,109.8,7.0 91 | 8.899999999999984,48.80490230357143,11.465936589285716,4.143750000000014,95.54666666666668,48.805123936786984,11.465912346401648,109.8,7.0 92 | 8.999999999999984,48.804924,11.465939,4.050000000000015,95.52055555555556,48.80514887840047,11.465915127956736,109.8,7.0 93 | 9.099999999999984,48.80494920833333,11.465941395833331,3.956250000000015,95.50972222222224,48.80517727537241,11.46591777741123,109.8,7.0 94 | 9.199999999999983,48.80497441666667,11.465943791666668,3.8791666666666735,95.50722222222224,48.805205673733695,11.465920607595207,109.8,7.0 95 | 9.299999999999985,48.804999625,11.465946187499998,3.8270833333333423,95.50166666666668,48.805234083954694,11.465923485518509,109.925,7.0 96 | 9.399999999999984,48.80502483333333,11.465948583333333,3.775000000000009,95.47444444444446,48.8052626153295,11.465926461568275,110.04999999999998,7.0 97 | 9.499999999999982,48.80505004166667,11.465950979166667,3.7229166666666753,95.43111111111114,48.805290892508694,11.46592954560284,110.17499999999998,7.0 98 | 9.599999999999982,48.80507525,11.465953375,3.670833333333342,95.38,48.80531916671187,11.465932715671352,110.25,7.0 99 | 9.699999999999982,48.805100458333335,11.465955770833334,3.61875000000001,95.34083333333334,48.80534768910438,11.465935948436584,110.25,7.0 100 | 9.79999999999998,48.80512566666667,11.465958166666669,3.566666666666677,95.3116666666667,48.80537621269786,11.465939172036652,110.25,7.0 101 | 9.89999999999998,48.805150875,11.4659605625,3.514583333333344,95.29666666666668,48.805404982722536,11.465942504234242,110.25,7.0 102 | 9.99999999999998,48.805166,11.465962,3.4625000000000106,95.28888888888888,48.80542354620092,11.465944856813744,110.25,7.0 103 | 10.09999999999998,48.805166,11.465962,3.410416666666677,95.2702777777778,48.80542679545355,11.465945840038112,110.25,7.0 104 | 10.19999999999998,48.805166,11.465962,3.4000000000000004,95.2538888888889,48.80542919257142,11.465946743533888,110.25,7.0 105 | -------------------------------------------------------------------------------- /tests/test_data/temp_ego_maneuver_array.csv: -------------------------------------------------------------------------------- 1 | 0,51,FM_EGO_decelerate,3728.331392794964,-17465.94162228238,26.87893518518519,0.15009199905033088 2 | 52,56,FM_EGO_keep_velocity,3746.2936896801984,-17323.433702771552,26.863580246913582,0.025308641975307467 3 | 57,90,FM_EGO_decelerate,3747.4091090696747,-17311.277664214373,26.53348765432099,0.09563362381989607 4 | 91,93,FM_EGO_keep_velocity,3753.074022886314,-17216.99667341914,26.528240740740742,0.0074588477366314505 5 | 94,99,FM_EGO_decelerate,3753.329332228779,-17208.574011523277,26.4712962962963,0.08230452674897157 6 | 99,101,FM_EGO_keep_velocity,3753.7548435605713,-17194.536241406575,26.463966049382723,0.024434156378584742 7 | -------------------------------------------------------------------------------- /tests/test_data/testfile_llc.osi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFS-OpenSource/OSC-Generator/48177576849e3a600d5d3914447286bf3d1d95e4/tests/test_data/testfile_llc.osi -------------------------------------------------------------------------------- /tests/test_data/testfile_rlc.osi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFS-OpenSource/OSC-Generator/48177576849e3a600d5d3914447286bf3d1d95e4/tests/test_data/testfile_rlc.osi -------------------------------------------------------------------------------- /tests/test_data/testfile_straight.csv: -------------------------------------------------------------------------------- 1 | timestamp,lat,long,heading,speed,lin_left_typ,lin_right_typ,lin_left_beginn_x,lin_left_y_abstand,lin_left_kruemm,lin_left_ende_x,lin_left_breite,lin_right_beginn_x,lin_right_y_abstand,lin_right_kruemm,lin_right_ende_x,lin_right_breite 2 | 0.0,48.96068916644596,11.424023849492427,87.54136678564001,0.0,2,1,126.25008,-3.5,0,174.705,0.15000001,126.25008,-7,0,176,0.15000001 3 | 0.1,48.96068916644596,11.424023849492427,87.54136711305549,3.29557968e-05,2,1,126.25008,-3.5,0,174.705,0.15000001,126.25008,-7,0,176,0.15000001 4 | 0.2,48.96068916644596,11.424023849492427,87.54136701950821,3.382819164000001e-05,2,1,126.25008,-3.5,0,174.705,0.15000001,126.25008,-7,0,176,0.15000001 5 | 0.3,48.96068916644596,11.424023849492427,87.54136641145088,5.5342998e-05,2,1,126.25008,-3.5,0,174.705,0.15000001,126.25008,-7,0,176,0.15000001 6 | 0.4,48.96068916644596,11.424023849492427,87.54136720660277,0.00020868163560000002,2,1,126.25008,-3.5,0,174.705,0.15000001,126.25008,-7,0,176,0.15000001 7 | 0.5,48.96068916644596,11.424023849492427,87.54137188396689,0.00047230668,2,1,126.25009,-3.5,0,174.705,0.15000001,126.25007,-7,0,176,0.15000001 8 | 0.6,48.96068916644596,11.424023849492427,87.5413781984085,0.00047320228800000003,2,1,126.25009,-3.5,0,174.705,0.15000001,126.25007,-7,0,176,0.15000001 9 | 0.7,48.96068916644596,11.424023849492427,87.54138381124552,0.000391373676,2,1,126.25009,-3.5,0,174.705,0.15000001,126.25007,-7,0,176,0.15000001 10 | 0.8,48.96068916644596,11.424023849492427,87.54138867570431,0.000397272744,2,1,126.25009,-3.5,0,174.705,0.15000001,126.25007,-7,0,176,0.15000001 11 | 0.9,48.96068916644596,11.424023849492427,87.54139564497709,0.000707323968,2,1,126.2501,-3.5,0,174.705,0.15000001,126.25006,-7,0,176,0.15000001 12 | 1.0,48.96068916644596,11.424023849492427,87.54140696419877,0.0010177684200000001,2,1,126.25011,-3.5,0,174.705,0.15000001,126.25005,-7,0,176,0.15000001 13 | 1.1,48.96068916644596,11.424023849492427,87.54142160434942,0.001235700648,2,1,126.25012,-3.5,0,174.705,0.15000001,126.25004,-7,0,176,0.15000001 14 | 1.2,48.96068916644596,11.424023849492427,87.54143947188196,0.00153975996,2,1,126.25013,-3.5,0,174.705,0.15000001,126.25003,-7,0,176,0.15000001 15 | 1.3,48.96068916644596,11.424023849492427,87.54146229742162,0.001989793224,2,1,126.25015,-3.5,0,174.705,0.15000001,126.25001,-7,0,176,0.15000001 16 | 1.4,48.96068916644596,11.424023849492427,87.54149153095202,0.002513035908,2,1,126.25017,-3.5,0,174.705,0.15000001,126.24999,-7,0,176,0.15000001 17 | 1.5,48.96068916644596,11.424023849492427,87.54152726602132,0.002938328028,2,1,126.25019,-3.5,0,174.705,0.15000001,126.24997,-7,0,176,0.15000001 18 | 1.6,48.96068916644596,11.424023849492427,87.54156487203873,0.002563151256,2,1,126.2502,-3.5,0,174.705,0.15000001,126.24996,-7,0,176,0.15000001 19 | 1.7,48.96068916644596,11.424023849492427,87.54158390891651,0.000422166024,2,1,126.25017,-3.5,0,174.705,0.15000001,126.24999,-7,0,176,0.15000001 20 | 1.8,48.96068916644596,11.424023849492427,87.54157015746175,0.002165074632,2,1,126.25011,-3.5,0,174.705,0.15000001,126.25005,-7,0,176,0.15000001 21 | 1.9,48.96068916644596,11.424023849492427,87.54156728767565,0.47770588799999997,2,1,126.2537,-3.5,0,174.705,0.15000001,126.25382,-7,0,176,0.15000001 22 | 2.0,48.96068916644596,11.424024422450223,87.54195844135359,1.560681612,2,1,126.28318,-3.5,0,174.705,0.15000001,126.28351,-7,0,176,0.15000001 23 | 2.1,48.96068916644596,11.424024995408018,87.54323559588907,2.79976932,2,1,126.34678,-3.5,0,174.705,0.15000001,126.34699,-7,0,176,0.15000001 24 | 2.2,48.96068916644596,11.424026714281402,87.54528555445641,4.12788744,2,1,126.41111,-3.5,0,174.705,0.15000001,126.41122,-7,0,176,0.15000001 25 | 2.3,48.96068916644596,11.42402843315479,87.54792434018557,5.44601988,2,1,126.54099,-3.5,0,174.705,0.15000001,126.5412,-7,0,176,0.15000001 26 | 2.4,48.96068916644596,11.42403072498597,87.55124370789554,6.72280308,2,1,126.71037,-3.5,0,176.705,0.15000001,126.71095,-7,0,176,0.15000001 27 | 2.5,48.96068916644596,11.424033589774945,87.55528514512494,7.953641999999999,2,1,126.91877,-3.5,0,176.705,0.15000001,126.91888,-7,0,176,0.15000001 28 | 2.6,48.96068916644596,11.424036454563922,87.55996870867544,9.14433408,2,1,127.16476,-3.5,0,176.705,0.15000001,127.16384,-7,0,176,0.15000001 29 | 2.7,48.96068916644596,11.424040465268488,87.56525653392393,10.37671236,2,1,127.44688,-3.5,0,176.705,0.15000001,127.44552,-7,0,176,0.15000001 30 | 2.8,48.96068916644596,11.424044475973053,87.57119383723385,11.54295,2,1,127.76722,-3.5,0,176.705,0.15000001,127.76579,-7,0,176,0.15000001 31 | 2.9,48.960688593488165,11.424049059635413,87.57773911401783,12.627299160000002,2,1,127.9992,-3.5,0,176.705,0.15000001,127.99738,-7,0,176,0.15000001 32 | 3.0,48.960688593488165,11.424054216255572,87.58481625038527,13.71147732,2,1,128.37531,-3.5,0,176.705,0.15000001,128.37263,-7,0,178,0.15000001 33 | 3.1,48.960688593488165,11.424059372875726,87.59241113809436,14.78915712,2,1,128.78462,-3.5,0,178.705,0.15000001,128.78162,-7,0,178,0.15000001 34 | 3.2,48.960688593488165,11.424065102453676,87.6005419338286,15.865379639999999,2,1,129.22674,-3.5,0,178.705,0.15000001,129.22361,-7,0,178,0.15000001 35 | 3.3,48.960688593488165,11.424071404989425,87.60919126705261,16.940886120000002,2,1,129.70186,-3.5,0,178.705,0.15000001,129.69837,-7,0,178,0.15000001 36 | 3.4,48.96068802053038,11.424078280482965,87.61833385374564,18.0152874,2,1,130.20988,-3.5,0,178.705,0.15000001,130.20615,-7,0,180,0.15000001 37 | 3.5,48.96068802053038,11.424085155976508,87.62796296206565,19.089194040000002,2,1,130.75072,-3.5,0,180.705,0.15000001,130.74691,-7,0,180,0.15000001 38 | 3.6,48.96068802053038,11.424092604427843,87.63807303310271,20.162988000000002,2,1,131.12954,-3.5,0,180.705,0.15000001,131.12569,-7,0,180,0.15000001 39 | 3.7,48.96068802053038,11.424100625836976,87.64864713424613,21.22989084,2,1,131.72518,-3.5,0,180.705,0.15000001,131.72125,-7,0,180,0.15000001 40 | 3.8,48.960687447572575,11.42410864724611,87.65966315154448,22.283682119999998,2,1,132.35324,-3.5,0,180.705,0.15000001,132.34925,-7,0,182,0.15000001 41 | 3.9,48.960687447572575,11.424117241613034,87.67110739722696,23.35022136,2,1,133.01379,-3.5,0,182.705,0.15000001,133.00981,-7,0,182,0.15000001 42 | 4.0,48.960687447572575,11.424126408937756,87.68296910289226,24.40572084,2,1,133.70682,-3.5,0,182.705,0.15000001,133.70289,-7,0,182,0.15000001 43 | 4.1,48.96068687461478,11.424136149220272,87.69523512703755,25.471974239999998,2,1,134.43231,-3.5,0,182.705,0.15000001,134.42843,-7,0,184,0.15000001 44 | 4.2,48.96068687461478,11.42414588950279,87.70788907101178,26.52916896,2,1,135.19032,-3.5,0,184.705,0.15000001,135.18652,-7,0,184,0.15000001 45 | 4.3,48.96068687461478,11.424156202743102,87.72091675479832,27.59416092,2,1,135.71368,-3.5,0,184.705,0.15000001,135.70994,-7,0,184,0.15000001 46 | 4.4,48.960686301656985,11.424166515983417,87.73430389475863,28.653374879999998,2,1,136.52589,-3.5,0,184.705,0.15000001,136.52226,-7,0,186,0.15000001 47 | 4.5,48.960686301656985,11.424177975139317,87.7480353915461,29.71614564,2,1,137.37052,-3.5,0,186.705,0.15000001,137.36701,-7,0,186,0.15000001 48 | 4.6,48.96068572869918,11.42418943429522,87.76209538584668,30.776336999999998,2,1,138.24772,-3.5,0,186.705,0.15000001,138.24432,-7,0,188,0.15000001 49 | 4.7,48.96068572869918,11.424201466408919,87.77646873009934,31.8377808,2,1,139.15736,-3.5,0,188.705,0.15000001,139.15409,-7,0,188,0.15000001 50 | 4.8,48.960685155741395,11.424213498522615,87.79114000526907,32.898246480000005,2,1,140.09951,-3.5,0,188.705,0.15000001,140.09639,-7,0,190,0.15000001 51 | 4.9,48.960685155741395,11.424226103594108,87.80608771508088,33.8948532,2,1,141.07349,-3.5,0,190.705,0.15000001,141.07047,-7,0,190,0.15000001 52 | 5.0,48.960685155741395,11.424239281623397,87.82126722954547,34.819937640000006,2,1,141.73892,-3.5,0,190.705,0.15000001,141.7359,-7,0,190,0.15000001 53 | 5.1,48.9606845827836,11.424252459652685,87.83663301816732,35.75047068,2,1,142.76085,-3.5,0,192.705,0.15000001,142.7578,-7,0,192,0.15000001 54 | 5.2,48.9606845827836,11.424266210639768,87.85216885887787,36.700358400000006,2,1,143.81166,-3.5,0,192.705,0.15000001,143.80873,-7,0,192,0.15000001 55 | 5.3,48.960684009825805,11.424280534584646,87.86788241939318,37.6407684,2,1,144.89159,-3.5,0,194.705,0.15000001,144.8889,-7,0,194,0.15000001 56 | 5.4,48.960683436868,11.424294858529525,87.88375547554712,38.5164288,2,1,145.99921,-3.5,0,194.705,0.15000001,145.99669,-7,0,194,0.15000001 57 | 5.5,48.960683436868,11.424309755432198,87.89974540103867,39.3465708,2,1,147.13309,-3.5,0,196.705,0.15000001,147.13069,-7,0,196,0.15000001 58 | 5.6,48.960682863910215,11.424324652334871,87.91581415859093,40.1257584,2,1,148.29175,-3.5,0,196.705,0.15000001,148.28952,-7,0,198,0.15000001 59 | 5.7,48.960682863910215,11.42434012219534,87.93193683675483,40.849596000000005,2,1,149.07707,-3.5,0,198.705,0.15000001,149.07496,-7,0,198,0.15000001 60 | 5.8,48.96068229095242,11.424355592055807,87.94808817897763,41.5189008,2,1,150.27303,-3.5,0,198.705,0.15000001,150.27109,-7,0,200,0.15000001 61 | 5.9,48.96068229095242,11.424371634874074,87.96423746612638,42.1368984,2,1,151.48909,-3.5,0,200.705,0.15000001,151.4873,-7,0,200,0.15000001 62 | 6.0,48.96068171799463,11.424387677692337,87.98035830453725,42.7069836,2,1,152.72362,-3.5,0,202.705,0.15000001,152.72197,-7,0,202,0.15000001 63 | 6.1,48.96068114503682,11.4244037205106,87.99642966614527,43.233271200000004,2,1,153.97512,-3.5,0,202.705,0.15000001,153.97362,-7,0,202,0.15000001 64 | 6.2,48.96068114503682,11.424420336286659,88.01243339179786,43.7194188,2,1,155.24223,-3.5,0,204.705,0.15000001,155.24088,-7,0,204,0.15000001 65 | 6.3,48.960680572079035,11.424436952062717,88.02835211284139,44.1693756,2,1,156.52373,-3.5,0,204.705,0.15000001,156.52251,-7,0,206,0.15000001 66 | 6.4,48.960680572079035,11.424454140796572,88.04417102253882,44.5849092,2,1,157.38549,-3.5,0,206.705,0.15000001,157.38436,-7,0,206,0.15000001 67 | 6.5,48.96067999912124,11.42447075657263,88.05985181510525,44.766918,2,1,158.68761,-3.5,0,206.705,0.15000001,158.68662,-7,0,208,0.15000001 68 | 6.6,48.96067942616344,11.424487945306483,88.07524245603621,44.447724,2,1,159.9895,-3.5,0,208.705,0.15000001,159.98874,-7,0,208,0.15000001 69 | 6.7,48.96067942616344,11.424504561082543,88.0902992698885,44.33598,2,1,161.28361,-3.5,0,210.705,0.15000001,161.28315,-7,0,210,0.15000001 70 | 6.8,48.96067885320565,11.424521176858603,88.10511005769436,44.2743156,2,1,162.57699,-3.5,0,210.705,0.15000001,162.57676,-7,0,212,0.15000001 71 | 6.9,48.96067885320565,11.424538365592456,88.11970217266344,44.2145016,2,1,163.86776,-3.5,0,212.705,0.15000001,163.86751,-7,0,212,0.15000001 72 | 7.0,48.96067828024785,11.424554981368514,88.13404499039967,44.152732799999995,2,1,165.15618,-3.5,0,214.705,0.15000001,165.1559,-7,0,214,0.15000001 73 | 7.1,48.96067770729006,11.424571597144572,88.14815358487287,44.090694,2,1,166.01427,-3.5,0,214.705,0.15000001,166.01391,-7,0,216,0.15000001 74 | 7.2,48.96067770729006,11.42458821292063,88.1621222856209,44.597898,2,1,167.30294,-3.5,0,216.705,0.15000001,167.30226,-7,0,216,0.15000001 75 | 7.3,48.96067713433226,11.424605401654487,88.1759777813283,45.1191312,2,1,168.60979,-3.5,0,216.705,0.15000001,168.60934,-7,0,218,0.15000001 76 | 7.4,48.96067656137447,11.42462259038834,88.18979648053839,45.564840000000004,2,1,169.9296,-3.5,0,218.705,0.15000001,169.92943,-7,0,218,0.15000001 77 | 7.5,48.96067656137447,11.424639779122193,88.20356527270842,45.9737856,2,1,171.26262,-3.5,0,220.705,0.15000001,171.26241,-7,0,220,0.15000001 78 | 7.6,48.960675988416675,11.424657540813842,88.21723078223862,46.351627199999996,2,1,172.60857,-3.5,0,220.705,0.15000001,172.60836,-7,0,222,0.15000001 79 | 7.7,48.960675988416675,11.424675302505491,88.23077753526798,46.6830936,2,1,173.96532,-3.5,0,222.705,0.15000001,173.96521,-7,0,222,0.15000001 80 | 7.8,48.96067541545888,11.424693064197141,88.2442136819515,46.9757484,2,1,174.87501,-3.5,0,224.705,0.15000001,174.87496,-7,0,224,0.15000001 81 | 7.9,48.96067484250108,11.42471082588879,88.25753144107955,47.2382496,2,1,176.24684,-3.5,0,224.705,0.15000001,176.24683,-7,0,226,0.15000001 82 | 8.0,48.96067484250108,11.42472858758044,88.27071444941265,47.4718572,2,1,177.62661,-3.5,0,226.705,0.15000001,177.62661,-7,0,226,0.15000001 83 | 8.1,48.96067426954329,11.424746922229884,88.28375619116653,47.6798472,2,1,179.01323,-3.5,0,228.705,0.15000001,179.01328,-7,0,228,0.15000001 84 | 8.2,48.960673696585495,11.424764683921532,88.2966560692408,47.866986000000004,2,1,180.40596,-3.5,0,228.705,0.15000001,180.40604,-7,0,230,0.15000001 85 | 8.3,48.960673696585495,11.424783018570977,88.30941116020382,48.036387600000005,2,1,181.80424,-3.5,0,230.705,0.15000001,181.80434,-7,0,230,0.15000001 86 | 8.4,48.9606731236277,11.42480135322042,88.32201770103265,48.1899852,2,1,183.20749,-3.5,0,232.705,0.15000001,183.20761,-7,0,232,0.15000001 87 | 8.5,48.9606725506699,11.424819687869865,88.33447411050616,48.329737200000004,2,1,184.14551,-3.5,0,232.705,0.15000001,184.14564,-7,0,234,0.15000001 88 | 8.6,48.9606725506699,11.42483802251931,88.3467803853298,48.45744,2,1,185.55599,-3.5,0,234.705,0.15000001,185.55613,-7,0,234,0.15000001 89 | 8.7,48.9606719777121,11.424856357168753,88.35893636678931,48.574569600000004,2,1,186.97031,-3.5,0,236.705,0.15000001,186.97045,-7,0,236,0.15000001 90 | 8.8,48.960671404754315,11.424874691818196,88.37094189198108,48.68225640000001,2,1,188.38812,-3.5,0,236.705,0.15000001,188.38827,-7,0,238,0.15000001 91 | 8.9,48.960671404754315,11.42489302646764,88.3827973723288,48.7814436,2,1,189.80914,-3.5,0,238.705,0.15000001,189.8093,-7,0,238,0.15000001 92 | 9.0,48.96067083179652,11.424911934074881,88.39450392966255,48.872916000000004,2,1,191.23311,-3.5,0,240.705,0.15000001,191.23326,-7,0,240,0.15000001 93 | 9.1,48.96067025883872,11.424930268724324,88.40606250619766,48.957253200000004,2,1,192.65977,-3.5,0,240.705,0.15000001,192.65993,-7,0,242,0.15000001 94 | 9.2,48.96066968588092,11.424948603373771,88.41747424653842,49.035034800000005,2,1,193.61227,-3.5,0,242.705,0.15000001,193.61242,-7,0,242,0.15000001 95 | 9.3,48.96066968588092,11.42496751098101,88.42874054269015,49.106898,2,1,195.04295,-3.5,0,244.705,0.15000001,195.0431,-7,0,244,0.15000001 96 | 9.4,48.960669112923135,11.424985845630452,88.43986273736472,49.173350400000004,2,1,196.4758,-3.5,0,244.705,0.15000001,196.47594,-7,0,246,0.15000001 97 | 9.5,48.96066853996534,11.425004753237692,88.45084256133487,49.2348348,2,1,197.91063,-3.5,0,246.705,0.15000001,197.91077,-7,0,246,0.15000001 98 | 9.6,48.96066853996534,11.42502366084493,88.4616815969985,49.2917472,2,1,199.34729,-3.5,0,248.705,0.15000001,199.34743,-7,0,248,0.15000001 99 | 9.7,48.96066796700754,11.425041995494373,88.47238174083566,49.3444476,2,1,200.78564,-3.5,0,250.705,0.15000001,200.78577,-7,0,250,0.15000001 100 | 9.8,48.96066739404974,11.425060903101615,88.48294459880317,49.393263600000004,2,1,202.22556,-3.5,0,250.705,0.15000001,202.22568,-7,0,252,0.15000001 101 | 9.9,48.96066739404974,11.425079810708853,88.49337212541788,49.438494,2,1,203.18631,-3.5,0,252.705,0.15000001,203.18642,-7,0,252,0.15000001 102 | 10.0,48.96066682109195,11.4250981453583,88.50366615411477,49.480398,2,1,204.62855,-3.5,0,252.705,0.15000001,204.62866,-7,0,254,0.15000001 103 | 10.1,48.96066624813416,11.425117052965538,88.5138286480562,49.519224,2,1,206.07206,-3.5,0,254.705,0.15000001,206.07216,-7,0,256,0.15000001 104 | 10.2,48.96066624813416,11.425135960572778,88.52386150348951,49.5551988,2,1,207.51673,-3.5,0,256.705,0.15000001,207.51682,-7,0,256,0.15000001 105 | 10.3,48.96066567517636,11.425154868180018,88.53376655959778,49.5885312,2,1,208.96247,-3.5,0,258.705,0.15000001,208.96256,-7,0,258,0.15000001 106 | 10.4,48.96066510221856,11.42517320282946,88.54354590950098,49.619415599999996,2,1,210.4092,-3.5,0,258.705,0.15000001,210.40928,-7,0,260,0.15000001 107 | 10.5,48.96066510221856,11.425192110436699,88.55320131226898,49.6480284,2,1,211.85685,-3.5,0,260.705,0.15000001,211.85692,-7,0,260,0.15000001 108 | 10.6,48.96066452926077,11.425211018043939,88.56273484125077,49.6745316,2,1,212.82242,-3.5,0,262.705,0.15000001,212.82248,-7,0,262,0.15000001 109 | 10.7,48.96066395630298,11.425229925651179,88.57214837800954,49.69908,2,1,214.27143,-3.5,0,262.705,0.15000001,214.27149,-7,0,264,0.15000001 110 | 10.8,48.96066338334518,11.425248833258417,88.58144376830754,49.721814,2,1,215.72118,-3.5,0,264.705,0.15000001,215.72123,-7,0,264,0.15000001 111 | 10.9,48.96066338334518,11.425267740865657,88.59062295491258,49.7428704,2,1,217.17161,-3.5,0,266.705,0.15000001,217.17166,-7,0,266,0.15000001 112 | 11.0,48.96066281038738,11.425286648472895,88.59968786619041,49.7623644,2,1,218.62267,-3.5,0,266.705,0.15000001,218.62271,-7,0,268,0.15000001 113 | 11.1,48.96066223742959,11.425305556080136,88.6086402081333,49.7804148,2,1,220.07431,-3.5,0,268.705,0.15000001,220.07434,-7,0,270,0.15000001 114 | 11.2,48.96066223742959,11.425324463687375,88.61748196170134,49.7971188,2,1,221.52648,-3.5,0,270.705,0.15000001,221.5265,-7,0,270,0.15000001 115 | 11.3,48.9606616644718,11.425343371294613,88.62621487439857,49.8125844,2,1,222.49486,-3.5,0,270.705,0.15000001,222.49489,-7,0,272,0.15000001 116 | 11.4,48.960661091514,11.425362278901854,88.63484079300956,49.8268908,2,1,223.94783,-3.5,0,272.705,0.15000001,223.94785,-7,0,272,0.15000001 117 | 11.5,48.960661091514,11.425381186509092,88.64336142608535,49.8401316,2,1,225.40123,-3.5,0,274.705,0.15000001,225.40124,-7,0,274,0.15000001 118 | 11.6,48.9606605185562,11.425400094116334,88.65177853312522,49.8523824,2,1,226.85502,-3.5,0,276.705,0.15000001,226.85503,-7,0,276,0.15000001 119 | 11.7,48.96065994559841,11.425419001723572,88.66009386991519,49.8637116,2,1,228.30918,-3.5,0,276.705,0.15000001,228.30919,-7,0,278,0.15000001 120 | 11.8,48.96065994559841,11.42543790933081,88.66830906877027,49.8741912,2,1,229.76367,-3.5,0,278.705,0.15000001,229.76368,-7,0,278,0.15000001 121 | 11.9,48.960659372640606,11.425456816938052,88.67642581082913,49.883878800000005,2,1,231.21848,-3.5,0,280.705,0.15000001,231.21848,-7,0,280,0.15000001 122 | 12.0,48.96065879968282,11.42547572454529,88.68444576514564,49.8928392,2,1,232.18851,-3.5,0,280.705,0.15000001,232.18851,-7,0,282,0.15000001 123 | 12.1,48.96065822672502,11.425494632152528,88.69237053915084,49.9011192,2,1,233.64377,-3.5,0,282.705,0.15000001,233.64377,-7,0,282,0.15000001 124 | 12.2,48.96065822672502,11.42551353975977,88.70020168039599,49.9087728,2,1,235.09929,-3.5,0,284.705,0.15000001,235.09928,-7,0,284,0.15000001 125 | 12.3,48.960657653767235,11.425532447367008,88.70794080753333,49.915846800000004,2,1,236.55503,-3.5,0,284.705,0.15000001,236.55502,-7,0,286,0.15000001 126 | 12.4,48.96065708080943,11.425551354974246,88.71558934415626,49.9219236,2,1,238.01098,-3.5,0,286.705,0.15000001,238.01098,-7,0,288,0.15000001 127 | 12.5,48.96065708080943,11.425570262581486,88.72314334144485,49.8311064,2,1,239.46625,-3.5,0,288.705,0.15000001,239.46626,-7,0,288,0.15000001 128 | 12.6,48.96065650785164,11.425589170188726,88.730574392354,49.5209484,2,1,240.91476,-3.5,0,290.705,0.15000001,240.91482,-7,0,290,0.15000001 129 | 12.7,48.96065593489384,11.42560750483817,88.7378782507574,49.4120124,2,1,241.87637,-3.5,0,290.705,0.15000001,241.87653,-7,0,290,0.15000001 130 | 12.8,48.96065593489384,11.425626412445409,88.74508843279244,49.3469244,2,1,243.31779,-3.5,0,292.705,0.15000001,243.31807,-7,0,292,0.15000001 131 | 12.9,48.96065536193604,11.425645320052647,88.75222728868762,49.2823872,2,1,244.75672,-3.5,0,294.705,0.15000001,244.75691,-7,0,294,0.15000001 132 | 13.0,48.96065478897825,11.425663654702094,88.75927648955856,49.216089600000004,2,1,246.19329,-3.5,0,294.705,0.15000001,246.19338,-7,0,296,0.15000001 133 | 13.1,48.96065478897825,11.425682562309332,88.76622811381782,49.1496516,2,1,247.628,-3.5,0,296.705,0.15000001,247.62806,-7,0,296,0.15000001 134 | 13.2,48.96065421602046,11.425700896958777,88.77309379698548,49.1436756,2,1,249.06215,-3.5,0,298.705,0.15000001,249.06225,-7,0,298,0.15000001 135 | 13.3,48.96065364306266,11.425719804566016,88.77989278190593,49.0205952,2,1,250.49332,-3.5,0,298.705,0.15000001,250.49315,-7,0,300,0.15000001 136 | 13.4,48.96065307010486,11.42573813921546,88.78660493032248,48.9869532,2,1,251.44612,-3.5,0,300.705,0.15000001,251.44586,-7,0,300,0.15000001 137 | 13.5,48.96065307010486,11.425757046822698,88.79321641920237,48.996399600000004,2,1,252.87522,-3.5,0,302.705,0.15000001,252.87503,-7,0,302,0.15000001 138 | 13.6,48.96065249714707,11.425775381472143,88.79975929605834,49.010238,2,1,254.30449,-3.5,0,302.705,0.15000001,254.30441,-7,0,304,0.15000001 139 | 13.7,48.96065192418928,11.425793716121587,88.80624552252677,49.0283928,2,1,255.73402,-3.5,0,304.705,0.15000001,255.73395,-7,0,304,0.15000001 140 | 13.8,48.96065192418928,11.425812623728826,88.81266589776402,49.053924,2,1,257.16429,-3.5,0,306.705,0.15000001,257.1642,-7,0,306,0.15000001 141 | 13.9,48.96065135123148,11.425830958378272,88.81901743474488,49.0853808,2,1,258.59546,-3.5,0,306.705,0.15000001,258.5954,-7,0,308,0.15000001 142 | 14.0,48.96065077827368,11.42584986598551,88.82530575064997,49.1212728,2,1,260.02762,-3.5,0,308.705,0.15000001,260.02759,-7,0,310,0.15000001 143 | 14.1,48.96065077827368,11.425868200634953,88.83153435205875,49.160862,2,1,260.98302,-3.5,0,310.705,0.15000001,260.983,-7,0,310,0.15000001 144 | 14.2,48.96065020531589,11.425887108242193,88.83770276195243,49.2031908,2,1,262.41717,-3.5,0,310.705,0.15000001,262.41716,-7,0,312,0.15000001 145 | 14.3,48.9606496323581,11.425905442891636,88.84381125503953,49.2470028,2,1,263.85264,-3.5,0,312.705,0.15000001,263.85264,-7,0,312,0.15000001 146 | 14.4,48.9606496323581,11.425924350498875,88.84986123451174,49.2913872,2,1,265.28944,-3.5,0,314.705,0.15000001,265.28946,-7,0,314,0.15000001 147 | 14.5,48.9606490594003,11.425943258106116,88.85585392163954,49.3357212,2,1,266.7276,-3.5,0,316.705,0.15000001,266.72763,-7,0,316,0.15000001 148 | 14.6,48.9606484864425,11.42596159275556,88.86178984142163,49.379633999999996,2,1,268.16712,-3.5,0,316.705,0.15000001,268.16716,-7,0,318,0.15000001 149 | 14.7,48.9606484864425,11.425980500362801,88.86766959662769,49.422949200000005,2,1,269.60798,-3.5,0,318.705,0.15000001,269.60803,-7,0,318,0.15000001 150 | 14.8,48.960647913484706,11.425999407970039,88.87349382497607,49.46508,2,1,270.56928,-3.5,0,318.705,0.15000001,270.56933,-7,0,320,0.15000001 151 | 14.9,48.96064734052692,11.42601831557728,88.87926326187474,49.5055656,2,1,272.01229,-3.5,0,320.705,0.15000001,272.01235,-7,0,322,0.15000001 152 | -------------------------------------------------------------------------------- /tests/test_data/testfile_straight.osi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFS-OpenSource/OSC-Generator/48177576849e3a600d5d3914447286bf3d1d95e4/tests/test_data/testfile_straight.osi -------------------------------------------------------------------------------- /tests/test_data/trajectories_file.osi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EFS-OpenSource/OSC-Generator/48177576849e3a600d5d3914447286bf3d1d95e4/tests/test_data/trajectories_file.osi -------------------------------------------------------------------------------- /tests/test_tools/__init__.py: -------------------------------------------------------------------------------- 1 | # **************************************************************************** 2 | # @__init__.py 3 | # 4 | # @copyright 2022 e:fs TechHub GmbH and Audi AG. All rights reserved. 5 | # 6 | # @license Apache v2.0 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # **************************************************************************** 20 | 21 | """Subpackage for test tools. 22 | """ 23 | 24 | # __all__ = ["test_coord_calculations", "test_man_helpers", "test_rulebased", "test_utils"] 25 | -------------------------------------------------------------------------------- /tests/test_tools/test_converter.py: -------------------------------------------------------------------------------- 1 | # **************************************************************************** 2 | # @test_converter.py 3 | # 4 | # @copyright 2023 e:fs TechHub GmbH and Audi AG. All rights reserved. 5 | # 6 | # @license Apache v2.0 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # **************************************************************************** 20 | 21 | from osc_generator.tools.converter import Converter 22 | from xmldiff import main 23 | import pytest 24 | import pandas as pd 25 | import os 26 | import warnings 27 | 28 | 29 | @pytest.fixture 30 | def test_data_dir(): 31 | return os.path.join(os.path.dirname(__file__), '../test_data') 32 | 33 | @pytest.fixture 34 | def df_lanes(test_data_dir): 35 | return pd.read_csv(os.path.join(test_data_dir, 'df_lanes_llc.csv')) 36 | 37 | 38 | class TestConverter: 39 | def test_converter_csv_relative_ego_llc(self, test_data_dir): # left lane change scenario, from csv file 40 | trajectories_path = os.path.join(test_data_dir, r'testfile_llc.csv') 41 | opendrive_path = os.path.join(test_data_dir, r'TestTrack.xodr') 42 | output_scenario_path = os.path.join(test_data_dir, r'output_scenario.xosc') 43 | expected_scenario_path = os.path.join(test_data_dir, r'expected_llc.xosc') 44 | system_under_test = Converter() 45 | system_under_test.osc_version = '1.2' 46 | system_under_test.set_paths(trajectories_path, opendrive_path, output_scenario_path) 47 | system_under_test.process_trajectories(relative=True) 48 | system_under_test.label_maneuvers(acc_threshold=0.2, optimize_acc=False, generate_kml=False) 49 | system_under_test.write_scenario(plot=False, 50 | radius_pos_trigger=2.0, 51 | timebased_lon=True, 52 | timebased_lat=True, 53 | output='xosc') 54 | diff = main.diff_files(output_scenario_path, expected_scenario_path) 55 | assert [] == diff 56 | 57 | def test_converter_osi_relative_ego_llc(self, test_data_dir): # left lane change scenario, from osi file 58 | trajectories_path = os.path.join(test_data_dir, r'testfile_llc.osi') 59 | opendrive_path = os.path.join(test_data_dir, r'TestTrack.xodr') 60 | output_scenario_path = os.path.join(test_data_dir, r'output_scenario.xosc') 61 | expected_scenario_path = os.path.join(test_data_dir, r'expected_llc.xosc') 62 | try: 63 | system_under_test = Converter() 64 | system_under_test.osc_version = '1.2' 65 | system_under_test.set_paths(trajectories_path, opendrive_path, output_scenario_path) 66 | system_under_test.process_trajectories(relative=True) 67 | system_under_test.label_maneuvers(acc_threshold=0.2, optimize_acc=False, generate_kml=False) 68 | system_under_test.write_scenario(plot=False, 69 | radius_pos_trigger=2.0, 70 | timebased_lon=True, 71 | timebased_lat=True, 72 | output='xosc') 73 | diff = main.diff_files(output_scenario_path, expected_scenario_path) 74 | assert [] == diff 75 | except NameError: 76 | warnings.warn( 77 | "Feature OSI Input Data is not available. Download from: " 78 | "https://github.com/OpenSimulationInterface/open-simulation-interface/blob/master/format/OSITrace.py", 79 | UserWarning) 80 | 81 | 82 | def test_converter_csv_absolute_ego_llc(self, test_data_dir, df_lanes): # left lane change, absolute co-ords 83 | trajectories_path = os.path.join(test_data_dir, r'testfile_llc.csv') 84 | opendrive_path = os.path.join(test_data_dir, r'TestTrack.xodr') 85 | output_scenario_path = os.path.join(test_data_dir, r'output_scenario.xosc') 86 | expected_scenario_path = os.path.join(test_data_dir, r'expected_llc.xosc') 87 | system_under_test = Converter() 88 | system_under_test.osc_version = '1.2' 89 | system_under_test.set_paths(trajectories_path, opendrive_path, output_scenario_path) 90 | system_under_test.process_trajectories(relative=False, df_lanes=df_lanes) 91 | system_under_test.label_maneuvers(acc_threshold=0.2, optimize_acc=False, generate_kml=False) 92 | system_under_test.write_scenario(plot=False, 93 | radius_pos_trigger=2.0, 94 | timebased_lon=True, 95 | timebased_lat=True, 96 | output='xosc') 97 | diff = main.diff_files(output_scenario_path, expected_scenario_path) 98 | assert [] == diff 99 | 100 | def test_converter_csv_relative_ego_rlc(self, test_data_dir): # right lane change scenario, from csv file 101 | trajectories_path = os.path.join(test_data_dir, r'testfile_rlc.csv') 102 | opendrive_path = os.path.join(test_data_dir, r'TestTrack.xodr') 103 | output_scenario_path = os.path.join(test_data_dir, r'output_scenario.xosc') 104 | expected_scenario_path = os.path.join(test_data_dir, r'expected_rlc.xosc') 105 | system_under_test = Converter() 106 | system_under_test.osc_version = '1.2' 107 | system_under_test.set_paths(trajectories_path, opendrive_path, output_scenario_path) 108 | system_under_test.process_trajectories(relative=True) 109 | system_under_test.label_maneuvers(acc_threshold=0.2, optimize_acc=False, generate_kml=False) 110 | system_under_test.write_scenario(plot=False, 111 | radius_pos_trigger=2.0, 112 | timebased_lon=True, 113 | timebased_lat=True, 114 | output='xosc') 115 | diff = main.diff_files(output_scenario_path, expected_scenario_path) 116 | assert [] == diff 117 | 118 | def test_converter_osi_relative_ego_rlc(self, test_data_dir): # right lane change scenario, from osi file 119 | trajectories_path = os.path.join(test_data_dir, r'testfile_rlc.osi') 120 | opendrive_path = os.path.join(test_data_dir, r'TestTrack.xodr') 121 | output_scenario_path = os.path.join(test_data_dir, r'output_scenario.xosc') 122 | expected_scenario_path = os.path.join(test_data_dir, r'expected_rlc.xosc') 123 | try: 124 | system_under_test = Converter() 125 | system_under_test.osc_version = '1.2' 126 | system_under_test.set_paths(trajectories_path, opendrive_path, output_scenario_path) 127 | system_under_test.process_trajectories(relative=True) 128 | system_under_test.label_maneuvers(acc_threshold=0.2, optimize_acc=False, generate_kml=False) 129 | system_under_test.write_scenario(plot=False, 130 | radius_pos_trigger=2.0, 131 | timebased_lon=True, 132 | timebased_lat=True, 133 | output='xosc') 134 | diff = main.diff_files(output_scenario_path, expected_scenario_path) 135 | assert [] == diff 136 | except NameError: 137 | warnings.warn( 138 | "Feature OSI Input Data is not available. Download from: " 139 | "https://github.com/OpenSimulationInterface/open-simulation-interface/blob/master/format/OSITrace.py", 140 | UserWarning) 141 | -------------------------------------------------------------------------------- /tests/test_tools/test_coord_calculations.py: -------------------------------------------------------------------------------- 1 | # **************************************************************************** 2 | # @test_coord_calculations.py 3 | # 4 | # @copyright 2022 e:fs TechHub GmbH and Audi AG. All rights reserved. 5 | # 6 | # @license Apache v2.0 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # **************************************************************************** 20 | 21 | import pandas as pd 22 | from osc_generator.tools import coord_calculations 23 | import pytest 24 | import os 25 | 26 | 27 | @pytest.fixture 28 | def test_data_dir(): 29 | return os.path.join(os.path.dirname(__file__), '../test_data') 30 | 31 | 32 | @pytest.fixture 33 | def df(test_data_dir): 34 | return pd.read_csv(os.path.join(test_data_dir, 'trajectories_file.csv')) 35 | 36 | 37 | @pytest.fixture 38 | def df_lanes(test_data_dir): 39 | return pd.read_csv(os.path.join(test_data_dir, 'df_lanes.csv')) 40 | 41 | 42 | @pytest.fixture 43 | def odr_path(test_data_dir): 44 | opendrive_path = os.path.join(test_data_dir, r'2017-04-04_Testfeld_A9_Nord_offset.xodr') 45 | return opendrive_path 46 | 47 | 48 | class TestCoordCalculations: 49 | def test_lanes_rel2abs_from_csv(self, df, df_lanes): 50 | actual = coord_calculations.transform_lanes_rel2abs(df, 'csv') 51 | expected = df_lanes 52 | pd.testing.assert_frame_equal(actual, expected) 53 | 54 | def test_get_proj_from_open_drive(self, odr_path): 55 | actual = coord_calculations.get_proj_from_open_drive(open_drive_path=odr_path) 56 | expected = '+proj=tmerc +lat_0=0 +lon_0=9 +k=0.9996 +x_0=-177308 +y_0=-5425923 +datum=WGS84 +units=m +no_defs' 57 | assert actual.srs == expected 58 | -------------------------------------------------------------------------------- /tests/test_tools/test_man_helpers.py: -------------------------------------------------------------------------------- 1 | # **************************************************************************** 2 | # @test_man_helpers.py 3 | # 4 | # @copyright 2022 e:fs TechHub GmbH and Audi AG. All rights reserved. 5 | # 6 | # @license Apache v2.0 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # **************************************************************************** 20 | 21 | import pandas as pd 22 | import numpy as np 23 | from osc_generator.tools import man_helpers 24 | import pytest 25 | import os 26 | 27 | 28 | @pytest.fixture 29 | def test_data_dir(): 30 | return os.path.join(os.path.dirname(__file__), '../test_data') 31 | 32 | 33 | @pytest.fixture 34 | def maneuver(test_data_dir): 35 | return pd.read_csv(os.path.join(test_data_dir, 'temp_ego_maneuver_array.csv')) 36 | 37 | 38 | @pytest.fixture 39 | def model_speed(test_data_dir): 40 | return np.load(os.path.join(test_data_dir, 'model_speed.npy')) 41 | 42 | 43 | @pytest.fixture 44 | def prepared_df(test_data_dir): 45 | return pd.read_csv(os.path.join(test_data_dir, 'prepared_df.csv')) 46 | 47 | 48 | @pytest.fixture 49 | def df_lanes(test_data_dir): 50 | return pd.read_csv(os.path.join(test_data_dir, 'df_lanes.csv')) 51 | 52 | 53 | @pytest.fixture 54 | def odr_path(test_data_dir): 55 | opendrive_path = os.path.join(test_data_dir, '2017-04-04_Testfeld_A9_Nord_offset.xodr') 56 | return opendrive_path 57 | 58 | 59 | @pytest.fixture 60 | def expected_ego_maneuver_array_0(test_data_dir): 61 | return np.load(os.path.join(test_data_dir, 'ego_maneuver_array_0.npy')) 62 | 63 | 64 | class TestManHelpers: 65 | def test_speed_model(self, maneuver, model_speed): 66 | actual = man_helpers.create_speed_model(maneuver, 99.57389) 67 | expected = model_speed 68 | 69 | np.testing.assert_array_almost_equal_nulp(actual, expected) 70 | np.testing.assert_array_equal(actual, expected) 71 | 72 | def test_opt_acc(self, prepared_df, df_lanes, odr_path, test_data_dir): 73 | actual = man_helpers.calc_opt_acc_thresh(prepared_df, 74 | df_lanes, 75 | odr_path, 76 | True, 77 | test_data_dir) 78 | expected = np.array([0.05, 0.1, 0.1]) 79 | 80 | np.testing.assert_array_almost_equal_nulp(actual, expected) 81 | np.testing.assert_array_equal(actual, expected) 82 | 83 | def test_man_labeling(self, prepared_df, df_lanes, odr_path, expected_ego_maneuver_array_0, test_data_dir): 84 | acc_threshold = np.array([0.05, 0.1, 0.1]) 85 | ego_maneuver_array, inf_maneuver_array, objlist, objects, ego, movobj_grps_coord = man_helpers.label_maneuvers( 86 | prepared_df, 87 | df_lanes, 88 | acc_threshold, 89 | False, 90 | odr_path, 91 | True, 92 | test_data_dir) 93 | expected_ego = [3728.331392794964, -17465.94162228238, 27.65941358024691, 7.663740745507101] 94 | 95 | np.testing.assert_array_equal(ego_maneuver_array[0], expected_ego_maneuver_array_0) 96 | np.testing.assert_array_equal(ego, expected_ego) 97 | -------------------------------------------------------------------------------- /tests/test_tools/test_osc_generator.py: -------------------------------------------------------------------------------- 1 | # **************************************************************************** 2 | # @test_osc_generator.py 3 | # 4 | # @copyright 2022 e:fs TechHub GmbH and Audi AG. All rights reserved. 5 | # 6 | # @license Apache v2.0 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # **************************************************************************** 20 | 21 | from osc_generator.osc_generator import OSCGenerator 22 | from xmldiff import main 23 | import pandas as pd 24 | import pytest 25 | import os 26 | import warnings 27 | 28 | 29 | @pytest.fixture 30 | def test_data_dir(): 31 | return os.path.join(os.path.dirname(__file__), '../test_data') 32 | 33 | 34 | class TestOSCGenerator: 35 | def test_generate_osc(self, test_data_dir): 36 | trajectories_path = os.path.join(test_data_dir, r'trajectories_file.csv') 37 | opendrive_path = os.path.join(test_data_dir, r'2017-04-04_Testfeld_A9_Nord_offset.xodr') 38 | output_scenario_path = os.path.join(test_data_dir, r'output_scenario.xosc') 39 | expected_scenario_path = os.path.join(test_data_dir, r'expected_scenario.xosc') 40 | system_under_test = OSCGenerator() 41 | system_under_test.generate_osc(trajectories_path, opendrive_path, output_scenario_path, osc_version="1.2") 42 | diff = main.diff_files(output_scenario_path, expected_scenario_path) 43 | assert [] == diff 44 | 45 | def test_generate_osc_class_reuse(self, test_data_dir): 46 | trajectories_path = os.path.join(test_data_dir, r'trajectories_file.csv') 47 | opendrive_path = os.path.join(test_data_dir, r'2017-04-04_Testfeld_A9_Nord_offset.xodr') 48 | output_scenario_path = os.path.join(test_data_dir, r'output_scenario.xosc') 49 | expected_scenario_path = os.path.join(test_data_dir, r'expected_scenario.xosc') 50 | system_under_test = OSCGenerator() 51 | system_under_test.generate_osc(trajectories_path, opendrive_path, output_scenario_path, osc_version="1.2") 52 | diff = main.diff_files(output_scenario_path, expected_scenario_path) 53 | assert [] == diff 54 | system_under_test.generate_osc(trajectories_path, opendrive_path, output_scenario_path, osc_version="1.2") 55 | diff = main.diff_files(output_scenario_path, expected_scenario_path) 56 | assert [] == diff 57 | 58 | def test_generate_osc_class_osi(self, test_data_dir): 59 | try: 60 | trajectories_path = os.path.join(test_data_dir, r'trajectories_file.osi') 61 | opendrive_path = os.path.join(test_data_dir, r'2017-04-04_Testfeld_A9_Nord_offset.xodr') 62 | output_scenario_path = os.path.join(test_data_dir, r'output_scenario.xosc') 63 | expected_scenario_path = os.path.join(test_data_dir, r'expected_scenario_osi.xosc') 64 | system_under_test = OSCGenerator() 65 | system_under_test.generate_osc(trajectories_path, opendrive_path, output_scenario_path, osc_version="1.2") 66 | diff = main.diff_files(output_scenario_path, expected_scenario_path) 67 | assert [] == diff 68 | system_under_test.generate_osc(trajectories_path, opendrive_path, output_scenario_path, osc_version="1.2") 69 | diff = main.diff_files(output_scenario_path, expected_scenario_path) 70 | assert [] == diff 71 | except NameError: 72 | warnings.warn( 73 | "Feature OSI Input Data is not available. Download from: https://github.com/OpenSimulationInterface/open-simulation-interface/blob/master/format/OSITrace.py", 74 | UserWarning) 75 | 76 | def test_generate_osc_class_osi_reuse(self, test_data_dir): 77 | try: 78 | trajectories_path = os.path.join(test_data_dir, r'trajectories_file.osi') 79 | opendrive_path = os.path.join(test_data_dir, r'2017-04-04_Testfeld_A9_Nord_offset.xodr') 80 | output_scenario_path = os.path.join(test_data_dir, r'output_scenario.xosc') 81 | expected_scenario_path = os.path.join(test_data_dir, r'expected_scenario_osi.xosc') 82 | system_under_test = OSCGenerator() 83 | system_under_test.generate_osc(trajectories_path, opendrive_path, output_scenario_path, osc_version="1.2") 84 | diff = main.diff_files(output_scenario_path, expected_scenario_path) 85 | assert [] == diff 86 | system_under_test.generate_osc(trajectories_path, opendrive_path, output_scenario_path, osc_version="1.2") 87 | diff = main.diff_files(output_scenario_path, expected_scenario_path) 88 | assert [] == diff 89 | except NameError: 90 | warnings.warn("Feature OSI Input Data is not available. Download from: https://github.com/OpenSimulationInterface/open-simulation-interface/blob/master/format/OSITrace.py", UserWarning) 91 | 92 | 93 | def test_generate_osc_class_csv_reuse(self, test_data_dir): 94 | trajectories_path = os.path.join(test_data_dir, r'trajectories_file.csv') 95 | opendrive_path = os.path.join(test_data_dir, r'2017-04-04_Testfeld_A9_Nord_offset.xodr') 96 | output_scenario_path = os.path.join(test_data_dir, r'output_scenario.xosc') 97 | expected_scenario_path = os.path.join(test_data_dir, r'expected_scenario.xosc') 98 | system_under_test = OSCGenerator() 99 | system_under_test.generate_osc(trajectories_path, opendrive_path, output_scenario_path, osc_version="1.2") 100 | diff = main.diff_files(output_scenario_path, expected_scenario_path) 101 | assert [] == diff 102 | system_under_test.generate_osc(trajectories_path, opendrive_path, output_scenario_path, osc_version="1.2") 103 | diff = main.diff_files(output_scenario_path, expected_scenario_path) 104 | assert [] == diff 105 | 106 | def test_generate_osc_straight_csv(self, test_data_dir): 107 | trajectories_path = os.path.join(test_data_dir, r'testfile_straight.csv') 108 | opendrive_path = os.path.join(test_data_dir, r'TestTrack.xodr') 109 | output_scenario_path = os.path.join(test_data_dir, r'output_scenario.xosc') 110 | expected_scenario_path = os.path.join(test_data_dir, r'expected_straight.xosc') 111 | system_under_test = OSCGenerator() 112 | system_under_test.generate_osc(trajectories_path, opendrive_path, output_scenario_path, osc_version="1.2") 113 | diff = main.diff_files(output_scenario_path, expected_scenario_path) 114 | assert [] == diff 115 | 116 | def test_generate_osc_straight_osi(self, test_data_dir): 117 | trajectories_path = os.path.join(test_data_dir, r'testfile_straight.osi') 118 | opendrive_path = os.path.join(test_data_dir, r'TestTrack.xodr') 119 | output_scenario_path = os.path.join(test_data_dir, r'output_scenario.xosc') 120 | expected_scenario_path = os.path.join(test_data_dir, r'expected_straight.xosc') 121 | try: 122 | system_under_test = OSCGenerator() 123 | system_under_test.generate_osc(trajectories_path, opendrive_path, output_scenario_path, osc_version="1.2") 124 | diff = main.diff_files(output_scenario_path, expected_scenario_path) 125 | assert [] == diff 126 | except NameError: 127 | warnings.warn("Feature OSI Input Data is not available. Download from: https://github.com/OpenSimulationInterface/open-simulation-interface/blob/master/format/OSITrace.py", UserWarning) 128 | -------------------------------------------------------------------------------- /tests/test_tools/test_rulebased.py: -------------------------------------------------------------------------------- 1 | # **************************************************************************** 2 | # @test_rulebased.py 3 | # 4 | # @copyright 2022 e:fs TechHub GmbH and Audi AG. All rights reserved. 5 | # 6 | # @license Apache v2.0 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # **************************************************************************** 20 | 21 | from osc_generator.tools import rulebased 22 | import pytest 23 | import pandas as pd 24 | import numpy as np 25 | import os 26 | 27 | 28 | @pytest.fixture 29 | def test_data_dir(): 30 | return os.path.join(os.path.dirname(__file__), '../test_data') 31 | 32 | 33 | @pytest.fixture 34 | def prepared_df(test_data_dir): 35 | return pd.read_csv(os.path.join(test_data_dir, 'prepared_df.csv')) 36 | 37 | 38 | @pytest.fixture 39 | def e_accelerate_array(test_data_dir): 40 | return np.load(os.path.join(test_data_dir, 'accelerate_array.npy')) 41 | 42 | 43 | @pytest.fixture 44 | def e_decelerate_array(test_data_dir): 45 | return np.load(os.path.join(test_data_dir, 'decelerate_array.npy')) 46 | 47 | 48 | @pytest.fixture 49 | def e_keep_velocity_array(test_data_dir): 50 | return np.load(os.path.join(test_data_dir, 'keep_velocity_array.npy')) 51 | 52 | 53 | @pytest.fixture 54 | def e_lane_change_right_array(test_data_dir): 55 | return np.load(os.path.join(test_data_dir, 'lane_change_right_array.npy')) 56 | 57 | 58 | @pytest.fixture 59 | def e_lane_change_left_array(test_data_dir): 60 | return np.load(os.path.join(test_data_dir, 'lane_change_left_array.npy')) 61 | 62 | 63 | @pytest.fixture 64 | def prepared_df(test_data_dir): 65 | return pd.read_csv(os.path.join(test_data_dir, 'prepared_df.csv')) 66 | 67 | 68 | @pytest.fixture 69 | def df_lanes(test_data_dir): 70 | return pd.read_csv(os.path.join(test_data_dir, 'df_lanes.csv')) 71 | 72 | 73 | class TestRulebased: 74 | def test_get_vehicle_state_maneuver(self, prepared_df, e_accelerate_array, e_decelerate_array, 75 | e_keep_velocity_array): 76 | speed = prepared_df['speed'] 77 | accelerate_array, start_array, keep_velocity_array, standstill_array, decelerate_array, stop_array, \ 78 | reversing_array = rulebased.create_longitudinal_maneuver_vectors(speed, acceleration_definition_threshold=0.2) 79 | 80 | np.testing.assert_array_equal(accelerate_array, e_accelerate_array) 81 | np.testing.assert_array_equal(decelerate_array, e_decelerate_array) 82 | np.testing.assert_array_equal(keep_velocity_array, e_keep_velocity_array) 83 | 84 | def test_get_lanechange_absolute(self, prepared_df, df_lanes, e_lane_change_right_array, e_lane_change_left_array): 85 | lane_change_left_array, lane_change_right_array = rulebased.create_lateral_maneuver_vectors(df_lanes, 86 | prepared_df['lat'], 87 | prepared_df['long']) 88 | 89 | np.testing.assert_array_equal(lane_change_right_array, e_lane_change_right_array) 90 | np.testing.assert_array_equal(lane_change_left_array, e_lane_change_left_array) 91 | -------------------------------------------------------------------------------- /tests/test_tools/test_utils.py: -------------------------------------------------------------------------------- 1 | # **************************************************************************** 2 | # @test_utils.py 3 | # 4 | # @copyright 2022 e:fs TechHub GmbH and Audi AG. All rights reserved. 5 | # 6 | # @license Apache v2.0 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # **************************************************************************** 20 | 21 | import pandas as pd 22 | import numpy as np 23 | from osc_generator.tools import utils 24 | import pytest 25 | import os 26 | 27 | 28 | @pytest.fixture 29 | def test_data_dir(): 30 | return os.path.join(os.path.dirname(__file__), '../test_data') 31 | 32 | 33 | @pytest.fixture 34 | def df(test_data_dir): 35 | return pd.read_csv(os.path.join(test_data_dir, 'trajectories_file.csv')) 36 | 37 | 38 | @pytest.fixture 39 | def cleaned_df(test_data_dir): 40 | return pd.read_csv(os.path.join(test_data_dir, 'cleaned_df.csv')) 41 | 42 | 43 | @pytest.fixture 44 | def cols(): 45 | cols = [['pos_x_1', 'pos_y_1', 'speed_x_1', 'speed_y_1', 'class_1'], 46 | ['pos_x_2', 'pos_y_2', 'speed_x_2', 'speed_y_2', 'class_2'], 47 | ['pos_x_3', 'pos_y_3', 'speed_x_3', 'speed_y_3', 'class_3'], 48 | ['pos_x_4', 'pos_y_4', 'speed_x_4', 'speed_y_4', 'class_4'], 49 | ['pos_x_5', 'pos_y_5', 'speed_x_5', 'speed_y_5', 'class_5'], 50 | ['pos_x_6', 'pos_y_6', 'speed_x_6', 'speed_y_6', 'class_6'], 51 | ['pos_x_7', 'pos_y_7', 'speed_x_7', 'speed_y_7', 'class_7']] 52 | return cols 53 | 54 | 55 | class TestUtils: 56 | def test_delete_not_relevant_objects(self, df, cleaned_df, cols): 57 | actual, _ = utils.delete_irrelevant_objects(df, cols) 58 | expected = cleaned_df 59 | pd.testing.assert_frame_equal(actual, expected) 60 | 61 | def test_new_coordinate_heading(self, df, cleaned_df): 62 | p = ['pos_x_6', 'pos_y_6', 'speed_x_6'] 63 | k = 64 64 | actual = utils.calc_new_geopos_from_2d_vector_on_spheric_earth(curr_coords=df.loc[k, ["lat", "long"]], 65 | heading=df.loc[k, "heading"], 66 | dist_x=df.loc[k, p[0]], dist_y=df.loc[k, p[1]]) 67 | expected = [1, 2] 68 | assert actual is not None 69 | assert len(actual) == len(expected) 70 | 71 | def test_flatten(self): 72 | cols_1 = [['pos_x_1', 'pos_y_1', 'speed_x_1', 'speed_y_1', 'class_1'], 73 | ['pos_x_2', 'pos_y_2', 'speed_x_2', 'speed_y_2', 'class_2']] 74 | expected_1 = ['pos_x_1', 'pos_y_1', 'speed_x_1', 'speed_y_1', 'class_1', 75 | 'pos_x_2', 'pos_y_2', 'speed_x_2', 'speed_y_2', 'class_2'] 76 | cols_2 = ['pos_x_1', 'pos_y_1', 'speed_x_1', 'speed_y_1', 'class_1', 77 | 'pos_x_2', 'pos_y_2', 'speed_x_2', 'speed_y_2', 'class_2'] 78 | expected_2 = ['pos_x_1', 'pos_y_1', 'speed_x_1', 'speed_y_1', 'class_1', 79 | 'pos_x_2', 'pos_y_2', 'speed_x_2', 'speed_y_2', 'class_2'] 80 | 81 | cols_3 = [[], []] 82 | expected_3 = [] 83 | assert utils.flatten(cols_1) == expected_1 84 | assert utils.flatten(cols_2) == expected_2 85 | assert utils.flatten(cols_3) == expected_3 86 | 87 | def test_find_vars(self, df, cols): 88 | actual = utils.find_vars('pos_x_|pos_y_|speed_x_|speed_y_|class_', df.columns, reshape=True) 89 | expected = np.asarray(cols) 90 | assert actual is not None 91 | assert isinstance(actual, np.ndarray) 92 | assert actual[2, 3] == expected[2, 3] 93 | 94 | def test_convert_heading(self): 95 | assert round(utils.convert_heading(0.0), 2) != 0 96 | assert round(utils.convert_heading(0), 2) != 0 97 | assert round(utils.convert_heading(360), 2) == 1.57 98 | assert round(utils.convert_heading(-270), 2) == 12.57 99 | assert round(utils.convert_heading(-735), 2) == 20.68 100 | 101 | def test_get_heading(self, df, cols): 102 | actual = utils.calc_heading_from_two_geo_positions(48.80437693633773, 48.80440442405007, 11.465818732551098, 11.465823006918304) 103 | assert round(actual, 2) == -127.32 104 | assert utils.calc_heading_from_two_geo_positions(0, 0, 0, 0) == 180 105 | --------------------------------------------------------------------------------