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

Add coordinate system retrieve for DWG using ODA (fixes https://github.com/OSGeo/gdal/issues/11672) #11694

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

Pourfex
Copy link

@Pourfex Pourfex commented Jan 20, 2025

What does this PR do?

Get Coordinate System WKT representation from ODA capabilities when GDAL is build with ODA. Uses the existing system of SpatialReference from gdal.

What are related issues/pull requests?

#11672

Tasklist

  • [ v ] Fix the compilation
  • [ ] Fix using ODA's code into GDAL
  • Make sure code is correctly formatted (cf pre-commit configuration)
  • Add test case(s)
  • Add documentation
  • Updated Python API documentation (swig/include/python/docs/)
  • Review
  • Adjust for comments
  • All CI builds and checks have passed

Environment

Provide environment details, if relevant:

  • OS: All
  • Compiler: gcc 13 (ODA compatibility)
  • ODA version : 25.12 (Kernel & Drawing)

@Pourfex
Copy link
Author

Pourfex commented Jan 20, 2025

Testing is extensively described in #11672

Basically :
1 - Build Gdal with ODA
2 - Use this file for test : https://blocinbloc-public-test.s3.fr...4-MONTAIGU.dwg that has a CRS.
3 - Use this command : CPL_DEBUG=ON ogrinfo -al -so ./DwgFile/DwgFiles/04-MONTAIGU.dwg

@rcoup rcoup self-assigned this Jan 20, 2025
poFeatureDefn->SetGeomType(wkbUnknown); // presumably, as I think DWG layers might fix geometries of different kind
poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
poSRS->Release();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to be moved after line 72, so poSRS is released in case of error

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree.

// Extract coordinate system string
OdDbGeoCoordinateSystemPtr pCS;
OdDbGeoCoordinateSystem::create(pGeoData->coordinateSystem(), pCS);
if (pCS.isNull())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it really an error ? I've not checked, but couldn't a GeoData object be valid without a coordinateSystem() ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at ODA's examples codebase, it seems it only serves as container for Geodata.
Basically it is use to CRUD the OdDbGeoCoordinateSystemPtr.

I'm no expert but will ask ODA member.

{
CPLError(CE_Warning, CPLE_AppDefined, "Invalid WKT format extracted.");
}
}else
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do all those those code formatting issues once the pull request is working

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done !

@Pourfex
Copy link
Author

Pourfex commented Jan 21, 2025

@rcoup @rouault

Succesfully setup a gdal compiling with ODA with good result on ogrinfo.

ogrinfo -al -so ./DwgFile/DwgFiles/04-MONTAIGU.dwg 
INFO: Open of `./DwgFile/DwgFiles/04-MONTAIGU.dwg'
      using driver `DWG' successful.

Layer name: entities
Geometry: Unknown (any)
Feature Count: 4017
Extent: (894961.977581, 6619909.555172) - (898410.145520, 6621914.678879)
Layer SRS WKT:
PROJCRS["Lambert93",
    BASEGEOGCRS["LL-RGF93",
        DATUM["RGF93",
            ELLIPSOID["GRS1980",6378137,298.2572221,
                LENGTHUNIT["metre",1,
                    ID["EPSG",9001]]]],
        PRIMEM["Greenwich",0,
            ANGLEUNIT["Degree",0.0174532925199433]]],
    CONVERSION["unnamed",
        METHOD["Lambert Conic Conformal (2SP)",
            ID["EPSG",9802]],
        PARAMETER["Easting at false origin",700000,
            LENGTHUNIT["Meter",1],
            ID["EPSG",8826]],
        PARAMETER["Northing at false origin",6600000,
            LENGTHUNIT["Meter",1],
            ID["EPSG",8827]],
        PARAMETER["Longitude of false origin",3,
            ANGLEUNIT["Degree",0.0174532925199433],
            ID["EPSG",8822]],
        PARAMETER["Latitude of false origin",46.5,
            ANGLEUNIT["Degree",0.0174532925199433],
            ID["EPSG",8821]],
        PARAMETER["Latitude of 1st standard parallel",44,
            ANGLEUNIT["Degree",0.0174532925199433],
            ID["EPSG",8823]],
        PARAMETER["Latitude of 2nd standard parallel",49,
            ANGLEUNIT["Degree",0.0174532925199433],
            ID["EPSG",8824]]],
    CS[Cartesian,2],
        AXIS["(E)",east,
            ORDER[1],
            LENGTHUNIT["Meter",1]],
        AXIS["(N)",north,
            ORDER[2],
            LENGTHUNIT["Meter",1]]]
Data axis to CRS axis mapping: 1,2
Layer: String (0.0)
SubClasses: String (0.0)
ExtendedEntity: String (0.0)
Linetype: String (0.0)
EntityHandle: String (0.0)
Text: String (0.0)

Main thing is to add the CSMap Dicts from ODA and correctly set the CS_MAP_DIR env variable.

I've done checks if the variables are correctly set.

Here is the updated DockerFile if anyone interested :

# syntax=docker/dockerfile:1

##
# osgeo/gdal:ubuntu-small

# This file is available at the option of the licensee under:
# Public domain
# or licensed under MIT (LICENSE.TXT) Copyright 2019 Even Rouault <[email protected]>

ARG PROJ_INSTALL_PREFIX=/usr/local
ARG BASE_IMAGE=ubuntu:24.04
ARG TARGET_BASE_IMAGE=ubuntu:24.04

FROM $BASE_IMAGE AS builder

# Derived from osgeo/proj by Howard Butler <[email protected]>
LABEL maintainer="Even Rouault <[email protected]>"

ENV HOME="/root"

ARG TARGET_ARCH=
RUN echo ${TARGET_ARCH}
COPY ./bh-set-envvars.sh /buildscripts/bh-set-envvars.sh

RUN rm -f /etc/apt/apt.conf.d/docker-clean \
    && echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache \
    && echo 'Acquire::Retries "10";' > /etc/apt/apt.conf.d/80-retries
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
    --mount=type=cache,target=/var/lib/apt,sharing=locked \
    . /buildscripts/bh-set-envvars.sh \
    && if test "${TARGET_ARCH}" != ""; then \
    rm -f /etc/apt/sources.list /etc/apt/sources.list.d/ubuntu.sources \
    && echo "deb [arch=amd64] http://us.archive.ubuntu.com/ubuntu/ noble main restricted universe" >> /etc/apt/sources.list \
    && echo "deb [arch=amd64] http://us.archive.ubuntu.com/ubuntu/ noble-updates main restricted universe" >> /etc/apt/sources.list \
    && echo "deb [arch=amd64] http://us.archive.ubuntu.com/ubuntu/ noble-backports main restricted universe" >> /etc/apt/sources.list \
    && echo "deb [arch=amd64] http://security.ubuntu.com/ubuntu noble-security main restricted universe" >> /etc/apt/sources.list \
    && echo "deb [arch=${TARGET_ARCH}] http://ports.ubuntu.com/ubuntu-ports/ noble main restricted universe" >> /etc/apt/sources.list \
    && echo "deb [arch=${TARGET_ARCH}] http://ports.ubuntu.com/ubuntu-ports/ noble-updates main restricted universe" >> /etc/apt/sources.list \
    && echo "deb [arch=${TARGET_ARCH}] http://ports.ubuntu.com/ubuntu-ports/ noble-security main restricted universe" >> /etc/apt/sources.list \
    && dpkg --add-architecture ${TARGET_ARCH} \
    && apt-get update -y \
    && DEBIAN_FRONTEND=noninteractive apt-get install -y g++-13-${GCC_ARCH}-linux-gnu \
    && ln -s ${GCC_ARCH}-linux-gnu-gcc-13 /usr/bin/${GCC_ARCH}-linux-gnu-gcc \
    && ln -s ${GCC_ARCH}-linux-gnu-g++-13 /usr/bin/${GCC_ARCH}-linux-gnu-g++; \
    fi

# Setup build env for PROJ
USER root
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
    --mount=type=cache,target=/var/lib/apt,sharing=locked \
    . /buildscripts/bh-set-envvars.sh \
    && apt-get update -y \
    && DEBIAN_FRONTEND=noninteractive apt-get install -y --fix-missing --no-install-recommends \
            build-essential ca-certificates ccache \
            git make ninja-build cmake wget unzip libtool automake \
            zlib1g-dev${APT_ARCH_SUFFIX} libsqlite3-dev${APT_ARCH_SUFFIX} pkg-config sqlite3 libcurl4-openssl-dev${APT_ARCH_SUFFIX} \
            libtiff-dev${APT_ARCH_SUFFIX} \
            rsync

# Setup build env for GDAL
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
    --mount=type=cache,target=/var/lib/apt,sharing=locked \
    . /buildscripts/bh-set-envvars.sh \
    && apt-get update -y \
    && DEBIAN_FRONTEND=noninteractive apt-get install -y --fix-missing --no-install-recommends \
       python3-dev${APT_ARCH_SUFFIX} python3-numpy${APT_ARCH_SUFFIX} python3-setuptools${APT_ARCH_SUFFIX} \
       libjpeg-dev${APT_ARCH_SUFFIX} libgeos-dev${APT_ARCH_SUFFIX} \
       libexpat-dev${APT_ARCH_SUFFIX} libxerces-c-dev${APT_ARCH_SUFFIX} \
       libwebp-dev${APT_ARCH_SUFFIX} libpng-dev${APT_ARCH_SUFFIX} \
       libdeflate-dev${APT_ARCH_SUFFIX} \
       libzstd-dev${APT_ARCH_SUFFIX} bash zip curl \
       libpq-dev${APT_ARCH_SUFFIX} libssl-dev${APT_ARCH_SUFFIX} libopenjp2-7-dev${APT_ARCH_SUFFIX} \
       libspatialite-dev${APT_ARCH_SUFFIX} \
       autoconf automake sqlite3 bash-completion swig

# Build openjpeg
ARG OPENJPEG_VERSION=
RUN . /buildscripts/bh-set-envvars.sh \
    && if test "${OPENJPEG_VERSION}" != ""; then ( \
    curl -LO -fsS https://github.com/uclouvain/openjpeg/archive/v${OPENJPEG_VERSION}.tar.gz \
    && tar xzf v${OPENJPEG_VERSION}.tar.gz \
    && rm -f v${OPENJPEG_VERSION}.tar.gz \
    && cd openjpeg-${OPENJPEG_VERSION} \
    && cmake . -G Ninja -DBUILD_SHARED_LIBS=ON  -DBUILD_STATIC_LIBS=OFF -DCMAKE_BUILD_TYPE=Release \
        -DCMAKE_INSTALL_PREFIX=/usr \
    && ninja \
    && ninja install \
    && mkdir -p /build_thirdparty/usr/lib \
    && cp -P /usr/lib/libopenjp2*.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 openjpeg-${OPENJPEG_VERSION} \
    ); fi

ARG PROJ_INSTALL_PREFIX
ARG PROJ_DATUMGRID_LATEST_LAST_MODIFIED
RUN \
    mkdir -p /build_projgrids/${PROJ_INSTALL_PREFIX}/share/proj \
    && curl -LO -fsS http://download.osgeo.org/proj/proj-datumgrid-latest.zip \
    && unzip -q -j -u -o proj-datumgrid-latest.zip  -d /build_projgrids/${PROJ_INSTALL_PREFIX}/share/proj \
    && rm -f *.zip

ARG RSYNC_REMOTE
ARG WITH_CCACHE

ARG oda_directory="oda_directory"

ARG oda_drawing="https://****/Drawings_lnxX64_13dll_25.12.tar"

ARG oda_kernel="https://****/Kernel_lnxX64_13dll_25.12.tar"

ARG oda_activation="https://****/OdActivationInfo"

RUN apt-get update && apt-get install -y \
    libgl1-mesa-dev \
    libx11-dev
# Prepare ODA Libraries
RUN if ! [ -d "$oda_directory" ]; then \
    curl -o oda_drawing.tar $oda_drawing \
    && curl -o oda_kernel.tar $oda_kernel \
    && curl -o OdActivationInfo $oda_activation \
    && mkdir -p "$oda_directory" \
    && tar xvf oda_kernel.tar -C "$oda_directory" \
    && tar xvf oda_drawing.tar -C "$oda_directory" \
    && rm oda_drawing.tar \
    && rm oda_kernel.tar \
    && cp OdActivationInfo "$oda_directory/ThirdParty/activation/"; \
fi

# Configure and build ODA Libraries - don't use -j options as it makes error in docker
RUN cd "$oda_directory" \
    && ./configure \
    && make \
    && cd ..

# Set LD_LIBRARY_PATH environment variable

RUN cd "$oda_directory" \
    && cd bin/lnxX64_13dll \
    && for f in *.tx ; do echo "Processing $f"; ln -s $f lib$f.so; done \
    && ldconfig

RUN apt install -y findutils \
    && find . -name "TG_Db.tx"

ARG LD_LIBRARY_PATH="/usr/local/bin/oda_directory/bin/lnxX64_13dll"
ARG TEIGHA_ROOT="oda_directory"
ARG TEIGHA_ACTIVATION_FILE_DIRECTORY="oda_directory/ThirdParty/activation"

# Build PROJ
ARG PROJ_VERSION=master
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
    --mount=type=cache,target=/var/lib/apt,sharing=locked \
    apt-get update -y \
    && DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y patchelf

RUN --mount=type=cache,id=ubuntu-small-proj,target=$HOME/.cache \
    . /buildscripts/bh-set-envvars.sh \
    && mkdir proj \
    && curl -L -fsS https://github.com/OSGeo/PROJ/archive/${PROJ_VERSION}.tar.gz \
        | tar xz -C proj --strip-components=1 \
    && export PROJ_DB_CACHE_PARAM="" \
    && cd proj \
    && if test "${RSYNC_REMOTE:-}" != ""; then \
        echo "Downloading cache..."; \
        rsync -ra ${RSYNC_REMOTE}/proj/${GCC_ARCH}/ $HOME/.cache/; \
        echo "Finished"; \
    fi \
    && if [ -n "${WITH_CCACHE:-}" ]; then \
        export CC="ccache ${GCC_ARCH}-linux-gnu-gcc"; \
        export CXX="ccache ${GCC_ARCH}-linux-gnu-g++"; \
        export PROJ_DB_CACHE_PARAM="-DPROJ_DB_CACHE_DIR=$HOME/.cache"; \
        ccache -M 100M; \
    fi \
    && CFLAGS='-DPROJ_RENAME_SYMBOLS -O2' CXXFLAGS='-DPROJ_RENAME_SYMBOLS -DPROJ_INTERNAL_CPP_NAMESPACE -O2' \
        cmake . \
	-G Ninja \
        -DBUILD_SHARED_LIBS=ON \
        -DCMAKE_BUILD_TYPE=Release \
        -DCMAKE_INSTALL_PREFIX=${PROJ_INSTALL_PREFIX} \
        -DBUILD_TESTING=OFF \
        $PROJ_DB_CACHE_PARAM \
    && ninja \
    && DESTDIR="/build" ninja install \
    && if test "${RSYNC_REMOTE:-}" != ""; then \
        echo "Uploading cache..."; \
        rsync -ra --delete $HOME/.cache/ ${RSYNC_REMOTE}/proj/${GCC_ARCH}/; \
        echo "Finished"; \
    fi \
    && if [ -n "${WITH_CCACHE:-}" ]; then \
        ccache -s; \
        unset CC; \
        unset CXX; \
    fi \
    && cd .. \
    && rm -rf proj \
    && PROJ_SO=$(readlink -f /build${PROJ_INSTALL_PREFIX}/lib/libproj.so | awk 'BEGIN {FS="libproj.so."} {print $2}') \
    && PROJ_SO_FIRST=$(echo $PROJ_SO | awk 'BEGIN {FS="."} {print $1}') \
    && mv /build${PROJ_INSTALL_PREFIX}/lib/libproj.so.${PROJ_SO} /build${PROJ_INSTALL_PREFIX}/lib/libinternalproj.so.${PROJ_SO} \
    && ln -s libinternalproj.so.${PROJ_SO} /build${PROJ_INSTALL_PREFIX}/lib/libinternalproj.so.${PROJ_SO_FIRST} \
    && ln -s libinternalproj.so.${PROJ_SO} /build${PROJ_INSTALL_PREFIX}/lib/libinternalproj.so \
    && rm /build${PROJ_INSTALL_PREFIX}/lib/libproj.*  \
    && ${GCC_ARCH}-linux-gnu-strip -s /build${PROJ_INSTALL_PREFIX}/lib/libinternalproj.so.${PROJ_SO} \
    && for i in /build${PROJ_INSTALL_PREFIX}/bin/*; do ${GCC_ARCH}-linux-gnu-strip -s $i 2>/dev/null || /bin/true; done \
    && patchelf --set-soname libinternalproj.so.${PROJ_SO_FIRST} /build${PROJ_INSTALL_PREFIX}/lib/libinternalproj.so.${PROJ_SO} \
    && for i in /build${PROJ_INSTALL_PREFIX}/bin/*; do patchelf --replace-needed libproj.so.${PROJ_SO_FIRST} libinternalproj.so.${PROJ_SO_FIRST} $i; done

# Get ODA CSmap dictionnaries : see https://docs.opendesign.com/td/db_geo_functionality.html
ARG oda_csmap_dict="https://***/CSDicts_25.12.0.0.zip"

RUN cd "$oda_directory" \
    && curl -o CSDicts.zip $oda_csmap_dict \
    && ls \
    && unzip CSDicts.zip -d "CSDict" \
    && rm CSDicts.zip \
    && pwd

# Build GDAL
ARG GDAL_VERSION=3.10.1
ARG GDAL_RELEASE_DATE
ARG GDAL_BUILD_IS_RELEASE
ARG GDAL_REPOSITORY=bloc-in-bloc/gdal
ARG GDAL_BRANCH=f/dwg_srs
ARG USE_GIT_CLONE=true

RUN --mount=type=cache,id=ubuntu-small-gdal,target=$HOME/.cache \
    . /buildscripts/bh-set-envvars.sh \
    && mkdir gdal \
    && if [ "${USE_GIT_CLONE}" = "true" ]; then \
        # Git clone approach
        git clone --depth 1 -b ${GDAL_BRANCH} https://github.com/${GDAL_REPOSITORY}.git gdal-clone \
        && cp -r gdal-clone/* gdal/ \
        && rm -rf gdal-clone; \
    else \
        # Release download approach
        if test "${GDAL_VERSION}" = "master"; then \
            export GDAL_VERSION=$(curl -Ls https://api.github.com/repos/${GDAL_REPOSITORY}/commits/HEAD -H "Accept: application/vnd.github.VERSION.sha"); \
            export GDAL_RELEASE_DATE=$(date "+%Y%m%d"); \
        fi \
        && if test "x${GDAL_BUILD_IS_RELEASE:-}" = "x"; then \
            export GDAL_SHA1SUM=${GDAL_VERSION}; \
        fi \
        && curl -L -fsS https://github.com/${GDAL_REPOSITORY}/releases/download/v${GDAL_VERSION}/gdal-${GDAL_VERSION}.tar.gz \
            | tar xz -C gdal --strip-components=1; \
    fi \
    && cd gdal \
    && if test "${RSYNC_REMOTE:-}" != ""; then \
        echo "Downloading cache..."; \
        rsync -ra ${RSYNC_REMOTE}/gdal/${GCC_ARCH}/ $HOME/.cache/; \
        echo "Finished"; \
    fi \
    && if [ -n "${WITH_CCACHE:-}" ]; then \
        # Little trick to avoid issues with Python bindings
        printf "#!/bin/sh\nccache %s-linux-gnu-gcc \$*" "${GCC_ARCH}" > ccache_gcc.sh; \
        chmod +x ccache_gcc.sh; \
        printf "#!/bin/sh\nccache %s-linux-gnu-g++ \$*" "${GCC_ARCH}" > ccache_g++.sh; \
        chmod +x ccache_g++.sh; \
        export CC=$PWD/ccache_gcc.sh; \
        export CXX=$PWD/ccache_g++.sh; \
        ccache -M 1G; \
    fi

RUN echo "root"  \
    && ls \
    && cd gdal \
    && mkdir build \
    && cd build \
    && echo "build"  \
    && ls \
    && echo "teigha"  \
    && ls ../../${TEIGHA_ROOT}/ \
    && echo "teiga lib"  \
    && ls ../../${TEIGHA_ROOT}/lib \
    # -Wno-psabi avoid 'note: parameter passing for argument of type 'std::pair<double, double>' when C++17 is enabled changed to match C++14 in GCC 10.1' on arm64
    && CFLAGS='-DPROJ_RENAME_SYMBOLS -O2' CXXFLAGS='-DPROJ_RENAME_SYMBOLS -DPROJ_INTERNAL_CPP_NAMESPACE -O2 -Wno-psabi' \
       cmake .. \
        -G Ninja \
        -DODBC=ON \
        -DCMAKE_INSTALL_PREFIX=/usr \
        -DGDAL_FIND_PACKAGE_PROJ_MODE=MODULE \
        -DPROJ_INCLUDE_DIR="/build${PROJ_INSTALL_PREFIX-/usr/local}/include" \
        -DPROJ_LIBRARY="/build${PROJ_INSTALL_PREFIX-/usr/local}/lib/libinternalproj.so" \
        -DGDAL_USE_TIFF_INTERNAL=ON \
        -DGDAL_USE_GEOTIFF_INTERNAL=ON \
        -DBUILD_TESTING=OFF \
        -DTEIGHA_ROOT="/../../${TEIGHA_ROOT}"  \
        -DTEIGHA_PLATFORM="lnxX64_13dll"  \
        -DTEIGHA_ACTIVATION_FILE_DIRECTORY="/../../${TEIGHA_ACTIVATION_FILE_DIRECTORY}"

RUN cd gdal \
    && cd build \
    && ninja -j$(nproc) \
    && DESTDIR="/build" ninja install \
    && cd .. \
    && if test "${RSYNC_REMOTE:-}" != ""; then \
        echo "Uploading cache..."; \
        rsync -ra --delete $HOME/.cache/ ${RSYNC_REMOTE}/gdal/${GCC_ARCH}/; \
        echo "Finished"; \
    fi \
    && if [ -n "${WITH_CCACHE:-}" ]; then \
        ccache -s; \
        unset CC; \
        unset CXX; \
    fi

RUN rm -rf gdal \
    && mkdir -p /build_gdal_python/usr/lib \
    && mkdir -p /build_gdal_python/usr/bin \
    && mkdir -p /build_gdal_version_changing/usr/include \
    && ls /build/usr/lib/ \
    && mv /build/usr/lib/python*            /build_gdal_python/usr/lib \
    && mv /build/usr/lib                    /build_gdal_version_changing/usr \
    && mv /build/usr/include/gdal_version.h /build_gdal_version_changing/usr/include \
    && mv /build/usr/bin/*.py               /build_gdal_python/usr/bin \
    && mv /build/usr/bin                    /build_gdal_version_changing/usr \
    && for i in /build_gdal_version_changing/usr/lib/${GCC_ARCH}-linux-gnu/*; do ${GCC_ARCH}-linux-gnu-strip -s $i 2>/dev/null || /bin/true; done \
    && for i in /build_gdal_python/usr/lib/python3/dist-packages/osgeo/*.so; do ${GCC_ARCH}-linux-gnu-strip -s $i 2>/dev/null || /bin/true; done \
    && for i in /build_gdal_version_changing/usr/bin/*; do ${GCC_ARCH}-linux-gnu-strip -s $i 2>/dev/null || /bin/true; done

# Build final image
FROM $TARGET_BASE_IMAGE AS runner

USER root
RUN date

# Update distro
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
    --mount=type=cache,target=/var/lib/apt,sharing=locked \
    export DEBIAN_FRONTEND=noninteractive \
    && apt-get update -y \
    && apt-get upgrade -y \
    # PROJ dependencies
    && apt-get install -y --no-install-recommends \
        libsqlite3-0 libtiff6 libcurl4 \
        curl unzip ca-certificates \
    # GDAL dependencies
    && apt-get install -y --no-install-recommends \
        python3-numpy libpython3.12 \
        libjpeg-turbo8 libgeos3.12.1 libgeos-c1v5 \
        libexpat1 \
        libxerces-c3.2 \
        libwebp7 libpng16-16 \
        libdeflate0 \
        libzstd1 bash libpq5 libssl3 libopenjp2-7 libspatialite8 \
        # pil for antialias option of gdal2tiles
        python3-pil \
    && apt-get install -y python-is-python3

# Order layers starting with less frequently varying ones
# Only used for custom libopenjp2
# COPY --from=builder  /build_thirdparty/usr/ /usr/

COPY --from=builder  /build_projgrids/usr/ /usr/

ARG PROJ_INSTALL_PREFIX
COPY --from=builder  /build${PROJ_INSTALL_PREFIX}/share/proj/ ${PROJ_INSTALL_PREFIX}/share/proj/
COPY --from=builder  /build${PROJ_INSTALL_PREFIX}/include/ ${PROJ_INSTALL_PREFIX}/include/
COPY --from=builder  /build${PROJ_INSTALL_PREFIX}/bin/ ${PROJ_INSTALL_PREFIX}/bin/
COPY --from=builder  /build${PROJ_INSTALL_PREFIX}/lib/ ${PROJ_INSTALL_PREFIX}/lib/

COPY --from=builder  /build/usr/share/gdal/ /usr/share/gdal/
COPY --from=builder  /build/usr/include/ /usr/include/
COPY --from=builder  /build_gdal_python/usr/ /usr/
COPY --from=builder  /build_gdal_version_changing/usr/ /usr/
COPY --from=builder /oda_directory/ /usr/local/bin/oda_directory/

ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/bin/oda_directory/bin/lnxX64_13dll
ENV CS_MAP_DIR=/usr/local/bin/oda_directory/CSDict/CSDicts

RUN ldconfig

Comment on lines +28 to +29
"Cannot load OdSpatialReference.tx The setup of "
"MENTOR_DICTIONARY_PATH (or CS_MAP_DIR) is probably missing");
Copy link
Member

@rcoup rcoup Jan 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need an explanation in the DWG driver docs

  • what is this, and why it doesn't match the usual GDAL/PROJ-supported CRS definitions
  • where to get this data. Either ODA...Downloads...Thirdparty Libraries; or https://trac.osgeo.org/csmap/ with a FTBFS patch
  • and what environment variable to set

(though I have no idea what MENTOR_DICTIONARY_PATH is??)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complete ODA support answer (deleting names, timestamps and so on) :

ODA member :

Hello,

To correctly work with OdDbGeoData you must set the MENTOR_DICTIONARY_PATH (or CS_MAP_DIR) environment variable to the path where your csmap library dictionaries are stored.

e.g., in my case it is: C:\ODA\CSDicts

see also: https://docs.opendesign.com/td/db_geo_functionality.html

About “Layer SRS WKT: (unknown)” in GDAL: I don't know how exactly GDAL tries to get WKT string, but you can do it in a next way:

OdDbObjectId objId;
oddbGetGeoDataObjId(pDb, objId);
OdDbGeoDataPtr pGeoData = objId.openObject();

//NOTE: pGeoData->coordinateSystem() can be represented in:
// XML (special autocad representation), WKT or coordinate system id (e.g. "WORLD-MERCATOR")
//Therefore OdDbGeoCoordinateSystem must be used for parsing.

OdDbGeoCoordinateSystemPtr pCS;
OdDbGeoCoordinateSystem::create(pGeoData->coordinateSystem(), pCS);

OdString sWktId;
pCS->getWktRepresentation(sWktId);
e.g. on your file “04-MONTAIGU.dwg“ as result I got next WKT string:

PROJCS["Lambert93",GEOGCS["LL-RGF93",DATUM["RGF93",SPHEROID["GRS1980",6378137.000,298.25722210]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["false_easting",700000.000],PARAMETER["false_northing",6600000.000],PARAMETER["central_meridian",3.00000000000000],PARAMETER["latitude_of_origin",46.50000000000000],PARAMETER["standard_parallel_1",44.00000000000000],PARAMETER["standard_parallel_2",49.00000000000000],UNIT["Meter",1.00000000000000]]
and pGeoData->coordinateSystem() string is represented by xml:

<?xml version="1.0" encoding="UTF-16" standalone="no" ?>
<Dictionary version="1.0" xmlns="http://www.osgeo.org/mapguide/coordinatesystem">
    <ProjectedCoordinateSystem id="Lambert93">
        <Name>Lambert93</Name>
        <Description>Lambert-93 with RGF93 datum, whole country</Description>
        <Authority>IGN Paris</Authority>
        <AdditionalInformation>
            <ParameterItem type="CsMap">
                <Key>CSQuadrantSimplified</Key>
                <IntegerValue>1</IntegerValue>
            </ParameterItem>
        </AdditionalInformation>
        <DomainOfValidity>
            <Extent>
                <GeographicElement>
                    <GeographicBoundingBox>
                        <WestBoundLongitude>-10.8666666666667</WestBoundLongitude>
                        <EastBoundLongitude>11.3833333333333</EastBoundLongitude>
                        <SouthBoundLatitude>40.6166666666667</SouthBoundLatitude>
                        <NorthBoundLatitude>52.0833333333333</NorthBoundLatitude>
                    </GeographicBoundingBox>
                </GeographicElement>
            </Extent>
        </DomainOfValidity>
        <DatumId>RGF93</DatumId>
        <Axis uom="METER">
            <CoordinateSystemAxis>
                <AxisOrder>1</AxisOrder>
                <AxisName>Easting</AxisName>
                <AxisAbbreviation>E</AxisAbbreviation>
                <AxisDirection>east</AxisDirection>
            </CoordinateSystemAxis>
            <CoordinateSystemAxis>
                <AxisOrder>2</AxisOrder>
                <AxisName>Northing</AxisName>
                <AxisAbbreviation>N</AxisAbbreviation>
                <AxisDirection>north</AxisDirection>
            </CoordinateSystemAxis>
        </Axis>
        <Conversion>
            <Projection>
                <OperationMethodId>Lambert Conic Conformal (2SP)</OperationMethodId>
                <ParameterValue>
                    <OperationParameterId>Latitude of 1st standard parallel</OperationParameterId>
                    <Value uom="degree">44</Value>
                </ParameterValue>
                <ParameterValue>
                    <OperationParameterId>Latitude of 2nd standard parallel</OperationParameterId>
                    <Value uom="degree">49</Value>
                </ParameterValue>
                <ParameterValue>
                    <OperationParameterId>Longitude of false origin</OperationParameterId>
                    <Value uom="degree">3</Value>
                </ParameterValue>
                <ParameterValue>
                    <OperationParameterId>Latitude of false origin</OperationParameterId>
                    <Value uom="degree">46.5</Value>
                </ParameterValue>
                <ParameterValue>
                    <OperationParameterId>False easting</OperationParameterId>
                    <Value uom="METER">700000</Value>
                </ParameterValue>
                <ParameterValue>
                    <OperationParameterId>False northing</OperationParameterId>
                    <Value uom="METER">6600000</Value>
                </ParameterValue>
            </Projection>
        </Conversion>
    </ProjectedCoordinateSystem>
    <Alias id="2154" type="CoordinateSystem">
        <ObjectId>Lambert93</ObjectId>
        <Namespace>EPSG Code</Namespace>
    </Alias>
    <GeodeticDatum id="RGF93">
        <Name>RGF93</Name>
        <Description>Reseau Geodesique Francais, RGF93</Description>
        <Authority>IGN Paris</Authority>
        <PrimeMeridianId>Greenwich</PrimeMeridianId>
        <EllipsoidId>GRS1980</EllipsoidId>
    </GeodeticDatum>
    <Alias id="6171" type="Datum">
        <ObjectId>RGF93</ObjectId>
        <Namespace>EPSG Code</Namespace>
    </Alias>
    <Ellipsoid id="GRS1980">
        <Name>GRS1980</Name>
        <Description>Geodetic Reference System of 1980</Description>
        <Authority>Stem, L.E., Jan 1989, State Plane Coordinate System of 1983</Authority>
        <SemiMajorAxis uom="meter">6378137</SemiMajorAxis>
        <SecondDefiningParameter>
            <SemiMinorAxis uom="meter">6356752.31414035</SemiMinorAxis>
        </SecondDefiningParameter>
    </Ellipsoid>
    <Alias id="7019" type="Ellipsoid">
        <ObjectId>GRS1980</ObjectId>
        <Namespace>EPSG Code</Namespace>
    </Alias>
    <Transformation id="RGF93_to_WGS84">
        <Name>RGF93_to_WGS84</Name>
        <Description>Reseau Geodesique Francais, RGF93</Description>
        <Authority>IGN Paris</Authority>
        <DomainOfValidity>
            <Extent>
                <GeographicElement>
                    <GeographicBoundingBox>
                        <WestBoundLongitude>-12.11</WestBoundLongitude>
                        <EastBoundLongitude>12.79</EastBoundLongitude>
                        <SouthBoundLatitude>39.885</SouthBoundLatitude>
                        <NorthBoundLatitude>52.835</NorthBoundLatitude>
                    </GeographicBoundingBox>
                </GeographicElement>
            </Extent>
        </DomainOfValidity>
        <OperationVersion>1</OperationVersion>
        <CoordinateOperationAccuracy>
            <Accuracy uom="meter">1</Accuracy>
        </CoordinateOperationAccuracy>
        <SourceDatumId>RGF93</SourceDatumId>
        <TargetDatumId>WGS84</TargetDatumId>
        <IsReversible>true</IsReversible>
        <OperationMethod>
            <OperationMethodId>Null transformation (no coordinate change)</OperationMethodId>
        </OperationMethod>
    </Transformation>
    <Alias id="1671" type="Transformation">
        <ObjectId>RGF93_to_WGS84</ObjectId>
        <Namespace>EPSG Code</Namespace>
    </Alias>
</Dictionary>
Best Regards

Me :

Hello !

Thanks for pointing out the CSDicts; they’re now installed and the env variable is set.
If you have a simple test for me to test if everything's ok; it’ll be great (like a simple script and a file to see if everything’s ok).

I’ve been working with gdal members to get gdal + oda working.
Here are the associated issues and PR :

https://github.com/OSGeo/gdal/issues/11672
https://github.com/OSGeo/gdal/pull/11694

as the issue state; I’m stuck at :

OdDbDatabasePtr pDb = poDS->GetDB();
if (pDb.isNull())
{
    CPLError(CE_Failure, CPLE_AppDefined, "Invalid DWG database pointer.");
    return;
}

// Get the GeoData object ID from the database
OdDbObjectId objId;
oddbGetGeoDataObjId(pDb, objId);
if (objId.isNull())
{
    CPLError(CE_Warning, CPLE_AppDefined, "No GeoData found in DWG file.");
    return;
}

// Open the GeoData object
OdDbGeoDataPtr pGeoData = objId.openObject();
if (pGeoData.isNull())
{
    CPLError(CE_Failure, CPLE_AppDefined, "Failed to open GeoData object.");
    return;
}

// Extract coordinate system string
OdDbGeoCoordinateSystemPtr pCS;
OdDbGeoCoordinateSystem::create(pGeoData->coordinateSystem(), pCS);
if (pCS.isNull())
{
    CPLError(CE_Failure, CPLE_AppDefined, "Failed to create GeoCoordinateSystem.");
    return;
}

// Retrieve the WKT representation of the coordinate system
OdString sWktId;
if (pCS->getWktRepresentation(sWktId) == eOk)
{
    // Store the WKT in OGR layer's spatial reference
    OGRSpatialReference *poSRS = new OGRSpatialReference();
    if (poSRS->importFromWkt(TextUnescape(sWktId, false)) == OGRERR_NONE)
    {
        poFeatureDefn->SetGeomType(wkbUnknown);  // presumably, as I think DWG layers might fix geometries of different kind
        poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
        poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
        poSRS->Release();
    }
    else
    {
        CPLError(CE_Warning, CPLE_AppDefined, "Invalid WKT format extracted.");
    }
}else
{
    CPLError(CE_Warning, CPLE_AppDefined, "Cannot get WKT Representation ");
}
at line 27 ; I cannot create the OdDbGeoCoordinateSystemPtr.
If you have any ways to debug the pGeoData and see what would be wrong and why the create method return a null object.

If you want to join the PR and issue, you’re welcome !

ODA member:

Hello,

I forgot to add that it is also needed to load module that enables PE (Protocol Extension) for GeoData-related classes (OdGeoData.tx):

odrxDynamicLinker()->loadModule(OdGeoDataModuleName, false);
About test case - you can try to execute your code with string above and file you have attached.

Just in case: here is the list of current geo-related modules dependencies (how they are loaded):

TD_DbCore.dll - stores OdDbGeoData class and interfaces for OdDbGeoCoordinateSystem related classes.
GeoCommands.tx - set of AutoCAD-like commands for working with GEO.
OdDbGeoMapPE.tx - implementation of online maps functionality.
AcGeolocationObj.tx - implementation of OdDbGeoMap (OdDbRasterImage) class.
OdGeoData.tx - implementation of GEO Protocol Extension (PE) interfaces related to Coordinate Systems.
OdSpatialReference.tx - part of Kernel SDK.
But for your needs you would need to load only OdGeoData.tx. It will try to load OdSpatialReference.tx. If your env variable setup is incorrect in some way, you will get exeption like "csmap initialization failed: OdSpatialReferenceModule is not loaded.". If there will be no such message - setup is ok and all should work.

Me :

Hello,

Added odrxDynamicLinker()->loadModule(OdGeoDataModuleName, false); to gdal usage of ODA ; please see here : https://github.com/OSGeo/gdal/pull/11694

Yet, same thing happens that is seen here : https://github.com/OSGeo/gdal/issues/11672

OdDbGeoCoordinateSystemPtr pCS;
OdDbGeoCoordinateSystem::create(pGeoData->coordinateSystem(), pCS);
if (pCS.isNull())
{
    CPLError(CE_Failure, CPLE_AppDefined, "Failed to create GeoCoordinateSystem.");
    return;
}
Gives error : ERROR 1: Failed to create GeoCoordinateSystem.

Please look at issue and pull request; as it is not a standard ODA env but gdal compiled with ODA.

ODA Member :

Hello,

So OdGeoData.tx module wasn’t loaded? That means that MENTOR_DICTIONARY_PATH (or CS_MAP_DIR (old name)) env variable setup is missing…

Your can try to load OdSpatialReference.tx module and check result for null:

OdRxModulePtr pOdSpatialReferenceModule = odrxDynamicLinker()->loadModule(OdSpatialReferenceModuleName);
if (pOdSpatialReferenceModule.isNull())
{
  // The setup of MENTOR_DICTIONARY_PATH (or CS_MAP_DIR) is missing.
}
Best Regards,

Me :

Thanks it was that; env variable was not correctly set to the right folder. Thanks for pointing out. The issue is now resolved.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So now you have as much as info as I have ; so either is sufficient to write some docs about it or we should double check with ODA members. What do you think ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the cs-map codebase only talks about CS_MAP_DIR, and the ODA docs mention the two as alternatives, so I'd be tempted to just document the former. Google suggests MENTOR_DICTIONARY_PATH is part of MapGuide, another Autodesk thing.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rcoup updated the documentation, feel free to review

@rcoup
Copy link
Member

rcoup commented Jan 21, 2025

While you're at it, is it worth adding CRS writing too? 😉

https://gist.github.com/rcoup/7e712c83179264ed49e6977ef0203207 has some sample code & associated gotchas.

Nevermind, I thought the GDAL driver did writing. That's a future project!

@rouault
Copy link
Member

rouault commented Jan 22, 2025

@Pourfex
https://readthedocs.org/projects/gdal/builds/26922126/:

/home/docs/checkouts/readthedocs.org/user_builds/gdal/checkouts/11694/doc/source/drivers/vector/oda.rst:119: WARNING: Inline interpreted text or phrase reference start-string without end-string. [docutils]
/home/docs/checkouts/readthedocs.org/user_builds/gdal/checkouts/11694/doc/source/drivers/vector/oda.rst:120: WARNING: Inline interpreted text or phrase reference start-string without end-string. [docutils]

@rouault
Copy link
Member

rouault commented Jan 22, 2025

@Pourfex We also have unit tests in autotest/ogr/ogr_dwg.py . It would be great if they could be enhanced, with a new test file (redistribuable under the MIT license of GDAL), as small as possible, added in autotest/ogr/data/cad/

Copy link

The GDAL project highly values your contribution and would love to see this work merged! Unfortunately this PR has not had any activity in the last 28 days and is being automatically marked as "stale". If you think this pull request should be merged, please check

  • that all unit tests are passing

  • that all comments by reviewers have been addressed

  • that there is enough information for reviewers, in particular link
    to any issues which this pull request fixes

  • that you have written unit tests where possible
    In case you should have any uncertainty, please leave a comment and we will be happy to help you proceed with this pull request.
    If there is no further activity on this pull request, it will be closed in 2 weeks.

@github-actions github-actions bot added the stale label Feb 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants