Quick start

This is a short introduction to the PROJ API. In the following section we create a simple program that transforms a geodetic coordinate to UTM and back again. The program is explained a few lines at a time. The complete program can be seen at the end of the section.

See the following sections for more in-depth descriptions of different parts of the PROJ API or consult the API reference for specifics.

Before the PROJ API can be used it is necessary to include the proj.h header file. Here stdio.h is also included so we can print some text to the screen:

#include <stdio.h>
#include <proj.h>

Let’s declare a few variables that’ll be used later in the program. Each variable will be discussed below. See the reference for more info on data types.

PJ_CONTEXT *C;
PJ *P;
PJ* P_for_GIS;
PJ_COORD a, b;

For use in multi-threaded programs the PJ_CONTEXT threading-context is used. In this particular example it is not needed, but for the sake of completeness it created here. The section on threads discusses this in detail.

C = proj_context_create();

Next we create the PJ transformation object P with the function proj_create_crs_to_crs(). proj_create_crs_to_crs() takes the threading context C created above, a string that describes the source coordinate reference system (CRS), a string that describes the target CRS and an optional description of the area of use. The strings for the source or target CRS may be PROJ strings (+proj=longlat +datum=WGS84), CRS identified by their code (EPSG:4326 or urn:ogc:def:crs:EPSG::4326) or by a well-known text (WKT) string (

GEOGCRS["WGS 84",
    DATUM["World Geodetic System 1984",
        ELLIPSOID["WGS 84",6378137,298.257223563,
            LENGTHUNIT["metre",1]]],
    PRIMEM["Greenwich",0,
        ANGLEUNIT["degree",0.0174532925199433]],
    CS[ellipsoidal,2],
        AXIS["geodetic latitude (Lat)",north,
            ORDER[1],
            ANGLEUNIT["degree",0.0174532925199433]],
        AXIS["geodetic longitude (Lon)",east,
            ORDER[2],
            ANGLEUNIT["degree",0.0174532925199433]],
    USAGE[
        SCOPE["unknown"],
        AREA["World"],
        BBOX[-90,-180,90,180]],
    ID["EPSG",4326]]

). The use of PROJ strings to describe a CRS is considered as legacy (one of the main weakness of PROJ strings is their inability to describe a geodetic datum, other than the few ones hardcoded in the +datum parameter). Here we transform from geographic coordinates to UTM zone 32N. It is recommended to create one threading-context per thread used by the program. This ensures that all PJ objects created in the same context will be sharing resources such as error-numbers and loaded grids. In case the creation of the PJ object fails an error message is displayed and the program returns. See Error handling for further details.

P = proj_create_crs_to_crs (C,
                            "EPSG:4326",
                            "+proj=utm +zone=32 +datum=WGS84", /* or EPSG:32632 */
                            NULL);

if (0==P) {
    fprintf(stderr, "Oops\n");
    return 1;
}

proj_create_crs_to_crs() creates a transformation object, which accepts coordinates expressed in the units and axis order of the definition of the source CRS, and return transformed coordinates in the units and axis order of the definition of the target CRS. For almost most geographic CRS, the units will be in most cases degrees (in rare cases, such as EPSG:4807 / NTF (Paris), this can be grads). For geographic CRS defined by the EPSG authority, the order of coordinates is latitude first, longitude second. When using a PROJ string, on contrary the order will be longitude first, latitude second. For projected CRS, the units may vary (metre, us-foot, etc..). For projected CRS defined by the EPSG authority, and with EAST / NORTH directions, the order might be easting first, northing second, or the reverse. When using a PROJ string, the order will be easting first, northing second, except if the +axis parameter modifies it.

If for the needs of your software, you want a uniform axis order (and thus do not care about axis order mandated by the authority defining the CRS), the proj_normalize_for_visualization() function can be used to modify the PJ* object returned by proj_create_crs_to_crs() so that it accepts as input and returns as output coordinates using the traditional GIS order, that is longitude, latitude (followed by elevation, time) for geographic CRS and easting, northing for most projected CRS.

P_for_GIS = proj_normalize_for_visualization(C, P);
if( 0 == P_for_GIS )  {
    fprintf(stderr, "Oops\n");
    return 1;
}
proj_destroy(P);
P = P_for_GIS;

PROJ uses its own data structures for handling coordinates. Here we use a PJ_COORD which is easily assigned with the function proj_coord(). When using +proj=longlat, the order of coordinates is longitude, latitude, and values are expressed in degrees. If you used instead a EPSG geographic CRS, like EPSG:4326 (WGS84), it would be latitude, longitude.

a = proj_coord (12, 55, 0, 0);

The coordinate defined above is transformed with proj_trans(). For this a PJ object, a transformation direction (either forward or inverse) and the coordinate is needed. The transformed coordinate is returned in b. Here the forward (PJ_FWD) transformation from geographic to UTM is made.

b = proj_trans (P, PJ_FWD, a);
printf ("easting: %.3f, northing: %.3f\n", b.enu.e, b.enu.n);

The inverse transformation (UTM to geographic) is done similar to above, this time using PJ_INV as the direction.

b = proj_trans (P, PJ_INV, b);
printf ("longitude: %g, latitude: %g\n", b.lp.lam, b.lp.phi);

Before ending the program the allocated memory needs to be released again:

proj_destroy (P);
proj_context_destroy (C); /* may be omitted in the single threaded case */

A complete compilable version of the above can be seen here:

 1#include <stdio.h>
 2#include <proj.h>
 3
 4int main (void) {
 5    PJ_CONTEXT *C;
 6    PJ *P;
 7    PJ* P_for_GIS;
 8    PJ_COORD a, b;
 9
10    /* or you may set C=PJ_DEFAULT_CTX if you are sure you will     */
11    /* use PJ objects from only one thread                          */
12    C = proj_context_create();
13
14    P = proj_create_crs_to_crs (C,
15                                "EPSG:4326",
16                                "+proj=utm +zone=32 +datum=WGS84", /* or EPSG:32632 */
17                                NULL);
18
19    if (0==P) {
20        fprintf(stderr, "Oops\n");
21        return 1;
22    }
23
24    /* This will ensure that the order of coordinates for the input CRS */
25    /* will be longitude, latitude, whereas EPSG:4326 mandates latitude, */
26    /* longitude */
27    P_for_GIS = proj_normalize_for_visualization(C, P);
28    if( 0 == P_for_GIS )  {
29        fprintf(stderr, "Oops\n");
30        return 1;
31    }
32    proj_destroy(P);
33    P = P_for_GIS;
34
35    /* a coordinate union representing Copenhagen: 55d N, 12d E    */
36    /* Given that we have used proj_normalize_for_visualization(), the order of
37    /* coordinates is longitude, latitude, and values are expressed in degrees. */
38    a = proj_coord (12, 55, 0, 0);
39
40    /* transform to UTM zone 32, then back to geographical */
41    b = proj_trans (P, PJ_FWD, a);
42    printf ("easting: %.3f, northing: %.3f\n", b.enu.e, b.enu.n);
43    b = proj_trans (P, PJ_INV, b);
44    printf ("longitude: %g, latitude: %g\n", b.lp.lam, b.lp.phi);
45
46    /* Clean up */
47    proj_destroy (P);
48    proj_context_destroy (C); /* may be omitted in the single threaded case */
49    return 0;
50}