GRASS GIS 8 Programmer's Manual  8.4.0dev(2024)-535c39c9fc
map.c
Go to the documentation of this file.
1 /*!
2  \file lib/vector/Vlib/map.c
3 
4  \brief Vector library - Manipulate vector map (copy, rename, delete)
5 
6  Higher level functions for reading/writing/manipulating vectors.
7 
8  (C) 2001-2009, 2012 by the GRASS Development Team
9 
10  This program is free software under the GNU General Public License
11  (>=v2). Read the file COPYING that comes with GRASS for details.
12 
13  \author Original author CERL, probably Dave Gerdes or Mike Higgins.
14  \author Update to GRASS 5.7 Radim Blazek and David D. Gray.
15  */
16 
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <dirent.h>
20 #include <string.h>
21 #include <unistd.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <errno.h>
26 
27 #include <grass/glocale.h>
28 #include <grass/vector.h>
29 #include <grass/dbmi.h>
30 #include <grass/glocale.h>
31 
32 #include "local_proto.h"
33 
34 /*!
35  \brief Creates and initializes Map_info structure
36 
37  To free allocated memory call Vect_destroy_map_struct().
38 
39  \return pointer to Map_info
40  */
42 {
43  struct Map_info *p;
44 
45  p = (struct Map_info *)malloc(sizeof(struct Map_info));
46 
47  if (NULL == p)
48  G_fatal_error("Vect_new_map_struct(): %s", _("Out of memory"));
49 
50  G_zero(p, sizeof(struct Map_info));
51 
52  return p;
53 }
54 
55 /*!
56  \brief Frees all memory associated with a Map_info structure,
57  including the structure itself
58 
59  \param p pointer to Map_info structure
60  */
62 {
63  /* We should free all allocated member structures, but they may be already
64  freed by other functions (e.g. Vect_close()) without resetting member
65  pointers to zero */
66 
67  G_free((char *)p);
68 }
69 
70 /*!
71  \brief Copy file
72 
73  \param src source file
74  \param[out] dst destination file
75 
76  \return 0 OK
77  \return 1 error
78  */
79 static int copy_file(const char *src, const char *dst)
80 {
81  char buf[4096];
82  int fd, fd2;
83  FILE *f2;
84  int len, len2;
85 
86  if ((fd = open(src, O_RDONLY)) < 0)
87  return 1;
88 
89  /* if((fd2 = open(dst, O_CREAT|O_TRUNC|O_WRONLY)) < 0) { */
90  if ((f2 = fopen(dst, "w")) == NULL) {
91  close(fd);
92  return 1;
93  }
94 
95  fd2 = fileno(f2);
96 
97  len2 = 0;
98  while ((len = read(fd, buf, 4096)) > 0) {
99  while (len && (len2 = write(fd2, buf, len)) >= 0)
100  len -= len2;
101  }
102 
103  close(fd);
104  /* close(fd2); */
105  fclose(f2);
106 
107  if (len == -1 || len2 == -1)
108  return 1;
109 
110  return 0;
111 }
112 
113 /*!
114  \brief Copy vector map including attribute tables
115 
116  Note: Output vector map is overwritten if exists!
117 
118  \param in name if vector map to be copied
119  \param mapset mapset name where the input map is located
120  \param out name for output vector map (new map is created in current mapset)
121 
122  \return -1 error
123  \return 0 success
124  */
125 int Vect_copy(const char *in, const char *mapset, const char *out)
126 {
127  int i, ret;
128  struct Map_info In, Out;
129  char old_path[GPATH_MAX], new_path[GPATH_MAX], buf[GPATH_MAX];
130 
131  const char *files[] = {
134  const char *inmapset;
135  char xname[GNAME_MAX], xmapset[GMAPSET_MAX];
136 
137  G_debug(2, "Copy vector '%s' in '%s' to '%s'", in, mapset, out);
138  /* check for [A-Za-z][A-Za-z0-9_]* in name */
139  if (Vect_legal_filename(out) < 0)
140  G_fatal_error(_("Vector map name is not SQL compliant"));
141 
142  inmapset = G_find_vector2(in, mapset);
143  if (!inmapset) {
144  G_warning(_("Unable to find vector map <%s> in <%s>"), in, mapset);
145  return -1;
146  }
147  mapset = inmapset;
148 
149  /* remove mapset from fully qualified name, confuses G_file_name() */
150  if (G_name_is_fully_qualified(in, xname, xmapset)) {
151  in = xname;
152  }
153 
154  /* Delete old vector if it exists */
155  if (G_find_vector2(out, G_mapset())) {
156  G_warning(_("Vector map <%s> already exists and will be overwritten"),
157  out);
158  ret = Vect_delete(out);
159  if (ret != 0) {
160  G_warning(_("Unable to delete vector map <%s>"), out);
161  return -1;
162  }
163  }
164 
165  /* Copy the directory */
167 
168  i = 0;
169  while (files[i]) {
170  sprintf(buf, "%s/%s", in, files[i]);
171  G_file_name(old_path, GV_DIRECTORY, buf, mapset);
172  sprintf(buf, "%s/%s", out, files[i]);
173  G_file_name(new_path, GV_DIRECTORY, buf, G_mapset());
174 
175  if (access(old_path, F_OK) == 0) { /* file exists? */
176  G_debug(2, "copy %s to %s", old_path, new_path);
177  if (copy_file(old_path, new_path)) {
178  G_warning(_("Unable to copy vector map <%s> to <%s>"), old_path,
179  new_path);
180  }
181  }
182  i++;
183  }
184 
185  G_file_name(old_path, GV_DIRECTORY, in, mapset);
186  G_file_name(new_path, GV_DIRECTORY, out, G_mapset());
187 
188  /* Open input */
190  if (Vect_open_old_head(&In, in, mapset) < 0)
191  G_fatal_error(_("Unable to open vector map <%s>"), in);
192 
193  if (In.format != GV_FORMAT_NATIVE) { /* Done */
194  Vect_close(&In);
195  return 0;
196  }
197 
198  /* Open output */
200  if (Vect_open_update_head(&Out, out, G_mapset()) < 0)
201  G_fatal_error(_("Unable to open vector map <%s>"), out);
202 
203  /* Copy tables */
204  if (Vect_copy_tables(&In, &Out, 0) != 0) {
205  Vect_close(&In);
206  Vect_close(&Out);
207 
208  return 1;
209  }
210 
211  Vect_close(&In);
212  Vect_close(&Out);
213 
214  return 0;
215 }
216 
217 /*!
218  \brief Rename existing vector map (in the current mapset).
219 
220  Attribute tables are created in the same database where input tables were
221  stored.
222 
223  The original format (native/OGR) is used.
224 
225  Note: Output vector map is overwritten if exists!
226 
227  \param in name of vector map to be renamed
228  \param out name for output vector map
229 
230  \return -1 error
231  \return 0 success
232  */
233 int Vect_rename(const char *in, const char *out)
234 {
235  int i, n, ret, type;
236  struct Map_info Map;
237  struct field_info *Fin, *Fout;
238  int *fields;
239  dbDriver *driver;
240  char xname[GNAME_MAX], xmapset[GMAPSET_MAX];
241 
242  G_debug(2, "Rename vector '%s' to '%s'", in, out);
243  /* check for [A-Za-z][A-Za-z0-9_]* in name */
244  if (Vect_legal_filename(out) < 0)
245  G_fatal_error(_("Vector map name is not SQL compliant"));
246 
247  /* Delete old vector if it exists */
248  if (G_find_vector2(out, G_mapset())) {
249  G_warning(_("Vector map <%s> already exists and will be overwritten"),
250  out);
251  Vect_delete(out);
252  }
253 
254  /* remove mapset from fully qualified name */
255  if (G_name_is_fully_qualified(in, xname, xmapset)) {
256  in = xname;
257  }
258 
259  /* Move the directory */
260  ret = G_rename(GV_DIRECTORY, in, out);
261 
262  if (ret == 0) {
263  G_warning(_("Vector map <%s> not found"), in);
264  return -1;
265  }
266  else if (ret == -1) {
267  G_warning(_("Unable to copy vector map <%s> to <%s>"), in, out);
268  return -1;
269  }
270 
271  /* Rename all tables if the format is native */
273  if (Vect_open_update_head(&Map, out, G_mapset()) < 0)
274  G_fatal_error(_("Unable to open vector map <%s>"), out);
275 
276  if (Map.format != GV_FORMAT_NATIVE) { /* Done */
277  Vect_close(&Map);
278  return 0;
279  }
280 
281  /* Copy tables */
282  n = Vect_get_num_dblinks(&Map);
283  type = GV_1TABLE;
284  if (n > 1)
285  type = GV_MTABLE;
286 
287  /* Make the list of fields */
288  fields = (int *)G_malloc(n * sizeof(int));
289 
290  for (i = 0; i < n; i++) {
291  Fin = Vect_get_dblink(&Map, i);
292  fields[i] = Fin->number;
293  }
294 
295  for (i = 0; i < n; i++) {
296  G_debug(3, "field[%d] = %d", i, fields[i]);
297 
298  Fin = Vect_get_field(&Map, fields[i]);
299  if (Fin == NULL) {
300  G_warning(_("Database connection not defined for layer %d"),
301  fields[i]);
302  Vect_close(&Map);
303  return -1;
304  }
305 
306  Fout = Vect_default_field_info(&Map, Fin->number, Fin->name, type);
307  G_debug(3, "Copy drv:db:table '%s:%s:%s' to '%s:%s:%s'", Fin->driver,
308  Fin->database, Fin->table, Fout->driver, Fout->database,
309  Fout->table);
310 
311  /* TODO: db_rename_table instead of db_copy_table */
312  ret =
313  db_copy_table(Fin->driver, Fin->database, Fin->table, Fout->driver,
314  Vect_subst_var(Fout->database, &Map), Fout->table);
315 
316  if (ret == DB_FAILED) {
317  G_warning(_("Unable to copy table <%s>"), Fin->table);
318  Vect_close(&Map);
319  return -1;
320  }
321 
322  /* Change the link */
323  Vect_map_del_dblink(&Map, Fin->number);
324 
325  Vect_map_add_dblink(&Map, Fout->number, Fout->name, Fout->table,
326  Fin->key, Fout->database, Fout->driver);
327 
328  /* Delete old table */
329  ret = db_delete_table(Fin->driver, Fin->database, Fin->table);
330  if (ret == DB_FAILED) {
331  G_warning(_("Unable to delete table <%s>"), Fin->table);
332  Vect_close(&Map);
333  return -1;
334  }
335 
337  Fout->driver, Vect_subst_var(Fout->database, &Map));
338  if (driver == NULL) {
339  G_warning(_("Unable to open database <%s> by driver <%s>"),
340  Fout->database, Fout->driver);
341  }
342  else {
343  if (db_create_index2(driver, Fout->table, Fin->key) != DB_OK)
344  G_warning(_("Unable to create index for table <%s>, key <%s>"),
345  Fout->table, Fout->key);
346 
348  }
349  }
350 
351  Vect_close(&Map);
352  free(fields);
353 
354  return 0;
355 }
356 
357 /*!
358  \brief Delete vector map including attribute tables
359 
360  Vector map must be located in current mapset.
361 
362  \param map name of vector map to be delete
363 
364  \return -1 error
365  \return 0 success
366  */
367 int Vect_delete(const char *map)
368 {
369  return Vect__delete(map, FALSE);
370 }
371 
372 /*!
373  \brief Delete vector map (internal use only)
374 
375  \param map name of vector map to be delete
376  \param is_tmp TRUE for temporary maps
377 
378  \return -1 error
379  \return 0 success
380  */
381 int Vect__delete(const char *map, int is_tmp)
382 {
383  int ret;
384  char path[GPATH_MAX], path_buf[GPATH_MAX];
385  char xname[GNAME_MAX], xmapset[GMAPSET_MAX];
386  const char *tmp, *mapset, *env;
387 
388  struct Map_info Map;
389 
390  DIR *dir;
391  struct dirent *ent;
392 
393  G_debug(3, "Delete vector '%s' (is_tmp = %d)", map, is_tmp);
394 
395  mapset = G_mapset();
396 
397  /* remove mapset from fully qualified name */
398  if (G_name_is_fully_qualified(map, xname, xmapset)) {
399  if (strcmp(mapset, xmapset) != 0)
400  G_warning(_("Ignoring invalid mapset: %s"), xmapset);
401  map = xname;
402  }
403 
404  if (map == NULL || strlen(map) == 0) {
405  G_warning(_("Invalid vector map name <%s>"), map ? map : "null");
406  return -1;
407  }
408 
409  Vect_set_open_level(1); /* Topo not needed */
410  ret = Vect__open_old(&Map, map, mapset, NULL, FALSE, TRUE, is_tmp);
411  if (ret < 1) {
412  if (is_tmp)
413  return 0; /* temporary vector map doesn't exist */
414  else {
415  G_warning(_("Unable to open header file for vector map <%s>"), map);
416  return -1;
417  }
418  }
419 
421  G_debug(1, "dbln file: %s", path);
422 
423  if (access(path, F_OK) == 0) {
424  int i, n;
425  struct field_info *Fi;
426 
427  /* Delete all tables, NOT external (OGR) */
428  if (Map.format == GV_FORMAT_NATIVE) {
429  n = Vect_get_num_dblinks(&Map);
430  for (i = 0; i < n; i++) {
431  Fi = Vect_get_dblink(&Map, i);
432  if (Fi == NULL) {
433  G_warning(_("Database connection not defined for layer %d"),
434  Map.dblnk->field[i].number);
435  /*
436  Vect_close(&Map);
437  return -1;
438  */
439  continue;
440  }
441  G_debug(3, "Delete drv:db:table '%s:%s:%s'", Fi->driver,
442  Fi->database, Fi->table);
443 
444  ret = db_table_exists(Fi->driver, Fi->database, Fi->table);
445  if (ret == -1) {
446  G_warning(_("Unable to find table <%s> linked to vector "
447  "map <%s>"),
448  Fi->table, map);
449  /*
450  Vect_close(&Map);
451  return -1;
452  */
453  continue;
454  }
455 
456  if (ret == 1) {
457  ret = db_delete_table(Fi->driver, Fi->database, Fi->table);
458  if (ret == DB_FAILED) {
459  G_warning(_("Unable to delete table <%s>"), Fi->table);
460  /*
461  Vect_close(&Map);
462  return -1;
463  */
464  continue;
465  }
466  }
467  else {
468  G_warning(_("Table <%s> linked to vector map <%s> does not "
469  "exist"),
470  Fi->table, map);
471  }
472  }
473  }
474  }
475 
476  /* Delete all files from vector/name directory */
478  Vect_close(&Map);
479  G_debug(3, "opendir '%s'", path);
480  dir = opendir(path);
481  if (dir == NULL) {
482  G_warning(_("Unable to open directory '%s'"), path);
483  return -1;
484  }
485 
486  while ((ent = readdir(dir))) {
487  G_debug(3, "file = '%s'", ent->d_name);
488  if ((strcmp(ent->d_name, ".") == 0) || (strcmp(ent->d_name, "..") == 0))
489  continue;
490 
491  ret = snprintf(path_buf, GPATH_MAX, "%s/%s", path, ent->d_name);
492  if (ret >= GPATH_MAX) {
493  G_warning(_("Filepath '%s/%s' exceeds max length"), path,
494  ent->d_name);
495  closedir(dir);
496  return -1;
497  }
498  G_debug(3, "delete file '%s'", path_buf);
499  ret = unlink(path_buf);
500  if (ret == -1) {
501  G_warning(_("Unable to delete file '%s'"), path_buf);
502  closedir(dir);
503  return -1;
504  }
505  }
506  closedir(dir);
507 
508  env = getenv("GRASS_VECTOR_TMPDIR_MAPSET");
509  if (env && strcmp(env, "0") == 0) {
510  tmp = path;
511  }
512  else {
513  /* NFS can create .nfsxxxxxxxx files for those deleted
514  * -> we have to move the directory to ./tmp before it is deleted */
515  tmp = G_tempfile();
516 
517  G_debug(3, "rename '%s' to '%s'", path, tmp);
518 
519  ret = rename(path, tmp);
520  if (ret == -1) {
521  G_warning(_("Unable to rename directory '%s' to '%s'"), path, tmp);
522  return -1;
523  }
524  }
525 
526  G_debug(3, "remove directory '%s'", tmp);
527  /* Warning: remove() fails on Windows */
528  ret = rmdir(tmp);
529  if (ret == -1) {
530  G_warning(_("Unable to remove directory '%s': %s"), tmp,
531  strerror(errno));
532  return -1;
533  }
534 
535  return 0;
536 }
537 
538 /*!
539  \brief Set spatial index to be released when vector is closed.
540 
541  By default, the memory occupied by spatial index is not released.
542 
543  \param Map vector map
544  */
546 {
547  Map->plus.release_support = TRUE;
548 }
549 
550 /*!
551  \brief Set category index to be updated when vector is changed.
552 
553  By default, category index is not updated if vector is changed,
554  this function sets category index update.
555 
556  WARNING: currently only category for elements is updated not for
557  areas
558 
559  \param Map vector map
560  */
562 {
563  Map->plus.update_cidx = TRUE;
564 }
#define NULL
Definition: ccmath.h:32
#define DB_FAILED
Definition: dbmi.h:72
#define DB_OK
Definition: dbmi.h:71
dbDriver * db_start_driver_open_database(const char *, const char *)
Open driver/database connection.
Definition: db.c:28
int db_delete_table(const char *, const char *, const char *)
Delete table.
Definition: delete_tab.c:29
int db_close_database_shutdown_driver(dbDriver *)
Close driver/database connection.
Definition: db.c:61
int db_create_index2(dbDriver *, const char *, const char *)
Create unique index.
Definition: c_create_idx.c:61
int db_table_exists(const char *, const char *, const char *)
Check if table exists.
int db_copy_table(const char *, const char *, const char *, const char *, const char *, const char *)
Copy a table.
Definition: copy_tab.c:443
int G_name_is_fully_qualified(const char *, char *, char *)
Check if map name is fully qualified (map @ mapset)
Definition: nme_in_mps.c:36
const char * G_find_vector2(const char *, const char *)
Find a vector map (look but don't touch)
Definition: find_vect.c:62
void G_zero(void *, int)
Zero out a buffer, buf, of length i.
Definition: gis/zero.c:23
void G_free(void *)
Free allocated memory.
Definition: gis/alloc.c:150
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
void G_warning(const char *,...) __attribute__((format(printf
int G_make_mapset_dir_object(const char *, const char *)
Create directory for an object of a given type.
Definition: mapset_msc.c:108
char * G_file_name(char *, const char *, const char *, const char *)
Builds full path names to GIS data files.
Definition: file_name.c:61
#define G_malloc(n)
Definition: defs/gis.h:94
char * G_tempfile(void)
Returns a temporary file name.
Definition: tempfile.c:62
const char * G_mapset(void)
Get current mapset name.
Definition: gis/mapset.c:33
int G_debug(int, const char *,...) __attribute__((format(printf
int G_rename(const char *, const char *, const char *)
Rename a database file.
Definition: rename.c:69
struct field_info * Vect_default_field_info(struct Map_info *, int, const char *, int)
Get default information about link to database for new dblink.
Definition: field.c:358
int Vect_map_del_dblink(struct Map_info *, int)
Delete db connection from Map_info structure.
Definition: field.c:160
int Vect_close(struct Map_info *)
Close vector map.
int Vect_open_update_head(struct Map_info *, const char *, const char *)
Open header file of existing vector map for updating (mostly for database link updates)
int Vect_legal_filename(const char *)
Check if output is legal vector name.
Definition: legal_vname.c:32
char * Vect_subst_var(const char *, struct Map_info *)
Substitute variable in string.
Definition: field.c:912
int Vect_get_num_dblinks(struct Map_info *)
Get number of defined dblinks.
Definition: level_two.c:159
struct field_info * Vect_get_field(struct Map_info *, int)
Get information about link to database (by layer number)
Definition: field.c:515
struct field_info * Vect_get_dblink(struct Map_info *, int)
Get information about link to database.
Definition: field.c:475
int Vect_map_add_dblink(struct Map_info *, int, const char *, const char *, const char *, const char *, const char *)
Add new db connection to Map_info structure.
Definition: field.c:120
int Vect_set_open_level(int)
Predetermine level at which a vector map will be opened for reading.
int Vect_open_old_head(struct Map_info *, const char *, const char *)
Reads only info about vector map (headers)
int Vect_copy_tables(struct Map_info *, struct Map_info *, int)
Copy attribute tables linked to vector map.
#define GV_FRMT_ELEMENT
Format description, data location (OGR)
Definition: dig_defines.h:10
#define GV_DIRECTORY
Name of vector directory.
Definition: dig_defines.h:8
#define GV_SIDX_ELEMENT
Native format, spatial index.
Definition: dig_defines.h:22
#define GV_1TABLE
One table linked to vector map.
Definition: dig_defines.h:99
#define GV_COOR_ELEMENT
Native format, coordinates.
Definition: dig_defines.h:12
#define GV_DBLN_ELEMENT
Native format, link to database.
Definition: dig_defines.h:16
#define GV_CIDX_ELEMENT
Native format, category index.
Definition: dig_defines.h:24
#define GV_TOPO_ELEMENT
Native format, topology file.
Definition: dig_defines.h:20
#define GV_MTABLE
More tables linked to vector map.
Definition: dig_defines.h:101
#define GV_HEAD_ELEMENT
Native format, header information.
Definition: dig_defines.h:14
#define GV_FORMAT_NATIVE
Geometry data formats supported by lib Don't change GV_FORMAT_* values, this order is hardcoded in li...
Definition: dig_defines.h:83
#define GV_HIST_ELEMENT
Native format, history file.
Definition: dig_defines.h:18
const struct driver * driver
Definition: driver/init.c:25
#define GMAPSET_MAX
Definition: gis.h:192
#define GPATH_MAX
Definition: gis.h:194
#define TRUE
Definition: gis.h:79
#define FALSE
Definition: gis.h:83
#define GNAME_MAX
Definition: gis.h:191
#define _(str)
Definition: glocale.h:10
void Vect_set_category_index_update(struct Map_info *Map)
Set category index to be updated when vector is changed.
Definition: map.c:561
void Vect_destroy_map_struct(struct Map_info *p)
Frees all memory associated with a Map_info structure, including the structure itself.
Definition: map.c:61
int Vect__delete(const char *map, int is_tmp)
Delete vector map (internal use only)
Definition: map.c:381
int Vect_rename(const char *in, const char *out)
Rename existing vector map (in the current mapset).
Definition: map.c:233
int Vect_copy(const char *in, const char *mapset, const char *out)
Copy vector map including attribute tables.
Definition: map.c:125
void Vect_set_release_support(struct Map_info *Map)
Set spatial index to be released when vector is closed.
Definition: map.c:545
struct Map_info * Vect_new_map_struct(void)
Creates and initializes Map_info structure.
Definition: map.c:41
int Vect_delete(const char *map)
Delete vector map including attribute tables.
Definition: map.c:367
void * malloc(YYSIZE_T)
void free(void *)
Vector map info.
Definition: dig_structs.h:1243
char * mapset
Mapset name.
Definition: dig_structs.h:1320
int type
Feature type constraint.
Definition: dig_structs.h:1359
int open
Open indicator.
Definition: dig_structs.h:1280
int format
Map format (native, ogr, postgis)
Definition: dig_structs.h:1255
struct dblinks * dblnk
Array of DB links.
Definition: dig_structs.h:1265
struct Plus_head plus
Plus info (topology, version, ...)
Definition: dig_structs.h:1270
int release_support
Release memory occupied by support structures (topo, spatial, category)
Definition: dig_structs.h:822
int update_cidx
Update category index if vector is modified.
Definition: dig_structs.h:1120
Definition: driver.h:21
Layer (old: field) information.
Definition: dig_structs.h:131
char * table
Name of DB table.
Definition: dig_structs.h:151
char * driver
Name of DB driver ('sqlite', 'dbf', ...)
Definition: dig_structs.h:143
char * name
Layer name (optional)
Definition: dig_structs.h:139
char * database
Definition: dig_structs.h:147
char * key
Name of key column (usually 'cat')
Definition: dig_structs.h:155
int number
Layer number.
Definition: dig_structs.h:135
Definition: path.h:15
int Vect__open_old(struct Map_info *Map, const char *name, const char *mapset, const char *layer, int update, int head_only, int is_tmp)
Open existing vector map for reading (internal use only)
char * Vect__get_element_path(char *file_path, struct Map_info *Map, const char *element)
Get map element full path (internal use only)