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 C++ API to transform coordinate and a C++ quickstart #3705

Merged
merged 6 commits into from
Apr 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,11 @@ if(BUILD_TESTING)
add_subdirectory(test)
endif()

option(BUILD_EXAMPLES "Whether to build example programs" OFF)
if(BUILD_EXAMPLES)
add_subdirectory(examples)
endif()

set(docfiles COPYING NEWS AUTHORS)
install(FILES ${docfiles}
DESTINATION ${CMAKE_INSTALL_DOCDIR})
Expand Down Expand Up @@ -367,7 +372,6 @@ set(CPACK_SOURCE_IGNORE_FILES
/Dockerfile
/docs/
/Doxyfile
/examples/
/HOWTO-RELEASE
/m4/lt*
/m4/libtool*
Expand Down
2 changes: 1 addition & 1 deletion docs/source/development/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ PROJ project or using the library in their own software.

dev_practices
quickstart
transformations
quickstart_cpp
errorhandling
reference/index
cmake
Expand Down
24 changes: 12 additions & 12 deletions docs/source/development/quickstart.rst
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
.. _dev_quickstart:

================================================================================
Quick start
Quick start for C API usage
================================================================================

This is a short introduction to the PROJ API. In the following section
This is a short introduction to the PROJ C API. In the following section
we create two simple programs that illustrate how to transform points
between two different coordinate systems, and how to convert between
projected and geodetic (geographic) coordinates for a single
Expand All @@ -19,15 +19,15 @@ file. Here :file:`stdio.h` is also included so we can print some text to the scr

.. literalinclude:: ../../../examples/pj_obs_api_mini_demo.c
:language: c
:lines: 39-40
:lines: 10-11

Let's declare a few variables that'll be used later in the program. Each variable
will be discussed below.
See the :doc:`reference for more info on data types <reference/datatypes>`.

.. literalinclude:: ../../../examples/pj_obs_api_mini_demo.c
:language: c
:lines: 43-46
:lines: 14-17
:dedent: 4

For use in multi-threaded programs the :c:type:`PJ_CONTEXT` threading-context
Expand All @@ -36,15 +36,15 @@ completeness we demonstrate its use here.

.. literalinclude:: ../../../examples/pj_obs_api_mini_demo.c
:language: c
:lines: 50
:lines: 21
:dedent: 4

Next we create the :c:type:`PJ` transformation object ``P`` with the function
:c:func:`proj_create_crs_to_crs`.

.. literalinclude:: ../../../examples/pj_obs_api_mini_demo.c
:language: c
:lines: 52-60
:lines: 23-31
:dedent: 4

Here we have set up a transformation from geographic coordinates to UTM zone
Expand Down Expand Up @@ -141,7 +141,7 @@ most projected CRS.

.. literalinclude:: ../../../examples/pj_obs_api_mini_demo.c
:language: c
:lines: 65-71
:lines: 36-42
:dedent: 4

Next we create a :c:type:`PJ_COORD` coordinate object, using the function
Expand All @@ -155,15 +155,15 @@ longitude followed by latitude, and the units are degrees.

.. literalinclude:: ../../../examples/pj_obs_api_mini_demo.c
:language: c
:lines: 76
:lines: 47
:dedent: 4

Now we are ready to transform the coordinate into UTM zone 32, using the
function :c:func:`proj_trans`.

.. literalinclude:: ../../../examples/pj_obs_api_mini_demo.c
:language: c
:lines: 79-80
:lines: 50-51
:dedent: 4

:c:func:`proj_trans` takes as its arguments:
Expand All @@ -185,15 +185,15 @@ geographic) as follows:

.. literalinclude:: ../../../examples/pj_obs_api_mini_demo.c
:language: c
:lines: 82-83
:lines: 53-54
:dedent: 4

Before ending the program, we need to release the memory allocated to our
objects:

.. literalinclude:: ../../../examples/pj_obs_api_mini_demo.c
:language: c
:lines: 86-87
:lines: 57-58
:dedent: 4


Expand All @@ -202,7 +202,7 @@ A complete compilable version of the example code can be seen below:
.. literalinclude:: ../../../examples/pj_obs_api_mini_demo.c
:language: c
:linenos:
:lines: 39-
:lines: 10-

The following example illustrates how to convert between a CRS and
geodetic coordinates for that CRS.
Expand Down
160 changes: 160 additions & 0 deletions docs/source/development/quickstart_cpp.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
.. _dev_quickstart_cpp:

================================================================================
Quick start for C++ API usage
================================================================================

This is a short introduction to the PROJ :ref:`C++ API <cpp>`. In the following section
we create a simple program that illustrate how to transform points
between two different coordinate systems.

Note: the essential osgeo::proj::operation::CoordinateOperation::coordinateTransformer()
method was only added in PROJ 9.3. For earlier versions, coordinate operations
must be exported as a PROJ string with the osgeo::proj::operation::CoordinateOperation::exportToPROJ() method, and
passed to :c:func:`proj_create` to instance a PJ* object to use with :c:func:`proj_trans`
(cf the :ref:`dev_quickstart`). The use of the C API :c:func:`proj_create_crs_to_crs`
might still be easier to let PROJ automatically select the "best" coordinate
operation when several ones are possible, as the corresponding automation is not
currently available in the C++ API.

Before the PROJ API can be used it is necessary to include the various header
files:

.. literalinclude:: ../../../examples/EPSG_4326_to_32631.cpp
:language: c++
:lines: 9-18

For convenience, we also declare using a few namespaces:

.. literalinclude:: ../../../examples/EPSG_4326_to_32631.cpp
:language: c++
:lines: 20-23

We start by creating a database context (:cpp:class:`osgeo::proj::io::DatabaseContext`)
with the default settings to find the PROJ database.

.. literalinclude:: ../../../examples/EPSG_4326_to_32631.cpp
:language: c++
:lines: 26
:dedent: 4

We then instantiate a generic authority factory (:cpp:class:`osgeo::proj::io::AuthorityFactory`),
that is not tied to a particular authority, to be able to get transformations
registered by different authorities. This can only be used for a
:cpp:class:`osgeo::proj::operation::CoordinateOperationContext`, and not to
instantiate objects of the database which are all tied to a non-generic authority.

.. literalinclude:: ../../../examples/EPSG_4326_to_32631.cpp
:language: c++
:lines: 31

We create a coordinate operation context, that can be customized to ammend
the way coordinate operations are computed. Here we ask for default settings,
as we have a coordinate operation that just involves a "simple" map projection
in the same datum.

.. literalinclude:: ../../../examples/EPSG_4326_to_32631.cpp
:language: c++
:lines: 36-37
:dedent: 4

We instantiate a authority factory for EPSG related objects.

.. literalinclude:: ../../../examples/EPSG_4326_to_32631.cpp
:language: c++
:lines: 40
:dedent: 4

We instantiate the source CRS from its code: 4326, for WGS 84 latitude/longitude.

.. literalinclude:: ../../../examples/EPSG_4326_to_32631.cpp
:language: c++
:lines: 43
:dedent: 4

We instantiate the source CRS from its PROJ.4 string (it would be possible
to instantiate it from its 32631 code, similarly to above), and cast the
generic :cpp:class:`osgeo::proj::util::BaseObject` to the
:cpp:class:`osgeo::proj::crs::CRS` class required later.

.. literalinclude:: ../../../examples/EPSG_4326_to_32631.cpp
:language: c++
:lines: 49-51
:dedent: 4

.. warning::

The use of PROJ strings to describe a CRS is not recommended. One of the
main weaknesses of PROJ strings is their inability to describe a geodetic
datum, other than the few ones hardcoded in the ``+datum`` parameter.

We ask for the list of operations available to transform from the source
to the target CRS with the :cpp:func:`osgeo::proj::operation::CoordinateOperationFactory::createOperations`
method.

.. literalinclude:: ../../../examples/EPSG_4326_to_32631.cpp
:language: c++
:lines: 55-56
:dedent: 4

We check that we got a non-empty list of operations. The list is sorted from
the most relevant to the less relevant one. Cf :ref:`operations_computation_filtering`
for more details on the sorting of those operations. For a transformation
between a projected CRS and its base CRS, like we do here, there will be only
one operation.

.. literalinclude:: ../../../examples/EPSG_4326_to_32631.cpp
:language: c++
:lines: 65
:dedent: 4

We create an execution context (must only be used by one thread at a time)
with the :c:func:`proj_context_create` function.

.. literalinclude:: ../../../examples/EPSG_4326_to_32631.cpp
:language: c++
:lines: 68
:dedent: 4

We create a coordinate transformer (:cpp:class:`osgeo::proj::operation::CoordinateTransformer`)
from the first operation of the list:

.. literalinclude:: ../../../examples/EPSG_4326_to_32631.cpp
:language: c++
:lines: 71
:dedent: 4

We can now transform a point with the :cpp:func:`osgeo::proj::operation::CoordinateTransformer::transform`
method. Note that the the expected input values should be passed in the order
and the unit of the successive axis of the input CRS. Similarly the values
returned in the v[] array of the output PJ_COORD are in the order and the unit
of the successive axis of the output CRS. For coordinate operations involving a
time-dependent operation, coord.v[3] is the decimal year of the coordinate epoch
of the input (or HUGE_VAL to indicate none).

.. literalinclude:: ../../../examples/EPSG_4326_to_32631.cpp
:language: c++
:lines: 74-80
:dedent: 4

and output the result:

.. literalinclude:: ../../../examples/EPSG_4326_to_32631.cpp
:language: c++
:lines: 83-85
:dedent: 4

We need to clean up the PJ_CONTEXT handle before exiting with the
:c:func:`proj_context_destroy` function.

.. literalinclude:: ../../../examples/EPSG_4326_to_32631.cpp
:language: c++
:lines: 88
:dedent: 4

A complete compilable version of the example code can be seen below:

.. literalinclude:: ../../../examples/EPSG_4326_to_32631.cpp
:language: c++
:linenos:
:lines: 9-
7 changes: 0 additions & 7 deletions docs/source/development/transformations.rst

This file was deleted.

2 changes: 2 additions & 0 deletions docs/source/operations/operations_computation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ does not cover the area of use of the 2 CRSs, a
'Ballpark geographic offset from NAD27 to NAD83' operation is synthetized by PROJ
(see :term:`Ballpark transformation`)

.. _operations_computation_filtering:

Filtering and sorting of coordinate operations
----------------------------------------------

Expand Down
8 changes: 8 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
add_executable(pj_obs_api_mini_demo pj_obs_api_mini_demo.c)
target_link_libraries(pj_obs_api_mini_demo PRIVATE ${PROJ_LIBRARIES})

add_executable(crs_to_geodetic crs_to_geodetic.c)
target_link_libraries(crs_to_geodetic PRIVATE ${PROJ_LIBRARIES})

add_executable(EPSG_4326_to_32631 EPSG_4326_to_32631.cpp)
target_link_libraries(EPSG_4326_to_32631 PRIVATE ${PROJ_LIBRARIES})
Loading