Contents of /trunk/src/geomath.c

Parent Directory Parent Directory | Revision Log Revision Log


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