awips2/nativeLib/rary.meteorological/src/va_advanced.c
root 06a8b51d6d Initial revision of AWIPS2 11.9.0-7p5
Former-commit-id: 64fa9254b946eae7e61bbc3f513b7c3696c4f54f
2012-01-06 08:55:05 -06:00

502 lines
16 KiB
C
Executable file

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "va_advanced.h"
#define BREAKAXIS 5
#define BREAKALL 25
#define BREAKAXIST 8
#define BREAKALLT 64
static float aspect = 0;
static int min_literal = 1;
static int max_literal = 0;
static int dist_pass = 0;
static int recomp = 0;
static float * xx, * yy, * zz;
static unsigned char * assigned;
static float * lats, * lons;
static float * dist;
static float * gooddist;
static int * goodness;
static double * goodcomp;
static float weighting = 0.0;
static int just_goodness = 0;
static float dtr;
/* This routine allows the client to place more importance on either user */
/* preferences or spatial uniformity in determining the visibility */
/* thresholds. 0 means only consider user preferences, 1 means only */
/* consider spatial uniformity. 0 is the default, values between 0 and 1 */
/* are meaningful. */
void va_weighting(float wgt)
{
weighting = wgt;
}
/* Normally, when selecting which station to include next, two things are */
/* considered. First, spatial uniformity, which means use the station */
/* furthest from any station currently included. Second, user preferences */
/* which are usually expressed in terms of the distance to the nearest */
/* other station with at least as great a goodness value. Expressing both */
/* in terms of a distance means that these two considerations can easily be */
/* blended. This routine allows one to just consider the goodness value as */
/* is for determining the next station to include. */
void va_just_goodness(int yes)
{
just_goodness = yes;
}
/* When this value is set to greater than one, it is assumed that the items */
/* being plotted using this progressive strategy are this factor wider than */
/* they are tall. */
void va_aspect(float a)
{
aspect = a;
}
/* Goodness values between these two numbers are taken literally as */
/* progressive disclosure distances. By default no goodness values will */
/* be treated this way. */
void va_literal(int mn, int mx)
{
min_literal = mn;
max_literal = mx;
}
/* If this is set to true, then valid progressive disclosure distances in */
/* the array input_dist will be preserved. */
void va_dist_pass(int dp)
{
dist_pass = dp;
}
/* If this is set to true, then valid progressive disclosure distances in */
/* the array input_dist will be converted to goodness values. */
void va_recomp(int rc)
{
recomp = rc;
}
/* The job of this routine is to convert goodness values into a precursor */
/* to progressive disclosure distances, namely the distance to the nearest */
/* other station which has at least as great a goodness value. */
static int goodness_to_dist(float minlat, float maxlat,
float minlon, float maxlon,
int * active, int na)
{
float dis1;
int best1;
int * subactive[BREAKALL];
int ns[BREAKALL];
float mnlts[BREAKALL], mxlts[BREAKALL];
float mnlns[BREAKALL], mxlns[BREAKALL];
int **subim, **subjm, **submm;
int *nsim, *nsjm, *nsmm;
int * topactive;
int nt;
int i,j,k,kk,a,g,gg;
float dlt,dln;
float mnlt,mxlt,mnln,mxln;
float dx,dy,dz,d,dd;
if (na<=0) return -1;
if (na>BREAKALL*2) {
/* We still have too many stations in this sub area for this
algorithm to work efficiently, divide this working area
further into subareas. */
nt = 0;
topactive = (int *)malloc(4*BREAKALL);
dlt = (maxlat-minlat)/(1+BREAKAXIS);
dln = (maxlon-minlon)/(1+BREAKAXIS);
mnlt = minlat;
mxlt = mnlt+2*dlt;
for (k=0,j=0; j<BREAKAXIS; j++) {
mnln = minlon;
mxln = mnln+2*dln;
for (i=0; i<BREAKAXIS; i++,k++) {
subactive[k] = (int *)malloc(4*na);
ns[k] = 0;
mnlns[k] = mnln;
mxlns[k] = mxln;
mnlts[k] = mnlt;
mxlts[k] = mxlt;
mnln += dln;
mxln += dln;
}/*end for i*/
mnlt += dlt;
mxlt += dlt;
}/*end for j*/
subim = subactive-1;
subjm = subactive-BREAKAXIS;
submm = subjm-1;
nsim = ns-1;
nsjm = ns-BREAKAXIS;
nsmm = nsjm-1;
/* Assign each station to its proper sub area. */
for (a=0; a<na; a++) {
g = active[a];
i = (int)((lons[g]-minlon)/dln);
if (i<0) i = 0;
if (i>BREAKAXIS) i = BREAKAXIS;
j = (int)((lats[g]-minlat)/dlt);
if (j<0) j = 0;
if (j>BREAKAXIS) j = BREAKAXIS;
k = j*BREAKAXIS+i;
if (i<=0) {
if (j>0)
subjm[k][nsjm[k]++] = g;
if (j<BREAKAXIS)
subactive[k][ns[k]++] = g;
} else if (i>=BREAKAXIS) {
if (j>0)
submm[k][nsmm[k]++] = g;
if (j<BREAKAXIS)
subim[k][nsim[k]++] = g;
} else {
if (j>0) {
subjm[k][nsjm[k]++] = g;
submm[k][nsmm[k]++] = g;
}/*endif*/
if (j<BREAKAXIS) {
subactive[k][ns[k]++] = g;
subim[k][nsim[k]++] = g;
}/*endif*/
}/*endif*/
}/*end for a*/
/* Do the goodness value to goodness distance conversions separately
for the stations in each sub area. Accumulate the indices of the
station with the highest goodness distance from each sub area. */
for (k=0; k<BREAKALL; k++) {
kk = goodness_to_dist(mnlts[k],mxlts[k],mnlns[k],mxlns[k],
subactive[k],ns[k]);
if (kk>=0) topactive[nt++] = kk;
free(subactive[k]);
}/*end for gg*/
} else {
nt = na;
topactive = active;
}/*endif*/
best1 = -1;
dis1 = -1;
/* We are finding the distance to the nearest
station that is at least as desirable. */
for (k=0; k<nt-1; k++) {
for (kk=k+1; kk<nt; kk++) {
g = topactive[k];
gg = topactive[kk];
if (g==gg) continue;
if (goodcomp[g]<goodcomp[gg])
dd = gooddist[g];
else if (goodcomp[gg]<goodcomp[g])
dd = gooddist[gg];
else if (gooddist[g]>gooddist[gg])
dd = gooddist[g];
else
dd = gooddist[gg];
dx = xx[g]-xx[gg];
if (dx>dd || -dx>dd) continue;
dy = yy[g]-yy[gg];
if (dy>dd || -dy>dd) continue;
dz = zz[g]-zz[gg];
if (dz>dd || -dz>dd) continue;
d = sqrt(dx*dx+dy*dy+dz*dz);
if (d>dd) continue;
if (goodcomp[g]<=goodcomp[gg] && d<gooddist[g])
gooddist[g] = d;
if (goodcomp[gg]<=goodcomp[g] && d<gooddist[gg])
gooddist[gg] = d;
}/*end for kk*/
}/*end for k*/
/* Report station with the furthest distance to the station
that is at least as desirable. */
for (k=0; k<nt; k++) {
g = topactive[k];
if (gooddist[g]<dis1) continue;
dis1 = gooddist[g];
best1 = g;
}/*end for*/
if (active!=topactive) free(topactive);
return best1;
}
/* This routine performs the primary function of this module for the client. */
/* The basic output of this module is a set of progressive disclosure */
/* distances in the array `input_dist'. The basic input, beyond the */
/* lat/lon locations of the points, is the goodness values in */
/* `input_goodness', which are relative client preferences as to which */
/* station should show up first when zooming. The goodness values are only */
/* hints; the routine makes the final determination of the progressive */
/* disclosure distances based primarily on two rules. The first rule is */
/* that once a point is revealed by zooming, further zooming will not cause */
/* it to dissappear. The second rule is that stations do not overlap each */
/* other when plotted, which of course it the main reason for the existence */
/* of this routine. The progressive disclosure distances are the distance */
/* in kilometers to the nearest other station which is at least as visible. */
/* In order to apply these distances to a progressive disclosure strategy, */
/* the client needs knowledge of the the physical size of the objects being */
/* plotted on the screen. */
void va_advanced(float * input_lats, float * input_lons, int * input_goodness,
float * input_dist, int input_ns)
{
int i,g,gg,k,kk,na;
int ns;
float lt,ln;
float dx,dy,dz,d,dd;
double d2,dd2;
float minlat,minlon,maxlat,maxlon;
int * active;
int * unassigned;
double big;
float xxa,yya,zza;
float xxn,yyn,zzn;
float xxe,yye;
float myaspect;
/* No points, give up. */
if (input_ns<=0) return;
/* set internal variables for the input arguments, allocate any memory
we will need. */
ns = input_ns;
lats = input_lats;
lons = input_lons;
goodness = input_goodness;
dist = input_dist;
xx = (float *) malloc(4*ns);
yy = (float *) malloc(4*ns);
zz = (float *) malloc(4*ns);
gooddist = (float *) malloc(4*ns);
assigned = (unsigned char *) malloc(ns);
active = (int *) malloc(4*ns);
i = 0x7FFFFFFF;
big = (double)i;
/* goodcomp is double because it must have the precision of a float and
the range of integral values of an int. It is workspace within
which we convert goodness values to goodness distances. */
goodcomp = (double *) malloc(8*ns);
/* Here we calculate a unit vector on the earths surface which is used
to make quick relative comparisons of point to point distances.
Here we also figure out the entire range of lats and lons in the
data set and we handle whether input valid progressive disclosure
distances are preserved, ignored, or turned into goodness values.
Stations with no initial progressive disclosure distance get a
relative distance of 2.0, which when converted to a real distance
is halfway round the globe. */
minlat = maxlat = lats[0];
minlon = maxlon = lons[0];
xxa = yya = zza = 0.0;
dtr = atan(1.0)/45.0;
for (i=0; i<ns; i++) {
active[i] = i;
lt = dtr*lats[i];
ln = dtr*lons[i];
xx[i] = cos(lt)*cos(ln);
yy[i] = cos(lt)*sin(ln);
zz[i] = sin(lt);
xxa += xx[i];
yya += yy[i];
zza += zz[i];
if ((dist_pass || recomp) && dist[i]>=0) {
assigned[i] = 1;
dist[i] = 2*sin(dist[i]/12740.0);
goodcomp[i] = (1+dist[i])*big;
} else if (goodness[i]>=min_literal && goodness[i]<=max_literal) {
assigned[i] = 1;
dist[i] = 2*sin(goodness[i]/12740.0);
goodcomp[i] = (1+dist[i])*big;
} else {
assigned[i] = 0;
dist[i] = 2.0;
goodcomp[i] = goodness[i];
}/*endif*/
if (recomp)
{
assigned[i] = 0;
dist[i] = 2.0;
}
gooddist[i] = 2.0;
if (lats[i]<minlat)
minlat = lats[i];
else if (lats[i]>maxlat)
maxlat = lats[i];
if (lons[i]<minlon)
minlon = lons[i];
else if (lons[i]>maxlon)
maxlon = lons[i];
}/*end for*/
/* try to account for the aspect ratio of what is being plotted if the
area of coverage is small. */
myaspect = 1.0;
while (aspect>1) {
/* unit vector of mean point */
xxa /= ns;
yya /= ns;
zza /= ns;
d = sqrt(xxa*xxa+yya*yya+zza*zza);
xxa /= d;
yya /= d;
zza /= d;
/* east unit vector at mean point */
xxe = -yya;
yye = xxa;
d = sqrt(xxe*xxe+yye*yye);
xxe /= d;
yye /= d;
/* get the greatest east-west separation from center */
dx = 0;
dd = 1.0;
for (i=0; i<ns; i++) {
d = xxe*xx[i]+yye*yy[i];
if (d<0) d = -d;
if (d>dx) dx = d;
d = xxa*xx[i]+yya*yy[i]+zza*zz[i];
if (d<dd) dd = d;
}/*end for*/
/* calculate working aspect based on the separation. */
dx *= dx;
dd = 1.0-dd*dd;
if (dd>dx) dx = dd;
dd = 1.0-2*dx;
if (dd<=0.0) break;
myaspect = pow(aspect,dd);
/* north unit vector at mean point (AxE) */
xxn = -zza*yye;
yyn = zza*xxe;
zzn = xxa*yye-yya*xxe;
/* artificially sqeeze points in the east-west dimension */
for (i=0; i<ns; i++) {
dx = (xxe*xx[i]+yye*yy[i])/myaspect;
dy = (xxn*xx[i]+yyn*yy[i]+zzn*zz[i]);
dz = (xxa*xx[i]+yya*yy[i]+zza*zz[i]);
xx[i] = dx;
yy[i] = dy;
zz[i] = dz;
}/*end for*/
break;
}/*end while*/
/* This is the area within which we will work as we turn relative
goodness values into goodness distances. */
d = (maxlat-minlat)/200;
minlat -= d;
maxlat += d;
d = (maxlon-minlon)/200;
minlon -= d;
maxlon += d;
/* Convert goodness values into goodness distances. */
if (!just_goodness)
goodness_to_dist(minlat,maxlat,minlon,maxlon,active,ns);
free(goodcomp);
/* Categorize stations as preassigned or not. Active becomes a map
for the order in which we consider stations for final resolution;
preassigned stations first, then all others. */
unassigned = (int *) malloc(4*ns);
na = kk = 0;
for (k=0; k<ns; k++)
if (assigned[k])
active[na++] = k;
else
unassigned[kk++] = k;
for (k=na,kk=0; k<ns; k++,kk++)
active[k] = unassigned[kk];
free(unassigned);
/* This loop is not technically through each station we need to assign,
but just for the number we need to assign. */
for (k=0; k<ns; k++) {
if (k>=na) {
/* Determine next non-preassigned station for final resolution. */
dd2 = -big;
g = -1;
for (kk=na; kk<ns; kk++) {
gg = active[kk];
if (assigned[gg]) continue;
/* Here is where we balance user preference versus spatial
uniformity. */
if (just_goodness)
d2 = goodness[gg];
else
d2 = dist[gg]*weighting+(1-weighting)*gooddist[gg];
if (d2<=dd2) continue;
g = gg;
dd2 = d2;
}/*end for*/
if (g<0) break;
} else
/* still working on preassigned stations. */
g = active[k];
/* Station g has been selected for final resolution. For all other
stations that have not yet been been resolved, reduce their
relative distance to the distance between it and station g, unless
its distance is already less. */
assigned[g] = 1;
for (kk=0; kk<ns; kk++) {
gg = active[kk];
if (assigned[gg]) continue;
dd = dist[gg];
if (dist[g]<dd) dd = dist[g];
dx = xx[g]-xx[gg];
if (dx>=dd || -dx>=dd) continue;
dy = yy[g]-yy[gg];
if (dy>=dd || -dy>=dd) continue;
dz = zz[g]-zz[gg];
if (dz>=dd || -dz>=dd) continue;
d = sqrt(dx*dx+dy*dy+dz*dz);
if (d>=dd) continue;
dist[gg] = d;
}/*end for*/
}/*end for*/
/* convert point to point distances in unit vector space to real distances
in kilometers. */
for (i=0; i<ns; i++)
dist[i] = myaspect*6370*2*asin(dist[i]/2);
/* Free up all the rest of the internally allocated memory. */
free(xx);
free(yy);
free(zz);
free(gooddist);
free(assigned);
free(active);
} /* end va_advanced */