10 #define DEM_BLOCK_SIZE 1024
11 #define GET_COLUMN(dem,n) ((VikDEMColumn *)g_ptr_array_index( (dem)->columns, (n) ))
13 static gboolean
get_double_and_continue ( gchar
**buffer
, gdouble
*tmp
, gboolean warn
)
16 *tmp
= g_strtod(*buffer
, &endptr
);
17 if ( endptr
== NULL
|| endptr
== *buffer
) {
19 g_warning("Invalid DEM");
27 static gboolean
get_int_and_continue ( gchar
**buffer
, gint
*tmp
, gboolean warn
)
30 *tmp
= strtol(*buffer
, &endptr
, 10);
31 if ( endptr
== NULL
|| endptr
== *buffer
) {
33 g_warning("Invalid DEM");
40 static gboolean
dem_parse_header ( gchar
*buffer
, VikDEM
*dem
)
47 /* incomplete header */
48 if ( strlen(buffer
) != 1024 )
51 /* fix Fortran-style exponentiation 1.0D5 -> 1.0E5 */
61 /* "DEM level code, pattern code, palaimetric reference system code" -- skip */
62 get_int_and_continue(&buffer
, &int_val
, TRUE
);
63 get_int_and_continue(&buffer
, &int_val
, TRUE
);
64 get_int_and_continue(&buffer
, &int_val
, TRUE
);
67 get_int_and_continue(&buffer
, &int_val
, TRUE
);
68 dem
->utm_zone
= int_val
;
69 /* TODO -- southern or northern hemisphere?! */
70 dem
->utm_letter
= 'N';
72 /* skip numbers 5-19 */
73 for ( i
= 0; i
< 15; i
++ ) {
74 if ( ! get_double_and_continue(&buffer
, &val
, FALSE
) ) {
75 g_warning ("Invalid DEM header");
80 /* number 20 -- horizontal unit code (utm/ll) */
81 get_double_and_continue(&buffer
, &val
, TRUE
);
82 dem
->horiz_units
= val
;
83 get_double_and_continue(&buffer
, &val
, TRUE
);
84 /* dem->orig_vert_units = val; now done below */
86 /* TODO: do this for real. these are only for 1:24k and 1:250k USGS */
87 if ( dem
->horiz_units
== VIK_DEM_HORIZ_UTM_METERS
) {
88 dem
->east_scale
= 10.0; /* meters */
89 dem
->north_scale
= 10.0;
90 dem
->orig_vert_units
= VIK_DEM_VERT_DECIMETERS
;
92 dem
->east_scale
= 3.0; /* arcseconds */
93 dem
->north_scale
= 3.0;
94 dem
->orig_vert_units
= VIK_DEM_VERT_METERS
;
98 get_double_and_continue(&buffer
, &val
, TRUE
);
100 /* now we get the four corner points. record the min and max. */
101 get_double_and_continue(&buffer
, &val
, TRUE
);
102 dem
->min_east
= dem
->max_east
= val
;
103 get_double_and_continue(&buffer
, &val
, TRUE
);
104 dem
->min_north
= dem
->max_north
= val
;
106 for ( i
= 0; i
< 3; i
++ ) {
107 get_double_and_continue(&buffer
, &val
, TRUE
);
108 if ( val
< dem
->min_east
) dem
->min_east
= val
;
109 if ( val
> dem
->max_east
) dem
->max_east
= val
;
110 get_double_and_continue(&buffer
, &val
, TRUE
);
111 if ( val
< dem
->min_north
) dem
->min_north
= val
;
112 if ( val
> dem
->max_north
) dem
->max_north
= val
;
118 static void dem_parse_block_as_cont ( gchar
*buffer
, VikDEM
*dem
, gint
*cur_column
, gint
*cur_row
)
121 while ( *cur_row
< GET_COLUMN(dem
, *cur_column
)->n_points
) {
122 if ( get_int_and_continue(&buffer
, &tmp
,FALSE
) ) {
123 if ( dem
->orig_vert_units
== VIK_DEM_VERT_DECIMETERS
)
124 GET_COLUMN(dem
, *cur_column
)->points
[*cur_row
] = (gint16
) (tmp
/ 10);
126 GET_COLUMN(dem
, *cur_column
)->points
[*cur_row
] = (gint16
) tmp
;
131 *cur_row
= -1; /* expecting new column */
134 static void dem_parse_block_as_header ( gchar
*buffer
, VikDEM
*dem
, gint
*cur_column
, gint
*cur_row
)
138 gdouble east_west
, south
;
141 /* 1 x n_rows 1 east_west south x x x DATA */
143 if ( (!get_double_and_continue(&buffer
, &tmp
, TRUE
)) || tmp
!= 1 ) {
144 g_warning("Incorrect DEM Class B record: expected 1");
148 /* don't need this */
149 if ( !get_double_and_continue(&buffer
, &tmp
, TRUE
) ) return;
152 if ( !get_double_and_continue(&buffer
, &tmp
, TRUE
) )
154 n_rows
= (guint
) tmp
;
156 if ( (!get_double_and_continue(&buffer
, &tmp
, TRUE
)) || tmp
!= 1 ) {
157 g_warning("Incorrect DEM Class B record: expected 1");
161 if ( !get_double_and_continue(&buffer
, &east_west
, TRUE
) )
163 if ( !get_double_and_continue(&buffer
, &south
, TRUE
) )
166 /* next three things we don't need */
167 if ( !get_double_and_continue(&buffer
, &tmp
, TRUE
)) return;
168 if ( !get_double_and_continue(&buffer
, &tmp
, TRUE
)) return;
169 if ( !get_double_and_continue(&buffer
, &tmp
, TRUE
)) return;
175 /* empty spaces for things before that were skipped */
176 (*cur_row
) = (south
- dem
->min_north
) / dem
->north_scale
;
177 if ( south
> dem
->max_north
|| (*cur_row
) < 0 )
182 g_ptr_array_add ( dem
->columns
, g_malloc(sizeof(VikDEMColumn
)) );
183 GET_COLUMN(dem
,*cur_column
)->east_west
= east_west
;
184 GET_COLUMN(dem
,*cur_column
)->south
= south
;
185 GET_COLUMN(dem
,*cur_column
)->n_points
= n_rows
;
186 GET_COLUMN(dem
,*cur_column
)->points
= g_malloc(sizeof(gint16
)*n_rows
);
188 /* no information for things before that */
189 for ( i
= 0; i
< (*cur_row
); i
++ )
190 GET_COLUMN(dem
,*cur_column
)->points
[i
] = VIK_DEM_INVALID_ELEVATION
;
192 /* now just continue */
193 dem_parse_block_as_cont ( buffer
, dem
, cur_column
, cur_row
);
198 static void dem_parse_block ( gchar
*buffer
, VikDEM
*dem
, gint
*cur_column
, gint
*cur_row
)
200 /* if haven't read anything or have read all items in a columns and are expecting a new column */
201 if ( *cur_column
== -1 || *cur_row
== -1 ) {
202 dem_parse_block_as_header(buffer
, dem
, cur_column
, cur_row
);
204 dem_parse_block_as_cont(buffer
, dem
, cur_column
, cur_row
);
208 static VikDEM
*vik_dem_read_srtm_hgt(FILE *f
, const gchar
*basename
)
215 dem
= g_malloc(sizeof(VikDEM
));
217 dem
->horiz_units
= VIK_DEM_HORIZ_LL_ARCSECONDS
;
218 dem
->orig_vert_units
= VIK_DEM_VERT_DECIMETERS
;
219 dem
->east_scale
= dem
->north_scale
= 3;
222 dem
->min_north
= atoi(basename
+1) * 3600;
223 dem
->min_east
= atoi(basename
+4) * 3600;
224 if ( basename
[0] == 'S' )
225 dem
->min_north
= - dem
->min_north
;
226 if ( basename
[3] == 'W' )
227 dem
->min_east
= - dem
->min_east
;
229 dem
->max_north
= 3600 + dem
->min_north
;
230 dem
->max_east
= 3600 + dem
->min_east
;
232 dem
->columns
= g_ptr_array_new();
235 for ( i
= 0; i
< 1201; i
++ ) {
237 g_ptr_array_add ( dem
->columns
, g_malloc(sizeof(VikDEMColumn
)) );
238 GET_COLUMN(dem
,i
)->east_west
= dem
->min_east
+ 3*i
;
239 GET_COLUMN(dem
,i
)->south
= dem
->min_north
;
240 GET_COLUMN(dem
,i
)->n_points
= 1201;
241 GET_COLUMN(dem
,i
)->points
= g_malloc(sizeof(gint16
)*1201);
244 for ( i
= 1200; i
>= 0; i
-- ) {
245 for ( j
= 0; j
< 1201; j
++ ) {
247 g_warning("Incorrect SRTM file: unexpected EOF");
248 g_print("%d %d\n", i
, j
);
251 fread(&elev
, sizeof(elev
), 1, f
);
252 gchar
*x
= (gchar
*) &elev
;
257 GET_COLUMN(dem
,j
)->points
[i
] = elev
;
265 #define IS_SRTM_HGT(fn) (strlen((fn))==11 && (fn)[7]=='.' && (fn)[8]=='h' && (fn)[9]=='g' && (fn)[10]=='t' && ((fn)[0]=='N' || (fn)[0]=='S') && ((fn)[3]=='E' || (fn)[3]=='W'))
267 VikDEM
*vik_dem_new_from_file(const gchar
*file
)
271 gchar buffer
[DEM_BLOCK_SIZE
+1];
273 /* use to record state for dem_parse_block */
274 gint cur_column
= -1;
276 const gchar
*basename
= a_file_basename(file
);
279 f
= fopen(file
, "r");
283 if ( strlen(basename
)==11 && basename
[7]=='.' && basename
[8]=='h' && basename
[9]=='g' && basename
[10]=='t' &&
284 (basename
[0] == 'N' || basename
[0] == 'S') && (basename
[3] == 'E' || basename
[3] =='W'))
285 return vik_dem_read_srtm_hgt(f
, basename
);
287 /* Create Structure */
288 rv
= g_malloc(sizeof(VikDEM
));
291 buffer
[fread(buffer
, 1, DEM_BLOCK_SIZE
, f
)] = '\0';
292 if ( ! dem_parse_header ( buffer
, rv
) ) {
296 /* TODO: actually use header -- i.e. GET # OF COLUMNS EXPECTED */
298 rv
->columns
= g_ptr_array_new();
306 buffer
[fread(buffer
, 1, DEM_BLOCK_SIZE
, f
)] = '\0';
308 /* Fix Fortran-style exponentiation */
316 dem_parse_block(buffer
, rv
, &cur_column
, &cur_row
);
319 /* TODO - class C records (right now says 'Invalid' and dies) */
324 if ( rv
->horiz_units
== VIK_DEM_HORIZ_UTM_METERS
&& rv
->n_columns
>= 2 )
325 rv
->north_scale
= rv
->east_scale
= GET_COLUMN(rv
, 1)->east_west
- GET_COLUMN(rv
,0)->east_west
;
327 /* FIXME bug in 10m DEM's */
328 if ( rv
->horiz_units
== VIK_DEM_HORIZ_UTM_METERS
&& rv
->north_scale
== 10 ) {
330 rv
->min_north
+= 200;
337 void vik_dem_free ( VikDEM
*dem
)
340 for ( i
= 0; i
< dem
->n_columns
; i
++)
341 g_free ( GET_COLUMN(dem
, i
)->points
);
342 g_ptr_array_free ( dem
->columns
, TRUE
);
346 gint16
vik_dem_get_xy ( VikDEM
*dem
, guint col
, guint row
)
348 if ( col
< dem
->n_columns
)
349 if ( row
< GET_COLUMN(dem
, col
)->n_points
)
350 return GET_COLUMN(dem
, col
)->points
[row
];
351 return VIK_DEM_INVALID_ELEVATION
;
354 gint16
vik_dem_get_east_north ( VikDEM
*dem
, gdouble east
, gdouble north
)
358 if ( east
> dem
->max_east
|| east
< dem
->min_east
||
359 north
> dem
->max_north
|| north
< dem
->min_north
)
360 return VIK_DEM_INVALID_ELEVATION
;
362 col
= (gint
) floor((east
- dem
->min_east
) / dem
->east_scale
);
363 row
= (gint
) floor((north
- dem
->min_north
) / dem
->north_scale
);
365 return vik_dem_get_xy ( dem
, col
, row
);
368 void vik_dem_east_north_to_xy ( VikDEM
*dem
, gdouble east
, gdouble north
, guint
*col
, guint
*row
)
370 *col
= (gint
) floor((east
- dem
->min_east
) / dem
->east_scale
);
371 *row
= (gint
) floor((north
- dem
->min_north
) / dem
->north_scale
);
372 if ( *col
< 0 ) *col
= 0;
373 if ( *row
< 0 ) *row
= 0;