Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Add new OGR driver for OpenDRIVE (XODR) #9504

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/ubuntu_20.04/Dockerfile.ci
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,21 @@ RUN mkdir sqlite \
&& cd .. \
&& rm -rf sqlite

# Build libOpenDRIVE
ARG OPENDRIVE_VERSION=0.5.0-gdal
RUN if test "${OPENDRIVE_VERSION}" != ""; then ( \
wget -q https://github.com/DLR-TS/libOpenDRIVE/archive/refs/tags/${OPENDRIVE_VERSION}.tar.gz \
&& tar xzf ${OPENDRIVE_VERSION}.tar.gz \
&& rm -f ${OPENDRIVE_VERSION}.tar.gz \
&& cd libOpenDRIVE-${OPENDRIVE_VERSION} \
&& cmake . -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr/ \
&& make -j$(nproc) \
&& make install \
&& cd .. \
&& rm -rf libOpenDRIVE-${OPENDRIVE_VERSION} \
); fi

RUN ldconfig

COPY requirements.txt /tmp/
Expand Down
15 changes: 15 additions & 0 deletions .github/workflows/ubuntu_22.04/Dockerfile.ci
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,21 @@ RUN mkdir mongocxx \
&& make install \
&& cd ../.. \
&& rm -rf mongocxx

# Build libOpenDRIVE
ARG OPENDRIVE_VERSION=0.5.0-gdal
RUN if test "${OPENDRIVE_VERSION}" != ""; then ( \
wget -q https://github.com/DLR-TS/libOpenDRIVE/archive/refs/tags/${OPENDRIVE_VERSION}.tar.gz \
&& tar xzf ${OPENDRIVE_VERSION}.tar.gz \
&& rm -f ${OPENDRIVE_VERSION}.tar.gz \
&& cd libOpenDRIVE-${OPENDRIVE_VERSION} \
&& cmake . -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr/ \
&& make -j$(nproc) \
&& make install \
&& cd .. \
&& rm -rf libOpenDRIVE-${OPENDRIVE_VERSION} \
); fi

# ESRI File Geodatabase API
RUN curl -L -O https://github.com/Esri/file-geodatabase-api/raw/master/FileGDB_API_1.5/FileGDB_API_1_5_64gcc51.tar.gz \
Expand Down
15 changes: 15 additions & 0 deletions .github/workflows/ubuntu_24.04/Dockerfile.ci
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,21 @@ RUN mkdir mongocxx \
&& cd ../.. \
&& rm -rf mongocxx

# Build libOpenDRIVE
ARG OPENDRIVE_VERSION=0.5.0-gdal
RUN if test "${OPENDRIVE_VERSION}" != ""; then ( \
wget -q https://github.com/DLR-TS/libOpenDRIVE/archive/refs/tags/${OPENDRIVE_VERSION}.tar.gz \
&& tar xzf ${OPENDRIVE_VERSION}.tar.gz \
&& rm -f ${OPENDRIVE_VERSION}.tar.gz \
&& cd libOpenDRIVE-${OPENDRIVE_VERSION} \
&& cmake . -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr/ \
&& make -j$(nproc) \
&& make install \
&& cd .. \
&& rm -rf libOpenDRIVE-${OPENDRIVE_VERSION} \
); fi

# ESRI File Geodatabase API
RUN curl -L -O https://github.com/Esri/file-geodatabase-api/raw/master/FileGDB_API_1.5/FileGDB_API_1_5_64gcc51.tar.gz \
&& tar xzf FileGDB_API_1_5_64gcc51.tar.gz \
Expand Down
2 changes: 1 addition & 1 deletion apps/test_ogrsf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2315,7 +2315,7 @@ static int TestSpatialFilter(OGRLayer *poLayer, int iGeomField)
if (poUniquePtrFeature != nullptr)
{
bRet = FALSE;
printf("ERROR: Spatial filter (%d) failed to eliminate"
printf("ERROR: Spatial filter (%d) failed to eliminate "
"a feature unexpectedly!\n",
iGeomField);
}
Expand Down
7,424 changes: 7,424 additions & 0 deletions autotest/ogr/data/xodr/5g_living_lab_A39_Wolfsburg-West.xodr

Large diffs are not rendered by default.

308 changes: 308 additions & 0 deletions autotest/ogr/ogr_xodr.py

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions cmake/helpers/CheckDependentLibraries.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,8 @@ if (Arrow_FOUND)
mark_as_advanced(ARROW_USE_STATIC_LIBRARIES)
endif()

gdal_check_package(OpenDrive "Enable libOpenDRIVE" CAN_DISABLE)

# bindings

# finding python in top of project because of common for autotest and bindings
Expand Down
20 changes: 16 additions & 4 deletions doc/source/development/building_from_source.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1157,6 +1157,20 @@ It is used by the internal libtiff library or the :ref:`raster.zarr` driver.
Control whether to use LibLZMA. Defaults to ON when LibLZMA is found.


libOpenDRIVE
michikommader marked this conversation as resolved.
Show resolved Hide resolved
************

`libOpenDRIVE <https://github.com/pageldev/libOpenDRIVE>`_ is required for the :ref:`vector.xodr` driver.

.. option:: OpenDrive_DIR

Path to libOpenDRIVE CMake configuration directory ``<installDir>/cmake/``. The :file:`cmake/` path is usually automatically created when installing libOpenDRIVE and contains the necessary configuration files for inclusion into other project builds.

.. option:: GDAL_USE_OPENDRIVE=ON/OFF

Control whether to use libOpenDRIVE. Defaults to ON when libOpenDRIVE is found.


LibQB3
******

Expand All @@ -1168,7 +1182,6 @@ by the :ref:`raster.marfa` driver.
Control whether to use LibQB3. Defaults to ON when LibQB3 is found.



LibXml2
*******

Expand All @@ -1189,7 +1202,6 @@ capabilities in GMLJP2v2 generation.
Control whether to use LibXml2. Defaults to ON when LibXml2 is found.



LURATECH
********

Expand Down Expand Up @@ -1569,7 +1581,7 @@ The Oracle Instant Client SDK (closed source/proprietary) is required for the

.. option:: Oracle_ROOT

Path to the root directory of the Oracle Instant Client SDK
Path to the root directory of the Oracle Instant Client SDK.

.. option:: GDAL_USE_ORACLE=ON/OFF

Expand Down Expand Up @@ -1604,7 +1616,7 @@ Regular Expressions support. It is used for the REGEXP operator in drivers using

.. option:: PCRE2_LIBRARY

Path to a shared or static library file with "pcre2-8" in its name
Path to a shared or static library file with "pcre2-8" in its name.

.. option:: GDAL_USE_PCRE2=ON/OFF

Expand Down
1 change: 1 addition & 0 deletions doc/source/drivers/vector/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,4 @@ Vector drivers
wfs
xls
xlsx
xodr
198 changes: 198 additions & 0 deletions doc/source/drivers/vector/xodr.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
.. _vector.xodr:

XODR -- OpenDRIVE Road Description Format
=========================================

michikommader marked this conversation as resolved.
Show resolved Hide resolved
.. versionadded:: 3.10

.. shortname:: XODR

.. build_dependencies:: libOpenDRIVE >= 0.5.0, GEOS

This driver provides read access to road network elements of OpenDRIVE datasets.

`OpenDRIVE <https://www.asam.net/standards/detail/opendrive/>`_ is an open industry format for lane-detailed description of road networks, commonly used in applications of the automotive and transportation systems domains. It bundles polynomial, continuous road geometry modelling with all necessary topological links and semantic information from traffic-regulating infrastructure (signs and traffic lights).

Driver capabilities
-------------------

.. supports_georeferencing::

Specification version
---------------------

The currently supported OpenDRIVE version is 1.4 and depends on what is provided by libOpenDRIVE_.

.. _libOpenDRIVE: https://github.com/pageldev/libOpenDRIVE/

Supported OpenDRIVE elements
++++++++++++++++++++++++++++

The XODR driver exposes OpenDRIVE's different road elements as separate layers by converting geometric elements into 3-dimensional OGR geometry types. The following _`layer types` are currently implemented:

* *ReferenceLine*: Road reference line (``<planView>``) as :cpp:class:`OGRLineString`.
* *LaneBorder*: Outer road lane border as :cpp:class:`OGRLineString`.
* *Lane*: Polygonal surface (TIN) of the lane mesh as :cpp:class:`OGRTriangulatedSurface`.
* *RoadMark*: Polygonal surface (TIN) of the road mark mesh as :cpp:class:`OGRTriangulatedSurface`.
* *RoadObject*: Polygonal surface (TIN) of the road object mesh as :cpp:class:`OGRTriangulatedSurface`.
* *RoadSignal*: Polygonal surface (TIN) of the road signal mesh as :cpp:class:`OGRTriangulatedSurface`.

Spatial reference
-----------------

By definition, OpenDRIVE geometries are always referenced in a Cartesian coordinate system which defaults to having its origin at ``(0, 0, 0)``. If real-world coordinates are used, the OpenDRIVE header provides a PROJ.4 string with the definition of a projected spatial reference system:

::

<header ...>
<geoReference><![CDATA[+proj=tmerc +lat_0=0 +lon_0=9 +k=0.9996 +x_0=500000 +y_0=0 +datum=WGS84 +units=m +no_defs]]></geoReference>
</header>

The XODR driver uses this PROJ definition as spatial reference for creation of all OGR geometry layers.
michikommader marked this conversation as resolved.
Show resolved Hide resolved

Limitations
-----------

The supported content encoding of OpenDRIVE XML files is limited to what pugixml is able to automatically guess (see `4.6. Encodings <https://pugixml.org/docs/manual.html#loading.encoding>`_). The default fallback encoding is UTF-8.

Open options
------------

The following open options can be specified
(typically with the ``-oo name=value`` parameters of :program:`ogrinfo` or :program:`ogr2ogr`):

- .. oo:: EPSILON
:choices: <float>
:default: 1.0

Epsilon value ``> 0.0`` for linear approximation of continuous OpenDRIVE geometries. A smaller value results in a finer sampling. This parameter corresponds to libOpenDRIVE's ``eps`` parameter.

- .. oo:: DISSOLVE_TIN
:choices: YES, NO
:default: NO

Whether to dissolve triangulated surfaces. By setting this option to YES, the TIN layers *Lane* and *RoadMark* of geometry type :cpp:class:`OGRTriangulatedSurface` will be simplified to single, simple :cpp:class:`OGRPolygon` geometries. This performs a :cpp:func:`UnaryUnion` which dissolves boundaries of all touching triangle patches and thus yields a slimmer dataset which often suffices for basic GIS usage. Be aware that this dissolving step increases processing time significantly.
Layer *RoadSignal* will be dissolved to a simple :cpp:class:`OGRPoint`.

Examples
--------

- Translate OpenDRIVE road *ReferenceLine* elements (``<planView>``) to :ref:`Shapefile <vector.shapefile>` using :program:`ogr2ogr`. The desired :ref:`layer type <layer types>` which is to be extracted from the dataset is specified as the last parameter of the function call.

::

ogr2ogr -f "ESRI Shapefile" CulDeSac.shp CulDeSac.xodr ReferenceLine

- Convert the whole OpenDRIVE dataset with all its different layers into a :ref:`GeoPackage <vector.gpkg>` using:

::

ogr2ogr -f "GPKG" CulDeSac.gpkg CulDeSac.xodr

- Convert the whole OpenDRIVE dataset with custom parameters :oo:`EPSILON` and :oo:`DISSOLVE_TIN` into a :ref:`GeoPackage <vector.gpkg>`:

::

ogr2ogr -f "GPKG" CulDeSac.gpkg CulDeSac.xodr -oo EPSILON=0.9 -oo DISSOLVE_TIN=YES

Convenient usage through docker image
-------------------------------------

To use the XODR driver through a docker image, first build the image from the corresponding docker directory

::

cd <gdal>/docker/ubuntu-full/
docker build -t gdal/xodr -f Dockerfile .

For general usage information refer to `GDAL Docker images <https://github.com/OSGeo/gdal/tree/master/docker#usage>`__. Usage examples:

- Use :program:`ogrinfo` to extract detailed information about a local `xodr` file by mounting your current working directory (`$PWD`) containing the file into the Docker container:

::

docker run --rm -v ${PWD}:/home -it gdal/xodr ogrinfo /home/<file>.xodr

- Use :program:`ogr2ogr` to convert a local `xodr` file into any other supported OGR output format. The result will be automatically available in your host machine's working directory which is mounted into the container:

::

docker run --rm -v ${PWD}:/home -it gdal/xodr ogr2ogr -f "GPKG" /home/<file>.gpkg /home/<file>.xodr


Alternatively, you can run a docker container that enables using the XODR driver in an isolated workspace from within the container

::

docker run --name <container_name> -it gdal/xodr /bin/bash


General building notes
----------------------

Building of the driver as plugin is tested to work on
michikommader marked this conversation as resolved.
Show resolved Hide resolved

* Ubuntu 24.04 using GCC
* Windows 10 using GCC 13.1.0 (with MCF threads) + MinGW-w64 11.0.0 (MSVCRT runtime), which is obtainable from `WinLibs <https://winlibs.com/>`_.

Ensure to meet the following driver dependencies:

* PROJ
* GEOS
* libOpenDRIVE_ as shared library (built with CMake option ``-DBUILD_SHARED_LIBS=ON``)

Then, after checking out GDAL sources with this driver extension, create the build directory:

::

cd <gdal>
mkdir build
cd build

From the build directory configure CMake to activate our XODR driver as plugin:

::

cmake .. -DOGR_ENABLE_DRIVER_XODR_PLUGIN=TRUE -DOpenDrive_DIR=/path/to/libOpenDRIVE/installdir/cmake/

.. note:: The :file:`cmake/` path is usually automatically created when installing libOpenDRIVE and contains the necessary configuration files for inclusion into other project builds.

Now, build GDAL and install it:

::

cmake --build .
cmake --build . --target install

Afterwards you will find a new shared library file :file:`{path/to/GDAL/installdir}/lib/gdalplugins/ogr_XODR`.

Verifying a successful build
++++++++++++++++++++++++++++

Check if XODR driver is found:

::

cd <gdal>/build/
./apps/ogrinfo --format XODR

This should print basic capabilities of the driver:

::

Format Details:
Short Name: XODR
Long Name: OpenDRIVE - Open Dynamic Road Information for Vehicle Environment
Supports: Vector
Supports: Open() - Open existing dataset.
<OpenOptionList>
...
</OpenOptionList>

If you are on Linux, depending on your environment, you might experience linker errors like:

::

ERROR 1: libOpenDrive.so: cannot open shared object file: No such file or directory

In such cases ensure that your environment variable ``LD_LIBRARY_PATH`` points to the corresponding install directories of libOpenDRIVE and GDAL and run ``ldconfig`` afterwards.
20 changes: 20 additions & 0 deletions docker/alpine-normal/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,24 @@ RUN if test "${HDF4_VERSION}" != "" -a "$(uname -m)" = "x86_64"; then ( \
&& apk del byacc flex portablexdr-dev \
); fi

# Build libOpenDRIVE
ARG OPENDRIVE_VERSION=0.5.0-gdal
RUN if test "${OPENDRIVE_VERSION}" != ""; then ( \
wget -q https://github.com/DLR-TS/libOpenDRIVE/archive/refs/tags/${OPENDRIVE_VERSION}.tar.gz \
&& tar xzf ${OPENDRIVE_VERSION}.tar.gz \
&& rm -f ${OPENDRIVE_VERSION}.tar.gz \
&& cd libOpenDRIVE-${OPENDRIVE_VERSION} \
&& cmake . -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr/ \
&& make -j$(nproc) \
&& make install \
&& mkdir -p /build_thirdparty/usr/lib \
&& cp -P /usr/lib/libOpenDrive*.so* /build_thirdparty/usr/lib \
&& for i in /build_thirdparty/usr/lib/*; do strip -s $i 2>/dev/null || /bin/true; done \
&& cd .. \
&& rm -rf libOpenDRIVE-${OPENDRIVE_VERSION} \
); fi

RUN apk add --no-cache rsync ccache
ARG RSYNC_REMOTE

Expand Down Expand Up @@ -239,6 +257,8 @@ RUN if test "${GDAL_VERSION}" = "master"; then \
-DIconv_INCLUDE_DIR=/usr/include/gnu-libiconv \
-DIconv_LIBRARY=/usr/lib/libiconv.so \
-DBUILD_TESTING=OFF \
-DOpenDrive_DIR=/usr/lib/ \
-DOGR_ENABLE_DRIVER_XODR_PLUGIN=TRUE \
&& make -j$(nproc) \
&& make install DESTDIR="/build" \
&& (make -j$(nproc) multireadtest && cp apps/multireadtest /build/usr/bin) \
Expand Down