Parent Directory | Revision Log
Various fremantle fixes
1 | /* |
2 | * Copyright (C) 2008 Till Harbaum <till@harbaum.org>. |
3 | * |
4 | * This file is part of GPXView. |
5 | * |
6 | * GPXView is free software: you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation, either version 3 of the License, or |
9 | * (at your option) any later version. |
10 | * |
11 | * GPXView is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU General Public License |
17 | * along with GPXView. If not, see <http://www.gnu.org/licenses/>. |
18 | */ |
19 | |
20 | #include "gpxview.h" |
21 | #include <math.h> |
22 | |
23 | #define STR_NAN "----" |
24 | |
25 | typedef struct { |
26 | appdata_t *appdata; |
27 | |
28 | GtkWidget *lon1, *lat1, *dist1, *dir1; |
29 | GtkWidget *lon2, *lat2; |
30 | |
31 | GtkWidget *distance, *proj_lat, *proj_lon; |
32 | GtkWidget *middle_lat, *middle_lon; |
33 | } math_dialog_state_t; |
34 | |
35 | static gboolean mark(GtkWidget *widget, gboolean valid) { |
36 | gtk_widget_set_state(widget, valid?GTK_STATE_NORMAL:TAG_STATE); |
37 | return valid; |
38 | } |
39 | |
40 | /* a entry that is colored red when being "active" */ |
41 | static GtkWidget *gtk_red_entry_new(void) { |
42 | GdkColor color; |
43 | GtkWidget *widget = gtk_entry_new(); |
44 | gdk_color_parse("#ff0000", &color); |
45 | gtk_widget_modify_text(widget, TAG_STATE, &color); |
46 | return widget; |
47 | } |
48 | |
49 | /* a comboboxentry that is colored red when being "active" */ |
50 | static GtkWidget *gtk_red_combo_box_entry_new_text(void) { |
51 | GdkColor color; |
52 | GtkWidget *widget = gtk_combo_box_entry_new_text(); |
53 | gdk_color_parse("#ff0000", &color); |
54 | gtk_widget_modify_text(GTK_BIN(widget)->child, TAG_STATE, &color); |
55 | return widget; |
56 | } |
57 | |
58 | /* a label that is left aligned */ |
59 | static GtkWidget *gtk_left_label_new(char *str) { |
60 | GtkWidget *widget = gtk_label_new(str); |
61 | gtk_misc_set_alignment(GTK_MISC(widget), 0.0f, 0.5f); |
62 | return widget; |
63 | } |
64 | |
65 | /* a label that is colored red when being "active" */ |
66 | static GtkWidget *gtk_red_label_new(char *str) { |
67 | GdkColor color; |
68 | GtkWidget *widget = gtk_left_label_new(str); |
69 | gdk_color_parse("#ff0000", &color); |
70 | gtk_widget_modify_fg(widget, TAG_STATE, &color); |
71 | return widget; |
72 | } |
73 | |
74 | static GtkWidget *gtk_dir_entry_new(float val) { |
75 | char str[16]; |
76 | GtkWidget *w = gtk_red_entry_new(); |
77 | |
78 | snprintf(str, sizeof(str), _("%.1f°"), val); |
79 | gtk_entry_set_text(GTK_ENTRY(w), str); |
80 | |
81 | return w; |
82 | } |
83 | |
84 | float pos_lat_eval_combo(GtkWidget *widget) { |
85 | char *p = (char*)gtk_combo_box_get_active_text(GTK_COMBO_BOX(widget)); |
86 | float val = pos_parse_lat(p); |
87 | mark(widget, !isnan(val)); |
88 | |
89 | return val; |
90 | } |
91 | |
92 | static float distance_eval(GtkWidget *widget, math_dialog_state_t *state) { |
93 | char *p = (char*)gtk_entry_get_text(GTK_ENTRY(widget)); |
94 | float val = distance_parse(p, state->appdata->imperial); |
95 | mark(widget, !isnan(val)); |
96 | |
97 | return val; |
98 | } |
99 | |
100 | float direction_eval(GtkWidget *widget) { |
101 | char *p = (char*)gtk_entry_get_text(GTK_ENTRY(widget)); |
102 | float val; |
103 | if(sscanf(p, _("%f°"), &val) != 1) |
104 | val = NAN; |
105 | |
106 | mark(widget, !isnan(val)); |
107 | |
108 | return val; |
109 | } |
110 | |
111 | static void on_calc_clicked(GtkButton *button, gpointer user_data) { |
112 | math_dialog_state_t *state = (math_dialog_state_t*)user_data; |
113 | pos_t pos1, pos2; |
114 | gboolean pos1_ok = FALSE, pos2_ok = FALSE; |
115 | float dist1; |
116 | gboolean dist1_ok = FALSE; |
117 | float dir1; |
118 | gboolean dir1_ok = FALSE; |
119 | |
120 | /* parse input */ |
121 | pos1.lat = lat_get(state->lat1); |
122 | pos1.lon = lon_get(state->lon1); |
123 | if(!isnan(pos1.lat) && !isnan(pos1.lon)) pos1_ok = TRUE; |
124 | |
125 | pos2.lat = lat_get(state->lat2); |
126 | pos2.lon = lon_get(state->lon2); |
127 | if(!isnan(pos2.lat) && !isnan(pos2.lon)) pos2_ok = TRUE; |
128 | |
129 | dist1 = distance_eval(state->dist1, state); |
130 | if(!isnan(dist1)) dist1_ok = TRUE; |
131 | |
132 | dir1 = direction_eval(state->dir1); |
133 | if(!isnan(dir1)) dir1_ok = TRUE; |
134 | |
135 | /* ------------------- do all calculations ------------------- */ |
136 | |
137 | |
138 | /* ------------------- distance of coo1 and coo2 ------------------- */ |
139 | if(mark(state->distance, pos1_ok && pos2_ok)) { |
140 | char str[32]; |
141 | float dist = gpx_pos_get_distance(pos1, pos2, state->appdata->imperial); |
142 | distance_str(str, sizeof(str), dist, state->appdata->imperial); |
143 | |
144 | gtk_label_set_text(GTK_LABEL(state->distance), str); |
145 | } else |
146 | gtk_label_set_text(GTK_LABEL(state->distance), STR_NAN); |
147 | |
148 | // N 53° 09.033' W 001° 50.666' 100km / 30° = N 53° 55.616, W001° 04.850 |
149 | /* ------------------- coordinate projection ---------------- */ |
150 | mark(state->proj_lat, pos1_ok && dist1_ok && dir1_ok); |
151 | if(mark(state->proj_lon, pos1_ok && dist1_ok && dir1_ok)) { |
152 | pos_t pro; |
153 | |
154 | /* get great circle radius in miles/kilometers */ |
155 | float gcrad = state->appdata->imperial?3959.0:6371.0; |
156 | |
157 | // from: http://www.movable-type.co.uk/scripts/latlong.html |
158 | pro.lat = asin(sin(pos1.lat/180*M_PI) * cos(dist1/gcrad) + |
159 | cos(pos1.lat/180*M_PI) * sin(dist1/gcrad) * |
160 | cos(dir1/180*M_PI) )/M_PI*180; |
161 | pro.lon = pos1.lon + atan2(sin(dir1/180*M_PI)*sin(dist1/gcrad)* |
162 | cos(pos1.lat/180*M_PI), |
163 | cos(dist1/gcrad)-sin(pos1.lat/180*M_PI)* |
164 | sin(pro.lat/180*M_PI))/M_PI*180; |
165 | pro.lon = fmodf(pro.lon+180,360) - 180; // normalise to -180...+180 |
166 | |
167 | char str[16]; |
168 | pos_lat_str(str, sizeof(str), pro.lat); |
169 | gtk_label_set_text(GTK_LABEL(state->proj_lat), str); |
170 | pos_lon_str(str, sizeof(str), pro.lon); |
171 | gtk_label_set_text(GTK_LABEL(state->proj_lon), str); |
172 | |
173 | if(!isnan(pro.lat) && !isnan(pro.lon)) |
174 | state->appdata->geomath = pro; |
175 | } else { |
176 | gtk_label_set_text(GTK_LABEL(state->proj_lat), STR_NAN); |
177 | gtk_label_set_text(GTK_LABEL(state->proj_lon), STR_NAN); |
178 | } |
179 | |
180 | #if 0 |
181 | /* ------------ middle between both points ------------- */ |
182 | mark(state->middle_lat, pos1_ok && pos2_ok); |
183 | if(mark(state->middle_lon, pos1_ok && pos2_ok)) { |
184 | pos_t res; |
185 | |
186 | float dlon = fabs(pos2.lon - pos1.lon)/180*M_PI; |
187 | |
188 | /* http://mathforum.org/library/drmath/view/51822.html */ |
189 | res.lon = 180/M_PI * atan(cos(pos2.lat/180*M_PI)*sin(pos2.lon/180*M_PI)/ |
190 | (cos(pos1.lat/180*M_PI)+cos(pos2.lat/180*M_PI)*cos(pos2.lon/180*M_PI))); |
191 | res.lat = 180/M_PI * atan((sin(pos1.lat/180*M_PI)+sin(pos2.lat/180*M_PI))/ |
192 | sqrt(pow(cos(pos2.lat/180*M_PI)+cos(pos2.lat/180*M_PI)*cos(dlon),2)+ |
193 | pow(cos(pos2.lat/180*M_PI)*sin(dlon),2))); |
194 | |
195 | char str1[32], str2[32]; |
196 | pos_lat_str(str1,sizeof(str1),res.lat); |
197 | pos_lon_str(str2,sizeof(str2),res.lon); |
198 | printf("pos = %s/%s\n", str1, str2); |
199 | } else { |
200 | gtk_label_set_text(GTK_LABEL(state->middle_lat), STR_NAN); |
201 | gtk_label_set_text(GTK_LABEL(state->middle_lon), STR_NAN); |
202 | } |
203 | #endif |
204 | } |
205 | |
206 | static gint waypoint_changed_event(GtkWidget *widget, gpointer data ) { |
207 | math_dialog_state_t *state = (math_dialog_state_t*)data; |
208 | int wpt_idx = gtk_combo_box_get_active(GTK_COMBO_BOX(widget)); |
209 | pos_t *pos = NULL; |
210 | |
211 | if(wpt_idx < 0) |
212 | return FALSE; |
213 | |
214 | if(wpt_idx == 0) |
215 | pos = gps_get_pos(state->appdata); |
216 | #ifdef USE_MAEMO |
217 | else if(wpt_idx == 1) { |
218 | pos_t cache_pos = gpx_cache_pos(state->appdata->cur_cache); |
219 | pos = &cache_pos; |
220 | } else { |
221 | wpt_t *wpt = state->appdata->cur_cache->wpt; |
222 | while(wpt_idx > 2) { |
223 | g_assert(wpt != NULL); |
224 | wpt = wpt->next; |
225 | wpt_idx--; |
226 | } |
227 | pos = &wpt->pos; |
228 | } |
229 | #endif |
230 | |
231 | if(pos) { |
232 | char str[32]; |
233 | pos_lat_str(str,sizeof(str),pos->lat); |
234 | gtk_entry_set_text(GTK_ENTRY(state->lat1), str); |
235 | pos_lon_str(str,sizeof(str),pos->lon); |
236 | gtk_entry_set_text(GTK_ENTRY(state->lon1), str); |
237 | } else { |
238 | gtk_entry_set_text(GTK_ENTRY(state->lat1), STR_NAN); |
239 | gtk_entry_set_text(GTK_ENTRY(state->lon1), STR_NAN); |
240 | } |
241 | |
242 | mark(state->lat1, pos != NULL); |
243 | mark(state->lon1, pos != NULL); |
244 | |
245 | return FALSE; |
246 | } |
247 | |
248 | void geomath_dialog(appdata_t *appdata) { |
249 | static pos_t pos1 = { 0.0, 0.0 }, pos2 = { 0.0, 0.0 }; |
250 | static float dist1 = 0.0; |
251 | static float dir1 = 0.0; |
252 | static gboolean is_imperial = FALSE; |
253 | |
254 | math_dialog_state_t state; |
255 | char str[32]; |
256 | |
257 | /* this is quite ugly. It would be nice to run the entire system on */ |
258 | /* one specific system (e.g. metric) and only convert for in- and output */ |
259 | if(!appdata->imperial && is_imperial) |
260 | dist1 *= 6371.0/3959.0; /* we just switched to metric */ |
261 | if(appdata->imperial && !is_imperial) |
262 | dist1 *= 3959.0/6371.0; /* we just switched to imperial */ |
263 | is_imperial = appdata->imperial; |
264 | |
265 | state.appdata = appdata; |
266 | |
267 | #ifdef USE_MAEMO |
268 | if(appdata->cur_cache) |
269 | printf("current cache is %s\n", appdata->cur_cache->id); |
270 | else |
271 | printf("no current cache\n"); |
272 | #endif |
273 | |
274 | GtkWidget *dialog = gtk_dialog_new_with_buttons(_("Geomath"), |
275 | GTK_WINDOW(appdata->window), |
276 | // GTK_DIALOG_NO_SEPARATOR | |
277 | GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, |
278 | GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, |
279 | NULL); |
280 | |
281 | #if defined(USE_MAEMO) && defined(HILDON_HELP) |
282 | hildon_help_dialog_help_enable(GTK_DIALOG(dialog), HELP_ID_GEOMATH, |
283 | appdata->osso_context); |
284 | #endif |
285 | |
286 | gtk_window_set_default_size(GTK_WINDOW(dialog), DIALOG_WIDTH, DIALOG_HEIGHT); |
287 | |
288 | /* do geomath dialog */ |
289 | GtkWidget *hbox = gtk_hbox_new(FALSE, 0); |
290 | |
291 | /* ------------------------- input area ------------------------- */ |
292 | GtkWidget *table = gtk_table_new(5, 3, FALSE); |
293 | gtk_table_set_col_spacing(GTK_TABLE(table), 1, 20); |
294 | |
295 | gtk_table_attach_defaults(GTK_TABLE(table), gtk_label_new(_("Coordinate 1")), 1, 2, 0, 1); |
296 | gtk_table_attach_defaults(GTK_TABLE(table), gtk_label_new(_("Coordinate 2")), 2, 3, 0, 1); |
297 | gtk_table_attach_defaults(GTK_TABLE(table), gtk_left_label_new(_("Latitude:")), 0, 1, 1, 2); |
298 | |
299 | GtkWidget *cbox = gtk_red_combo_box_entry_new_text(); |
300 | gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), _("GPS")); |
301 | |
302 | #ifdef USE_MAEMO |
303 | if(appdata->cur_cache) { |
304 | gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), appdata->cur_cache->id); |
305 | wpt_t *wpt = appdata->cur_cache->wpt; |
306 | while(wpt) { |
307 | gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), wpt->id); |
308 | wpt = wpt->next; |
309 | } |
310 | } |
311 | #endif |
312 | |
313 | state.lat1 = GTK_BIN(cbox)->child; |
314 | pos_lat_str(str, sizeof(str), pos1.lat); |
315 | gtk_entry_set_text(GTK_ENTRY(state.lat1), str); |
316 | |
317 | gtk_signal_connect(GTK_OBJECT(cbox), "changed", |
318 | (GtkSignalFunc)waypoint_changed_event, &state); |
319 | gtk_table_attach_defaults(GTK_TABLE(table), cbox, 1,2,1,2); |
320 | |
321 | |
322 | gtk_table_attach_defaults(GTK_TABLE(table), state.lat2 = lat_entry_new(pos2.lat), 2, 3, 1, 2); |
323 | gtk_table_attach_defaults(GTK_TABLE(table), gtk_left_label_new(_("Longitude:")), 0, 1, 2, 3); |
324 | gtk_table_attach_defaults(GTK_TABLE(table), state.lon1 = lon_entry_new(pos1.lon), 1, 2, 2, 3); |
325 | gtk_table_attach_defaults(GTK_TABLE(table), state.lon2 = lon_entry_new(pos2.lon), 2, 3, 2, 3); |
326 | gtk_table_attach_defaults(GTK_TABLE(table), gtk_left_label_new(_("Distance:")), 0, 1, 3, 4); |
327 | gtk_table_attach_defaults(GTK_TABLE(table), state.dist1 = dist_entry_new(dist1, appdata->imperial), 1, 2, 3, 4); |
328 | gtk_table_attach_defaults(GTK_TABLE(table), gtk_left_label_new(_("Direction:")), 0, 1, 4, 5); |
329 | gtk_table_attach_defaults(GTK_TABLE(table), state.dir1 = gtk_dir_entry_new(dir1), 1, 2, 4, 5); |
330 | |
331 | gtk_box_pack_start(GTK_BOX(hbox), table, TRUE, TRUE, 0); |
332 | |
333 | gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, TRUE, TRUE, 0); |
334 | |
335 | /* ------------------------- do-it-button ------------------------- */ |
336 | |
337 | hbox = gtk_hbox_new(FALSE, 0); |
338 | GtkWidget *button = gtk_button_new_with_label(_("Calculate!")); |
339 | #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR == 5) |
340 | hildon_gtk_widget_set_theme_size(button, |
341 | (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH)); |
342 | #endif |
343 | gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0); |
344 | gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, TRUE, FALSE, 0); |
345 | g_signal_connect(button, "clicked", (GCallback)on_calc_clicked, &state); |
346 | |
347 | /* ------------------------- output area ------------------------- */ |
348 | |
349 | table = gtk_table_new(3, 3, FALSE); |
350 | |
351 | gtk_table_attach_defaults(GTK_TABLE(table), gtk_left_label_new(_("Distance = ")), |
352 | 0, 1, 0, 1); |
353 | gtk_table_attach_defaults(GTK_TABLE(table), state.distance = gtk_red_label_new(STR_NAN), |
354 | 1, 3, 0, 1); |
355 | |
356 | gtk_table_attach_defaults(GTK_TABLE(table), gtk_left_label_new(_("Projection = ")), |
357 | 0, 1, 1, 2); |
358 | gtk_table_attach_defaults(GTK_TABLE(table), state.proj_lat = gtk_red_label_new(STR_NAN), |
359 | 1, 2, 1, 2); |
360 | gtk_table_attach_defaults(GTK_TABLE(table), state.proj_lon = gtk_red_label_new(STR_NAN), |
361 | 2, 3, 1, 2); |
362 | #if 0 |
363 | gtk_table_attach_defaults(GTK_TABLE(table), gtk_left_label_new(_("Middle = ")), |
364 | 0, 1, 2, 3); |
365 | gtk_table_attach_defaults(GTK_TABLE(table), state.middle_lat = gtk_red_label_new(STR_NAN), |
366 | 1, 2, 2, 3); |
367 | gtk_table_attach_defaults(GTK_TABLE(table), state.middle_lon = gtk_red_label_new(STR_NAN), |
368 | 2, 3, 2, 3); |
369 | #endif |
370 | |
371 | gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), table, TRUE, TRUE, 0); |
372 | |
373 | |
374 | gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CLOSE); |
375 | |
376 | gtk_widget_show_all(dialog); |
377 | gtk_dialog_run(GTK_DIALOG(dialog)); |
378 | |
379 | /* copy values back to local static variables so they re-appear if */ |
380 | /* the dialog is re-opened, convert illegal values (NAN) to 0 */ |
381 | |
382 | pos1.lat = lat_get(state.lat1); if(isnan(pos1.lat)) pos1.lat=0; |
383 | pos1.lon = lon_get(state.lon1); if(isnan(pos1.lon)) pos1.lon=0; |
384 | pos2.lat = lat_get(state.lat2); if(isnan(pos2.lat)) pos2.lat=0; |
385 | pos2.lon = lon_get(state.lon2); if(isnan(pos2.lon)) pos2.lon=0; |
386 | dist1 = distance_eval(state.dist1, &state); if(isnan(dist1)) dist1=0; |
387 | dir1 = direction_eval(state.dir1); if(isnan(dir1)) dir1=0; |
388 | |
389 | gtk_widget_destroy(dialog); |
390 | } |