/*[LISTING FOUR]*/
/* --------------------------------------------------------------- *
* VIEW Program: View the line segments from a global database. *
* Display areas of the Earth in stereographic projection at dif- *
* ferent scales and with different map center (tangency) points. *
* The user interface is simple: it provides for change of scale *
* and shift of map center point using the sign and arrow keys. *
* For each map scene, calculate its radius of view. Then retrieve *
* from the coordinate file only those coastline segments that *
* might come into view. Draw the line segments, relaying on the *
* display-graphics to clip the parts still outside the window. *
* --------------------------------------------------------------- */
#include
#include
#include
#include
#include /* Using MS C 6.0 graphics... */
#include "algebra.c"
#define DISPL_WIDE 0.24 /* screen width, meters */
#define DISPL_HIGH 0.18 /* screen height, meters */
#define EARTH_RAD 6.371e6 /* approximate Earth's radius, meters */
#define MERIDIAN 1 /* grid drawing selectors */
#define PARALLEL 0
#define COLOR_FRAME 7
#define COLOR_GRID 1
#define COLOR_COAST 3
#define COLOR_SCALE 7
#define COLOR_PROMPT_TEXT 5
#define COLOR_PROMPT_KEYS 7
void drawDataLines(FILE *, FILE *);
void drawGrid(int, int);
void drawGridSegment(const struct ltln *, double, double, int);
static double xfmArray[8]; /* Plane/display transfomation */
static double maxDispDist; /* Radius of view */
static struct vct3 displayCenter; /* Map center, spherical */
void main(void) {
/* Initial map scale and center (projection plane tangency) point: */
double worldScale = 100.0e6;
struct ltln llStart = {DEG2RAD * 50.0, DEG2RAD * -100.0};
struct plpt pUpperLeft, pLowerRight, pNewCntr;
struct dpxl dUpperLeft, dUpperMid, dUpperRight,
dLowerLeft, dLowerMid, dLowerRight,
dLeftMid, dRightMid, dNewCntr, dCntr;
double worldWide, worldHigh;
struct videoconfig vcnfg;
struct vct3 sphVx;
int ich;
char outStr[32];
char *fnIndex0 = "coast0.idx";
char *fnIndex1 = "coast1.idx";
char *fnCoordinates = "coast.dat";
FILE *fpIndex0;
FILE *fpIndex1;
FILE *fpCoordinates;
/* ---------------------------------------------------------------- */
LatLongToDcos3(&llStart, &displayCenter); /* initial map center */
if ((fpIndex0 = fopen(fnIndex0, "rb")) == NULL) {
fprintf(stderr,"Index file (%s) open failed.\n", fnIndex0);
exit(1);
}
if ((fpIndex1 = fopen(fnIndex1, "rb")) == NULL) {
fprintf(stderr,"Index file (%s) open failed.\n", fnIndex1);
exit(1);
}
if ((fpCoordinates = fopen(fnCoordinates, "rb")) == NULL) {
fprintf(stderr,"Data file (%s) open failed.\n", fnCoordinates);
exit(1);
}
if (_setvideomode(_VRES16COLOR) == 0) { /* assume VGA graphics */
fprintf(stderr, "Graphics mode set failed.\n");
exit(1);
}
_getvideoconfig(&vcnfg);
dUpperLeft.x = dUpperLeft.y = 0;
dLowerRight.x = vcnfg.numxpixels - 1;
dLowerRight.y = vcnfg.numypixels - 1 - 20;
_setcliprgn(dUpperLeft.x, dUpperLeft.y,
dLowerRight.x, dLowerRight.y);
dCntr.x = (dUpperLeft.x + dLowerRight.x)/2;
dCntr.y = (dUpperLeft.y + dLowerRight.y)/2;
dLowerLeft.x = dLeftMid.x = dUpperLeft.x;
dUpperRight.x = dRightMid.x = dLowerRight.x;
dUpperMid.x = dLowerMid.x = dCntr.x;
dUpperRight.y = dUpperMid.y = dUpperLeft.y;
dLowerLeft.y = dLowerMid.y = dLowerRight.y;
dLeftMid.y = dRightMid.y = dCntr.y;
for (;;) {
worldWide = (worldScale * DISPL_WIDE) / EARTH_RAD;
worldHigh = (worldScale * DISPL_HIGH) / EARTH_RAD;
pUpperLeft.est = -worldWide * 0.5;
pUpperLeft.nrt = worldHigh * 0.5;
pLowerRight.est = worldWide * 0.5;
pLowerRight.nrt = -worldHigh * 0.5;
SetPlaneDisplay(xfmArray,
&pUpperLeft, &pLowerRight, &dUpperLeft, &dLowerRight);
UnMapStereo(&displayCenter, &pUpperLeft, &sphVx);
maxDispDist = ArcDist(&displayCenter, &sphVx);
_clearscreen(_GCLEARSCREEN);
_setcolor(COLOR_GRID);
if (worldScale > 10.0e6) drawGrid( 1, 1);
else if (worldScale > 3.0e6) drawGrid( 2, 3);
else drawGrid(10, 15);
_settextcolor(COLOR_PROMPT_TEXT);
_settextposition(vcnfg.numtextrows, 20);
_outtext("Press space bar to interrupt this scene...");
_setcolor(COLOR_COAST);
if (worldScale > 20.0e6) drawDataLines(fpIndex1, fpCoordinates);
else drawDataLines(fpIndex0, fpCoordinates);
sprintf(outStr, worldScale > 20.0e6 ?
"1 : %.0lfM" : "1 : %.1lfM", worldScale / 1.0e6);
_settextposition(vcnfg.numtextrows - 2, 37);
_settextcolor(COLOR_SCALE);
_outtext(outStr);
_setcolor(COLOR_FRAME);
_rectangle(_GBORDER, dUpperLeft.x, dUpperLeft.y,
dLowerRight.x, dLowerRight.y);
_settextcolor(COLOR_PROMPT_TEXT);
_settextposition(vcnfg.numtextrows, 1);
_outtext(" Press: (+)|(-) to change scale, (\x1b)|(\x18)|"
"(\x19)|(\x1a) to move center, (Esc) to quit.");
_settextcolor(COLOR_PROMPT_KEYS); /* highlight key characters */
_settextposition(vcnfg.numtextrows, 10); _outtext("+");
_settextposition(vcnfg.numtextrows, 14); _outtext("-");
_settextposition(vcnfg.numtextrows, 35); _outtext("\x1b");
_settextposition(vcnfg.numtextrows, 39); _outtext("\x18");
_settextposition(vcnfg.numtextrows, 43); _outtext("\x19");
_settextposition(vcnfg.numtextrows, 47); _outtext("\x1a");
_settextposition(vcnfg.numtextrows, 67); _outtext("Esc");
do {
if (ich = getch()) { /* non-0 scan code, ACSII character */
switch (ich) {
case 45: worldScale *= 2.0; break; /* - */
case 43: worldScale /= 2.0; break; /* + */
case 27: _setvideomode(_DEFAULTMODE); exit(0);/* Esc */
default: putch('\a'); ich = 0; /* invalid key */
}
if (ich) { /* OK, scale changed, enforce limits */
if (worldScale < 1.5625e6) worldScale = 1.5625e6;
if (worldScale > 200.0e6) worldScale = 200.0e6;
}
}
else { /* arrow or diagonal key, move map tangency point */
switch (ich = getch()) { /* get "extended scan code" */
case 73: dNewCntr = dUpperRight; break; /* up/right */
case 72: dNewCntr = dUpperMid; break; /* up */
case 71: dNewCntr = dUpperLeft; break; /* up/left */
case 75: dNewCntr = dLeftMid; break; /* left */
case 79: dNewCntr = dLowerLeft; break; /* down/left */
case 76: dNewCntr = dCntr; break; /* 5, center */
case 80: dNewCntr = dLowerMid; break; /* down */
case 81: dNewCntr = dLowerRight; break;/* down/right */
case 77: dNewCntr = dRightMid; break; /* right */
default: putch('\a'); ich = 0; /* invalid key */
}
if (ich) { /* OK, center was re-positioned */
DisplayToPlane(xfmArray, &dNewCntr, &pNewCntr);
UnMapStereo(&displayCenter, &pNewCntr, &displayCenter);
}
}
} while (ich == 0); /* i.e. until valid input was obtained */
}
}
/* ---- Traverse the Line Index and Display Close Line Segments ---- */
void drawDataLines(FILE *fpIndex, FILE *fpCoordinates) {
struct indexRec indexRec;
struct lclpt shortVx;
struct vct3 sphereVx;
struct plpt stereoPlaneVx;
struct dpxl displayVx;
double s;
int i;
/* ---------------------------------------------------------------- */
rewind(fpIndex); /* whole index will be searched sequentially */
while (fread(&indexRec, sizeof(struct indexRec), 1, fpIndex)) {
if (kbhit()) if (getch() == 32) break; /* space bar, break */
/* Skip line segments which are so far away from the display
that they can't have any vertices in it... */
if (ArcDist(&displayCenter, &indexRec.center)
> maxDispDist + indexRec.radius) continue;
s = indexRec.radius / LC_SCALE;
fseek(fpCoordinates, indexRec.fileOffset, SEEK_SET);
for (i = 0; i < indexRec.vertexCount; i++) {
fread(&shortVx, sizeof(struct lclpt), 1, fpCoordinates);
stereoPlaneVx.est = s * (double)shortVx.est;
stereoPlaneVx.nrt = s * (double)shortVx.nrt;
UnMapStereo(&indexRec.center, &stereoPlaneVx, &sphereVx);
MapStereo(&displayCenter, &sphereVx, &stereoPlaneVx);
PlaneToDisplay(xfmArray, &stereoPlaneVx, &displayVx);
if (i == 0) _moveto(displayVx.x, displayVx.y);
else _lineto(displayVx.x, displayVx.y);
}
}
}
/* ---- Display Latitude/Longitude "Rectangles" in the Display ---- */
#define RCT_LAT_DEG 10 /* Rectangle extent in latitude... */
#define RCT_LNG_DEG 15 /* ... and longitude, in degrees */
#define RCT_HALFDIAG_DEG 9 /* Rect. center-to-corner distance */
#define RCT_LAT_RAD (DEG2RAD * RCT_LAT_DEG) /* same in radians */
#define RCT_LNG_RAD (DEG2RAD * RCT_LNG_DEG)
void drawGrid(int densityLat, int densityLng) {
struct ltln llVx;
struct vct3 sphereVx;
double sLat, sLng;
int i, j, k;
/* ---------------------------------------------------------------- */
for (i = -80; i <= 80; i += RCT_LAT_DEG) {
for (j = -180; j < 180; j += RCT_LNG_DEG) {
llVx.lat = i * DEG2RAD + 0.5 * RCT_LAT_RAD;
llVx.lng = j * DEG2RAD + 0.5 * RCT_LNG_RAD;
LatLongToDcos3(&llVx, &sphereVx); /* grid rectangle center */
if (ArcDist(&displayCenter, &sphereVx)
> maxDispDist + DEG2RAD * RCT_HALFDIAG_DEG) continue;
sLat = (RCT_LAT_RAD / densityLat);
sLng = (RCT_LNG_RAD / densityLng);
for (k = 0; k < densityLat; k++) {
llVx.lat = i * DEG2RAD + k * sLat;
llVx.lng = j * DEG2RAD;
drawGridSegment(&llVx, sLng / 4, RCT_LNG_RAD, PARALLEL);
}
if (i == 80) continue;
for (k = 0; k < densityLng; k++) {
llVx.lat = i * DEG2RAD;
llVx.lng = j * DEG2RAD + k * sLng;
drawGridSegment(&llVx, sLat / 4, RCT_LAT_RAD, MERIDIAN);
}
}
}
}
/* ---- Display a Segment of a Meridian or a Parallel in Short Steps ---- */
void drawGridSegment(const struct ltln *llVxStart,
double step, double maxDist, int m) {
struct ltln llVx;
struct vct3 sphereVx;
struct plpt stereoPlaneVx;
struct dpxl displayVx;
double d = 0.0;
/* ---------------------------------------------------------------- */
LatLongToDcos3(llVxStart, &sphereVx);
MapStereo(&displayCenter, &sphereVx, &stereoPlaneVx);
PlaneToDisplay(xfmArray, &stereoPlaneVx, &displayVx);
_moveto(displayVx.x, displayVx.y);
do {
d += step;
if (d + 1.0e-10 > maxDist) d = maxDist;
llVx.lat = llVxStart->lat + (m * d); /* is 0 or 1 */
llVx.lng = llVxStart->lng + ((1 - m) * d);
LatLongToDcos3(&llVx, &sphereVx);
MapStereo(&displayCenter, &sphereVx, &stereoPlaneVx);
PlaneToDisplay(xfmArray, &stereoPlaneVx, &displayVx);
_lineto(displayVx.x, displayVx.y);
} while (d != maxDist);
}