GRASS 8 Programmer's Manual 8.6.0dev(2026)-d2adb3a889
Loading...
Searching...
No Matches
get_proj.c
Go to the documentation of this file.
1/**
2 \file get_proj.c
3
4 \brief GProj library - Functions for re-projecting point data
5
6 \author Original Author unknown, probably Soil Conservation Service,
7 Eric Miller, Paul Kelly, Markus Metz
8
9 SPDX-FileCopyrightText: 2003-2008, 2018 GRASS Development Team
10 SPDX-License-Identifier: GPL-2.0-or-later
11**/
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <ctype.h>
16#include <math.h>
17#include <string.h>
18#include <grass/gis.h>
19#include <grass/gprojects.h>
20#include <grass/glocale.h>
21
22/* Finder function for datum transformation grids */
23#define FINDERFUNC set_proj_share
24#define PERMANENT "PERMANENT"
25#define MAX_PARGS 100
26
27static void alloc_options(char *);
28
29static char *opt_in[MAX_PARGS];
30static int nopt;
31
32/* TODO: rename pj_ to GPJ_ to avoid symbol clash with PROJ lib */
33
34/**
35 * \brief Create a pj_info struct Co-ordinate System definition from a set of
36 * PROJ_INFO / PROJ_UNITS-style key-value pairs
37 *
38 * This function takes a GRASS-style co-ordinate system definition as stored
39 * in the PROJ_INFO and PROJ_UNITS files and processes it to create a pj_info
40 * representation for use in re-projecting with pj_do_proj(). In addition to
41 * the parameters passed to it it may also make reference to the system
42 * ellipse.table and datum.table files if necessary.
43 *
44 * \param info Pointer to a pj_info struct (which must already exist) into
45 * which the co-ordinate system definition will be placed
46 * \param in_proj_keys PROJ_INFO-style key-value pairs
47 * \param in_units_keys PROJ_UNITS-style key-value pairs
48 *
49 * \return -1 on error (unable to initialise PROJ.4)
50 * 2 if "default" 3-parameter datum shift values from datum.table
51 * were used
52 * 3 if an unrecognised datum name was passed on to PROJ.4 (and
53 * initialization was successful)
54 * 1 otherwise
55 **/
56
57int pj_get_kv(struct pj_info *info, const struct Key_Value *in_proj_keys,
58 const struct Key_Value *in_units_keys)
59{
60 const char *str;
61 int i;
62 double a, es, rf;
63 int returnval = 1;
64 char buffa[300], factbuff[50];
65 int deflen;
66 char proj_in[250], *datum, *params;
67
68 PJ *pj;
70
71 proj_in[0] = '\0';
72 info->zone = 0;
73 info->meters = 1.0;
74 info->proj[0] = '\0';
75 info->def = NULL;
76 info->pj = NULL;
77 info->srid = NULL;
78 info->wkt = NULL;
79
80 str = G_find_key_value("meters", in_units_keys);
81 if (str != NULL) {
82 strcpy(factbuff, str);
83 if (strlen(factbuff) > 0)
84 sscanf(factbuff, "%lf", &(info->meters));
85 }
86 str = G_find_key_value("name", in_proj_keys);
87 if (str != NULL) {
88 snprintf(proj_in, sizeof(proj_in), "%s", str);
89 }
90 str = G_find_key_value("proj", in_proj_keys);
91 if (str != NULL) {
92 snprintf(info->proj, sizeof(info->proj), "%s", str);
93 }
94 if (strlen(info->proj) <= 0)
95 snprintf(info->proj, sizeof(info->proj), "ll");
96 str = G_find_key_value("init", in_proj_keys);
97 if (str != NULL) {
98 info->srid = G_store(str);
99 }
100
101 nopt = 0;
102 for (i = 0; i < in_proj_keys->nitems; i++) {
103 /* the name parameter is just for grasses use */
104 if (strcmp(in_proj_keys->key[i], "name") == 0) {
105 continue;
106
107 /* init is here ignored */
108 }
109 else if (strcmp(in_proj_keys->key[i], "init") == 0) {
110 continue;
111
112 /* zone handled separately at end of loop */
113 }
114 else if (strcmp(in_proj_keys->key[i], "zone") == 0) {
115 continue;
116
117 /* Datum and ellipsoid-related parameters will be handled
118 * separately after end of this loop PK */
119 }
120 else if (strcmp(in_proj_keys->key[i], "datum") == 0 ||
121 strcmp(in_proj_keys->key[i], "dx") == 0 ||
122 strcmp(in_proj_keys->key[i], "dy") == 0 ||
123 strcmp(in_proj_keys->key[i], "dz") == 0 ||
124 strcmp(in_proj_keys->key[i], "datumparams") == 0 ||
125 strcmp(in_proj_keys->key[i], "nadgrids") == 0 ||
126 strcmp(in_proj_keys->key[i], "towgs84") == 0 ||
127 strcmp(in_proj_keys->key[i], "ellps") == 0 ||
128 strcmp(in_proj_keys->key[i], "a") == 0 ||
129 strcmp(in_proj_keys->key[i], "b") == 0 ||
130 strcmp(in_proj_keys->key[i], "es") == 0 ||
131 strcmp(in_proj_keys->key[i], "f") == 0 ||
132 strcmp(in_proj_keys->key[i], "rf") == 0) {
133 continue;
134
135 /* PROJ.4 uses longlat instead of ll as 'projection name' */
136 }
137 else if (strcmp(in_proj_keys->key[i], "proj") == 0) {
138 if (strcmp(in_proj_keys->value[i], "ll") == 0)
139 snprintf(buffa, sizeof(buffa), "proj=longlat");
140 else
141 snprintf(buffa, sizeof(buffa), "proj=%s",
142 in_proj_keys->value[i]);
143
144 /* 'One-sided' PROJ.4 flags will have the value in
145 * the key-value pair set to 'defined' and only the
146 * key needs to be passed on. */
147 }
148 else if (strcmp(in_proj_keys->value[i], "defined") == 0)
149 snprintf(buffa, sizeof(buffa), "%s", in_proj_keys->key[i]);
150
151 else
152 snprintf(buffa, sizeof(buffa), "%s=%s", in_proj_keys->key[i],
153 in_proj_keys->value[i]);
154
155 alloc_options(buffa);
156 }
157
158 str = G_find_key_value("zone", in_proj_keys);
159 if (str != NULL) {
160 if (sscanf(str, "%d", &(info->zone)) != 1) {
161 G_fatal_error(_("Invalid zone %s specified"), str);
162 }
163 if (info->zone < 0) {
164
165 /* if zone is negative, write abs(zone) and define south */
166 info->zone = -info->zone;
167
168 if (G_find_key_value("south", in_proj_keys) == NULL) {
169 snprintf(buffa, sizeof(buffa), "south");
170 alloc_options(buffa);
171 }
172 }
173 snprintf(buffa, sizeof(buffa), "zone=%d", info->zone);
174 alloc_options(buffa);
175 }
176
177 if ((GPJ__get_ellipsoid_params(in_proj_keys, &a, &es, &rf) == 0) &&
178 (str = G_find_key_value("ellps", in_proj_keys)) != NULL) {
179 /* Default values were returned but an ellipsoid name not recognised
180 * by GRASS is present---perhaps it will be recognised by
181 * PROJ.4 even though it wasn't by GRASS */
182 snprintf(buffa, sizeof(buffa), "ellps=%s", str);
183 alloc_options(buffa);
184 }
185 else {
186 snprintf(buffa, sizeof(buffa), "a=%.16g", a);
187 alloc_options(buffa);
188 /* Cannot use es directly because the OSRImportFromProj4()
189 * function in OGR only accepts b or rf as the 2nd parameter */
190 if (es == 0)
191 snprintf(buffa, sizeof(buffa), "b=%.16g", a);
192 else
193 snprintf(buffa, sizeof(buffa), "rf=%.16g", rf);
194 alloc_options(buffa);
195 }
196 /* Workaround to stop PROJ reading values from defaults file when
197 * rf (and sometimes ellps) is not specified */
198 if (G_find_key_value("no_defs", in_proj_keys) == NULL) {
199 snprintf(buffa, sizeof(buffa), "no_defs");
200 alloc_options(buffa);
201 }
202
203 /* If datum parameters are present in the PROJ_INFO keys, pass them on */
204 if (GPJ__get_datum_params(in_proj_keys, &datum, &params) == 2) {
205 snprintf(buffa, sizeof(buffa), "%s", params);
206 alloc_options(buffa);
207 G_free(params);
208
209 /* else if a datum name is present take it and look up the parameters
210 * from the datum.table file */
211 }
212 else if (datum != NULL) {
213
214 if (GPJ_get_default_datum_params_by_name(datum, &params) > 0) {
215 snprintf(buffa, sizeof(buffa), "%s", params);
216 alloc_options(buffa);
217 returnval = 2;
218 G_free(params);
219
220 /* else just pass the datum name on and hope it is recognised by
221 * PROJ.4 even though it isn't recognised by GRASS */
222 }
223 else {
224 snprintf(buffa, sizeof(buffa), "datum=%s", datum);
225 alloc_options(buffa);
226 returnval = 3;
227 }
228 /* else there'll be no datum transformation taking place here... */
229 }
230 else {
231 returnval = 4;
232 }
233 G_free(datum);
234
235 /* without type=crs, PROJ6 does not recognize what this is,
236 * a crs or some kind of coordinate operation, falling through to
237 * PJ_TYPE_OTHER_COORDINATE_OPERATION */
238 alloc_options("type=crs");
240 if (!(pj = proj_create_argv(pjc, nopt, opt_in))) {
241 strcpy(
242 buffa,
243 _("Unable to initialise PROJ with the following parameter list:"));
244 for (i = 0; i < nopt; i++) {
245 char err[50];
246
247 snprintf(err, sizeof(err), " +%s", opt_in[i]);
248 strcat(buffa, err);
249 }
250 G_warning("%s", buffa);
251 return -1;
252 }
253
254 int perr = proj_errno(pj);
255
256 if (perr)
257 G_fatal_error("PROJ error %d", perr);
258
259 if (proj_get_type(pj) == PJ_TYPE_BOUND_CRS) {
261 if (source_crs) {
262 proj_destroy(pj);
263 pj = source_crs;
264 }
265 }
266
267 info->pj = pj;
268
269 deflen = 0;
270 for (i = 0; i < nopt; i++)
271 deflen += strlen(opt_in[i]) + 2;
272
273 info->def = G_malloc(deflen + 1);
274
275 snprintf(buffa, sizeof(buffa), "+%s ", opt_in[0]);
276 strcpy(info->def, buffa);
277 G_free(opt_in[0]);
278
279 for (i = 1; i < nopt; i++) {
280 snprintf(buffa, sizeof(buffa), "+%s ", opt_in[i]);
281 strcat(info->def, buffa);
282 G_free(opt_in[i]);
283 }
284
285 return returnval;
286}
287
288static void alloc_options(char *buffa)
289{
290 size_t nsize;
291
292 nsize = strlen(buffa) + 1;
293 opt_in[nopt++] = (char *)G_malloc(nsize);
294 snprintf(opt_in[nopt - 1], nsize, "%s", buffa);
295 return;
296}
297
298/**
299 * \brief Create a pj_info struct Co-ordinate System definition from a
300 * string with a sequence of key=value pairs
301 *
302 * This function takes a GRASS- or PROJ style co-ordinate system definition
303 * and processes it to create a pj_info representation for use in
304 * re-projecting with pj_do_proj(). In addition to the parameters passed
305 * to it it may also make reference to the system ellipse.table and
306 * datum.table files if necessary.
307 *
308 * \param info Pointer to a pj_info struct (which must already exist) into
309 * which the co-ordinate system definition will be placed
310 * \param str input string with projection definition
311 *
312 * \return -1 on error (unable to initialise PROJ.4)
313 * 1 on success
314 **/
315
316int pj_get_string(struct pj_info *info, char *str)
317{
318 char *s;
319 int i, nsize;
320 char zonebuff[50], buffa[300];
321 int deflen;
322
323 PJ *pj;
325
326 info->zone = 0;
327 info->proj[0] = '\0';
328 info->meters = 1.0;
329 info->def = NULL;
330 info->srid = NULL;
331 info->pj = NULL;
332
333 nopt = 0;
334
335 if ((str == NULL) || (str[0] == '\0')) {
336 /* Null Pointer or empty string is supplied for parameters,
337 * implying latlong projection; just need to set proj
338 * parameter and call pj_init PK */
339 snprintf(info->proj, sizeof(info->proj), "ll");
340 snprintf(buffa, sizeof(buffa), "proj=latlong ellps=WGS84");
341 alloc_options(buffa);
342 }
343 else {
344 /* Parameters have been provided; parse through them but don't
345 * bother with most of the checks in pj_get_kv; assume the
346 * programmer knows what he / she is doing when using this
347 * function rather than reading a PROJ_INFO file PK */
348 s = str;
349 while (s = strtok(s, " \t\n"), s) {
350 if (strncmp(s, "+unfact=", 8) == 0) {
351 s = s + 8;
352 info->meters = atof(s);
353 }
354 else {
355 if (strncmp(s, "+", 1) == 0)
356 ++s;
357 if (nsize = strlen(s), nsize) {
358 if (nopt >= MAX_PARGS) {
359 fprintf(stderr, "nopt = %d, s=%s\n", nopt, str);
361 _("Option input overflowed option table"));
362 }
363
364 if (strncmp("zone=", s, 5) == 0) {
365 snprintf(zonebuff, sizeof(zonebuff), "%s", s + 5);
366 sscanf(zonebuff, "%d", &(info->zone));
367 }
368
369 if (strncmp(s, "init=", 5) == 0) {
370 info->srid = G_store(s + 6);
371 }
372
373 if (strncmp("proj=", s, 5) == 0) {
374 snprintf(info->proj, sizeof(info->proj), "%s", s + 5);
375 if (strcmp(info->proj, "ll") == 0)
376 snprintf(buffa, sizeof(buffa), "proj=latlong");
377 else
378 snprintf(buffa, sizeof(buffa), "%s", s);
379 }
380 else {
381 snprintf(buffa, sizeof(buffa), "%s", s);
382 }
383 alloc_options(buffa);
384 }
385 }
386 s = 0;
387 }
388 }
389
390 /* without type=crs, PROJ6 does not recognize what this is,
391 * a crs or some kind of coordinate operation, falling through to
392 * PJ_TYPE_OTHER_COORDINATE_OPERATION */
393 alloc_options("type=crs");
395 if (!(pj = proj_create_argv(pjc, nopt, opt_in))) {
396 G_warning(_("Unable to initialize pj cause: %s"),
398 return -1;
399 }
400
401 if (proj_get_type(pj) == PJ_TYPE_BOUND_CRS) {
403 if (source_crs) {
404 proj_destroy(pj);
405 pj = source_crs;
406 }
407 }
408 info->pj = pj;
409
410 deflen = 0;
411 for (i = 0; i < nopt; i++)
412 deflen += strlen(opt_in[i]) + 2;
413
414 info->def = G_malloc(deflen + 1);
415
416 snprintf(buffa, sizeof(buffa), "+%s ", opt_in[0]);
417 strcpy(info->def, buffa);
418 G_free(opt_in[0]);
419
420 for (i = 1; i < nopt; i++) {
421 snprintf(buffa, sizeof(buffa), "+%s ", opt_in[i]);
422 strcat(info->def, buffa);
423 G_free(opt_in[i]);
424 }
425
426 return 1;
427}
428
429/* set_proj_share()
430 * 'finder function' for use with PROJ.4 pj_set_finder() function
431 * this is used to find grids, usually in /usr/share/proj
432 * GRASS no longer provides copies of proj grids in GRIDDIR
433 * -> do not use gisbase/GRIDDIR */
434
435const char *set_proj_share(const char *name)
436{
437 static char *buf = NULL;
438 const char *projshare;
439 static size_t buf_len = 0;
440 size_t len;
441
442 projshare = getenv("GRASS_PROJSHARE");
443 if (!projshare)
444 return NULL;
445
446 len = strlen(projshare) + strlen(name) + 2;
447
448 if (buf_len < len) {
449 if (buf != NULL)
450 G_free(buf);
451 buf_len = len + 20;
452 buf = G_malloc(buf_len);
453 }
454
455 snprintf(buf, buf_len, "%s/%s", projshare, name);
456
457 return buf;
458}
459
460/**
461 * \brief Print projection parameters as used by PROJ.4 for input and
462 * output co-ordinate systems
463 *
464 * \param iproj 'Input' co-ordinate system
465 * \param oproj 'Output' co-ordinate system
466 *
467 * \return 1 on success, -1 on error (i.e. if the PROJ-style definition
468 * is NULL for either co-ordinate system)
469 **/
470
472 const struct pj_info *oproj)
473{
474 char *str;
475
476 if (iproj) {
477 str = iproj->def;
478 if (str != NULL) {
479 fprintf(stderr, "%s: %s\n", _("Input Projection Parameters"), str);
480 fprintf(stderr, "%s: %.16g\n", _("Input Unit Factor"),
481 iproj->meters);
482 }
483 else
484 return -1;
485 }
486
487 if (oproj) {
488 str = oproj->def;
489 if (str != NULL) {
490 fprintf(stderr, "%s: %s\n", _("Output Projection Parameters"), str);
491 fprintf(stderr, "%s: %.16g\n", _("Output Unit Factor"),
492 oproj->meters);
493 }
494 else
495 return -1;
496 }
497
498 return 1;
499}
#define NULL
Definition ccmath.h:32
void G_free(void *)
Free allocated memory.
Definition gis/alloc.c:145
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
void G_warning(const char *,...) __attribute__((format(printf
#define G_malloc(n)
Definition defs/gis.h:136
const char * G_find_key_value(const char *, const struct Key_Value *)
Find given key (case sensitive)
Definition key_value1.c:83
char * G_store(const char *)
Copy string to allocated memory.
Definition strings.c:85
int GPJ_get_default_datum_params_by_name(const char *, char **)
"Last resort" function to retrieve a "default" set of datum parameters for a datum (N....
Definition proj/datum.c:83
int GPJ__get_datum_params(const struct Key_Value *, char **, char **)
Extract the datum transformation-related parameters from a set of general PROJ_INFO parameters.
Definition proj/datum.c:170
int GPJ__get_ellipsoid_params(const struct Key_Value *, double *, double *, double *)
Get the ellipsoid parameters from proj keys structure.
Definition ellipse.c:71
int pj_get_kv(struct pj_info *info, const struct Key_Value *in_proj_keys, const struct Key_Value *in_units_keys)
Create a pj_info struct Co-ordinate System definition from a set of PROJ_INFO / PROJ_UNITS-style key-...
Definition get_proj.c:57
#define MAX_PARGS
Definition get_proj.c:25
int pj_print_proj_params(const struct pj_info *iproj, const struct pj_info *oproj)
Print projection parameters as used by PROJ.4 for input and output co-ordinate systems.
Definition get_proj.c:471
const char * set_proj_share(const char *name)
Definition get_proj.c:435
int pj_get_string(struct pj_info *info, char *str)
Create a pj_info struct Co-ordinate System definition from a string with a sequence of key=value pair...
Definition get_proj.c:316
#define _(str)
Definition glocale.h:10
const char * name
Definition named_colr.c:6
#define strcpy
Definition parson.c:66
char proj[100]
Definition gprojects.h:42
int zone
Definition gprojects.h:41
char * def
Definition gprojects.h:43
PJ * pj
Definition gprojects.h:39
char * srid
Definition gprojects.h:44
char * wkt
Definition gprojects.h:45
double meters
Definition gprojects.h:40
SYMBOL * err(FILE *fp, SYMBOL *s, char *msg)