GRASS 8 Programmer's Manual  8.5.0dev(2025)-c070206eb1
make_loc.c
Go to the documentation of this file.
1 /*!
2  * \file lib/gis/make_loc.c
3  *
4  * \brief GIS Library - Functions to create a new location
5  *
6  * Creates a new location automatically given a "Cell_head", PROJ_INFO
7  * and PROJ_UNITS information.
8  *
9  * (C) 2000-2013 by the GRASS Development Team
10  *
11  * This program is free software under the GNU General Public License
12  * (>=v2). Read the file COPYING that comes with GRASS for details.
13  *
14  * \author Frank Warmerdam
15  */
16 
17 #include <grass/gis.h>
18 
19 #include <stdlib.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <unistd.h>
23 #include <sys/stat.h>
24 #include <math.h>
25 #include <grass/glocale.h>
26 
27 /*!
28  * \brief Create a new location
29  *
30  * This function creates a new location in the current database,
31  * initializes the projection, default window and current window.
32  *
33  * \param location_name Name of the new location. Should not include
34  * the full path, the location will be created within
35  * the current database.
36  * \param wind default window setting for the new location.
37  * All fields should be set in this
38  * structure, and care should be taken to ensure that
39  * the proj/zone fields match the definition in the
40  * proj_info parameter(see G_set_cellhd_from_projinfo()).
41  *
42  * \param proj_info projection definition suitable to write to the
43  * PROJ_INFO file, or NULL for PROJECTION_XY.
44  *
45  * \param proj_units projection units suitable to write to the PROJ_UNITS
46  * file, or NULL.
47  *
48  * \return 0 on success
49  * \return -1 to indicate a system error (check errno).
50  * \return -2 failed to create projection file (currently not used)
51  * \return -3 illegal name
52  */
53 int G_make_location(const char *location_name, struct Cell_head *wind,
54  const struct Key_Value *proj_info,
55  const struct Key_Value *proj_units)
56 {
57  char path[GPATH_MAX];
58 
59  /* check if location name is legal */
60  if (G_legal_filename(location_name) != 1)
61  return -3;
62 
63  /* Try to create the location directory, under the gisdbase. */
64  snprintf(path, sizeof(path), "%s/%s", G_gisdbase(), location_name);
65  if (G_mkdir(path) != 0)
66  return -1;
67 
68  /* Make the PERMANENT mapset. */
69  snprintf(path, sizeof(path), "%s/%s/%s", G_gisdbase(), location_name,
70  "PERMANENT");
71  if (G_mkdir(path) != 0) {
72  return -1;
73  }
74 
75  /* make these the new current location and mapset */
76  G_setenv_nogisrc("LOCATION_NAME", location_name);
77  G_setenv_nogisrc("MAPSET", "PERMANENT");
78 
79  /* Create the default, and current window files */
80  G_put_element_window(wind, "", "DEFAULT_WIND");
81  G_put_element_window(wind, "", "WIND");
82 
83  /* Write out the PROJ_INFO, and PROJ_UNITS if available. */
84  if (proj_info != NULL) {
85  G_file_name(path, "", "PROJ_INFO", "PERMANENT");
86  G_write_key_value_file(path, proj_info);
87  }
88 
89  if (proj_units != NULL) {
90  G_file_name(path, "", "PROJ_UNITS", "PERMANENT");
91  G_write_key_value_file(path, proj_units);
92  }
93 
94  return 0;
95 }
96 
97 /*!
98  * \brief Create a new location
99  *
100  * This function creates a new location in the current database,
101  * initializes the projection, default window and current window,
102  * and sets the EPSG code if present
103  *
104  * \param location_name Name of the new location. Should not include
105  * the full path, the location will be created within
106  * the current database.
107  * \param wind default window setting for the new location.
108  * All fields should be set in this
109  * structure, and care should be taken to ensure that
110  * the proj/zone fields match the definition in the
111  * proj_info parameter(see G_set_cellhd_from_projinfo()).
112  *
113  * \param proj_info projection definition suitable to write to the
114  * PROJ_INFO file, or NULL for PROJECTION_XY.
115  *
116  * \param proj_units projection units suitable to write to the PROJ_UNITS
117  * file, or NULL.
118  *
119  * \param proj_epsg EPSG code suitable to write to the PROJ_EPSG
120  * file, or NULL.
121  *
122  * \return 0 on success
123  * \return -1 to indicate a system error (check errno).
124  * \return -2 failed to create projection file (currently not used)
125  * \return -3 illegal name
126  */
127 int G_make_location_epsg(const char *location_name, struct Cell_head *wind,
128  const struct Key_Value *proj_info,
129  const struct Key_Value *proj_units,
130  const struct Key_Value *proj_epsg)
131 {
132  int ret;
133 
134  ret = G_make_location(location_name, wind, proj_info, proj_units);
135 
136  if (ret != 0)
137  return ret;
138 
139  /* Write out the PROJ_EPSG if available. */
140  if (proj_epsg != NULL) {
141  char path[GPATH_MAX];
142 
143  G_file_name(path, "", "PROJ_EPSG", "PERMANENT");
144  G_write_key_value_file(path, proj_epsg);
145  }
146 
147  return 0;
148 }
149 
150 /*!
151  * \brief Create a new location
152  *
153  * This function creates a new location in the current database,
154  * initializes the projection, default window and current window,
155  * and sets WKT, srid, and EPSG code if present
156  *
157  * \param location_name Name of the new location. Should not include
158  * the full path, the location will be created within
159  * the current database.
160  * \param wind default window setting for the new location.
161  * All fields should be set in this
162  * structure, and care should be taken to ensure that
163  * the proj/zone fields match the definition in the
164  * proj_info parameter(see G_set_cellhd_from_projinfo()).
165  *
166  * \param proj_info projection definition suitable to write to the
167  * PROJ_INFO file, or NULL for PROJECTION_XY.
168  *
169  * \param proj_units projection units suitable to write to the PROJ_UNITS
170  * file, or NULL.
171  *
172  * \param proj_srid Spatial reference ID suitable to write to the PROJ_SRID
173  * file, or NULL.
174  *
175  * \param proj_wkt WKT definition suitable to write to the PROJ_WKT
176  * file, or NULL.
177  *
178  * \return 0 on success
179  * \return -1 to indicate a system error (check errno).
180  * \return -2 failed to create projection file (currently not used)
181  * \return -3 illegal name
182  */
183 int G_make_location_crs(const char *location_name, struct Cell_head *wind,
184  const struct Key_Value *proj_info,
185  const struct Key_Value *proj_units,
186  const char *proj_srid, const char *proj_wkt)
187 {
188  int ret;
189 
190  ret = G_make_location(location_name, wind, proj_info, proj_units);
191 
192  if (ret != 0)
193  return ret;
194 
195  /* Write out PROJ_SRID if srid is available. */
196  if (proj_srid != NULL) {
197  G_write_projsrid(location_name, proj_srid);
198  }
199 
200  /* Write out PROJ_WKT if WKT is available. */
201  if (proj_wkt != NULL) {
202  G_write_projwkt(location_name, proj_wkt);
203  }
204 
205  return 0;
206 }
207 
208 /*!
209  * \brief Compare projections including units
210  *
211  * \param proj_info1 projection info to compare
212  * \param proj_units1 projection units to compare
213  * \param proj_info2 projection info to compare
214  * \param proj_units2 projection units to compare
215 
216  * \return -1 if not the same projection
217  * \return -2 if linear unit translation to meters fails
218  * \return -3 if not the same datum,
219  * \return -4 if not the same ellipsoid,
220  * \return -5 if UTM zone differs
221  * \return -6 if UTM hemisphere differs,
222  * \return -7 if false easting differs
223  * \return -8 if false northing differs,
224  * \return -9 if center longitude differs,
225  * \return -10 if center latitude differs,
226  * \return -11 if standard parallels differ,
227  * \return 1 if projections match.
228  */
229 int G_compare_projections(const struct Key_Value *proj_info1,
230  const struct Key_Value *proj_units1,
231  const struct Key_Value *proj_info2,
232  const struct Key_Value *proj_units2)
233 {
234  const char *proj1, *proj2;
235 
236  if (proj_info1 == NULL && proj_info2 == NULL)
237  return TRUE;
238 
239  /* -------------------------------------------------------------------- */
240  /* Are they both in the same projection? */
241  /* -------------------------------------------------------------------- */
242  /* prevent seg fault in G_find_key_value */
243  if (proj_info1 == NULL || proj_info2 == NULL)
244  return -1;
245 
246  proj1 = G_find_key_value("proj", proj_info1);
247  proj2 = G_find_key_value("proj", proj_info2);
248 
249  if (proj1 == NULL || proj2 == NULL || strcmp(proj1, proj2))
250  return -1;
251 
252  /* -------------------------------------------------------------------- */
253  /* Verify that the linear unit translation to meters is OK. */
254  /* -------------------------------------------------------------------- */
255  /* prevent seg fault in G_find_key_value */
256  if (proj_units1 == NULL && proj_units2 == NULL)
257  return 1;
258 
259  if (proj_units1 == NULL || proj_units2 == NULL)
260  return -2;
261 
262  {
263  double a1 = 0, a2 = 0;
264 
265  if (G_find_key_value("meters", proj_units1) != NULL)
266  a1 = atof(G_find_key_value("meters", proj_units1));
267  if (G_find_key_value("meters", proj_units2) != NULL)
268  a2 = atof(G_find_key_value("meters", proj_units2));
269 
270  if (a1 && a2 && (fabs(a2 - a1) > 0.000001))
271  return -2;
272  }
273  /* compare unit name only if there is no to meter conversion factor */
274  if (G_find_key_value("meters", proj_units1) == NULL ||
275  G_find_key_value("meters", proj_units2) == NULL) {
276  const char *u_1 = NULL, *u_2 = NULL;
277 
278  u_1 = G_find_key_value("unit", proj_units1);
279  u_2 = G_find_key_value("unit", proj_units2);
280 
281  if ((u_1 && !u_2) || (!u_1 && u_2))
282  return -2;
283 
284  /* the unit name can be arbitrary: the following can be the same
285  * us-ft (proj.4 keyword)
286  * U.S. Surveyor's Foot (proj.4 name)
287  * US survey foot (WKT)
288  * Foot_US (WKT)
289  */
290  if (u_1 && u_2 && G_strcasecmp(u_1, u_2))
291  return -2;
292  }
293 
294  /* -------------------------------------------------------------------- */
295  /* Do they both have the same datum? */
296  /* -------------------------------------------------------------------- */
297  {
298  const char *d_1 = NULL, *d_2 = NULL;
299 
300  d_1 = G_find_key_value("datum", proj_info1);
301  d_2 = G_find_key_value("datum", proj_info2);
302 
303  if ((d_1 && !d_2) || (!d_1 && d_2))
304  return -3;
305 
306  if (d_1 && d_2 && strcmp(d_1, d_2)) {
307  /* different datum short names can mean the same datum,
308  * see lib/gis/datum.table */
309  G_debug(1, "Different datum names");
310  }
311  }
312 
313  /* -------------------------------------------------------------------- */
314  /* Do they both have the same ellipsoid? */
315  /* -------------------------------------------------------------------- */
316  {
317  const char *e_1 = NULL, *e_2 = NULL;
318 
319  e_1 = G_find_key_value("ellps", proj_info1);
320  e_2 = G_find_key_value("ellps", proj_info2);
321 
322  if (e_1 && e_2 && strcmp(e_1, e_2))
323  return -4;
324 
325  if (e_1 == NULL || e_2 == NULL) {
326  double a1 = 0, a2 = 0;
327  double es1 = 0, es2 = 0;
328 
329  /* it may happen that one proj_info has ellps,
330  * while the other has a, es: translate ellps to a, es */
331  if (e_1)
332  G_get_ellipsoid_by_name(e_1, &a1, &es1);
333  else {
334  if (G_find_key_value("a", proj_info1) != NULL)
335  a1 = atof(G_find_key_value("a", proj_info1));
336  if (G_find_key_value("es", proj_info1) != NULL)
337  es1 = atof(G_find_key_value("es", proj_info1));
338  }
339 
340  if (e_2)
341  G_get_ellipsoid_by_name(e_2, &a2, &es2);
342  else {
343  if (G_find_key_value("a", proj_info2) != NULL)
344  a2 = atof(G_find_key_value("a", proj_info2));
345  if (G_find_key_value("es", proj_info2) != NULL)
346  es2 = atof(G_find_key_value("es", proj_info2));
347  }
348 
349  /* it should be an error if a = 0 */
350  if ((a1 == 0 && a2 != 0) || (a1 != 0 && a2 == 0))
351  return -4;
352 
353  if (a1 && a2 && (fabs(a2 - a1) > 0.000001))
354  return -4;
355 
356  if ((es1 == 0 && es2 != 0) || (es1 != 0 && es2 == 0))
357  return -4;
358 
359  if (es1 && es2 && (fabs(es2 - es1) > 0.000001))
360  return -4;
361  }
362  }
363 
364  /* -------------------------------------------------------------------- */
365  /* Zone check specially for UTM */
366  /* -------------------------------------------------------------------- */
367  if (!strcmp(proj1, "utm") && !strcmp(proj2, "utm") &&
368  atof(G_find_key_value("zone", proj_info1)) !=
369  atof(G_find_key_value("zone", proj_info2)))
370  return -5;
371 
372  /* -------------------------------------------------------------------- */
373  /* Hemisphere check specially for UTM */
374  /* -------------------------------------------------------------------- */
375  if (!strcmp(proj1, "utm") && !strcmp(proj2, "utm") &&
376  !!G_find_key_value("south", proj_info1) !=
377  !!G_find_key_value("south", proj_info2))
378  return -6;
379 
380  /* -------------------------------------------------------------------- */
381  /* Do they both have the same false easting? */
382  /* -------------------------------------------------------------------- */
383 
384  {
385  const char *x_0_1 = NULL, *x_0_2 = NULL;
386 
387  x_0_1 = G_find_key_value("x_0", proj_info1);
388  x_0_2 = G_find_key_value("x_0", proj_info2);
389 
390  if ((x_0_1 && !x_0_2) || (!x_0_1 && x_0_2))
391  return -7;
392 
393  if (x_0_1 && x_0_2 && (fabs(atof(x_0_1) - atof(x_0_2)) > 0.000001))
394  return -7;
395  }
396 
397  /* -------------------------------------------------------------------- */
398  /* Do they both have the same false northing? */
399  /* -------------------------------------------------------------------- */
400 
401  {
402  const char *y_0_1 = NULL, *y_0_2 = NULL;
403 
404  y_0_1 = G_find_key_value("y_0", proj_info1);
405  y_0_2 = G_find_key_value("y_0", proj_info2);
406 
407  if ((y_0_1 && !y_0_2) || (!y_0_1 && y_0_2))
408  return -8;
409 
410  if (y_0_1 && y_0_2 && (fabs(atof(y_0_1) - atof(y_0_2)) > 0.000001))
411  return -8;
412  }
413 
414  /* -------------------------------------------------------------------- */
415  /* Do they have the same center longitude? */
416  /* -------------------------------------------------------------------- */
417 
418  {
419  const char *l_1 = NULL, *l_2 = NULL;
420 
421  l_1 = G_find_key_value("lon_0", proj_info1);
422  l_2 = G_find_key_value("lon_0", proj_info2);
423 
424  if ((l_1 && !l_2) || (!l_1 && l_2))
425  return -9;
426 
427  if (l_1 && l_2 && (fabs(atof(l_1) - atof(l_2)) > 0.000001))
428  return -9;
429 
430  /* --------------------------------------------------------------------
431  */
432  /* Do they have the same center latitude? */
433  /* --------------------------------------------------------------------
434  */
435 
436  l_1 = G_find_key_value("lat_0", proj_info1);
437  l_2 = G_find_key_value("lat_0", proj_info2);
438 
439  if ((l_1 && !l_2) || (!l_1 && l_2))
440  return -10;
441 
442  if (l_1 && l_2 && (fabs(atof(l_1) - atof(l_2)) > 0.000001))
443  return -10;
444 
445  /* --------------------------------------------------------------------
446  */
447  /* Do they have the same standard parallels? */
448  /* --------------------------------------------------------------------
449  */
450 
451  l_1 = G_find_key_value("lat_1", proj_info1);
452  l_2 = G_find_key_value("lat_1", proj_info2);
453 
454  if ((l_1 && !l_2) || (!l_1 && l_2))
455  return -11;
456 
457  if (l_1 && l_2 && (fabs(atof(l_1) - atof(l_2)) > 0.000001)) {
458  /* lat_1 differ */
459  /* check for swapped lat_1, lat_2 */
460  l_2 = G_find_key_value("lat_2", proj_info2);
461 
462  if (!l_2)
463  return -11;
464  if (fabs(atof(l_1) - atof(l_2)) > 0.000001) {
465  return -11;
466  }
467  }
468 
469  l_1 = G_find_key_value("lat_2", proj_info1);
470  l_2 = G_find_key_value("lat_2", proj_info2);
471 
472  if ((l_1 && !l_2) || (!l_1 && l_2))
473  return -11;
474 
475  if (l_1 && l_2 && (fabs(atof(l_1) - atof(l_2)) > 0.000001)) {
476  /* lat_2 differ */
477  /* check for swapped lat_1, lat_2 */
478  l_2 = G_find_key_value("lat_1", proj_info2);
479 
480  if (!l_2)
481  return -11;
482  if (fabs(atof(l_1) - atof(l_2)) > 0.000001) {
483  return -11;
484  }
485  }
486  }
487 
488  /* towgs84 ? */
489 
490  /* -------------------------------------------------------------------- */
491  /* Add more details in later. */
492  /* -------------------------------------------------------------------- */
493 
494  return 1;
495 }
496 
497 /*!
498  \brief Write WKT definition to file
499 
500  Any WKT string and version recognized by PROJ is supported.
501 
502  \param location_name name of the location to write the WKT definition
503  \param wktstring pointer to WKT string
504 
505  \return 0 success
506  \return -1 error writing
507  */
508 int G_write_projwkt(const char *location_name, const char *wktstring)
509 {
510  FILE *fp;
511  char path[GPATH_MAX];
512  int err, n;
513 
514  if (!wktstring)
515  return 0;
516 
517  if (location_name && *location_name)
518  snprintf(path, sizeof(path), "%s/%s/%s/%s", G_gisdbase(), location_name,
519  "PERMANENT", WKT_FILE);
520  else
521  G_file_name(path, "", WKT_FILE, "PERMANENT");
522 
523  fp = fopen(path, "w");
524 
525  if (!fp)
526  G_fatal_error(_("Unable to open output file <%s>: %s"), path,
527  strerror(errno));
528 
529  err = 0;
530  n = strlen(wktstring);
531  if (wktstring[n - 1] != '\n') {
532  if (n != fprintf(fp, "%s\n", wktstring))
533  err = -1;
534  }
535  else {
536  if (n != fprintf(fp, "%s", wktstring))
537  err = -1;
538  }
539 
540  if (fclose(fp) != 0)
541  G_fatal_error(_("Error closing output file <%s>: %s"), path,
542  strerror(errno));
543 
544  return err;
545 }
546 
547 /*!
548  \brief Write srid (spatial reference id) to file
549 
550  A srid consists of an authority name and code and must be known to
551  PROJ.
552 
553  \param location_name name of the location to write the srid
554  \param sridstring pointer to srid string
555 
556  \return 0 success
557  \return -1 error writing
558  */
559 int G_write_projsrid(const char *location_name, const char *sridstring)
560 {
561  FILE *fp;
562  char path[GPATH_MAX];
563  int err, n;
564 
565  if (!sridstring)
566  return 0;
567 
568  if (location_name && *location_name)
569  snprintf(path, sizeof(path), "%s/%s/%s/%s", G_gisdbase(), location_name,
570  "PERMANENT", SRID_FILE);
571  else
572  G_file_name(path, "", SRID_FILE, "PERMANENT");
573 
574  fp = fopen(path, "w");
575 
576  if (!fp)
577  G_fatal_error(_("Unable to open output file <%s>: %s"), path,
578  strerror(errno));
579 
580  err = 0;
581  n = strlen(sridstring);
582  if (sridstring[n - 1] != '\n') {
583  if (n != fprintf(fp, "%s\n", sridstring))
584  err = -1;
585  }
586  else {
587  if (n != fprintf(fp, "%s", sridstring))
588  err = -1;
589  }
590 
591  if (fclose(fp) != 0)
592  G_fatal_error(_("Error closing output file <%s>: %s"), path,
593  strerror(errno));
594 
595  return err;
596 }
#define NULL
Definition: ccmath.h:32
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
char * G_file_name(char *, const char *, const char *, const char *)
Builds full path names to GIS data files.
Definition: file_name.c:61
const char * G_gisdbase(void)
Get name of top level database directory.
Definition: gisdbase.c:26
int G_legal_filename(const char *)
Check for legal database file name.
Definition: legal_name.c:34
const char * G_find_key_value(const char *, const struct Key_Value *)
Find given key (case sensitive)
Definition: key_value1.c:85
int G_get_ellipsoid_by_name(const char *, double *, double *)
Get ellipsoid parameters by name.
Definition: get_ellipse.c:104
void G_write_key_value_file(const char *, const struct Key_Value *)
Write key/value pairs to file.
Definition: key_value3.c:28
int int G_strcasecmp(const char *, const char *)
String compare ignoring case (upper or lower)
Definition: strings.c:47
int G_put_element_window(const struct Cell_head *, const char *, const char *)
Write the region.
Definition: put_window.c:74
int G_mkdir(const char *)
Creates a new directory.
Definition: paths.c:27
int G_debug(int, const char *,...) __attribute__((format(printf
void G_setenv_nogisrc(const char *, const char *)
Set environment name to value (doesn't update .gisrc)
Definition: env.c:472
#define WKT_FILE
Definition: gis.h:136
#define GPATH_MAX
Definition: gis.h:199
#define SRID_FILE
Definition: gis.h:137
#define TRUE
Definition: gis.h:78
#define _(str)
Definition: glocale.h:10
int G_make_location_crs(const char *location_name, struct Cell_head *wind, const struct Key_Value *proj_info, const struct Key_Value *proj_units, const char *proj_srid, const char *proj_wkt)
Create a new location.
Definition: make_loc.c:183
int G_write_projsrid(const char *location_name, const char *sridstring)
Write srid (spatial reference id) to file.
Definition: make_loc.c:559
int G_write_projwkt(const char *location_name, const char *wktstring)
Write WKT definition to file.
Definition: make_loc.c:508
int G_compare_projections(const struct Key_Value *proj_info1, const struct Key_Value *proj_units1, const struct Key_Value *proj_info2, const struct Key_Value *proj_units2)
Compare projections including units.
Definition: make_loc.c:229
int G_make_location(const char *location_name, struct Cell_head *wind, const struct Key_Value *proj_info, const struct Key_Value *proj_units)
Create a new location.
Definition: make_loc.c:53
int G_make_location_epsg(const char *location_name, struct Cell_head *wind, const struct Key_Value *proj_info, const struct Key_Value *proj_units, const struct Key_Value *proj_epsg)
Create a new location.
Definition: make_loc.c:127
2D/3D raster map header (used also for region)
Definition: gis.h:446
Definition: gis.h:534
Definition: path.h:15
SYMBOL * err(FILE *fp, SYMBOL *s, char *msg)
Definition: symbol/read.c:216