/*-------------------------------------------------------------------- * $Id: grdimage.c,v 1.5.4.3 2002/01/17 19:04:41 pwessel Exp $ * * Copyright (c) 1991-2002 by P. Wessel and W. H. F. Smith * See COPYING file for copying and redistribution conditions. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Contact info: gmt.soest.hawaii.edu *--------------------------------------------------------------------*/ /* * grdimage will read a grdfile and image the area using the PostScript * image command. If non-linear scaling is chosen, we first resample the data * onto the new grid before calling the image command. The output image * will be 1-, 8-, or 24-bit depending on colors used. * * Author: Paul Wessel * Date: 20-SEP-1999 * Ver: 3.0 based on old 2.x * Ver: 3.1 based on old 3.0 * Ver: 3.3.2 Added -S as in grdproject * Ver: 3.3.3 Added -T[s] for non-interpolated tiling * Ver: 3.3.6 -T needed polygon clipping and wrapping (like psxy) * Ver: 3.4.1 */ #include "gmt.h" float *tmp1, *tmp2, *map, *intensity; unsigned char *bitimage_8, *bitimage_24; char *c_method[2] = { "colorimage", "colortiles", }; void GMT_limits (struct GRD_HEADER *r, struct GRD_HEADER *g); void GMT_set_proj_limits (struct GRD_HEADER *r, struct GRD_HEADER *g); main (int argc, char **argv) { int i, j, k, kk, rgb[3], nm, nm2, byte, off, nx_f, ny_f, grid_type, polarity; int nx, ny, dpi = 0, nx_proj = 0, ny_proj = 0, node, f_rgb[3], tiling = 0, index; BOOLEAN error = FALSE, intens = FALSE, monochrome = FALSE; BOOLEAN mapped = FALSE, set_dpi = FALSE, subset = FALSE, transparent = FALSE; double dx, dy, x_side, y_side, x0 = 0.0, y0 = 0.0, max_radius = 0.0; double west, east, south, north, data_west, data_east, data_south, data_north; char *grdfile, *intensfile, *cpt_file; struct GRD_HEADER g_head, r_head, i_head, j_head; argc = GMT_begin (argc, argv); GMT_grd_init (&g_head, argc, argv, FALSE); GMT_grd_init (&r_head, argc, argv, FALSE); grdfile = intensfile = cpt_file = CNULL; west = east = south = north = 0.0; for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { switch (argv[i][1]) { /* Common parameters */ case 'B': case 'J': case 'K': case 'O': case 'P': case 'R': case 'U': case 'V': case 'X': case 'x': case 'Y': case 'y': case 'c': case '\0': error += GMT_get_common_args (argv[i], &west, &east, &south, &north); break; /* Supplemental parameters */ case 'C': cpt_file = &argv[i][2]; break; case 'E': dpi = atoi (&argv[i][2]); set_dpi = TRUE; break; case 'G': /* 1-bit fore or background color for transparent masks */ transparent = TRUE; polarity = 1; j = 2; if (argv[i][2] == 'F' || argv[i][2] == 'f') j++; if (argv[i][2] == 'B' || argv[i][2] == 'b') polarity = 0, j++; if (GMT_getrgb (&argv[i][j], f_rgb)) { GMT_rgb_syntax ('G'); error++; } break; case 'I': intens = TRUE; intensfile = &argv[i][2]; break; case 'M': monochrome = TRUE; break; case '0': gmtdefs.color_image = 0; break; case 'S': max_radius = atof (&argv[i][2]); break; case 'T': tiling = 1; if (argv[i][2] == 's') tiling = 2; break; case '1': gmtdefs.color_image = 1; break; default: error = TRUE; GMT_default_error (argv[i][1]); break; } } else grdfile = argv[i]; } if (argc == 1 || GMT_quick) { fprintf (stderr,"grdimage %s - Plot grdfiles in 2-D\n\n", GMT_VERSION); fprintf (stderr, "usage: grdimage -C -J [-B] [-E] [-G[f|b]]\n"); fprintf (stderr, "\t[-I] [-K] [-M] [-O] [-P] [-R] [-S] [-T[s]] [-U] [-V]\n"); fprintf (stderr, "\t[-X] [-Y] [-c]\n\n"); if (GMT_quick) exit (EXIT_FAILURE); fprintf (stderr,"\t is data set to be plotted\n"); fprintf (stderr,"\t-C color palette file\n"); GMT_explain_option ('j'); fprintf (stderr,"\n\tOPTIONS:\n"); GMT_explain_option ('b'); fprintf(stderr, "\t-E sets dpi for the projected grid which must be constructed\n"); fprintf(stderr, "\t if -Jx or -Jm is not selected [Default gives same size as input grid]\n"); fprintf (stderr,"\t-G sets transparency color for images that\n"); fprintf (stderr,"\t otherwise would result in 1-bit images\n"); fprintf (stderr,"\t-I use illumination. Append name of intensity grd file\n"); GMT_explain_option ('K'); fprintf (stderr, "\t-M force monochrome image\n"); GMT_explain_option ('O'); GMT_explain_option ('P'); GMT_explain_option ('R'); fprintf(stderr, "\t-S sets the search radius in projected units [Default avoids aliasing]\n"); fprintf(stderr, "\t-T will image the data without interpolation by painting polygonal tiles\n"); fprintf(stderr, "\t Append s to skip tiles for nodes with z = NaN [Default paints all tiles]\n"); GMT_explain_option ('U'); GMT_explain_option ('V'); GMT_explain_option ('X'); GMT_explain_option ('c'); GMT_explain_option ('.'); exit (EXIT_FAILURE); } if (!grdfile) { fprintf (stderr, "%s: GMT SYNTAX ERROR: Must specify input file\n", GMT_program); error++; } if (!cpt_file) { fprintf (stderr, "%s: GMT SYNTAX ERROR: Must specify color palette table\n", GMT_program); error++; } if (intens && !intensfile) { fprintf (stderr, "%s: GMT SYNTAX ERROR -I option: Must specify intensity file\n", GMT_program); error++; } if (set_dpi && dpi <= 0) { fprintf (stderr, "%s: GMT SYNTAX ERROR -E option: dpi must be positive\n", GMT_program); error++; } if (error) exit (EXIT_FAILURE); GMT_put_history (argc, argv); /* Update .gmtcommands */ /* Get color palette file */ GMT_read_cpt (cpt_file); /* Check limits and get data file */ if (gmtdefs.verbose) fprintf (stderr, "%s: Allocates memory and read data file\n", GMT_program); if (GMT_read_grd_info (grdfile, &g_head)) { fprintf (stderr, "%s: Error opening file %s\n", GMT_program, grdfile); exit (EXIT_FAILURE); } off = (g_head.node_offset) ? 0 : 1; /* Determine what wesn to pass to map_setup */ if (!project_info.region_supplied) { west = g_head.x_min; east = g_head.x_max; south = g_head.y_min; north = g_head.y_max; } else if (!(west == g_head.x_min && east == g_head.x_max && south == g_head.y_min && north == g_head.y_max)) subset = TRUE; GMT_map_setup (west, east, south, north); /* Determine the wesn to be used to read the grdfile */ GMT_grd_setregion (&g_head, &data_west, &data_east, &data_south, &data_north); nx_f = g_head.nx; ny_f = g_head.ny; /* Read data */ nx = irint ( (data_east - data_west) / g_head.x_inc) + off; ny = irint ( (data_north - data_south) / g_head.y_inc) + off; nm = nx * ny; tmp1 = (float *) GMT_memory (VNULL, (size_t)nm, sizeof (float), GMT_program); if (GMT_read_grd (grdfile, &g_head, tmp1, data_west, data_east, data_south, data_north, GMT_pad, FALSE)) { fprintf (stderr, "%s: Error reading file %s\n", GMT_program, grdfile); exit (EXIT_FAILURE); } /* If given, get intensity file or compute intensities */ if (intens) { /* Illumination wanted */ if (gmtdefs.verbose) fprintf (stderr, "%s: Allocates memory and read intensity file\n", GMT_program); GMT_grd_init (&i_head, argc, argv, FALSE); GMT_grd_init (&j_head, argc, argv, FALSE); if (GMT_read_grd_info (intensfile, &i_head)) { fprintf (stderr, "%s: Error opening file %s\n", GMT_program, intensfile); exit (EXIT_FAILURE); } if (i_head.nx != nx_f || i_head.ny != ny_f) { fprintf (stderr, "%s: Intensity file has improper dimensions!\n", GMT_program); exit (EXIT_FAILURE); } tmp2 = (float *) GMT_memory (VNULL, (size_t)nm, sizeof (float), GMT_program); if (GMT_read_grd (intensfile, &i_head, tmp2, data_west, data_east, data_south, data_north, GMT_pad, FALSE)) { fprintf (stderr, "%s: Error reading file %s\n", GMT_program, intensfile); exit (EXIT_FAILURE); } } GMT_set_proj_limits (&r_head, &g_head); /* if (dpi > 0 || MAPPING) { */ if (!tiling && (dpi > 0 || project_info.projection > 5)) { /* Need to resample the grd file */ mapped = TRUE; if (gmtdefs.verbose) fprintf (stderr, "%s: project grdfiles\n", GMT_program); if (dpi == 0) { /* Use input # of nodes as # of projected nodes */ nx_proj = g_head.nx; ny_proj = g_head.ny; } grid_type = (dpi > 0) ? 1 : g_head.node_offset; /* Force pixel if dpi is set */ GMT_grdproject_init (&r_head, 0.0, 0.0, nx_proj, ny_proj, dpi, grid_type); GMT_init_search_radius (&max_radius, &r_head, &g_head, FALSE); nm2 = r_head.nx * r_head.ny; map = (float *) GMT_memory (VNULL, (size_t)nm2, sizeof (float), "grdproject"); GMT_grd_forward (tmp1, &g_head, map, &r_head, max_radius); GMT_free ((void *)tmp1); if (intens) { j_head.x_min = r_head.x_min; j_head.x_max = r_head.x_max; j_head.y_min = r_head.y_min; j_head.y_max = r_head.y_max; if (dpi == 0) { /* Use input # of nodes as # of projected nodes */ nx_proj = i_head.nx; ny_proj = i_head.ny; } GMT_grdproject_init (&j_head, 0.0, 0.0, nx_proj, ny_proj, dpi, grid_type); intensity = (float *) GMT_memory (VNULL, (size_t)nm2, sizeof (float), "grdproject"); GMT_grd_forward (tmp2, &i_head, intensity, &j_head, max_radius); GMT_free ((void *)tmp2); } nm = nm2; } else { /* Simply copy g_head info to r_head */ map = tmp1; r_head.nx = g_head.nx; r_head.ny = g_head.ny; r_head.x_inc = g_head.x_inc; r_head.y_inc = g_head.y_inc; if (intens) { j_head.nx = i_head.nx; j_head.ny = i_head.ny; j_head.x_inc = i_head.x_inc; j_head.y_inc = i_head.y_inc; intensity = tmp2; } grid_type = g_head.node_offset; } ps_plotinit (CNULL, gmtdefs.overlay, gmtdefs.page_orientation, gmtdefs.x_origin, gmtdefs.y_origin, gmtdefs.global_x_scale, gmtdefs.global_y_scale, gmtdefs.n_copies, gmtdefs.dpi, GMT_INCH , gmtdefs.paper_width, gmtdefs.page_rgb, GMT_epsinfo (argv[0])); GMT_echo_command (argc, argv); if (gmtdefs.unix_time) GMT_timestamp (argc, argv); GMT_map_clip_on (GMT_no_rgb, 3); if (tiling) { /* Plot image as polygonal pieces and then exit */ int np, index, plot_n, n_use, start; double *grd_x, grd_y, *xx, *yy, *xp, *yp, d, dx2, dy2, y_bot, y_top; struct GMT_FILL *f; d = (off) ? 0.0 : 0.5; dx2 = 0.5 * g_head.x_inc; dy2 = 0.5 * g_head.y_inc; if (gmtdefs.verbose) fprintf (stderr, "%s: Tiling without interpolation\n", GMT_program); grd_x = (double *) GMT_memory (VNULL, (size_t)g_head.nx, sizeof (double), GMT_program); for (i = 0; i < g_head.nx; i++) grd_x[i] = g_head.x_min + (i + d) * g_head.x_inc; for (j = node = 0; j < g_head.ny; j++) { grd_y = g_head.y_max - (j + d) * g_head.y_inc; y_bot = grd_y - dy2; y_top = grd_y + dy2; if (MAPPING) { if (y_bot < -90.0) y_bot = -90.0; if (y_top > +90.0) y_top = +90.0; } for (i = 0; i < g_head.nx; i++, node++) { /* Compute rgb for each pixel */ if (GMT_is_fnan (map[node]) && tiling == 2) continue; index = GMT_get_rgb24 (map[node], rgb); if (GMT_cpt_skip) continue; /* Skip this z-slice */ if (intens && index != -1) GMT_illuminate (intensity[node], rgb); if (monochrome) rgb[0] = rgb[1] = rgb[2] = YIQ (rgb); np = GMT_graticule_path (&xx, &yy, 1, grd_x[i] - dx2, grd_x[i] + dx2, y_bot, y_top); plot_n = GMT_clip_to_map (xx, yy, np, &xp, &yp); GMT_free ((void *)xx); GMT_free ((void *)yy); if (plot_n == 0) continue; /* Outside */ if ((*GMT_will_it_wrap) (xp, yp, plot_n, &start)) { /* Polygon wraps */ /* First truncate agains left border */ GMT_n_plot = (*GMT_truncate) (xp, yp, plot_n, start, -1); n_use = GMT_compact_line (GMT_x_plot, GMT_y_plot, GMT_n_plot, FALSE, 0); if (index >= 0 && (f = GMT_lut[index].fill)) GMT_fill (GMT_x_plot, GMT_y_plot, n_use, f, FALSE); else if (index < 0 && (f = GMT_bfn.fill[index+3])) GMT_fill (GMT_x_plot, GMT_y_plot, n_use, f, FALSE); else ps_polygon (GMT_x_plot, GMT_y_plot, n_use, rgb, FALSE); /* Then truncate agains right border */ GMT_n_plot = (*GMT_truncate) (xp, yp, plot_n, start, +1); n_use = GMT_compact_line (GMT_x_plot, GMT_y_plot, GMT_n_plot, FALSE, 0); if (index >= 0 && (f = GMT_lut[index].fill)) GMT_fill (GMT_x_plot, GMT_y_plot, n_use, f, FALSE); else if (index < 0 && (f = GMT_bfn.fill[index+3])) GMT_fill (GMT_x_plot, GMT_y_plot, n_use, f, FALSE); else ps_polygon (GMT_x_plot, GMT_y_plot, n_use, rgb, FALSE); } else { if (index >= 0 && (f = GMT_lut[index].fill)) GMT_fill (xp, yp, plot_n, f, FALSE); else if (index < 0 && (f = GMT_bfn.fill[index+3])) GMT_fill (xp, yp, plot_n, f, FALSE); else ps_polygon (xp, yp, plot_n, rgb, FALSE); } GMT_free ((void *)xp); GMT_free ((void *)yp); } } GMT_free ((void *)grd_x); GMT_map_clip_off(); if (frame_info.plot) { ps_setpaint (gmtdefs.basemap_frame_rgb); GMT_map_basemap (); } ps_plotend (gmtdefs.last_page); GMT_end (argc, argv); } if (gmtdefs.verbose) { if (GMT_cpt_pattern) fprintf (stderr, "%s: Warning: Patterns in cpt file only apply to -T\n", GMT_program); fprintf (stderr, "%s: Evaluate pixel colors\n", GMT_program); } if (monochrome || GMT_gray) bitimage_8 = (unsigned char *) GMT_memory (VNULL, (size_t)nm, sizeof (char), GMT_program); else bitimage_24 = (unsigned char *) GMT_memory (VNULL, (size_t)(3 * nm), sizeof (char), GMT_program); for (j = byte = 0; j < r_head.ny; j++) { kk = r_head.nx * (project_info.xyz_pos[1] ? j : r_head.ny - j - 1); for (i = 0; i < r_head.nx; i++) { /* Compute rgb for each pixel */ node = kk + (project_info.xyz_pos[0] ? i : r_head.nx - i - 1); index = GMT_get_rgb24 (map[node], rgb); if (intens && index != -1) GMT_illuminate (intensity[node], rgb); if (GMT_gray) /* Color table only has grays, pick r */ bitimage_8[byte++] = (unsigned char) rgb[0]; else if (monochrome) /* Convert rgb to gray using the YIQ transformation */ bitimage_8[byte++] = (unsigned char) YIQ (rgb); else { bitimage_24[byte++] = (unsigned char) rgb[0]; bitimage_24[byte++] = (unsigned char) rgb[1]; bitimage_24[byte++] = (unsigned char) rgb[2]; } } } GMT_free ((void *)map); if (intens) GMT_free ((void *)intensity); /* Get actual size of each pixel */ dx = (r_head.x_max - r_head.x_min) / (r_head.nx - off); dy = (r_head.y_max - r_head.y_min) / (r_head.ny - off); /* Set lower left position of image on map */ x0 = r_head.x_min; y0 = r_head.y_min; if (grid_type == 0) { /* Grid registration, move 1/2 pixel down/left */ x0 -= 0.5 * dx; y0 -= 0.5 * dy; } if (subset && GRID_CLIP_OK) GMT_grid_clip_on (&g_head, GMT_no_rgb, 3); x_side = dx * r_head.nx; y_side = dy * r_head.ny; if (gmtdefs.verbose) fprintf (stderr, "%s: Creating PostScript image ", GMT_program); if (GMT_gray) for (k = 0, GMT_b_and_w = TRUE; GMT_b_and_w && k < nm; k++) if (!(bitimage_8[k] == 0 || bitimage_8[k] == 255)) GMT_b_and_w = FALSE; if (GMT_b_and_w) { /* Can get away with 1 bit image */ int nx8, shift, byte, b_or_w, k8, nx_pixels; unsigned char *bit; nx8 = (int)ceil (r_head.nx / 8.0); nx_pixels = nx8 * 8; bit = (unsigned char *) GMT_memory (VNULL, (size_t)(nx8 * r_head.ny), sizeof (char), GMT_program); for (j = k = k8 = 0; j < r_head.ny; j++) { shift = byte = 0; for (i = 0; i < r_head.nx; i++, k++) { b_or_w = (bitimage_8[k] == 255); byte |= b_or_w; shift++; if (shift == 8) { /* Time to dump out byte */ bit[k8++] = (unsigned char) byte; byte = shift = 0; } else byte <<= 1; } if (shift) { byte |= 1; shift++; while (shift < 8) { byte <<= 1; byte |= 1; shift++; } bit[k8++] = (unsigned char) byte; } } GMT_free ((void *)bitimage_8); x_side = nx_pixels * dx; if (transparent) { if (gmtdefs.verbose) fprintf (stderr, "[1-bit image mask painted %d/%d/%d]\n", f_rgb[0], f_rgb[1], f_rgb[2]); ps_imagemask (x0, y0, x_side, y_side, bit, nx_pixels, r_head.ny, polarity, f_rgb); } else { if (gmtdefs.verbose) fprintf (stderr, "[1-bit B/W image]\n"); ps_image (x0, y0, x_side, y_side, bit, nx_pixels, r_head.ny, 1); } GMT_free ((void *)bit); } else if (GMT_gray || monochrome) { if (gmtdefs.verbose) fprintf (stderr, "[8-bit grayshade image]\n"); ps_image (x0, y0, x_side, y_side, bitimage_8, r_head.nx, r_head.ny, 8); GMT_free ((void *)bitimage_8); } else { if (gmtdefs.verbose) fprintf (stderr, "24-bit [%s]\n", c_method[gmtdefs.color_image]); GMT_color_image (x0, y0, x_side, y_side, bitimage_24, r_head.nx, r_head.ny); GMT_free ((void *)bitimage_24); } if (subset && GRID_CLIP_OK) GMT_grid_clip_off(); GMT_map_clip_off(); if (frame_info.plot) { ps_setpaint (gmtdefs.basemap_frame_rgb); GMT_map_basemap (); } ps_plotend (gmtdefs.last_page); GMT_end (argc, argv); } void GMT_set_proj_limits (struct GRD_HEADER *r, struct GRD_HEADER *g) { /* Sets the projected extent of the grid given the map projection * The extreme x/y coordinates are returned in r, and dx/dy, and * nx/ny are set accordingly. Not that some of these may change * if GMT_grdproject_init is called at a later stage */ int i, j; BOOLEAN all_lats, all_lons; double lon, lat, x, y; r->nx = g->nx; r->ny = g->ny; all_lats = (fabs (g->y_max - 90.0) < GMT_CONV_LIMIT && fabs (g->y_min + 90.0) < GMT_CONV_LIMIT); all_lons = (fabs (g->x_max - g->x_min - 360.0) < GMT_CONV_LIMIT); if (AZIMUTHAL && all_lons && all_lats) { /* Whole globe, get square box */ r->x_min = project_info.xmin; r->x_max = project_info.xmax; r->y_min = project_info.ymin; r->y_max = project_info.ymax; return; } /* Must search for extent along perimeter */ r->x_min = r->y_min = +DBL_MAX; r->x_max = r->y_max = -DBL_MAX; for (i = j = 0; i < g->nx; i++, j++) { /* South and north */ lon = g->x_min + i * g->x_inc; GMT_geo_to_xy (lon, g->y_min, &x, &y); r->x_min = MIN (r->x_min, x); r->x_max = MAX (r->x_max, x); r->y_min = MIN (r->y_min, y); r->y_max = MAX (r->y_max, y); GMT_geo_to_xy (lon, g->y_max, &x, &y); r->x_min = MIN (r->x_min, x); r->x_max = MAX (r->x_max, x); r->y_min = MIN (r->y_min, y); r->y_max = MAX (r->y_max, y); } for (i = 0; i < g->ny; j++, i++) { /* East and west */ lat = g->y_min + i * g->y_inc; GMT_geo_to_xy (g->x_min, lat, &x, &y); r->x_min = MIN (r->x_min, x); r->x_max = MAX (r->x_max, x); r->y_min = MIN (r->y_min, y); r->y_max = MAX (r->y_max, y); GMT_geo_to_xy (g->x_max, lat, &x, &y); r->x_min = MIN (r->x_min, x); r->x_max = MAX (r->x_max, x); r->y_min = MIN (r->y_min, y); r->y_max = MAX (r->y_max, y); } if (AZIMUTHAL && all_lons) { /* Full 360, use min/max for x */ r->x_min = project_info.xmin; r->x_max = project_info.xmax; } if (AZIMUTHAL && all_lats) { /* Full -90/+90, use min/max for y */ r->y_min = project_info.ymin; r->y_max = project_info.ymax; } /* Then truncate, if neccesary, values to fit inside plotbox */ r->x_min = MAX (r->x_min, project_info.xmin); r->x_max = MIN (r->x_max, project_info.xmax); r->y_min = MAX (r->y_min, project_info.ymin); r->y_max = MIN (r->y_max, project_info.ymax); r->x_inc = (r->x_max - r->x_min) / (r->nx - r->node_offset); r->y_inc = (r->y_max - r->y_min) / (r->ny - r->node_offset); }