/*! \brief Old sites library These functions and definitions support the site format for 5.0 (format proposed by Dave Gerdes): \verbatim easting|northing|[z|[d4|]...][#category_int] [ [@attr_text OR %flt] ... ] \endverbatim to allow multidimensions (everything preceding the last '|') and any number of text or numeric attribute fields. \author James Darrell McCauley (31 Jan 1994) */ #include #include #include #include #include #include #include #include #include #include #define DQUOTE '"' #define SPACE ' ' #define BSLASH 92 #define PIPE '|' #define ispipe(c) (c==PIPE) #define isnull(c) (c=='\0') #define FOUND_ALL(s,n,dim,c,d) (((s->cattype != -1 && !n) || \ (dim < s->dim_alloc) || \ (c < s->str_alloc) || \ (d < s->dbl_alloc))?0:1) static char *next_att(const char *); static int cleanse_string(char *); static int G__oldsite_get(FILE *, Site *, int); static int site_att_cmp(const void *pa, const void *pb) { const struct site_att *a = pa, *b = pb; return a->cat - b->cat; } /*! \brief Get site \param Map \param s \return 0 on success \return -1 on EOF \return -2 on other fatal error or insufficient data, \return 1 on format mismatch (extra data) */ int G_site_get(struct Map_info *Map, Site * s) { int i, type, cat; static struct line_pnts *Points = NULL; static struct line_cats *Cats = NULL; struct site_att *sa; if (Points == NULL) Points = Vect_new_line_struct(); if (Cats == NULL) Cats = Vect_new_cats_struct(); while (1) { type = Vect_read_next_line(Map, Points, Cats); if (type == -1) return -2; /* Error */ if (type == -2) return -1; /* EOF */ if (type != GV_POINT) continue; /* Is not point */ Vect_cat_get(Cats, 1, &cat); G_debug(4, "Site: %f|%f|%f|#%d", Points->x[0], Points->y[0], Points->z[0], cat); s->east = Points->x[0]; s->north = Points->y[0]; if (Vect_is_3d(Map)) s->dim[0] = Points->z[0]; s->ccat = cat; /* find att */ if (Map->n_site_att > 0) { sa = (struct site_att *) bsearch((void *)&cat, (void *)Map->site_att, Map->n_site_att, sizeof(struct site_att), site_att_cmp); if (sa == NULL) { G_warning(_("Attributes for category %d not found"), cat); for (i = 0; i < Map->n_site_dbl; i++) s->dbl_att[i] = 0; for (i = 0; i < Map->n_site_str; i++) strncpy(s->str_att[i], "", MAX_SITE_STRING); } else { for (i = 0; i < Map->n_site_dbl; i++) s->dbl_att[i] = sa->dbl[i]; for (i = 0; i < Map->n_site_str; i++) strncpy(s->str_att[i], sa->str[i], MAX_SITE_STRING); } } return 0; } } /*! \brief Writes a site to file open on fptr \param Map \param s \return */ int G_site_put(struct Map_info *Map, const Site * s) { static struct line_pnts *Points = NULL; static struct line_cats *Cats = NULL; if (Points == NULL) Points = Vect_new_line_struct(); if (Cats == NULL) Cats = Vect_new_cats_struct(); Vect_reset_line(Points); Vect_reset_cats(Cats); /* no 3D support so far: s->dim[0] */ Vect_append_point(Points, s->east, s->north, 0.0); G_debug(4, "cattype = %d", s->cattype); if (s->cattype == FCELL_TYPE || s->cattype == DCELL_TYPE) G_fatal_error(_("Category must be integer")); if (s->cattype == CELL_TYPE) Vect_cat_set(Cats, 1, s->ccat); Vect_write_line(Map, GV_POINT, Points, Cats); return 0; } /*! \brief Tries to guess the format of a sites list The dimensionality, the presence/type of a category, and the number of string and decimal attributes) by reading the first record in the file. \return 0 on success \return -1 on EOF \return -2 for other error */ int G_site_describe(struct Map_info *Map, int *dims, int *cat, int *strs, int *dbls) { if (Vect_is_3d(Map)) { G_debug(1, "Vector is 3D -> number of site dimensions is 3"); *dims = 3; } else { G_debug(1, "Vector is 2D -> number of site dimensions is 2"); *dims = 2; } *cat = CELL_TYPE; /* attributes ignored for now, later read from DB */ *dbls = Map->n_site_dbl; *strs = Map->n_site_str; return 0; } /*! \brief Writes site_head struct */ int G_site_put_head(struct Map_info *Map, Site_head * head) { static char buf[128]; if (head->name != NULL) Vect_set_map_name(Map, head->name); /* crashes: if (head->desc!=NULL) Vect_set_comment (Map, head->desc); */ /* if (head->form!=NULL) fprintf(ptr,"form|%s\n",head->form); if (head->labels!=NULL) fprintf(ptr,"labels|%s\n",head->labels); */ /* time could be in (char *) stime, (struct TimeStamp *) time, both, or neither */ if (head->stime != NULL || head->time != NULL) { if (head->time != NULL) { /* TimeStamp struct has precendence */ G_format_timestamp(head->time, buf); Vect_set_date(Map, buf); } else if (head->stime != NULL) { /* next check string */ if (head->time == NULL) { if ((head->time = (struct TimeStamp *)G_malloc(sizeof(struct TimeStamp))) == NULL) G_fatal_error(_("Memory error in writing timestamp")); else if (G_scan_timestamp(head->time, head->stime) < 0) { G_warning(_("Illegal TimeStamp string")); return -1; /* added to prevent crash 5/2000 MN */ } } G_format_timestamp(head->time, buf); head->stime = G_store(buf); Vect_set_date(Map, head->stime); } } return 0; } struct Map_info *G_sites_open_old(const char *name, const char *mapset) { struct Map_info *Map; struct field_info *fi; int more, nrows, row, ncols, col, ndbl, nstr, adbl, astr, ctype; struct site_att *sa; dbDriver *driver; dbString stmt; dbCursor cursor; dbTable *table; dbColumn *column; dbValue *value; G_message( _("Dev note: Adapted sites library used for vector points. " "(module should be updated to GRASS 6 vector library)")); Map = (struct Map_info *)G_malloc(sizeof(struct Map_info)); Vect_set_open_level(1); Vect_open_old(Map, name, mapset); G_debug(1, "Vector map opened"); /* Load attributes */ Map->site_att = NULL; Map->n_site_att = 0; Map->n_site_dbl = 0; Map->n_site_str = 0; fi = Vect_get_field(Map, 1); if (fi == NULL) { /* not attribute table */ G_debug(1, "No attribute table"); return Map; } driver = db_start_driver_open_database(fi->driver, fi->database); if (driver == NULL) G_fatal_error(_("Unable to open database <%s> by driver <%s>"), fi->database, fi->driver); db_init_string(&stmt); db_set_string(&stmt, "select * from "); db_append_string(&stmt, fi->table); if (db_open_select_cursor(driver, &stmt, &cursor, DB_SEQUENTIAL) != DB_OK) G_fatal_error(_("Unable to open select cursor: '%s'"), db_get_string(&stmt)); nrows = db_get_num_rows(&cursor); G_debug(1, "%d rows selected from vector attribute table", nrows); Map->site_att = (struct site_att *) malloc(nrows * sizeof(struct site_att)); Map->n_site_att = nrows; table = db_get_cursor_table(&cursor); ncols = db_get_table_number_of_columns(table); row = 0; adbl = astr = 0; while (1) { if (db_fetch(&cursor, DB_NEXT, &more) != DB_OK) G_fatal_error(_("Cannot fetch row")); if (!more) break; /* Get number of each type */ if (row == 0) { for (col = 0; col < ncols; col++) { column = db_get_table_column(table, col); ctype = db_sqltype_to_Ctype(db_get_column_sqltype(column)); if (strcmp(db_get_column_name(column), fi->key) == 0) continue; switch (ctype) { case DB_C_TYPE_INT: case DB_C_TYPE_DOUBLE: adbl++; break; case DB_C_TYPE_STRING: case DB_C_TYPE_DATETIME: astr++; break; } } Map->n_site_dbl = adbl; Map->n_site_str = astr; G_debug(1, "adbl = %d astr = %d", adbl, astr); } sa = &(Map->site_att[row]); sa->dbl = (double *)malloc(adbl * sizeof(double)); sa->str = (char **)malloc(astr * sizeof(char *)); ndbl = nstr = 0; for (col = 0; col < ncols; col++) { column = db_get_table_column(table, col); ctype = db_sqltype_to_Ctype(db_get_column_sqltype(column)); value = db_get_column_value(column); if (strcmp(db_get_column_name(column), fi->key) == 0) { sa->cat = db_get_value_int(value); } else { switch (ctype) { case DB_C_TYPE_INT: sa->dbl[ndbl] = db_get_value_int(value); ndbl++; break; case DB_C_TYPE_DOUBLE: sa->dbl[ndbl] = db_get_value_double(value); ndbl++; break; case DB_C_TYPE_STRING: sa->str[nstr] = G_store(db_get_value_string(value)); nstr++; break; case DB_C_TYPE_DATETIME: sa->str[nstr] = ""; /* TODO */ nstr++; break; } } } row++; } db_close_database_shutdown_driver(driver); /* sort attributes */ qsort((void *)Map->site_att, Map->n_site_att, sizeof(struct site_att), site_att_cmp); return Map; } struct Map_info *G_sites_open_new(const char *name) { struct Map_info *Map; G_message( _("Dev note: Adapted sites library used for vector points. " "(module should be updated to GRASS 6 vector library)")); G_warning("Site/vector attributes ignored."); Map = (struct Map_info *)G_malloc(sizeof(struct Map_info)); Vect_open_new(Map, name, 0); G_debug(1, "New vector map opened"); return Map; } struct Map_info *G_fopen_sites_old(const char *name, const char *mapset) { return G_sites_open_old(name, mapset); } struct Map_info *G_fopen_sites_new(const char *name) { return G_sites_open_new(name); } /*! \brief Free memory for a Site struct \param s */ void G_site_free_struct(Site * s) { if (s->dim_alloc) G_free(s->dim); if (s->str_alloc) G_free(s->str_att); if (s->dbl_alloc) G_free(s->dbl_att); G_free(s); return; } /*! \brief Allocate memory for a Site struct. cattype= -1 (no cat), CELL_TYPE, FCELL_TYPE, or DCELL_TYPE \return properly allocated site struct or NULL on error. */ Site *G_site_new_struct(RASTER_MAP_TYPE cattype, int n_dim, int n_s_att, int n_d_att) { int i; Site *s; if (n_dim < 2 || n_s_att < 0 || n_d_att < 0) G_fatal_error(_("G_oldsite_new_struct: invalid # dims or fields")); if ((s = (Site *) G_malloc(sizeof(Site))) == NULL) return (Site *) NULL; s->cattype = cattype; s->ccat = s->fcat = s->dcat = 0; if (n_dim > 2) { if ((s->dim = (double *)G_malloc((n_dim - 2) * sizeof(double))) == NULL) { G_free(s); return (Site *) NULL; } } s->dim_alloc = n_dim - 2; if (n_d_att > 0) { if ((s->dbl_att = (double *)G_malloc(n_d_att * sizeof(double))) == NULL) { if (n_dim > 2) G_free(s->dim); G_free(s); return (Site *) NULL; } } s->dbl_alloc = n_d_att; if (n_s_att > 0) { if ((s->str_att = (char **)G_malloc(n_s_att * sizeof(char *))) == NULL) { if (n_d_att > 0) G_free(s->dbl_att); if (n_dim > 2) G_free(s->dim); G_free(s); return (Site *) NULL; } else for (i = 0; i < n_s_att; ++i) if ((s->str_att[i] = (char *)G_malloc(MAX_SITE_STRING * sizeof(char))) == NULL) { while (--i) G_free(s->str_att[i]); G_free(s->str_att); if (n_d_att > 0) G_free(s->dbl_att); if (n_dim > 2) G_free(s->dim); G_free(s); return (Site *) NULL; } } s->str_alloc = n_s_att; return s; } /*! \brief Writes a site to file open on fptr */ int G_oldsite_get(FILE * fptr, Site * s) { return G__oldsite_get(fptr, s, G_projection()); } /*! \brief Get site (old version) \return 0 on success, \return -1 on EOF, \return -2 on other fatal error or insufficient data, \return 1 on format mismatch (extra data) */ int G__oldsite_get(FILE * ptr, Site * s, int fmt) { char sbuf[MAX_SITE_LEN], *buf, *last, *p1, *p2; char ebuf[128], nbuf[128]; int n = 0, d = 0, c = 0, dim = 0, err = 0, tmp; buf = sbuf; if ((buf = fgets(sbuf, 1024, ptr)) == (char *)NULL) return EOF; while ((*buf == '#' || !isdigit(*buf)) && *buf != '-' && *buf != '+') if ((buf = fgets(sbuf, 1024, ptr)) == (char *)NULL) return EOF; if (buf[strlen(buf) - 1] == '\n') buf[strlen(buf) - 1] = '\0'; if (sscanf(buf, "%[^|]|%[^|]|%*[^\n]", ebuf, nbuf) < 2) { fprintf(stderr, "ERROR: ebuf %s nbuf %s\n", ebuf, nbuf); return -2; } if (!G_scan_northing(nbuf, &(s->north), fmt) || !G_scan_easting(ebuf, &(s->east), fmt)) { fprintf(stderr, "ERROR: ebuf %s nbuf %s\n", ebuf, nbuf); return -2; } /* move pointer past easting and northing fields */ if (NULL == (buf = strchr(buf, PIPE))) return -2; if (NULL == (buf = strchr(buf + 1, PIPE))) return -2; /* check for remaining dimensional fields */ do { buf++; if (isnull(*buf)) return (FOUND_ALL(s, n, dim, c, d) ? 0 : -2); last = buf; if (dim < s->dim_alloc) { /* should be more dims to read */ if (sscanf(buf, "%lf|", &(s->dim[dim++])) < 1) return -2; /* no more dims, though expected */ } else if (NULL != (p1 = strchr(buf, PIPE))) { if (NULL == (p2 = strchr(buf, DQUOTE))) err = 1; /* more dims, though none expected */ else if (strlen(p1) > strlen(p2)) err = 1; /* more dims, though none expected */ } } while ((buf = strchr(buf, PIPE)) != NULL); buf = last; /* no more dimensions-now we parse attribute fields */ while (!isnull(*buf)) { switch (*buf) { case '#': /* category field */ if (n == 0) { switch (s->cattype) { case CELL_TYPE: if (sscanf(buf, "#%d", &s->ccat) == 1) n++; break; case FCELL_TYPE: if (sscanf(buf, "#%f", &s->fcat) == 1) n++; break; case DCELL_TYPE: if (sscanf(buf, "#%lf", &s->dcat) == 1) n++; break; default: err = 1; /* has cat, none expected */ break; } } else { err = 1; /* extra cat */ } /* move to beginning of next attribute */ if ((buf = next_att(buf)) == (char *)NULL) return (FOUND_ALL(s, n, dim, c, d) ? err : -2); break; case '%': /* decimal attribute */ if (d < s->dbl_alloc) { p1 = ++buf; errno = 0; s->dbl_att[d++] = strtod(buf, &p1); if (p1 == buf || errno == ERANGE) { /* replace with: * s->dbl_att[d - 1] = NAN * when we add NULL attribute support */ return -2; } /* err = 0; Make sure this is zeroed */ } else { err = 1; /* extra decimal */ } if ((buf = next_att(buf)) == (char *)NULL) { return (FOUND_ALL(s, n, dim, c, d)) ? err : -2; } break; case '@': /* string attribute */ if (isnull(*buf) || isnull(*(buf + 1))) return (FOUND_ALL(s, n, dim, c, d) ? err : -2); else buf++; default: /* defaults to string attribute */ /* allow both prefixed and unprefixed strings */ if (c < s->str_alloc) { if ((tmp = cleanse_string(buf)) > 0) { strncpy(s->str_att[c++], buf, tmp); buf += tmp; } else return (FOUND_ALL(s, n, dim, c, d) ? err : -2); } if ((buf = next_att(buf)) == (char *)NULL) { return (FOUND_ALL(s, n, dim, c, d) ? err : -2); } break; } } return (FOUND_ALL(s, n, dim, c, d) ? err : -2); } /*! \brief Tries to guess the format of a sites list (old version) The dimensionality, the presence/type of a category, and the number of string and decimal attributes) by reading the first record in the file. \return 0 on success, \return -1 on EOF, \return -2 for other error */ int G_oldsite_describe(FILE * ptr, int *dims, int *cat, int *strs, int *dbls) { char sbuf[MAX_SITE_LEN], *buf; char ebuf[128], nbuf[128]; int err; int itmp; float ftmp; if (G_ftell(ptr) != 0L) { G_warning(_("G_oldsite_describe() must be called " "immediately after G_fopen_sites_old().")); return -2; } *dims = *strs = *dbls = 0; *cat = -1; buf = sbuf; if ((buf = fgets(sbuf, 1024, ptr)) == (char *)NULL) { rewind(ptr); return EOF; } /* skip over comment & header lines */ while ((*buf == '#' || !isdigit(*buf)) && *buf != '-' && *buf != '+') if ((buf = fgets(sbuf, 1024, ptr)) == (char *)NULL) { rewind(ptr); return EOF; } if (buf[strlen(buf) - 1] == '\n') buf[strlen(buf) - 1] = '\0'; if ((err = sscanf(buf, "%[^|]|%[^|]|%*[^\n]", ebuf, nbuf)) < 2) { G_debug(1, "ebuf %s nbuf %s", ebuf, nbuf); rewind(ptr); return -2; } *dims = 2; /* move pointer past easting and northing fields */ while (!ispipe(*buf) && !isnull(*buf)) buf++; if (!isnull(*buf) && !isnull(*(buf + 1))) buf++; else { rewind(ptr); return -2; } while (!ispipe(*buf) && !isnull(*buf)) buf++; if (!isnull(*buf) && !isnull(*(buf + 1))) buf++; else { rewind(ptr); return 0; } /* check for remaining dimensional fields */ while (strchr(buf, PIPE) != (char *)NULL) { (*dims)++; while (!ispipe(*buf) && !isnull(*buf)) buf++; if (isnull(*buf) || isnull(*(buf + 1))) { rewind(ptr); return 0; } if (!isnull(*(buf + 1))) buf++; else { rewind(ptr); return -2; } } /* no more dimensions-now we parse attribute fields */ while (!isnull(*buf)) { switch (*buf) { case '#': /* category field */ sscanf(buf, "#%s ", ebuf); if (strstr(ebuf, ".") == NULL && sscanf(ebuf, "%d", &itmp) == 1) *cat = CELL_TYPE; else if (strstr(ebuf, ".") != NULL && sscanf(ebuf, "%f", &ftmp) == 1) *cat = FCELL_TYPE; else *cat = -1; /* move to beginning of next attribute */ while (!isspace(*buf) && !isnull(*buf)) buf++; if (isnull(*buf) || isnull(*(buf + 1))) { rewind(ptr); return 0; } else buf++; break; case '%': /* decimal attribute */ (*dbls)++; /* move to beginning of next attribute */ while (!isspace(*buf) && !isnull(*buf)) buf++; if (isnull(*buf) || isnull(*(buf + 1))) { rewind(ptr); return 0; } else buf++; break; case '@': /* string attribute */ if (isnull(*buf) || isnull(*(buf + 1))) { rewind(ptr); return 0; } else buf++; default: /* defaults to string attribute */ /* allow both prefixed and unprefixed strings */ if ((err = cleanse_string(buf)) > 0) { (*strs)++; buf += err; } /* move to beginning of next attribute */ while (!isspace(*buf) && !isnull(*buf)) buf++; if (isnull(*buf) || isnull(*(buf + 1))) { rewind(ptr); return 0; } else buf++; break; } } rewind(ptr); return 0; } /*! \brief Test if site is in region \return 1 if site is contained within region \return 0 otherwise */ int G_site_in_region(const Site * site, const struct Cell_head *region) { /* northwest corner is in region, southeast corner is not. */ double e_ing; e_ing = G_adjust_easting(site->east, region); if (e_ing >= region->west && e_ing < region->east && site->north <= region->north && site->north > region->south) return 1; return 0; } int cleanse_string(char *buf) { char *stop, *p, *p2; p = buf; /* * get rid of any SPACEs at beginning while ( !isspace(*buf) && *buf != * (char) NULL) buf++; if (*buf == (char) NULL) return -1; */ /* find where this string terminates */ if (*buf != DQUOTE) { /* if no DQUOTEs, */ stop = strchr(buf, SPACE); /* then SPACE separates */ if (stop == (char *)NULL) return strlen(buf); else return (int)(stop - buf); } else { /* otherwise string is in DQUOTEs */ /* but we must skip over escaped */ /* (BSLASHed) DQUOTEs */ if (*p == DQUOTE) { while (*p != '\0') { /* get rid of first DQUOTE */ *p = *(p + 1); p++; } p = buf; stop = strchr(p + 1, DQUOTE); while (*(stop - 1) == BSLASH) stop = strchr(++stop, DQUOTE); } } /* remove backslashes between buf and stop */ p = buf; while ((p = strchr(p, BSLASH)) != (char *)NULL && p <= stop) { p2 = p + 1; if (*p2 != '\0' && (*p2 == DQUOTE || *p2 == BSLASH)) { while (*p != '\0') { *p = *(p + 1); p++; } stop--; } p = p2; } return (int)(stop - buf); } char *next_att(const char *buf) { while (!isspace(*buf) && !isnull(*buf)) buf++; if (isnull(*buf) || isnull(*(buf + 1))) return NULL; else while (isspace(*(buf + 1)) && !isnull(*(buf + 1))) buf++; buf++; return (char *)buf; } int G_oldsite_s_cmp(const void *a, const void *b) /* qsort() comparison function for sorting an array of site structures by first decimal attribute. */ { return strcmp((*(char **)((*(Site **) a)->str_att)), (*(char **)((*(Site **) b)->str_att))); } /*! \brief Open site list (old version) Opens the existing site list file 'name' in the 'mapset'. \param name \param mapset mapset (empty for search path) \return pointer to FILE */ FILE *G_oldsites_open_old(const char *name, const char *mapset) { return G_fopen_old("site_lists", name, mapset); }