Contents of /trunk/src/geomath.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 221 - (hide annotations)
Mon Nov 30 21:28:04 2009 UTC (14 years, 6 months ago) by harbaum
File MIME type: text/plain
File size: 10825 byte(s)
Fremantle coordinate picker
1 harbaum 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 harbaum 198 #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR >= 5)
24     #include <hildon/hildon-entry.h>
25     #endif
26    
27 harbaum 1 #define STR_NAN "----"
28    
29     typedef struct {
30     appdata_t *appdata;
31    
32     GtkWidget *lon1, *lat1, *dist1, *dir1;
33     GtkWidget *lon2, *lat2;
34    
35     GtkWidget *distance, *proj_lat, *proj_lon;
36     GtkWidget *middle_lat, *middle_lon;
37     } math_dialog_state_t;
38    
39     static gboolean mark(GtkWidget *widget, gboolean valid) {
40     gtk_widget_set_state(widget, valid?GTK_STATE_NORMAL:TAG_STATE);
41     return valid;
42     }
43    
44     /* a entry that is colored red when being "active" */
45     static GtkWidget *gtk_red_entry_new(void) {
46     GdkColor color;
47 harbaum 212 GtkWidget *widget = entry_new();
48 harbaum 1 gdk_color_parse("#ff0000", &color);
49     gtk_widget_modify_text(widget, TAG_STATE, &color);
50     return widget;
51     }
52    
53     /* a label that is colored red when being "active" */
54     static GtkWidget *gtk_red_label_new(char *str) {
55     GdkColor color;
56 harbaum 198 GtkWidget *widget = left_label_new(str);
57 harbaum 1 gdk_color_parse("#ff0000", &color);
58     gtk_widget_modify_fg(widget, TAG_STATE, &color);
59     return widget;
60     }
61    
62     static GtkWidget *gtk_dir_entry_new(float val) {
63     char str[16];
64     GtkWidget *w = gtk_red_entry_new();
65    
66     snprintf(str, sizeof(str), _("%.1f°"), val);
67     gtk_entry_set_text(GTK_ENTRY(w), str);
68    
69     return w;
70     }
71    
72     float pos_lat_eval_combo(GtkWidget *widget) {
73     char *p = (char*)gtk_combo_box_get_active_text(GTK_COMBO_BOX(widget));
74     float val = pos_parse_lat(p);
75     mark(widget, !isnan(val));
76    
77     return val;
78     }
79    
80     static float distance_eval(GtkWidget *widget, math_dialog_state_t *state) {
81     char *p = (char*)gtk_entry_get_text(GTK_ENTRY(widget));
82     float val = distance_parse(p, state->appdata->imperial);
83     mark(widget, !isnan(val));
84    
85     return val;
86     }
87    
88     float direction_eval(GtkWidget *widget) {
89     char *p = (char*)gtk_entry_get_text(GTK_ENTRY(widget));
90     float val;
91     if(sscanf(p, _("%f°"), &val) != 1)
92     val = NAN;
93    
94     mark(widget, !isnan(val));
95    
96     return val;
97     }
98    
99     static void on_calc_clicked(GtkButton *button, gpointer user_data) {
100     math_dialog_state_t *state = (math_dialog_state_t*)user_data;
101     pos_t pos1, pos2;
102     gboolean pos1_ok = FALSE, pos2_ok = FALSE;
103     float dist1;
104     gboolean dist1_ok = FALSE;
105     float dir1;
106     gboolean dir1_ok = FALSE;
107    
108     /* parse input */
109 harbaum 221 pos1.lat = lat_entry_get(state->lat1);
110     pos1.lon = lon_entry_get(state->lon1);
111 harbaum 1 if(!isnan(pos1.lat) && !isnan(pos1.lon)) pos1_ok = TRUE;
112    
113 harbaum 221 pos2.lat = lat_entry_get(state->lat2);
114     pos2.lon = lon_entry_get(state->lon2);
115 harbaum 1 if(!isnan(pos2.lat) && !isnan(pos2.lon)) pos2_ok = TRUE;
116    
117     dist1 = distance_eval(state->dist1, state);
118     if(!isnan(dist1)) dist1_ok = TRUE;
119    
120     dir1 = direction_eval(state->dir1);
121     if(!isnan(dir1)) dir1_ok = TRUE;
122    
123     /* ------------------- do all calculations ------------------- */
124    
125    
126     /* ------------------- distance of coo1 and coo2 ------------------- */
127     if(mark(state->distance, pos1_ok && pos2_ok)) {
128     char str[32];
129     float dist = gpx_pos_get_distance(pos1, pos2, state->appdata->imperial);
130     distance_str(str, sizeof(str), dist, state->appdata->imperial);
131    
132     gtk_label_set_text(GTK_LABEL(state->distance), str);
133     } else
134     gtk_label_set_text(GTK_LABEL(state->distance), STR_NAN);
135    
136     // N 53° 09.033' W 001° 50.666' 100km / 30° = N 53° 55.616, W001° 04.850
137     /* ------------------- coordinate projection ---------------- */
138     mark(state->proj_lat, pos1_ok && dist1_ok && dir1_ok);
139     if(mark(state->proj_lon, pos1_ok && dist1_ok && dir1_ok)) {
140     pos_t pro;
141    
142     /* get great circle radius in miles/kilometers */
143     float gcrad = state->appdata->imperial?3959.0:6371.0;
144    
145     // from: http://www.movable-type.co.uk/scripts/latlong.html
146     pro.lat = asin(sin(pos1.lat/180*M_PI) * cos(dist1/gcrad) +
147     cos(pos1.lat/180*M_PI) * sin(dist1/gcrad) *
148     cos(dir1/180*M_PI) )/M_PI*180;
149     pro.lon = pos1.lon + atan2(sin(dir1/180*M_PI)*sin(dist1/gcrad)*
150     cos(pos1.lat/180*M_PI),
151     cos(dist1/gcrad)-sin(pos1.lat/180*M_PI)*
152     sin(pro.lat/180*M_PI))/M_PI*180;
153     pro.lon = fmodf(pro.lon+180,360) - 180; // normalise to -180...+180
154    
155 harbaum 221 lat_label_set(state->proj_lat, pro.lat);
156     lon_label_set(state->proj_lon, pro.lon);
157 harbaum 1
158     if(!isnan(pro.lat) && !isnan(pro.lon))
159     state->appdata->geomath = pro;
160     } else {
161     gtk_label_set_text(GTK_LABEL(state->proj_lat), STR_NAN);
162     gtk_label_set_text(GTK_LABEL(state->proj_lon), STR_NAN);
163     }
164     }
165    
166     void geomath_dialog(appdata_t *appdata) {
167     static pos_t pos1 = { 0.0, 0.0 }, pos2 = { 0.0, 0.0 };
168     static float dist1 = 0.0;
169     static float dir1 = 0.0;
170     static gboolean is_imperial = FALSE;
171    
172     math_dialog_state_t state;
173    
174     /* this is quite ugly. It would be nice to run the entire system on */
175     /* one specific system (e.g. metric) and only convert for in- and output */
176     if(!appdata->imperial && is_imperial)
177     dist1 *= 6371.0/3959.0; /* we just switched to metric */
178     if(appdata->imperial && !is_imperial)
179     dist1 *= 3959.0/6371.0; /* we just switched to imperial */
180     is_imperial = appdata->imperial;
181    
182     state.appdata = appdata;
183    
184     #ifdef USE_MAEMO
185     if(appdata->cur_cache)
186     printf("current cache is %s\n", appdata->cur_cache->id);
187     else
188     printf("no current cache\n");
189     #endif
190    
191     GtkWidget *dialog = gtk_dialog_new_with_buttons(_("Geomath"),
192     GTK_WINDOW(appdata->window),
193     // GTK_DIALOG_NO_SEPARATOR |
194     GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
195     GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
196     NULL);
197    
198     #if defined(USE_MAEMO) && defined(HILDON_HELP)
199     hildon_help_dialog_help_enable(GTK_DIALOG(dialog), HELP_ID_GEOMATH,
200     appdata->osso_context);
201     #endif
202    
203     gtk_window_set_default_size(GTK_WINDOW(dialog), DIALOG_WIDTH, DIALOG_HEIGHT);
204    
205     /* do geomath dialog */
206     GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
207    
208     /* ------------------------- input area ------------------------- */
209 harbaum 214 GtkWidget *table = gtk_table_new(5, 5, FALSE);
210     gtk_table_set_col_spacing(GTK_TABLE(table), 2, 20);
211 harbaum 1
212 harbaum 214 gtk_table_attach_defaults(GTK_TABLE(table), left_label_new(_("Latitude:")), 0, 1, 1, 2);
213     gtk_table_attach_defaults(GTK_TABLE(table), left_label_new(_("Longitude:")), 0, 1, 2, 3);
214 harbaum 1
215 harbaum 214 gtk_table_attach_defaults(GTK_TABLE(table), left_label_new(_("Coordinate 1")), 1, 3, 0, 1);
216     gtk_table_attach_defaults(GTK_TABLE(table), state.lat1 = lat_entry_new(pos1.lat), 1, 2, 1, 2);
217     gtk_table_attach_defaults(GTK_TABLE(table), state.lon1 = lon_entry_new(pos1.lon), 1, 2, 2, 3);
218 harbaum 221 gtk_table_attach_defaults(GTK_TABLE(table), preset_coordinate_picker(appdata, state.lat1, state.lon1), 2, 3, 1, 2);
219 harbaum 1
220 harbaum 214 gtk_table_attach_defaults(GTK_TABLE(table), left_label_new(_("Coordinate 2")), 3, 5, 0, 1);
221     gtk_table_attach_defaults(GTK_TABLE(table), state.lat2 = lat_entry_new(pos2.lat), 3, 4, 1, 2);
222     gtk_table_attach_defaults(GTK_TABLE(table), state.lon2 = lon_entry_new(pos2.lon), 3, 4, 2, 3);
223 harbaum 221 gtk_table_attach_defaults(GTK_TABLE(table), preset_coordinate_picker(appdata, state.lat2, state.lon2), 4, 5, 1, 2);
224 harbaum 1
225 harbaum 198 gtk_table_attach_defaults(GTK_TABLE(table), left_label_new(_("Distance:")), 0, 1, 3, 4);
226 harbaum 214 gtk_table_attach_defaults(GTK_TABLE(table), state.dist1 = dist_entry_new(dist1, appdata->imperial), 1, 3, 3, 4);
227    
228 harbaum 198 gtk_table_attach_defaults(GTK_TABLE(table), left_label_new(_("Direction:")), 0, 1, 4, 5);
229 harbaum 214 gtk_table_attach_defaults(GTK_TABLE(table), state.dir1 = gtk_dir_entry_new(dir1), 1, 3, 4, 5);
230 harbaum 1
231     gtk_box_pack_start(GTK_BOX(hbox), table, TRUE, TRUE, 0);
232    
233     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, TRUE, TRUE, 0);
234    
235     /* ------------------------- do-it-button ------------------------- */
236    
237     GtkWidget *button = gtk_button_new_with_label(_("Calculate!"));
238 harbaum 198 g_signal_connect(button, "clicked", (GCallback)on_calc_clicked, &state);
239    
240 harbaum 133 #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR == 5)
241     hildon_gtk_widget_set_theme_size(button,
242     (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
243 harbaum 214 gtk_table_attach(GTK_TABLE(table), button, 3,5,3,5, GTK_EXPAND, GTK_EXPAND, 0, 0);
244 harbaum 198 #else
245     /* in non-maemo5 the button has its own row */
246     hbox = gtk_hbox_new(FALSE, 0);
247 harbaum 1 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
248     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, TRUE, FALSE, 0);
249 harbaum 198 #endif
250 harbaum 1
251     /* ------------------------- output area ------------------------- */
252    
253     table = gtk_table_new(3, 3, FALSE);
254    
255 harbaum 198 gtk_table_attach_defaults(GTK_TABLE(table), left_label_new(_("Distance = ")),
256 harbaum 1 0, 1, 0, 1);
257     gtk_table_attach_defaults(GTK_TABLE(table), state.distance = gtk_red_label_new(STR_NAN),
258     1, 3, 0, 1);
259    
260 harbaum 198 gtk_table_attach_defaults(GTK_TABLE(table), left_label_new(_("Projection = ")),
261 harbaum 1 0, 1, 1, 2);
262     gtk_table_attach_defaults(GTK_TABLE(table), state.proj_lat = gtk_red_label_new(STR_NAN),
263     1, 2, 1, 2);
264     gtk_table_attach_defaults(GTK_TABLE(table), state.proj_lon = gtk_red_label_new(STR_NAN),
265     2, 3, 1, 2);
266     #if 0
267 harbaum 198 gtk_table_attach_defaults(GTK_TABLE(table), left_label_new(_("Middle = ")),
268 harbaum 1 0, 1, 2, 3);
269     gtk_table_attach_defaults(GTK_TABLE(table), state.middle_lat = gtk_red_label_new(STR_NAN),
270     1, 2, 2, 3);
271     gtk_table_attach_defaults(GTK_TABLE(table), state.middle_lon = gtk_red_label_new(STR_NAN),
272     2, 3, 2, 3);
273     #endif
274    
275     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), table, TRUE, TRUE, 0);
276    
277    
278     gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CLOSE);
279    
280     gtk_widget_show_all(dialog);
281     gtk_dialog_run(GTK_DIALOG(dialog));
282    
283     /* copy values back to local static variables so they re-appear if */
284     /* the dialog is re-opened, convert illegal values (NAN) to 0 */
285    
286 harbaum 221 pos1.lat = lat_entry_get(state.lat1); if(isnan(pos1.lat)) pos1.lat=0;
287     pos1.lon = lon_entry_get(state.lon1); if(isnan(pos1.lon)) pos1.lon=0;
288     pos2.lat = lat_entry_get(state.lat2); if(isnan(pos2.lat)) pos2.lat=0;
289     pos2.lon = lon_entry_get(state.lon2); if(isnan(pos2.lon)) pos2.lon=0;
290 harbaum 1 dist1 = distance_eval(state.dist1, &state); if(isnan(dist1)) dist1=0;
291     dir1 = direction_eval(state.dir1); if(isnan(dir1)) dir1=0;
292    
293     gtk_widget_destroy(dialog);
294     }