예제 #1
0
/*!
   \brief Get next marked raster categories (DCELL)

   \param pcats pointer to Categories structure
   \param rast1, rast2 cell values (raster range)
   \param[out] count count

   \return NULL if not found
   \return description if found
 */
char *Rast_get_next_marked_d_cat(struct Categories *pcats,
				 DCELL * rast1, DCELL * rast2, long *count)
{
    char *descr = NULL;
    int found, i;

    found = 0;
    /* pcats->ncats should be == Rast_quant_nof_rules(&pcats->q) */

    G_debug(3, "last marked %d nrules %d\n", pcats->last_marked_rule,
	    Rast_quant_nof_rules(&pcats->q));

    for (i = pcats->last_marked_rule + 1; i < Rast_quant_nof_rules(&pcats->q);
	 i++) {
	descr = Rast_get_ith_d_cat(pcats, i, rast1, rast2);
	G_debug(5, "%d %d", i, pcats->marks[i]);
	if (pcats->marks[i]) {
	    found = 1;
	    break;
	}
    }

    if (!found)
	return NULL;

    *count = pcats->marks[i];
    pcats->last_marked_rule = i;
    return descr;
}
예제 #2
0
/*!
   \brief Sort categories

   \param pcats pointer to Categories structure

   \return -1 on error (nothing to sort)
   \return 0 on success
 */
int Rast_sort_cats(struct Categories *pcats)
{
    int *indexes, i, ncats;
    char *descr;
    DCELL d1, d2;

    if (pcats->ncats <= 1)
	return -1;

    ncats = pcats->ncats;
    G_debug(3, "Rast_sort_cats(): Copying to save cats buffer");
    Rast_copy_cats(&save_cats, pcats);
    Rast_free_cats(pcats);

    indexes = (int *)G_malloc(sizeof(int) * ncats);
    for (i = 0; i < ncats; i++)
	indexes[i] = i;

    qsort(indexes, ncats, sizeof(int), cmp);
    Rast_init_cats(save_cats.title, pcats);
    for (i = 0; i < ncats; i++) {
	descr = Rast_get_ith_d_cat(&save_cats, indexes[i], &d1, &d2);
	G_debug(4, "  Write sorted cats, pcats = %p pcats->labels = %p",
		pcats, pcats->labels);
	Rast_set_d_cat(&d1, &d2, descr, pcats);
    }
    Rast_free_cats(&save_cats);

    return 0;
}
예제 #3
0
static void write_cats(const char *element, const char *name,
		       struct Categories *cats)
{
    FILE *fd;
    int i, fp_map;
    char *descr;
    DCELL val1, val2;
    char str1[100], str2[100];

    fd = G_fopen_new(element, name);
    if (!fd)
	G_fatal_error(_("Unable to open %s file for map <%s>"), element, name);

    /* write # cats - note # indicate 3.0 or later */
    fprintf(fd, "# %ld categories\n", (long)cats->num);

    /* title */
    fprintf(fd, "%s\n", cats->title != NULL ? cats->title : "");

    /* write format and coefficients */
    fprintf(fd, "%s\n", cats->fmt != NULL ? cats->fmt : "");
    fprintf(fd, "%.2f %.2f %.2f %.2f\n",
	    cats->m1, cats->a1, cats->m2, cats->a2);

    /* if the map is integer or if this is a vector map, sort labels */
    if (strncmp(element, "dig", 3) == 0)
	fp_map = 0;
    else
	fp_map = Rast_map_is_fp(name, G_mapset());
    if (!fp_map)
	Rast_sort_cats(cats);

    /* write the cat numbers:label */
    for (i = 0; i < Rast_quant_nof_rules(&cats->q); i++) {
	descr = Rast_get_ith_d_cat(cats, i, &val1, &val2);
	if ((cats->fmt && cats->fmt[0])
	    || (descr && descr[0])) {
	    if (val1 == val2) {
		sprintf(str1, "%.10f", val1);
		G_trim_decimal(str1);
		fprintf(fd, "%s:%s\n", str1, descr != NULL ? descr : "");
	    }
	    else {
		sprintf(str1, "%.10f", val1);
		G_trim_decimal(str1);
		sprintf(str2, "%.10f", val2);
		G_trim_decimal(str2);
		fprintf(fd, "%s:%s:%s\n", str1, str2,
			descr != NULL ? descr : "");
	    }
	}
    }
    fclose(fd);
}
예제 #4
0
/*!
 * \brief Get category description
 *
 * Returns i-th description and i-th data range from the list of
 * category descriptions with corresponding data ranges. end points of
 * data interval in <i>rast1</i> and <i>rast2</i>.
 *
 * \param pcats pointer to Categories structure
 * \param i index
 * \param rast1, rast2 raster values (range)
 * \param data_type map type
 *
 * \return "" on error
 * \return pointer to category description
 */
char *Rast_get_ith_cat(const struct Categories *pcats, int i, void *rast1,
		       void *rast2, RASTER_MAP_TYPE data_type)
{
    char *tmp;
    DCELL val1, val2;

    tmp = Rast_get_ith_d_cat(pcats, i, &val1, &val2);
    Rast_set_d_value(rast1, val1, data_type);
    Rast_set_d_value(rast2, val2, data_type);
    return tmp;
}
예제 #5
0
/*!
 * \brief Copy raster categories
 *
 * Allocates NEW space for quant rules and labels n <i>pcats_to</i>
 * and copies all info from <i>pcats_from</i> cats to
 * <i>pcats_to</i> cats.
 *
 * \param pcats_to pointer to destination Categories structure
 * \param pcats_from pointer to source Categories structure
 */
void Rast_copy_cats(struct Categories *pcats_to,
		    const struct Categories *pcats_from)
{
    int i;
    char *descr;
    DCELL d1, d2;

    Rast_init_cats(pcats_from->title, pcats_to);
    for (i = 0; i < pcats_from->ncats; i++) {
	descr = Rast_get_ith_d_cat(pcats_from, i, &d1, &d2);
	Rast_set_d_cat(&d1, &d2, descr, pcats_to);
    }
}
예제 #6
0
int Rast3d_write_cats(const char *name, struct Categories *cats)
 /* adapted from Rast_write_cats */
{
    FILE *fd;
    int i;
    const char *descr;
    DCELL val1, val2;
    char str1[100], str2[100];

    fd = G_fopen_new_misc(RASTER3D_DIRECTORY, RASTER3D_CATS_ELEMENT, name);
    if (!fd)
	return -1;

    /* write # cats - note # indicate 3.0 or later */
    fprintf(fd, "# %ld categories\n", (long)cats->num);

    /* title */
    fprintf(fd, "%s\n", cats->title != NULL ? cats->title : "");

    /* write format and coefficients */
    fprintf(fd, "%s\n", cats->fmt != NULL ? cats->fmt : "");
    fprintf(fd, "%.2f %.2f %.2f %.2f\n",
	    cats->m1, cats->a1, cats->m2, cats->a2);

    /* write the cat numbers:label */
    for (i = 0; i < Rast_quant_nof_rules(&cats->q); i++) {
	descr = Rast_get_ith_d_cat(cats, i, &val1, &val2);
	if ((cats->fmt && cats->fmt[0]) || (descr && descr[0])) {
	    if (val1 == val2) {
		sprintf(str1, "%.10f", val1);
		G_trim_decimal(str1);
		fprintf(fd, "%s:%s\n", str1, descr != NULL ? descr : "");
	    }
	    else {
		sprintf(str1, "%.10f", val1);
		G_trim_decimal(str1);
		sprintf(str2, "%.10f", val2);
		G_trim_decimal(str2);
		fprintf(fd, "%s:%s:%s\n", str1, str2,
			descr != NULL ? descr : "");
	    }
	}
    }
    fclose(fd);
    return 1;
}
예제 #7
0
/*!
 * \brief Set a raster category label (DCELL)
 *
 * Adds the label for range <i>rast1</i> through <i>rast2</i> in
 * category structure <i>pcats</i>.
 *
 * \param rast1, rast2 raster values (range)
 * \param label category label
 * \param pcats pointer to Categories structure
 *
 * \return -1 on error
 * \return 0 if null value detected
 * \return 1 on success
 */
int Rast_set_d_cat(const DCELL * rast1, const DCELL * rast2,
		   const char *label, struct Categories *pcats)
{
    long len;
    DCELL dtmp1, dtmp2;
    int i;
    char *descr;

    /* DEBUG fprintf(stderr,"Rast_set_d_cat(rast1 = %p,rast2 = %p,label = '%s',pcats = %p)\n",
       rast1,rast2,label,pcats); */
    if (Rast_is_d_null_value(rast1))
	return 0;
    if (Rast_is_d_null_value(rast2))
	return 0;
    /* DEBUG fprintf (stderr, "Rast_set_d_cat(): adding quant rule: %f %f %d %d\n", *rast1, *rast2, pcats->ncats, pcats->ncats); */
    /* the set_cat() functions are used in many places to reset the labels
       for the range (or cat) with existing label. In this case we don't
       want to store both rules with identical range even though the result
       of get_cat() will be correct, since it will use rule added later.
       we don't want to overuse memory and we don't want rules which are
       not used to be writen out in cats file. So we first look if
       the label for this range has been sen, and if it has, overwrite it */

    for (i = 0; i < pcats->ncats; i++) {
	descr = Rast_get_ith_d_cat(pcats, i, &dtmp1, &dtmp2);
	if ((dtmp1 == *rast1 && dtmp2 == *rast2)
	    || (dtmp1 == *rast2 && dtmp2 == *rast1)) {
	    if (pcats->labels[i] != NULL)
		G_free(pcats->labels[i]);
	    pcats->labels[i] = G_store(label);
	    G_newlines_to_spaces(pcats->labels[i]);
	    G_strip(pcats->labels[i]);
	    return 1;
	}
    }
    /* when rule for this range does not exist */
    /* DEBUG fprintf (stderr, "Rast_set_d_cat(): New rule: adding %d %p\n", i, pcats->labels); */
    Rast_quant_add_rule(&pcats->q, *rast1, *rast2, pcats->ncats,
			pcats->ncats);
    pcats->ncats++;
    if (pcats->nalloc < pcats->ncats) {
	/* DEBUG fprintf (stderr, "Rast_set_d_cat(): need more space nalloc = %d ncats = %d\n", pcats->nalloc,pcats->ncats); */
	len = (pcats->nalloc + 256) * sizeof(char *);
	/* DEBUG fprintf (stderr, "Rast_set_d_cat(): allocating %d labels(%d)\n", pcats->nalloc + 256,(int)len); */
	if (len != (int)len) {	/* make sure len doesn't overflow int */
	    pcats->ncats--;
	    return -1;
	}
	/* DEBUG fprintf(stderr,"Rast_set_d_cat(): pcats->nalloc = %d, pcats->labels = (%p), len = %d\n",pcats->nalloc,pcats->labels,(int)len); */
	if (pcats->nalloc) {
	    /* DEBUG fprintf(stderr,"Rast_set_d_cat(): Realloc-ing pcats->labels (%p)\n",pcats->labels); */
	    pcats->labels =
		(char **)G_realloc((char *)pcats->labels, (int)len);
	}
	else {
	    /* DEBUG fprintf(stderr,"Rast_set_d_cat(): alloc-ing new labels pointer array\n"); */
	    pcats->labels = (char **)G_malloc((int)len);
	}
	/* fflush(stderr); */
	/* DEBUG fprintf (stderr, "Rast_set_d_cats(): allocating %d marks(%d)\n", pcats->nalloc + 256,(int)len); */
	len = (pcats->nalloc + 256) * sizeof(int);
	if (len != (int)len) {	/* make sure len doesn't overflow int */
	    pcats->ncats--;
	    return -1;
	}
	if (pcats->nalloc)
	    pcats->marks = (int *)G_realloc((char *)pcats->marks, (int)len);
	else
	    pcats->marks = (int *)G_malloc((int)len);
	pcats->nalloc += 256;
    }
    /* DEBUG fprintf(stderr,"Rast_set_d_cats(): store new label\n"); */
    pcats->labels[pcats->ncats - 1] = G_store(label);
    G_newlines_to_spaces(pcats->labels[pcats->ncats - 1]);
    G_strip(pcats->labels[pcats->ncats - 1]);
    /* DEBUG
       fprintf (stderr, "%d %s\n", pcats->ncats - 1, pcats->labels[pcats->ncats - 1]);
     */
    /* updates cats.num = max cat values. This is really just used in old
       raster programs, and I am doing it for backwards cmpatibility (Olga) */
    if ((CELL) * rast1 > pcats->num)
	pcats->num = (CELL) * rast1;
    if ((CELL) * rast2 > pcats->num)
	pcats->num = (CELL) * rast2;
    /* DEBUG fprintf(stderr,"Rast_set_d_cat(): done\n"); */
    /* DEBUG fflush(stderr); */
    return 1;
}
예제 #8
0
파일: bar.c 프로젝트: GRASS-GIS/grass-ci
int bar(struct stat_list *dist_stats,	/* list of distribution statistics */
	struct Colors *colors)
{
    struct stat_node *ptr;
    int draw = YES;
    long int bar_height;	/* height, in pixels, of a histogram bar */
    CELL bar_color;		/* color/category number of a histogram bar */
    DCELL dmax, range_dmin, range_dmax, dmin, dval;
    long int max_tics;		/* maximum tics allowed on an axis */
    long int xoffset;		/* offset for x-axis */
    long int yoffset;		/* offset for y-axis */
    long int stat_start;
    long int stat_finis;
    int text_height;
    int text_width;
    long int i, j;
    long int num_cats = 0;
    long int num_stats = 0;
    long int tic_every;		/* spacing, in units of category value, of tics */

    long int tic_unit;
    double t, b, l, r;
    double tt, tb, tl, tr;
    double x_line[3];		/* for border of histogram */
    double y_line[3];
    double x_box[5];		/* for histogram bar coordinates */
    double y_box[5];
    double height, width;
    double xscale;		/* scaling factors */
    double yscale;
    char xlabel[1024];
    char ylabel[1024];
    char txt[1024];
    char tic_name[80];

    /* get coordinates of current screen window */
    D_get_src(&t, &b, &l, &r);

    /* create axis lines, to be drawn later */
    height = b - t;
    width = r - l;
    x_line[0] = x_line[1] = l + (ORIGIN_X * width);
    x_line[2] = l + (XAXIS_END * width);
    y_line[0] = b - (YAXIS_END * height);
    y_line[1] = y_line[2] = b - (ORIGIN_Y * height);

    /* figure scaling factors and offsets */
    num_cats = dist_stats->maxcat - dist_stats->mincat + 1;

    if (nodata) {
	num_cats++;
	dist_stats->mincat--;
    }
    xscale = ((x_line[2] - x_line[1]) / ((double)num_cats));
    yscale = ((y_line[1] - y_line[0])) / dist_stats->maxstat;
    if (num_cats >= x_line[2] - x_line[1])
	xoffset = (long int)x_line[1];
    else
	xoffset = (long int)x_line[0] + 0.5 * xscale;	/* boxes need extra space */
    yoffset = (double)(y_line[1]);

    /* figure tic_every and tic_units for the x-axis of the bar-chart.
     * tic_every tells how often to place a tic-number.  tic_unit tells
     * the unit to use in expressing tic-numbers.
     */
    if (xscale < XTIC_DIST) {
	max_tics = (x_line[2] - x_line[1]) / XTIC_DIST;
	if (nodata)
	    max_tics--;
	i = 0;
	if (is_fp) {
	    Rast_get_fp_range_min_max(&fp_range, &range_dmin, &range_dmax);
	    if (Rast_is_d_null_value(&range_dmin) ||
		Rast_is_d_null_value(&range_dmax))
		G_fatal_error("Floating point data range is empty");

	    if ((range_dmax - range_dmin) < 1.0)
		tics[i].every = 5;
	    if ((range_dmax - range_dmin) < 110)
		tics[i].every = 20;	/* dirrty hack */
	    while ((range_dmax - range_dmin) / tics[i].every > max_tics)
		i++;
	}
	else {
	    while ((num_cats / tics[i].every) > max_tics)
		i++;
	}
	tic_every = tics[i].every;
	tic_unit = tics[i].unit;
	strcpy(tic_name, tics[i].name);
    }
    else {
	if (is_fp && !cat_ranges) {
	    Rast_get_fp_range_min_max(&fp_range, &range_dmin, &range_dmax);
	    if (Rast_is_d_null_value(&range_dmin) ||
		Rast_is_d_null_value(&range_dmax))
		G_fatal_error("Floating point data range is empty");
	}
	tic_every = 1;
	tic_unit = 1;
    }

    /* X-AXIS LOOP
     *
     * loop through category range, drawing a pie-slice and a
     * legend bar on each iteration evenly divisible, a tic-mark
     * on those evenly divisible by tic_unit, and a tic_mark
     * number on those evenly divisible by tic_every
     *
     */
    ptr = dist_stats->ptr;
    for (i = dist_stats->mincat; i <= dist_stats->maxcat; i++) {
	if (!ptr)
	    break;
	draw = NO;
	/* figure bar color and height 
	 *
	 * the cat number determines the color, the corresponding stat,
	 * determines the bar height.  if a stat cannot be found for the
	 * cat, then it doesn't drow anything, before it used to draw the
	 * box of size 0 in black. Later when the option to provide the
	 * background color will be added , we might still draw a box in
	 * this color.
	 */
	if (nodata && i == dist_stats->mincat) {
	    if (dist_stats->null_stat == 0 && xscale > 1)
		draw = NO;
	    else {
		draw = YES;
		Rast_set_c_null_value(&bar_color, 1);
		bar_height =
		    (yoffset - yscale * (double)dist_stats->null_stat);
	    }
	}
	else if (ptr->cat == i) {	/* AH-HA!! found the stat */
	    if (ptr->stat == 0 && xscale > 1)
		draw = NO;
	    else {
		draw = YES;
		bar_color = ptr->cat;
		bar_height = (yoffset - yscale * (double)ptr->stat);
	    }
	    if (ptr->next != NULL)
		ptr = ptr->next;
	}
	else {			/* we have to look for the stat */

	    /* loop until we find it, or pass where it should be */
	    while (ptr->cat < i && ptr->next != NULL)
		ptr = ptr->next;
	    if (ptr->cat == i) {	/* AH-HA!! found the stat */
		if (ptr->stat == 0 && xscale > 1)
		    draw = NO;
		else {
		    draw = YES;
		    bar_color = ptr->cat;
		    bar_height = (yoffset - yscale * (double)ptr->stat);
		}
		if (ptr->next != NULL)
		    ptr = ptr->next;
	    }
	    else {		/* stat cannot be found */

		if (xscale > 1) {
		    draw = NO;

#ifdef notdef
		    draw = YES;
		    bar_color = D_translate_color("black");
		    bar_height = yoffset;	/* zero */
#endif
		}
		else
		    draw = NO;
	    }
	}

	/* draw the bar */
	if (draw == YES) {
	    if (xscale != 1) {
		/* draw the bar as a box */
		if (!Rast_is_c_null_value(&bar_color) && is_fp) {
		    if (cat_ranges)
			Rast_get_ith_d_cat(&cats, bar_color,
					       &dmin, &dmax);
		    else {
			dmin = range_dmin + i * (range_dmax - range_dmin) / nsteps;
			dmax = range_dmin + (i + 1) * (range_dmax - range_dmin) / nsteps;
		    }
		    if (dmin != dmax) {
			for (j = 0; j < xscale; j++) {
			    dval = dmin + j * (dmax - dmin) / xscale;
			    D_d_color(dval, colors);
			    x_box[0] = x_box[1] =
				xoffset + ((i - dist_stats->mincat) * xscale -
					   0.5 * xscale + j);
			    x_box[2] = x_box[3] =
				xoffset + ((i - dist_stats->mincat) * xscale -
					   0.5 * xscale + j + 1);
			    y_box[0] = y_box[3] = yoffset;
			    y_box[1] = y_box[2] = bar_height;
			    D_polygon_abs(x_box, y_box, 4);
			}
		    }
		    else {	/* 1-color bar */

			D_d_color(dmin, colors);
			x_box[0] = x_box[1] =
			    xoffset + ((i - dist_stats->mincat) * xscale -
				       0.5 * xscale);
			x_box[2] = x_box[3] =
			    xoffset + ((i - dist_stats->mincat) * xscale +
				       0.5 * xscale);
			y_box[0] = y_box[3] = yoffset;
			y_box[1] = y_box[2] = bar_height;
			D_polygon_abs(x_box, y_box, 4);
		    }
		}		/* fp */
		else {		/* 1-color bar for int data or null */

		    D_color((CELL) bar_color, colors);
		    x_box[0] = x_box[1] =
			xoffset + ((i - dist_stats->mincat) * xscale -
				   0.5 * xscale);
		    x_box[2] = x_box[3] =
			xoffset + ((i - dist_stats->mincat) * xscale +
				   0.5 * xscale);
		    y_box[0] = y_box[3] = yoffset;
		    y_box[1] = y_box[2] = bar_height;
		    D_polygon_abs(x_box, y_box, 4);
		}
	    }
	    else {
		/* draw the bar as a line */
		if (is_fp) {
		    if (cat_ranges)
			Rast_get_ith_d_cat(&cats, bar_color,
					       &dmin, &dmax);
		    else {
			dmin = range_dmin + i * (range_dmax - range_dmin) / nsteps;
			dmax = range_dmin + (i + 1) * (range_dmax - range_dmin) / nsteps;
		    }
		    D_d_color(dmin, colors);
		}
		else
		    D_color((CELL) bar_color, colors);
		x_box[0] = x_box[1] =
		    xoffset + (i - dist_stats->mincat) * xscale;
		y_box[0] = yoffset;
		y_box[1] = bar_height;
		D_line_abs(x_box[0], y_box[0], x_box[1], y_box[1]);
	    }
	}

	/* draw x-axis tic-marks and numbers */
	/* draw tick for null and for numbers at every tic step
	   except when there is null, don't draw tic for mincat+1 */

	if (((rem((long int)i, tic_every) == 0L) ||
	     ((i == dist_stats->mincat) && nodata))
	    && !(nodata && i == dist_stats->mincat + 1)) {

	    /* draw a numbered tic-mark */
	    D_use_color(color);
	    D_begin();
	    D_move_abs(xoffset + (i - dist_stats->mincat) * xscale - 0.5 * xscale,
		       b - ORIGIN_Y * (b - t));
	    D_cont_rel(0, BIG_TIC * (b - t));
	    D_end();
	    D_stroke();

	    if (nodata && i == dist_stats->mincat)
		sprintf(txt, "null");
	    else if (is_fp) {
		dmin = range_dmin + i * (range_dmax - range_dmin) / nsteps;
		if ((tic_every * (range_dmax - range_dmin) / nsteps) < 1.0)
		    sprintf(txt, "%.2f", dmin / (double)tic_unit);
		else
		    sprintf(txt, "%d", (int)(dmin / (double)tic_unit));
	    }
	    else
		sprintf(txt, "%d", (int)(i / tic_unit));
	    text_height = (b - t) * TEXT_HEIGHT;
	    text_width = (r - l) * TEXT_WIDTH;
	    D_text_size(text_width, text_height);
	    D_get_text_box(txt, &tt, &tb, &tl, &tr);
	    while ((tr - tl) > XTIC_DIST) {
		text_width *= 0.75;
		text_height *= 0.75;
		D_text_size(text_width, text_height);
		D_get_text_box(txt, &tt, &tb, &tl, &tr);
	    }
	    D_pos_abs(xoffset + (i - dist_stats->mincat) * xscale - 0.5 * xscale - (tr - tl) / 2,
		      b - XNUMS_Y * (b - t));
	    D_text(txt);
	}
	else if (rem(i, tic_unit) == 0.0) {
	    /* draw a tic-mark */
	    D_use_color(color);
	    D_begin();
	    D_move_abs(xoffset + (i - dist_stats->mincat) * xscale - 0.5 * xscale,
		       b - ORIGIN_Y * (b - t));
	    D_cont_rel(0, SMALL_TIC * (b - t));
	    D_end();
	    D_stroke();
	}
    }

    /* draw the x-axis label */
    if (tic_unit != 1)
	sprintf(xlabel, "X-AXIS: Cell Values %s", tic_name);
    else
	sprintf(xlabel, "X-AXIS: Cell Values");
    text_height = (b - t) * TEXT_HEIGHT;
    text_width = (r - l) * TEXT_WIDTH;
    D_text_size(text_width, text_height);
    D_get_text_box(xlabel, &tt, &tb, &tl, &tr);
    D_pos_abs(l + (r - l) / 2 - (tr - tl) / 2,
	       b - LABEL_1 * (b - t));
    D_use_color(color);
    D_text(xlabel);

    /* DRAW Y-AXIS TIC-MARKS AND NUMBERS
     * 
     * first, figure tic_every and tic_units for the x-axis of the bar-chart.
     * tic_every tells how often to place a tic-number.  tic_unit tells
     * the unit to use in expressing tic-numbers.
     */

    max_tics = (long)((y_line[1] - y_line[0]) / YTIC_DIST);

    if (dist_stats->maxstat == dist_stats->minstat)
	dist_stats->minstat = 0;	/* LOOKS FUNNY TO ME */
    num_stats = dist_stats->maxstat - dist_stats->minstat;
    i = 0;
    while ((num_stats / tics[i].every) > max_tics)
	i++;
    tic_every = tics[i].every;
    tic_unit = tics[i].unit;
    strcpy(tic_name, tics[i].name);

    stat_start = tic_unit * ((long)(dist_stats->minstat / tic_unit));
    stat_finis = tic_unit * ((long)(dist_stats->maxstat / tic_unit));

    /* Y-AXIS LOOP
     *
     */
    for (i = stat_start; i <= stat_finis; i += tic_unit) {
	if (rem(i, tic_every) == (float)0) {
	    /* draw a tic-mark */
	    D_begin();
	    D_move_abs(x_line[0], yoffset - yscale * i);
	    D_cont_rel((-(r - l) * BIG_TIC), 0);
	    D_end();
	    D_stroke();

	    /* draw a tic-mark number */
	    sprintf(txt, "%d", (int)(i / tic_unit));
	    text_height = (b - t) * TEXT_HEIGHT;
	    text_width = (r - l) * TEXT_WIDTH;
	    D_text_size(text_width, text_height);
	    D_get_text_box(txt, &tt, &tb, &tl, &tr);
	    while ((tt - tb) > YTIC_DIST) {
		text_width *= 0.75;
		text_height *= 0.75;
		D_text_size(text_width, text_height);
		D_get_text_box(txt, &tt, &tb, &tl, &tr);
	    }
	    D_pos_abs(l + (r - l) * YNUMS_X - (tr - tl) / 2,
		      yoffset - (yscale * i + 0.5 * (tt - tb)));
	    D_text(txt);
	}
	else if (rem(i, tic_unit) == 0.0) {
	    /* draw a tic-mark */
	    D_begin();
	    D_move_abs(x_line[0], yoffset - yscale * i);
	    D_cont_rel(-(r - l) * SMALL_TIC, 0);
	    D_end();
	    D_stroke();
	}
    }

    /* draw the y-axis label */
    if (tic_unit != 1) {
	if (type == COUNT)
	    sprintf(ylabel, "Y-AXIS: Number of cells %s", tic_name);
	else
	    sprintf(ylabel, "Y-AXIS: Area %s sq. meters", tic_name);
    }
    else {
	if (type == COUNT)
	    sprintf(ylabel, "Y-AXIS: Number of cells");
	else
	    sprintf(ylabel, "Y-AXIS: Area");
    }

    text_height = (b - t) * TEXT_HEIGHT;
    text_width = (r - l) * TEXT_WIDTH;
    D_text_size(text_width, text_height);
    D_get_text_box(ylabel, &tt, &tb, &tl, &tr);
    D_pos_abs(l + (r - l) / 2 - (tr - tl) / 2,
	      b - LABEL_2 * (b - t));
    D_use_color(color);
    D_text(ylabel);

    /* draw x and y axis lines */
    D_use_color(color);
    D_polyline_abs(x_line, y_line, 3);

    return 0;
}