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