Contents of /trunk/src/area_edit.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 203 - (show annotations)
Thu Jul 9 18:39:42 2009 UTC (14 years, 10 months ago) by harbaum
File MIME type: text/plain
File size: 17405 byte(s)
Area edit map position setting
1 /*
2 * Copyright (C) 2008 Till Harbaum <till@harbaum.org>.
3 *
4 * This file is part of OSM2Go.
5 *
6 * OSM2Go 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 * OSM2Go 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 OSM2Go. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "appdata.h"
21 #include "osm-gps-map.h"
22
23 typedef struct {
24 GtkWidget *dialog, *notebook;
25 area_edit_t *area;
26 pos_t min, max; /* local copy to work on */
27 GtkWidget *minlat, *maxlat, *minlon, *maxlon;
28
29 struct {
30 GtkWidget *minlat, *maxlat, *minlon, *maxlon;
31 } direct;
32
33 struct {
34 GtkWidget *lat, *lon, *height, *width, *mil_km;
35 gboolean is_mil;
36 } extent;
37
38 #ifdef USE_HILDON
39 struct {
40 GtkWidget *fetch;
41 } mmapper;
42 #endif
43
44 struct {
45 GtkWidget *widget;
46 GtkWidget *zoomin, *zoomout;
47 } map;
48
49 } context_t;
50
51 static void parse_and_set_lat(GtkWidget *src, GtkWidget *dst, pos_float_t *store) {
52 pos_float_t i = pos_parse_lat((char*)gtk_entry_get_text(GTK_ENTRY(src)));
53 if(pos_lat_valid(i)) {
54 *store = i;
55 pos_lat_label_set(dst, i);
56 }
57 }
58
59 static void parse_and_set_lon(GtkWidget *src, GtkWidget *dst, pos_float_t *store) {
60 pos_float_t i = pos_parse_lon((char*)gtk_entry_get_text(GTK_ENTRY(src)));
61 if(pos_lon_valid(i)) {
62 *store = i;
63 pos_lon_label_set(dst, i);
64 }
65 }
66
67 #define log2(x) (log(x) / log(2))
68
69 void get_zoom(context_t *context) {
70 pos_float_t center_lat = (context->max.lat + context->min.lat)/2;
71
72 /* we know pixel size, we know the real size, we want the zoom! */
73
74 printf("map_update: %d x %d\n",
75 context->map.widget->allocation.width,
76 context->map.widget->allocation.height);
77
78 double vscale = DEG2RAD(POS_EQ_RADIUS);
79 double hscale = DEG2RAD(cos(DEG2RAD(center_lat)) * POS_EQ_RADIUS);
80
81 printf("scale: %f m/pix %f m/pix\n", hscale*256, vscale*256);
82
83 // double height = 8 * (1<<zoom) / vscale; // 2^zoom ln2(zoom)
84 // double hzoom = log2(context->map.widget->allocation.height * vscale / 8);
85 // double width = 16 * (1<<zoom) / hscale;
86 // double vzoom = log2(context->map.widget->allocation.width * hscale / 8);
87
88 // printf("zoom: %f %f\n", hzoom, vzoom);
89 }
90
91 /* the contents of the map tab have been changed */
92 static void map_update(context_t *context) {
93 pos_float_t center_lat = (context->max.lat + context->min.lat)/2;
94 pos_float_t center_lon = (context->max.lon + context->min.lon)/2;
95
96 get_zoom(context);
97
98 osm_gps_map_set_center(OSM_GPS_MAP(context->map.widget),
99 center_lat, center_lon);
100
101 osm_gps_map_set_zoom(OSM_GPS_MAP(context->map.widget), 14);
102 }
103
104 static gboolean on_map_configure(GtkWidget *widget,
105 GdkEventConfigure *event,
106 context_t *context) {
107
108 map_update(context);
109 return FALSE;
110 }
111
112 /* the contents of the direct tab have been changed */
113 static void direct_update(context_t *context) {
114 pos_lat_entry_set(context->direct.minlat, context->min.lat);
115 pos_lon_entry_set(context->direct.minlon, context->min.lon);
116 pos_lat_entry_set(context->direct.maxlat, context->max.lat);
117 pos_lon_entry_set(context->direct.maxlon, context->max.lon);
118 }
119
120 /* update the contents of the extent tab */
121 static void extent_update(context_t *context) {
122 pos_float_t center_lat = (context->max.lat + context->min.lat)/2;
123 pos_float_t center_lon = (context->max.lon + context->min.lon)/2;
124
125 pos_lat_entry_set(context->extent.lat, center_lat);
126 pos_lat_entry_set(context->extent.lon, center_lon);
127
128 double vscale = DEG2RAD(POS_EQ_RADIUS / 1000.0);
129 double hscale = DEG2RAD(cos(DEG2RAD(center_lat)) * POS_EQ_RADIUS / 1000.0);
130
131 double height = vscale * (context->max.lat - context->min.lat);
132 double width = hscale * (context->max.lon - context->min.lon);
133
134 pos_dist_entry_set(context->extent.width, width, context->extent.is_mil);
135 pos_dist_entry_set(context->extent.height, height, context->extent.is_mil);
136 }
137
138 static void callback_modified_direct(GtkWidget *widget, gpointer data) {
139 context_t *context = (context_t*)data;
140
141 /* direct is first tab (page 0) */
142 if(gtk_notebook_get_current_page(GTK_NOTEBOOK(context->notebook)) != 0)
143 return;
144
145 /* parse the fields from the direct entry pad */
146 parse_and_set_lat(context->direct.minlat, context->minlat, &context->min.lat);
147 parse_and_set_lon(context->direct.minlon, context->minlon, &context->min.lon);
148 parse_and_set_lat(context->direct.maxlat, context->maxlat, &context->max.lat);
149 parse_and_set_lon(context->direct.maxlon, context->maxlon, &context->max.lon);
150
151 /* also adjust other views */
152 extent_update(context);
153 }
154
155 static void callback_modified_extent(GtkWidget *widget, gpointer data) {
156 context_t *context = (context_t*)data;
157
158 /* extent is second tab (page 1) */
159 if(gtk_notebook_get_current_page(GTK_NOTEBOOK(context->notebook)) != 1)
160 return;
161
162 pos_float_t center_lat = pos_lat_get(context->extent.lat);
163 pos_float_t center_lon = pos_lon_get(context->extent.lon);
164
165 if(!pos_lat_valid(center_lat) || !pos_lon_valid(center_lon))
166 return;
167
168 double vscale = DEG2RAD(POS_EQ_RADIUS / 1000.0);
169 double hscale = DEG2RAD(cos(DEG2RAD(center_lat)) * POS_EQ_RADIUS / 1000.0);
170
171 double height = pos_dist_get(context->extent.height, context->extent.is_mil);
172 double width = pos_dist_get(context->extent.width, context->extent.is_mil);
173
174 height /= 2 * vscale;
175 context->min.lat = center_lat - height;
176 pos_lat_label_set(context->minlat, context->min.lat);
177 context->max.lat = center_lat + height;
178 pos_lat_label_set(context->maxlat, context->max.lat);
179
180 width /= 2 * hscale;
181 context->min.lon = center_lon - width;
182 pos_lon_label_set(context->minlon, context->min.lon);
183 context->max.lon = center_lon + width;
184 pos_lon_label_set(context->maxlon, context->max.lon);
185
186 /* also update other tabs */
187 direct_update(context);
188 map_update(context);
189 }
190
191 static void callback_modified_unit(GtkWidget *widget, gpointer data) {
192 context_t *context = (context_t*)data;
193
194 /* get current values */
195 double height = pos_dist_get(context->extent.height, context->extent.is_mil);
196 double width = pos_dist_get(context->extent.width, context->extent.is_mil);
197
198 /* adjust unit flag */
199 context->extent.is_mil = gtk_combo_box_get_active(
200 GTK_COMBO_BOX(context->extent.mil_km)) == 0;
201
202 /* save values */
203 pos_dist_entry_set(context->extent.width, width, context->extent.is_mil);
204 pos_dist_entry_set(context->extent.height, height, context->extent.is_mil);
205 }
206
207 #ifdef USE_HILDON
208 static void callback_fetch_mm_clicked(GtkButton *button, gpointer data) {
209 context_t *context = (context_t*)data;
210
211 printf("clicked fetch mm!\n");
212
213 if(!dbus_mm_set_position(context->area->osso_context, NULL)) {
214 errorf(context->dialog,
215 _("Unable to communicate with Maemo Mapper. "
216 "You need to have Maemo Mapper installed "
217 "to use this feature."));
218 return;
219 }
220
221 if(!context->area->mmpos->valid) {
222 errorf(context->dialog,
223 _("No valid position received yet. You need "
224 "to scroll or zoom the Maemo Mapper view "
225 "in order to force it to send its current "
226 "view position to osm2go."));
227 return;
228 }
229
230 /* maemo mapper is third tab (page 2) */
231 if(gtk_notebook_get_current_page(GTK_NOTEBOOK(context->notebook)) != 2)
232 return;
233
234 /* maemo mapper pos data ... */
235 pos_float_t center_lat = context->area->mmpos->pos.lat;
236 pos_float_t center_lon = context->area->mmpos->pos.lon;
237 int zoom = context->area->mmpos->zoom;
238
239 if(!pos_lat_valid(center_lat) || !pos_lon_valid(center_lon))
240 return;
241
242 double vscale = DEG2RAD(POS_EQ_RADIUS);
243 double height = 8 * (1<<zoom) / vscale;
244 context->min.lat = center_lat - height;
245 pos_lat_label_set(context->minlat, context->min.lat);
246 context->max.lat = center_lat + height;
247 pos_lat_label_set(context->maxlat, context->max.lat);
248
249 double hscale = DEG2RAD(cos(DEG2RAD(center_lat)) * POS_EQ_RADIUS);
250 double width = 16 * (1<<zoom) / hscale;
251 context->min.lon = center_lon - width;
252 pos_lon_label_set(context->minlon, context->min.lon);
253 context->max.lon = center_lon + width;
254 pos_lon_label_set(context->maxlon, context->max.lon);
255
256 /* also update other tabs */
257 direct_update(context);
258 extent_update(context);
259 map_update(context);
260 }
261 #endif
262
263 static void map_zoom(context_t *context, int step) {
264 int zoom;
265 OsmGpsMap *map = OSM_GPS_MAP(context->map.widget);
266 g_object_get(map, "zoom", &zoom, NULL);
267 zoom = osm_gps_map_set_zoom(map, zoom+step);
268
269 get_zoom(context);
270
271 /* enable/disable zoom buttons as required */
272 gtk_widget_set_sensitive(context->map.zoomin, zoom<17);
273 gtk_widget_set_sensitive(context->map.zoomout, zoom>1);
274 }
275
276 static gboolean
277 cb_map_zoomin(GtkButton *button, context_t *context) {
278 map_zoom(context, +1);
279 return FALSE;
280 }
281
282 static gboolean
283 cb_map_zoomout(GtkButton *button, context_t *context) {
284 map_zoom(context, -1);
285 return FALSE;
286 }
287
288 gboolean area_edit(area_edit_t *area) {
289 gboolean ok = FALSE;
290
291 context_t context;
292 memset(&context, 0, sizeof(context_t));
293 context.area = area;
294 context.min.lat = area->min->lat;
295 context.min.lon = area->min->lon;
296 context.max.lat = area->max->lat;
297 context.max.lon = area->max->lon;
298
299 context.dialog =
300 misc_dialog_new(MISC_DIALOG_HIGH, _("Area editor"),
301 GTK_WINDOW(area->parent),
302 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
303 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
304 NULL);
305
306 GtkWidget *table = gtk_table_new(4, 2, FALSE); // x, y
307
308 GtkWidget *label = gtk_label_new(_("Latitude:"));
309 misc_table_attach(table, label, 0, 0);
310 context.minlat = pos_lat_label_new(area->min->lat);
311 misc_table_attach(table, context.minlat, 1, 0);
312 label = gtk_label_new(_("to"));
313 misc_table_attach(table, label, 2, 0);
314 context.maxlat = pos_lat_label_new(area->max->lat);
315 misc_table_attach(table, context.maxlat, 3, 0);
316
317 label = gtk_label_new(_("Longitude:"));
318 misc_table_attach(table, label, 0, 1);
319 context.minlon = pos_lon_label_new(area->min->lon);
320 misc_table_attach(table, context.minlon, 1, 1);
321 label = gtk_label_new(_("to"));
322 misc_table_attach(table, label, 2, 1);
323 context.maxlon = pos_lon_label_new(area->max->lon);
324 misc_table_attach(table, context.maxlon, 3, 1);
325
326 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(context.dialog)->vbox),
327 table, FALSE, FALSE, 0);
328
329 context.notebook = gtk_notebook_new();
330
331 /* ------------- fetch from map ------------------------ */
332
333 GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
334
335 context.map.widget = g_object_new(OSM_TYPE_GPS_MAP,
336 "repo-uri", MAP_SOURCE_OPENSTREETMAP,
337 "proxy-uri", misc_get_proxy_uri(area->settings),
338 NULL);
339
340 g_signal_connect(G_OBJECT(context.map.widget), "configure-event",
341 G_CALLBACK(on_map_configure), &context);
342
343 gtk_box_pack_start_defaults(GTK_BOX(hbox), context.map.widget);
344
345 /* zoom button box */
346 GtkWidget *vbox = gtk_vbox_new(FALSE,0);
347
348 context.map.zoomin = gtk_button_new();
349 gtk_button_set_image(GTK_BUTTON(context.map.zoomin),
350 gtk_image_new_from_stock(GTK_STOCK_ZOOM_IN, GTK_ICON_SIZE_MENU));
351 g_signal_connect(context.map.zoomin, "clicked",
352 G_CALLBACK(cb_map_zoomin), &context);
353 gtk_box_pack_start(GTK_BOX(vbox), context.map.zoomin, FALSE, FALSE, 0);
354
355 context.map.zoomout = gtk_button_new();
356 gtk_button_set_image(GTK_BUTTON(context.map.zoomout),
357 gtk_image_new_from_stock(GTK_STOCK_ZOOM_OUT, GTK_ICON_SIZE_MENU));
358 g_signal_connect(context.map.zoomout, "clicked",
359 G_CALLBACK(cb_map_zoomout), &context);
360 gtk_box_pack_start(GTK_BOX(vbox), context.map.zoomout, FALSE, FALSE, 0);
361
362 gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
363
364 gtk_notebook_append_page(GTK_NOTEBOOK(context.notebook),
365 hbox, gtk_label_new(_("Map")));
366
367 /* ------------ direct min/max edit --------------- */
368
369 vbox = gtk_vbox_new(FALSE, 10);
370 table = gtk_table_new(3, 3, FALSE); // x, y
371
372 context.direct.minlat = pos_lat_entry_new(0.0);
373 misc_table_attach(table, context.direct.minlat, 0, 0);
374 label = gtk_label_new(_("to"));
375 misc_table_attach(table, label, 1, 0);
376 context.direct.maxlat = pos_lat_entry_new(0.0);
377 misc_table_attach(table, context.direct.maxlat, 2, 0);
378
379 context.direct.minlon = pos_lon_entry_new(area->min->lon);
380 misc_table_attach(table, context.direct.minlon, 0, 1);
381 label = gtk_label_new(_("to"));
382 misc_table_attach(table, label, 1, 1);
383 context.direct.maxlon = pos_lon_entry_new(0.0);
384 misc_table_attach(table, context.direct.maxlon, 2, 1);
385
386 /* setup this page */
387 direct_update(&context);
388
389 g_signal_connect(G_OBJECT(context.direct.minlat), "changed",
390 G_CALLBACK(callback_modified_direct), &context);
391 g_signal_connect(G_OBJECT(context.direct.minlon), "changed",
392 G_CALLBACK(callback_modified_direct), &context);
393 g_signal_connect(G_OBJECT(context.direct.maxlat), "changed",
394 G_CALLBACK(callback_modified_direct), &context);
395 g_signal_connect(G_OBJECT(context.direct.maxlon), "changed",
396 G_CALLBACK(callback_modified_direct), &context);
397
398
399 /* --- hint --- */
400 label = gtk_label_new(_("(recommended min/max diff <0.03 degrees)"));
401 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 3, 2, 3);
402
403 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
404 gtk_notebook_append_page(GTK_NOTEBOOK(context.notebook),
405 vbox, gtk_label_new(_("Direct")));
406
407 /* ------------- center/extent edit ------------------------ */
408
409 vbox = gtk_vbox_new(FALSE, 10);
410 table = gtk_table_new(3, 4, FALSE); // x, y
411
412 label = gtk_label_new(_("Center:"));
413 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
414 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
415 context.extent.lat = pos_lat_entry_new(0.0);
416 gtk_table_attach_defaults(GTK_TABLE(table), context.extent.lat, 1, 2, 0, 1);
417 context.extent.lon = pos_lon_entry_new(0.0);
418 gtk_table_attach_defaults(GTK_TABLE(table), context.extent.lon, 2, 3, 0, 1);
419
420 gtk_table_set_row_spacing(GTK_TABLE(table), 0, 8);
421
422 label = gtk_label_new(_("Width:"));
423 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
424 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
425 context.extent.width = gtk_entry_new();
426 gtk_table_attach_defaults(GTK_TABLE(table), context.extent.width, 1, 2, 1, 2);
427
428 label = gtk_label_new(_("Height:"));
429 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
430 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3);
431 context.extent.height = gtk_entry_new();
432 gtk_table_attach_defaults(GTK_TABLE(table), context.extent.height, 1, 2, 2, 3);
433
434 context.extent.mil_km = gtk_combo_box_new_text();
435 gtk_combo_box_append_text(GTK_COMBO_BOX(context.extent.mil_km), _("mi"));
436 gtk_combo_box_append_text(GTK_COMBO_BOX(context.extent.mil_km), _("km"));
437 gtk_combo_box_set_active(GTK_COMBO_BOX(context.extent.mil_km), 1); // km
438
439 gtk_table_attach(GTK_TABLE(table), context.extent.mil_km, 2, 3, 1, 3,
440 0, 0, 0, 0);
441
442 /* setup this page */
443 extent_update(&context);
444
445 /* connect signals after inital update to avoid confusion */
446 g_signal_connect(G_OBJECT(context.extent.lat), "changed",
447 G_CALLBACK(callback_modified_extent), &context);
448 g_signal_connect(G_OBJECT(context.extent.lon), "changed",
449 G_CALLBACK(callback_modified_extent), &context);
450 g_signal_connect(G_OBJECT(context.extent.width), "changed",
451 G_CALLBACK(callback_modified_extent), &context);
452 g_signal_connect(G_OBJECT(context.extent.height), "changed",
453 G_CALLBACK(callback_modified_extent), &context);
454 g_signal_connect(G_OBJECT(context.extent.mil_km), "changed",
455 G_CALLBACK(callback_modified_unit), &context);
456
457 /* --- hint --- */
458 label = gtk_label_new(_("(recommended width/height < 2km/1.25mi)"));
459 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 3, 3, 4);
460
461 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
462 gtk_notebook_append_page(GTK_NOTEBOOK(context.notebook),
463 vbox, gtk_label_new(_("Extent")));
464
465 #ifdef USE_HILDON
466 /* ------------- fetch from maemo mapper ------------------------ */
467
468 vbox = gtk_vbox_new(FALSE, 8);
469 context.mmapper.fetch =
470 gtk_button_new_with_label(_("Get from Maemo Mapper"));
471 gtk_box_pack_start(GTK_BOX(vbox), context.mmapper.fetch, FALSE, FALSE, 0);
472
473 g_signal_connect(G_OBJECT(context.mmapper.fetch), "clicked",
474 G_CALLBACK(callback_fetch_mm_clicked), &context);
475
476 /* --- hint --- */
477 label = gtk_label_new(_("(recommended MM zoom level < 7)"));
478 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
479
480
481 gtk_notebook_append_page(GTK_NOTEBOOK(context.notebook),
482 vbox, gtk_label_new(_("Maemo Mapper")));
483 #endif
484
485 /* ------------------------------------------------------ */
486
487 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(context.dialog)->vbox),
488 context.notebook);
489
490
491 gtk_widget_show_all(context.dialog);
492
493 if(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(context.dialog))) {
494 /* copy modified values back to given storage */
495 area->min->lat = context.min.lat;
496 area->min->lon = context.min.lon;
497 area->max->lat = context.max.lat;
498 area->max->lon = context.max.lon;
499 ok = TRUE;
500 }
501
502 gtk_widget_destroy(context.dialog);
503
504 return ok;
505 }