/* * Vis5D system for visualizing five dimensional gridded data sets. * Copyright (C) 1990 - 2000 Bill Hibbard, Johan Kellum, Brian Paul, * Dave Santek, and Andre Battaiola. * * 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; either version 2 of the License, or * (at your option) any later version. * * As a special exception to the terms of the GNU General Public * License, you are permitted to link Vis5D with (and distribute the * resulting source and executables) the LUI library (copyright by * Stellar Computer Inc. and licensed for distribution with Vis5D), * the McIDAS library, and/or the NetCDF library, where those * libraries are governed by the terms of their own licenses. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "../config.h" /* analyze.c */ #include #include #include #include #include "analyze_i.h" #include "grid_i.h" #include "misc_i.h" #include "proj_i.h" #include "projlist_i.h" #include "v5d.h" static struct grid_db *sort_db; /* * Compare the two grids and return -1, 0 or 1 to indicate their relationship * as required by the qsort() function used below. */ static int compare_grids( const void *a, const void *b ) { struct grid_info *grida, *gridb; int i, j; grida = *( (struct grid_info **) a); gridb = *( (struct grid_info **) b); /* Compare Dates */ if (v5dYYDDDtoDays(grida->DateStamp) < v5dYYDDDtoDays(gridb->DateStamp)) { return -1; } else if (v5dYYDDDtoDays(grida->DateStamp) > v5dYYDDDtoDays(gridb->DateStamp)) { return 1; } /* Equal dates, compare times */ if (v5dHHMMSStoSeconds(grida->TimeStamp) < v5dHHMMSStoSeconds(gridb->TimeStamp)) { return -1; } else if (v5dHHMMSStoSeconds(grida->TimeStamp) > v5dHHMMSStoSeconds(gridb->TimeStamp)) { return 1; } /* Equal dates and times, compare varnames */ #ifdef LEAVEOUT n = strcmp( grida->VarName, gridb->VarName ); if (n) { return n; } /* Equal varnames, compare rows */ if (grida->Nr < gridb->Nr) { return -1; } else if (grida->Nr > gridb->Nr) { return 1; } /* Equal rows, compare columns */ if (grida->Nc < gridb->Nc) { return -1; } else if (grida->Nc > gridb->Nc) { return 1; } #else /* i = position of grida->VarName in varname list */ for (i=0; iNumVars; i++) { if (strcmp(grida->VarName, sort_db->VarNames[i])==0) { break; } } /* j = position of gridb->VarName in varname list */ for (j=0; jNumVars; j++) { if (strcmp(gridb->VarName, sort_db->VarNames[j])==0) { break; } } if (ij) { return 1; } #endif /* Default */ return 0; } /* * Sort the grid list by time, date, and variable name, respecively. */ void sort_grids( struct grid_db *db ) { struct grid_info **grid_array; struct grid_info *g; int i; /* make sure each variable name is in the variable list */ for (g=db->FirstGrid; g; g=g->Next) { int inlist = 0; for (i=0; iNumVars; i++) { if (strcmp( g->VarName, db->VarNames[i])==0) { inlist = 1; /* grab this units string if we don't already have one */ if (!db->Units[i] && g->Units) { db->Units[i] = strdup( g->Units ); } break; } } if (!inlist) { /* add to list */ if (db->NumVarsVarNames[db->NumVars] = strdup( g->VarName ); if (g->Units) { db->Units[db->NumVars] = strdup( g->Units ); } db->NumVars++; } else { printf("Error: too many variables, %d is limit,", IMAXVARS ); printf(" ignoring %s\n", g->VarName ); } } } /* Special case: */ if (db->NumGrids<=1) { db->Sorted = 1; return; } /* * Convert the linked list into an array of pointers, sort the array, * then rebuild the linked list. */ /* Allocate an array of pointers to grid_info structs */ grid_array = (struct grid_info **) malloc( db->NumGrids * sizeof(struct grid_info *) ); /* initialize the grid_array */ g = db->FirstGrid; for (i=0;iNumGrids;i++) { grid_array[i] = g; g = g->Next; } sort_db = db; /* Use quicksort to sort the array */ qsort( grid_array, db->NumGrids, sizeof(struct grid_info *), compare_grids ); sort_db = NULL; /* Now rebuild the linked list so it's in sorted order */ for (i=0;iNumGrids-1;i++) { grid_array[i]->Next = grid_array[i+1]; } db->FirstGrid = grid_array[0]; db->LastGrid = grid_array[db->NumGrids-1]; db->LastGrid->Next = NULL; /* Deallocate the grid_array */ free( grid_array ); db->Sorted = 1; } /* * Look at all the grids in the list to initialize the NumVars and VarName * fields. */ static void make_var_list( struct grid_db *db ) { struct grid_info *g; int i, inlist; db->NumVars = 0; /* scan over list of file_info structs */ for (g=db->FirstGrid; g && db->NumVarsNext) { /* check if g->VarName is already in list */ inlist = 0; for (i=0; iNumVars; i++) { if (strcmp( g->VarName, db->VarNames[i])==0) { /* already in list */ inlist = 1; /* grab the units string if don't already have one */ if (!db->Units[i] && g->Units) { db->Units[i] = strdup( g->Units ); } break; } } if (!inlist) { /* add to list */ db->VarNames[db->NumVars] = strdup( g->VarName ); if (g->Units) { db->Units[db->NumVars] = strdup( g->Units ); } db->NumVars++; } } } /* * Look at all the grids in the list to initialize the NumTimes, TimeStamp, * and DateStamp fields. */ static void make_time_list( struct grid_db *db ) { struct grid_info *g; assert( db->Sorted ); db->NumTimes = 0; for (g=db->FirstGrid; g && db->NumTimesNext) { if (db->NumTimes==0) { db->TimeStamp[db->NumTimes] = g->TimeStamp; db->DateStamp[db->NumTimes] = g->DateStamp; db->NumTimes = 1; } else if ( g->TimeStamp != db->TimeStamp[db->NumTimes-1] || g->DateStamp != db->DateStamp[db->NumTimes-1]) { if (db->NumTimesTimeStamp[db->NumTimes] = g->TimeStamp; db->DateStamp[db->NumTimes] = g->DateStamp; db->NumTimes++; } else { printf("Error: too many timesteps, %d is limit,", IMAXTIMES ); printf(" ignoring %05d %06d\n", g->DateStamp, g->TimeStamp ); } } } } /* * From the linked list of grids_info structs, determine number of variables, * their names, number of timesteps, build the grid table, etc... */ void analyze_grids( struct grid_db *db ) { int i, it, iv; struct grid_info *g; sort_grids( db ); /* make_var_list( db );*/ make_time_list( db ); /* Initialize selection flag arrays */ for (i=0;iNumVars;i++) { db->VarSelected[i] = 0; } for (i=0;iNumTimes;i++) { db->TimeSelected[i] = 0; } for (i=0;iNumProj;i++) { db->ProjSelected[i] = 0; } for (i=0;iNumVcs;i++) { db->VcsSelected[i] = 0; } for (it=0;itNumTimes;it++) { for (iv=0;ivNumVars;iv++) { db->Matrix[it][iv] = NULL; } } /* * Setup the matrix of pointers to grids. When finished, table[time][var] * will point to the grid_info struct for every timestep and variable. */ g = db->FirstGrid; for (it=0;itNumTimes;it++) { /* search grid list for correct timestep */ while (v5dYYDDDtoDays(g->DateStamp) < v5dYYDDDtoDays(db->DateStamp[it]) || (v5dYYDDDtoDays(g->DateStamp) == v5dYYDDDtoDays(db->DateStamp[it]) && v5dHHMMSStoSeconds(g->TimeStamp) < v5dHHMMSStoSeconds(db->TimeStamp[it]))) { g = g->Next; if (!g) { assert(g); } } if (g->DateStamp==db->DateStamp[it] && g->TimeStamp==db->TimeStamp[it]) { /* Found the timestep!! */ for (iv=0;ivNumVars;iv++) { struct grid_info *g2; /* search grid list for correct variable */ g2 = g; while (strcmp(g2->VarName,db->VarNames[iv])!=0 && g2->DateStamp==db->DateStamp[it] && g2->TimeStamp==db->TimeStamp[it]) { g2 = g2->Next; if (!g2) break; } if (g2 && strcmp(g2->VarName,db->VarNames[iv])==0 && g2->DateStamp==db->DateStamp[it] && g2->TimeStamp==db->TimeStamp[it]) { struct grid_info *prev; /* Found the variable!! */ db->Matrix[it][iv] = g2; /* There might be more grids with the same timestamp and */ /* variable name so we chain them together... */ prev = g2; while (1) { g2 = g2->Next; if (g2 && strcmp(g2->VarName,db->VarNames[iv])==0 && g2->DateStamp==db->DateStamp[it] && g2->TimeStamp==db->TimeStamp[it]){ /* another grid w/ same time and variable name */ prev->Sibling = g2; prev = g2; } else { break; } } prev->Sibling = NULL; /* Done chaining */ } else { /* Didn't find the variable */ db->Matrix[it][iv] = NULL; } } } else { /* didn't find the timestep */ for (iv=0;ivNumVars;iv++) { db->Matrix[it][iv] = NULL; } } } } static int is_vcs_selected( struct grid_db *db, struct vcs *vcs ) { int i; for (i=0;iNumVcs;i++) { if (db->VcsList[i]==vcs) { return db->VcsSelected[i]; } } return 0; } /* * When we create the v5d output file we need to know how many grid levels * are to be produced for each variable. This function tries to determine * these values. * * Input: db - the grid data base * Output: nl - the array of grid level values, one per variable */ void estimate_grid_levels( struct grid_db *db, int nl[] ) { int var, time; int nvcs, ivcs, index, vcs_indices[IMAXPROJ], unique; /* WLH 7-19-96 */ for (var=0; varNumVars; var++) { nvcs = 0; nl[var] = 0; if (db->VarSelected[var]) { for (time=0; timeNumTimes; time++) { if (db->TimeSelected[time]) { /* Determine Nl for this timestep and variable */ struct grid_info *g; int one_level_count = 0; for (g=db->Matrix[time][var]; g; g=g->Sibling) { if (is_vcs_selected(db,g->Vcs)) { if (g->Vcs->Nl==1) { /* another grid w/ one level */ unique = 1; for (ivcs=0; ivcsVcs); if (vcs_indices[ivcs] == index) { unique = 0; break; } } if (unique) { vcs_indices[nvcs] = index; nvcs++; one_level_count++; } } else { /* check if max so far */ if (g->Vcs->Nl > nl[var]) { nl[var] = g->Vcs->Nl; } } } } if (one_level_count>nl[var]) { nl[var] = one_level_count; } } } } /* printf("Nl[%s] = %d\n", db->VarNames[var], nl[var] );*/ } } /* * Determine a reasonable default vertical coordinate system for the output * file. * Input: db - the grid database * max_out_nl - max grid levels for any VCS returned * Output: vcs - the VCS number * vcsargs - array of arguments. */ void find_default_vcs( struct grid_db *db, int max_out_nl, int *vcs, float *vcsargs ) { int i; int one_level_count, maxnl, maxnl_index; one_level_count = 0; maxnl = 0; maxnl_index = -1; for (i=0; iNumVcs; i++) { if (db->VcsSelected[i]) { if (db->VcsList[i]->Nl==1) { one_level_count++; } else { if (db->VcsList[i]->Nl > maxnl) { maxnl = db->VcsList[i]->Nl; maxnl_index = i; } } } } if (maxnl > one_level_count) { /* use one the VCS with the most levels */ assert( maxnl_index>=0 ); if (db->VcsList[maxnl_index]->Kind==VERT_EPA) { /* Special case: v5d files don't directly support this VCS so */ /* we have to "guestimate" a new one. */ float height[MAXLEVELS]; *vcs = VERT_UNEQUAL_KM; for (i=0;iVcsList[maxnl_index], 0.0 ); } memcpy( vcsargs, height, MAXVERTARGS*sizeof(float) ); } else { *vcs = db->VcsList[maxnl_index]->Kind; memcpy( vcsargs, db->VcsList[maxnl_index]->Args, MAXVERTARGS*sizeof(float)); } } else { /* make a new VCS out of a "stack" of 1-D slices */ int j; int n = 0; float height[MAXLEVELS]; struct vcs *first_selected = NULL; for (i=0; iNumVcs; i++) { if (db->VcsSelected[i] && db->VcsList[i]->Nl==1 && nVcsList[i]; height[n] = db->VcsList[i]->Args[0]; n++; } } /* bubble sort the heights */ for (i=0;iKind; vcsargs[0] = first_selected->Args[0]; vcsargs[1] = first_selected->Args[1]; } else { *vcs = VERT_UNEQUAL_KM; /* copy height values */ for (i=0;iNumVars;var++) { /* scan all selected timesteps' VCS's for height values... */ lowlev[var] = 0; } } /* NEW */ /* * Find the minimum and maximum vertical coordinates of all selected * grids for variable 'var'. */ static void find_min_max_heights( struct grid_db *db, int var, float *min, float *max ) { int time; *min = 1.0e30; *max = -1.0e30; for (time=0; timeNumTimes; time++) { if (db->TimeSelected[time]) { struct grid_info *g; for (g=db->Matrix[time][var]; g; g=g->Sibling) { if (g->SelectBits==ALL_BITS) { float bottom, top; level_to_height( 0.0, &bottom, g->Vcs, 0.0 ); level_to_height( (float) (g->Vcs->Nl-1), &top, g->Vcs, 0.0 ); if (bottom < *min) { *min = bottom; } if (top > *max) { *max = top; } } } } } } /* * When we create the v5d output file we need to know, for each physical * variable, how many grid levels are to be produced, and the location of * the bottom-most one. This function tries to determine these values. * * Input: db - the grid data base * Output: lowlev - position of lowest grid level, one per variable * nl - the array of grid level values, one per variable */ void compute_grid_levels( struct grid_db *db, struct vcs *outvcs, int lowlev[], int nl[] ) { int var; for (var=0; varNumVars; var++) { if (db->VarSelected[var]) { float min, max; float minlevel, maxlevel; int iminlev, imaxlev; find_min_max_heights( db, var, &min, &max ); if (height_to_level( min, &minlevel, outvcs, 0.0 )) { iminlev = (int) minlevel; } else { iminlev = 0; } if (height_to_level( max, &maxlevel, outvcs, 0.0 )) { imaxlev = (int) (maxlevel+0.99999); } else { imaxlev = outvcs->Nl-1; } #if V5D_VERSION >= 42 /* Only Vis5D version 4.2 or later support the lowlev scheme */ lowlev[var] = iminlev; nl[var] = imaxlev - iminlev + 1; #else lowlev[var] = 0; nl[var] = imaxlev+1; #endif } else { lowlev[var] = nl[var] = 0; } printf("%s: lowlev=%d nl=%d\n", db->VarNames[var], lowlev[var], nl[var]); } } /* * Find the min and max lat/lon values for the given projection. * Input: p - a map projection * Output: minlat, maxlat, minlon, maxlon - min and max lat and lon values */ static void find_projection_extents( struct projection *p, float *min_lat, float *max_lat, float *min_lon, float *max_lon ) { /* TODO: this is inefficient: only have to check the perimeter! */ int i, j; float minlat, maxlat, minlon, maxlon; float lat, lon; minlat = 90.0; maxlat = -90.0; minlon = 180.0; maxlon = -180.0; for (i=0;iNr;i++) { for (j=0;jNc;j++) { if (rowcol_to_latlon_i( (float) i, (float) j, &lat, &lon, p )) { if (latmaxlat) maxlat = lat; if (lonmaxlon) maxlon = lon; } } } *min_lat = minlat; *max_lat = maxlat; *min_lon = minlon; *max_lon = maxlon; } /* * Scan the grid list to determine various output file parameters such as: * 1. Rows, columns, max grid levels * 2. Map projection * 3. Vertical coordinate system * * Input: db - the grid_db struct. * v - the v5dstruct to return results through * rowcol_flag - boolean: compute Nr, Nc, Nl? * proj_flag - boolean: compute map projection? * vert_flag - boolean: compute vertical coordinate system? * Output: v - contains computed parameters */ void setup_defaults( struct grid_db *db, v5dstruct *v, int rowcol_flag, int proj_flag, int vert_flag ) { int i; /*** Grid Dimensions ***/ if (rowcol_flag) { /* Get rows and columns from first selected projection */ for (i=0;iNumProj;i++) { if (db->ProjSelected[i]) { v->Nr = db->ProjList[i]->Nr; v->Nc = db->ProjList[i]->Nc; break; } } estimate_grid_levels( db, v->Nl ); } /*** Projection ***/ if (proj_flag) { /* first selected projection */ for (i=0;iNumProj;i++) { if (db->ProjSelected[i]) { if (db->ProjList[i]->Kind==PROJ_EPA) { /* Special case: v5d files don't directly support this */ /* projection so we have to "guestimate" a new one. */ float minlat, maxlat, minlon, maxlon; float args[4]; find_projection_extents( db->ProjList[i], &minlat, &maxlat, &minlon, &maxlon); v->Projection = PROJ_LINEAR; args[0] = maxlat; args[1] = maxlon; args[2] = (maxlat-minlat) / (db->ProjList[i]->Nr-1); args[3] = (maxlon-minlon) / (db->ProjList[i]->Nc-1); memcpy( v->ProjArgs, args, 4*sizeof(float) ); } else { v->Projection = db->ProjList[i]->Kind; memcpy( v->ProjArgs, db->ProjList[i]->Args, MAXPROJARGS*sizeof(float)); } break; } } } /*** Vert Coord Sys ***/ if (vert_flag) { int maxnl = 0; for (i=0;iNumVars;i++) { if (v->Nl[i]>maxnl) { maxnl = v->Nl[i]; } } find_default_vcs( db, maxnl, &v->VerticalSystem, v->VertArgs ); } }