]> git.street.me.uk Git - andy/viking.git/blob - src/dems.c
[QA] Add ifdef macro for Google Directions related
[andy/viking.git] / src / dems.c
1 /*
2  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3  *
4  * Copyright (C) 2003-2008, Evan Battaglia <gtoevan@gmx.net>
5  * Copyright (C) 2007, Quy Tonthat <qtonthat@gmail.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22 #include <glib.h>
23
24 #include "dems.h"
25 #include "background.h"
26
27 typedef struct {
28   VikDEM *dem;
29   guint ref_count;
30 } LoadedDEM;
31
32 GHashTable *loaded_dems = NULL;
33 /* filename -> DEM */
34
35 static void loaded_dem_free ( LoadedDEM *ldem )
36 {
37   vik_dem_free ( ldem->dem );
38   g_free ( ldem );
39 }
40
41 void a_dems_uninit ()
42 {
43   if ( loaded_dems )
44     g_hash_table_destroy ( loaded_dems );
45 }
46
47 /* To load a dem. if it was already loaded, will simply
48  * reference the one already loaded and return it.
49  */
50 VikDEM *a_dems_load(const gchar *filename)
51 {
52   LoadedDEM *ldem;
53
54   /* dems init hash table */
55   if ( ! loaded_dems )
56     loaded_dems = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) loaded_dem_free );
57
58   ldem = (LoadedDEM *) g_hash_table_lookup ( loaded_dems, filename );
59   if ( ldem ) {
60     ldem->ref_count++;
61     return ldem->dem;
62   } else {
63     VikDEM *dem = vik_dem_new_from_file ( filename );
64     if ( ! dem )
65       return NULL;
66     ldem = g_malloc ( sizeof(LoadedDEM) );
67     ldem->ref_count = 1;
68     ldem->dem = dem;
69     g_hash_table_insert ( loaded_dems, g_strdup(filename), ldem );
70     return dem;
71   }
72 }
73
74 void a_dems_unref(const gchar *filename)
75 {
76   LoadedDEM *ldem = (LoadedDEM *) g_hash_table_lookup ( loaded_dems, filename );
77   if ( !ldem ) {
78     /* This is fine - probably means the loaded list was aborted / not completed for some reason */
79     return;
80   }
81   ldem->ref_count--;
82   if ( ldem->ref_count == 0 )
83     g_hash_table_remove ( loaded_dems, filename );
84 }
85
86 /* to get a DEM that was already loaded.
87  * assumes that its in there already,
88  * although it could not be if earlier load failed.
89  */
90 VikDEM *a_dems_get(const gchar *filename)
91 {
92   LoadedDEM *ldem = g_hash_table_lookup ( loaded_dems, filename );
93   if ( ldem )
94     return ldem->dem;
95   return NULL;
96 }
97
98
99 /* Load a string list (GList of strings) of dems. You have to use get to at them later.
100  * When updating a list as a parameter, this should be bfore freeing the list so
101  * the same DEMs won't be loaded & unloaded.
102  * Modifies the list to remove DEMs which did not load.
103  */
104
105 /* TODO: don't delete them when they don't exist.
106  * we need to warn the user, but we should keep them in the GList.
107  * we need to know that they weren't referenced though when we
108  * do the a_dems_list_free().
109  */
110 int a_dems_load_list ( GList **dems, gpointer threaddata )
111 {
112   GList *iter = *dems;
113   guint dem_count = 0;
114   const guint dem_total = g_list_length ( *dems );
115   while ( iter ) {
116     if ( ! a_dems_load((const gchar *) (iter->data)) ) {
117       GList *iter_temp = iter->next;
118       g_free ( iter->data );
119       (*dems) = g_list_remove_link ( (*dems), iter );
120       iter = iter_temp;
121     } else {
122       iter = iter->next;
123     }
124     /* When running a thread - inform of progress */
125     if ( threaddata ) {
126       dem_count++;
127       /* NB Progress also detects abort request via the returned value */
128       int result = a_background_thread_progress ( threaddata, ((gdouble)dem_count) / dem_total );
129       if ( result != 0 )
130         return -1; /* Abort thread */
131     }
132   }
133   return 0;
134 }
135
136 /* Takes a string list (GList of strings) of dems (filenames).
137  * Unrefs all the dems (i.e. "unloads" them), then frees the
138  * strings, the frees the list.
139  */
140 void a_dems_list_free ( GList *dems )
141 {
142   GList *iter = dems;
143   while ( iter ) {
144     a_dems_unref ((const gchar *)iter->data);
145     g_free ( iter->data );
146     iter = iter->next;
147   }
148   g_list_free ( dems );
149 }
150
151 GList *a_dems_list_copy ( GList *dems )
152 {
153   GList *rv = g_list_copy ( dems );
154   GList *iter = rv;
155   while ( iter ) {
156     if ( ! a_dems_load((const gchar *) (iter->data)) ) {
157       GList *iter_temp = iter->next; /* delete link, don't bother strdup'ing and free'ing string */
158       rv = g_list_remove_link ( rv, iter );
159       iter = iter_temp;
160     } else {
161       iter->data = g_strdup((gchar *)iter->data); /* copy the string too. */
162       iter = iter->next;
163     }
164   }
165   return rv;
166 }
167
168 gint16 a_dems_list_get_elev_by_coord ( GList *dems, const VikCoord *coord )
169 {
170   static struct UTM utm_tmp;
171   static struct LatLon ll_tmp;
172   GList *iter = dems;
173   VikDEM *dem;
174   gint elev;
175
176   while ( iter ) {
177     dem = a_dems_get ( (gchar *) iter->data );
178     if ( dem ) {
179       if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
180         vik_coord_to_latlon ( coord, &ll_tmp );
181         ll_tmp.lat *= 3600;
182         ll_tmp.lon *= 3600;
183         elev = vik_dem_get_east_north(dem, ll_tmp.lon, ll_tmp.lat);
184         if ( elev != VIK_DEM_INVALID_ELEVATION )
185           return elev;
186       } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
187         vik_coord_to_utm ( coord, &utm_tmp );
188         if ( utm_tmp.zone == dem->utm_zone &&
189              (elev = vik_dem_get_east_north(dem, utm_tmp.easting, utm_tmp.northing)) != VIK_DEM_INVALID_ELEVATION )
190             return elev;
191       }
192     }
193     iter = iter->next;
194   }
195   return VIK_DEM_INVALID_ELEVATION;
196 }
197
198 typedef struct {
199   const VikCoord *coord;
200   VikDemInterpol method;
201   gint elev;
202 } CoordElev;
203
204 static gboolean get_elev_by_coord(gpointer key, LoadedDEM *ldem, CoordElev *ce)
205 {
206   VikDEM *dem = ldem->dem;
207   gdouble lat, lon;
208
209   if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
210     struct LatLon ll_tmp;
211     vik_coord_to_latlon (ce->coord, &ll_tmp );
212     lat = ll_tmp.lat * 3600;
213     lon = ll_tmp.lon * 3600;
214   } else if (dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS) {
215     static struct UTM utm_tmp;
216     if (utm_tmp.zone != dem->utm_zone)
217       return FALSE;
218     vik_coord_to_utm (ce->coord, &utm_tmp);
219     lat = utm_tmp.northing;
220     lon = utm_tmp.easting;
221   } else
222     return FALSE;
223
224   switch (ce->method) {
225     case VIK_DEM_INTERPOL_NONE:
226       ce->elev = vik_dem_get_east_north(dem, lon, lat);
227       break;
228     case VIK_DEM_INTERPOL_SIMPLE:
229       ce->elev = vik_dem_get_simple_interpol(dem, lon, lat);
230       break;
231     case VIK_DEM_INTERPOL_BEST:
232       ce->elev = vik_dem_get_shepard_interpol(dem, lon, lat);
233       break;
234   }
235   return (ce->elev != VIK_DEM_INVALID_ELEVATION);
236 }
237
238 /* TODO: keep a (sorted) linked list of DEMs and select the best resolution one */
239 gint16 a_dems_get_elev_by_coord ( const VikCoord *coord, VikDemInterpol method )
240 {
241   CoordElev ce;
242
243   if (!loaded_dems)
244     return VIK_DEM_INVALID_ELEVATION;
245
246   ce.coord = coord;
247   ce.method = method;
248   ce.elev = VIK_DEM_INVALID_ELEVATION;
249
250   if(!g_hash_table_find(loaded_dems, (GHRFunc)get_elev_by_coord, &ce))
251     return VIK_DEM_INVALID_ELEVATION;
252   return ce.elev;
253 }