3 // dem.c -- DEM management class
5 // Written by Curtis Olson, started March 1998.
7 // Copyright (C) 1998 Curtis L. Olson - curt@me.umn.edu
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License as
11 // published by the Free Software Foundation; either version 2 of the
12 // License, or (at your option) any later version.
14 // This program is distributed in the hope that it will be useful, but
15 // WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // General Public License for more details.
19 // You should have received a copy of the GNU General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 // (Log is kept at end of this file)
27 #include <ctype.h> // isspace()
28 #include <math.h> // rint()
30 #include <stdlib.h> // atoi()
31 #include <sys/stat.h> // stat()
32 #include <unistd.h> // stat()
38 fgDEM::fgDEM( void ) {
39 // printf("class fgDEM CONstructor called.\n");
44 int fgDEM::open ( char *file ) {
45 // open input file (or read from stdin)
46 if ( strcmp(file, "-") == 0 ) {
47 printf("Loading DEM data file: stdin\n");
50 if ( (fd = fopen(file, "r")) == NULL ) {
51 printf("Cannot open %s\n", file);
54 printf("Loading DEM data file: %s\n", file);
62 int fgDEM::close ( void ) {
69 // return next token from input stream
70 static void next_token(FILE *fd, char *token) {
73 result = fscanf(fd, "%s", token);
75 if ( result == EOF ) {
76 strcpy(token, "__END_OF_FILE__");
77 printf(" Warning: Reached end of file!\n");
80 // printf(" returning %s\n", token);
84 // return next integer from input stream
85 static int next_int(FILE *fd) {
88 next_token(fd, token);
89 return ( atoi(token) );
93 // return next double from input stream
94 double next_double(FILE *fd) {
97 next_token(fd, token);
98 return ( atof(token) );
102 // return next exponential num from input stream
103 int next_exp(FILE *fd) {
108 fscanf(fd, "%lfD%d", &mantissa, &exp);
110 // printf(" Mantissa = %.4f Exp = %d\n", mantissa, exp);
114 for ( i = 1; i <= exp; i++ ) {
117 } else if ( exp < 0 ) {
118 for ( i = -1; i >= exp; i-- ) {
123 return( (int)rint(mantissa * (double)acc) );
127 // read and parse DEM "A" record
128 void fgDEM::read_a_record( void ) {
135 // get the name field (144 characters)
136 for ( i = 0; i < 144; i++ ) {
141 // clean off the whitespace at the end
142 for ( i = strlen(name)-2; i > 0; i-- ) {
143 if ( !isspace(name[i]) ) {
149 printf(" Quad name field: %s\n", name);
151 // DEM level code, 3 reflects processing by DMA
153 printf(" DEM level code = %d\n", inum);
155 // Pattern code, 1 indicates a regular elevation pattern
157 printf(" Pattern code = %d\n", inum);
159 // Planimetric reference system code, 0 indicates geographic
160 // coordinate system.
162 printf(" Planimetric reference code = %d\n", inum);
166 printf(" Zone code = %d\n", inum);
168 // Map projection parameters (ignored)
169 for ( i = 0; i < 15; i++ ) {
170 dnum = next_double(fd);
171 // printf("%d: %f\n",i,dnum);
174 // Units code, 3 represents arc-seconds as the unit of measure for
175 // ground planimetric coordinates throughout the file.
178 printf(" Unknown (X,Y) units code = %d!\n", inum);
182 // Units code; 2 represents meters as the unit of measure for
183 // elevation coordinates throughout the file.
186 printf(" Unknown (Z) units code = %d!\n", inum);
190 // Number (n) of sides in the polygon which defines the coverage of
191 // the DEM file (usually equal to 4).
194 printf(" Unknown polygon dimension = %d!\n", inum);
198 // Ground coordinates of bounding box in arc-seconds
199 dem_x1 = originx = next_exp(fd);
200 dem_y1 = originy = next_exp(fd);
201 printf(" Origin = (%.2f,%.2f)\n", originx, originy);
203 dem_x2 = next_exp(fd);
204 dem_y2 = next_exp(fd);
206 dem_x3 = next_exp(fd);
207 dem_y3 = next_exp(fd);
209 dem_x4 = next_exp(fd);
210 dem_y4 = next_exp(fd);
212 // Minimum/maximum elevations in meters
213 dem_z1 = next_exp(fd);
214 dem_z2 = next_exp(fd);
215 printf(" Elevation range %.4f %.4f\n", dem_z1, dem_z2);
217 // Counterclockwise angle from the primary axis of ground
218 // planimetric referenced to the primary axis of the DEM local
220 next_token(fd, token);
222 // Accuracy code; 0 indicates that a record of accuracy does not
223 // exist and that no record type C will follow.
225 // DEM spacial resolution. Usually (3,3,1) (3,6,1) or (3,9,1)
226 // depending on latitude
228 // I will eventually have to do something with this for data at
229 // higher latitudes */
230 next_token(fd, token);
231 printf(" accuracy & spacial resolution string = %s\n", token);
233 printf(" length = %d\n", i);
235 ptr = token + i - 12;
236 printf(" last field = %s = %.2f\n", ptr, atof(ptr));
240 col_step = atof(ptr);
241 printf(" last field = %s = %.2f\n", ptr, col_step);
245 row_step = atof(ptr);
246 printf(" last field = %s = %.2f\n", ptr, row_step);
249 // accuracy code = atod(token)
251 printf(" Accuracy code = %d\n", inum);
253 printf(" column step = %.2f row step = %.2f\n",
255 // dimension of arrays to follow (1)
256 next_token(fd, token);
258 // number of profiles
259 dem_num_profiles = rows = next_int(fd);
260 printf(" Expecting %d profiles\n", dem_num_profiles);
264 // read and parse DEM "B" record
265 void fgDEM::read_b_record(float dem_data[DEM_SIZE_1][DEM_SIZE_1])
270 // row / column id of this profile
271 prof_col = next_int(fd);
272 prof_row = next_int(fd);
273 // printf("col id = %d row id = %d\n", prof_col, prof_row);
275 // Number of columns and rows (elevations) in this profile
276 prof_num_cols = cols = next_int(fd);
277 prof_num_rows = next_int(fd);
278 // printf(" profile num rows = %d\n", prof_num_cols);
280 // Ground planimetric coordinates (arc-seconds) of the first
281 // elevation in the profile
282 prof_x1 = next_exp(fd);
283 prof_y1 = next_exp(fd);
284 // printf(" Starting at %.2f %.2f\n", prof_x1, prof_y1);
286 // Elevation of local datum for the profile. Always zero for
287 // 1-degree DEM, the reference is mean sea level.
288 next_token(fd, token);
290 // Minimum and maximum elevations for the profile.
291 next_token(fd, token);
292 next_token(fd, token);
294 // One (usually) dimensional array (prof_num_cols,1) of elevations
295 for ( i = 0; i < prof_num_cols; i++ ) {
296 prof_data = next_int(fd);
297 dem_data[i][cur_row] = (float)prof_data;
303 int fgDEM::parse( float dem_data[DEM_SIZE_1][DEM_SIZE_1] ) {
310 for ( i = 0; i < dem_num_profiles; i++ ) {
311 read_b_record( dem_data );
314 if ( cur_row % 100 == 0 ) {
315 printf(" loaded %d profiles of data\n", cur_row);
319 printf(" Done parsing\n");
325 // return the current altitude based on mesh data. We should rewrite
326 // this to interpolate exact values, but for now this is good enough
327 double fgDEM::interpolate_altitude( float dem_data[DEM_SIZE_1][DEM_SIZE_1],
328 double lon, double lat)
330 // we expect incoming (lon,lat) to be in arcsec for now
332 double xlocal, ylocal, dx, dy, zA, zB, elev;
333 int x1, x2, x3, y1, y2, y3;
337 /* determine if we are in the lower triangle or the upper triangle
345 then calculate our end points
348 xlocal = (lon - originx) / col_step;
349 ylocal = (lat - originy) / row_step;
351 xindex = (int)(xlocal);
352 yindex = (int)(ylocal);
354 // printf("xindex = %d yindex = %d\n", xindex, yindex);
356 if ( xindex + 1 == cols ) {
360 if ( yindex + 1 == rows ) {
364 if ( (xindex < 0) || (xindex + 1 >= cols) ||
365 (yindex < 0) || (yindex + 1 >= rows) ) {
369 dx = xlocal - xindex;
370 dy = ylocal - yindex;
374 // printf(" Lower triangle\n");
378 z1 = dem_data[x1][y1];
382 z2 = dem_data[x2][y2];
386 z3 = dem_data[x3][y3];
388 // printf(" dx = %.2f dy = %.2f\n", dx, dy);
389 // printf(" (x1,y1,z1) = (%d,%d,%d)\n", x1, y1, z1);
390 // printf(" (x2,y2,z2) = (%d,%d,%d)\n", x2, y2, z2);
391 // printf(" (x3,y3,z3) = (%d,%d,%d)\n", x3, y3, z3);
393 zA = dx * (z2 - z1) + z1;
394 zB = dx * (z3 - z1) + z1;
396 // printf(" zA = %.2f zB = %.2f\n", zA, zB);
398 if ( dx > EPSILON ) {
399 elev = dy * (zB - zA) / dx + zA;
405 // printf(" Upper triangle\n");
409 z1 = dem_data[x1][y1];
413 z2 = dem_data[x2][y2];
417 z3 = dem_data[x3][y3];
419 // printf(" dx = %.2f dy = %.2f\n", dx, dy);
420 // printf(" (x1,y1,z1) = (%d,%d,%d)\n", x1, y1, z1);
421 // printf(" (x2,y2,z2) = (%d,%d,%d)\n", x2, y2, z2);
422 // printf(" (x3,y3,z3) = (%d,%d,%d)\n", x3, y3, z3);
424 zA = dy * (z2 - z1) + z1;
425 zB = dy * (z3 - z1) + z1;
427 // printf(" zA = %.2f zB = %.2f\n", zA, zB );
428 // printf(" xB - xA = %.2f\n", col_step * dy / row_step);
430 if ( dy > EPSILON ) {
431 elev = dx * (zB - zA) / dy + zA;
441 // Use least squares to fit a simpler data set to dem data
442 void fgDEM::fit( float dem_data[DEM_SIZE_1][DEM_SIZE_1],
443 float output_data[DEM_SIZE_1][DEM_SIZE_1],
444 char *fg_root, double error, struct fgBUCKET *p )
446 double x[DEM_SIZE_1], y[DEM_SIZE_1];
447 double m, b, ave_error, max_error;
449 int n, row, start, end, good_fit;
450 int colmin, colmax, rowmin, rowmax;
451 // FILE *dem, *fit, *fit1;
453 printf("Initializing output mesh structure\n");
454 outputmesh_init( output_data );
456 // determine dimensions
457 colmin = p->x * ( (cols - 1) / 8);
458 colmax = colmin + ( (cols - 1) / 8);
459 rowmin = p->y * ( (rows - 1) / 8);
460 rowmax = rowmin + ( (rows - 1) / 8);
461 printf("Fitting region = %d,%d to %d,%d\n", colmin, rowmin, colmax, rowmax);
463 // include the corners explicitly
464 outputmesh_set_pt(output_data, colmin, rowmin, dem_data[colmin][rowmin]);
465 outputmesh_set_pt(output_data, colmin, rowmax, dem_data[colmin][rowmax]);
466 outputmesh_set_pt(output_data, colmax, rowmax, dem_data[colmax][rowmax]);
467 outputmesh_set_pt(output_data, colmax, rowmin, dem_data[colmax][rowmin]);
469 printf("Beginning best fit procedure\n");
471 for ( row = rowmin; row <= rowmax; row++ ) {
472 // fit = fopen("fit.dat", "w");
473 // fit1 = fopen("fit1.dat", "w");
477 // printf(" fitting row = %d\n", row);
479 while ( start < colmax ) {
483 x[(end - start) - 1] = 0.0 + ( start * col_step );
484 y[(end - start) - 1] = dem_data[start][row];
486 while ( (end <= colmax) && good_fit ) {
487 n = (end - start) + 1;
488 // printf("Least square of first %d points\n", n);
489 x[end - start] = 0.0 + ( end * col_step );
490 y[end - start] = dem_data[end][row];
491 least_squares(x, y, n, &m, &b);
492 ave_error = least_squares_error(x, y, n, m, b);
493 max_error = least_squares_max_error(x, y, n, m, b);
496 printf("%d - %d ave error = %.2f max error = %.2f y = %.2f*x + %.2f\n",
497 start, end, ave_error, max_error, m, b);
499 f = fopen("gnuplot.dat", "w");
500 for ( j = 0; j <= end; j++) {
501 fprintf(f, "%.2f %.2f\n", 0.0 + ( j * col_step ),
504 for ( j = start; j <= end; j++) {
505 fprintf(f, "%.2f %.2f\n", 0.0 + ( j * col_step ),
510 printf("Please hit return: "); gets(junk);
513 if ( max_error > error ) {
521 // error exceeded the threshold, back up
522 end -= 2; // back "end" up to the last good enough fit
523 n--; // back "n" up appropriately too
525 // we popped out of the above loop while still within
526 // the error threshold, so we must be at the end of
531 least_squares(x, y, n, &m, &b);
532 ave_error = least_squares_error(x, y, n, m, b);
533 max_error = least_squares_max_error(x, y, n, m, b);
537 printf("%d - %d ave error = %.2f max error = %.2f y = %.2f*x + %.2f\n",
538 start, end, ave_error, max_error, m, b);
541 fprintf(fit1, "%.2f %.2f\n", x[0], m * x[0] + b);
542 fprintf(fit1, "%.2f %.2f\n", x[end-start], m * x[end-start] + b);
545 if ( start > colmin ) {
546 // skip this for the first line segment
548 outputmesh_set_pt(output_data, start, row, (lasty + cury) / 2);
549 // fprintf(fit, "%.2f %.2f\n", x[0], (lasty + cury) / 2);
552 lasty = m * x[end-start] + b;
560 dem = fopen("gnuplot.dat", "w");
561 for ( j = 0; j < DEM_SIZE_1; j++) {
562 fprintf(dem, "%.2f %.2f\n", 0.0 + ( j * col_step ),
568 // NOTICE, this is for testing only. This instance of
569 // output_nodes should be removed. It should be called only
570 // once at the end once all the nodes have been generated.
571 // newmesh_output_nodes(&nm, "mesh.node");
572 // printf("Please hit return: "); gets(junk);
575 outputmesh_output_nodes(output_data, fg_root, p);
579 // Initialize output mesh structure
580 void fgDEM::outputmesh_init( float output_data[DEM_SIZE_1][DEM_SIZE_1] ) {
583 for ( i = 0; i < DEM_SIZE_1; i++ ) {
584 for ( j = 0; j < DEM_SIZE_1; j++ ) {
585 output_data[i][j] = -9999.0;
591 // Get the value of a mesh node
592 double fgDEM::outputmesh_get_pt( float output_data[DEM_SIZE_1][DEM_SIZE_1],
595 return ( output_data[i][j] );
599 // Set the value of a mesh node
600 void fgDEM::outputmesh_set_pt( float output_data[DEM_SIZE_1][DEM_SIZE_1],
601 int i, int j, double value )
603 // printf("Setting data[%d][%d] = %.2f\n", i, j, value);
604 output_data[i][j] = value;
608 // Write out a node file that can be used by the "triangle" program
609 void fgDEM::outputmesh_output_nodes( float output_data[DEM_SIZE_1][DEM_SIZE_1],
610 char *fg_root, struct fgBUCKET *p )
612 struct stat stat_buf;
613 char base_path[256], dir[256], file[256];
617 int colmin, colmax, rowmin, rowmax;
618 int i, j, count, result;
620 // determine dimensions
621 colmin = p->x * ( (cols - 1) / 8);
622 colmax = colmin + ( (cols - 1) / 8);
623 rowmin = p->y * ( (rows - 1) / 8);
624 rowmax = rowmin + ( (rows - 1) / 8);
625 printf(" dumping region = %d,%d to %d,%d\n",
626 colmin, rowmin, colmax, rowmax);
628 // generate the base directory
629 fgBucketGenBasePath(p, base_path);
630 printf("fg_root = %s Base Path = %s\n", fg_root, base_path);
631 sprintf(dir, "%s/Scenery/%s", fg_root, base_path);
632 printf("Dir = %s\n", dir);
634 // stat() directory and create if needed
635 result = stat(dir, &stat_buf);
637 printf("Stat error need to create directory\n");
638 sprintf(command, "mkdir -p %s\n", dir);
641 // assume directory exists
644 // get index and generate output file name
645 index = fgBucketGenIndex(p);
646 sprintf(file, "%s/%ld.node", dir, index);
648 printf("Creating node file: %s\n", file);
649 fd = fopen(file, "w");
651 // first count nodes to generate header
653 for ( j = rowmin; j <= rowmax; j++ ) {
654 for ( i = colmin; i <= colmax; i++ ) {
655 if ( output_data[i][j] > -9000.0 ) {
659 // printf(" count = %d\n", count);
661 fprintf(fd, "%d 2 1 0\n", count);
663 // now write out actual node data
665 for ( j = rowmin; j <= rowmax; j++ ) {
666 for ( i = colmin; i <= colmax; i++ ) {
667 if ( output_data[i][j] > -9000.0 ) {
668 fprintf(fd, "%d %.2f %.2f %.2f\n",
670 originx + (double)i * col_step,
671 originy + (double)j * row_step,
675 // printf(" count = %d\n", count);
682 fgDEM::~fgDEM( void ) {
683 // printf("class fgDEM DEstructor called.\n");
688 // Revision 1.1 1998/03/19 02:54:47 curt
689 // Reorganized into a class lib called fgDEM.
691 // Revision 1.1 1998/03/19 01:46:28 curt