/* * mappix-trackedit.c * * Copyright (C) 2006 Nils Faerber * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include "mappix.h" #include "geo-dist.h" #include "geo-tools.h" /* * takes head of a track * returns number of data points in a track */ static gint track_get_number_of_points(track_head *track) { GList *lp; gint pcount=0; lp = track->track; while ((lp = g_list_next(lp))) { pcount++; } return pcount; } /* * takes head of a track * returns total length of a track in meters */ double track_get_length(track_head *track) { gdouble plat, plong; gdouble olat, olong; trackdata *tdata; GList *lp; double dist=0.; double tdist=0.; lp = track->track; tdata = (trackdata *)lp->data; unit2latlon(tdata->unitx, tdata->unity, olat, olong); while ((lp = g_list_next(lp))) { tdata = (trackdata *)lp->data; unit2latlon(tdata->unitx, tdata->unity, plat, plong); dist = dist_ell(olat, olong, plat, plong, WGS84); if (!isnan(dist)) { tdist += dist; } olat = plat; olong = plong; } tdist *= NAUTICAL_MILE; return tdist; } /* * takes head of a track * returns mean distance between two data points in meters */ double track_get_mean_pointdistance(track_head *track) { gdouble dist; gint pcount; gdouble mdist; dist = track_get_length(track); pcount = track_get_number_of_points(track); mdist = (dist / (double)pcount) * 1000.; // fprintf(stderr, "total data points in track = %d\ntotal tracklength = %lf km\nmean distance = %lf m\n", pcount, tdist, mdist); return mdist; } void track_add_new_to_main(mapwin *Mapper, track_head *track) { GtkListStore *store; GtkTreeIter iter; g_list_foreach(track->track, track_find_minmax, track); Mapper->tracklist = g_list_prepend(Mapper->tracklist, track); store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(Mapper->track_treeview))); gtk_list_store_append (store, &iter); if (g_str_has_suffix(track->name, ".gpx")) track->name[strlen(track->name)-4] = 0; gtk_list_store_set (store, &iter, 0, track->show, 1, g_path_get_basename(track->name), 2, track, -1); } /* calculate distance from point to next point, * if actual distance > dist then create new track: * cut old track at current position, * end old track here, * start new track at next point */ static int do_auto_split_track(mapwin *Mapper, track_head *track, gint dist) { gdouble olat, olong; gdouble plat, plong; trackdata *tdata; GList *lp; gint pcount=0; double pdist=0.; track_head *split_track; trackdata *split_track_data; lp = track->track; tdata = (trackdata *)lp->data; unit2latlon(tdata->unitx, tdata->unity, olat, olong); split_track = (track_head *)malloc(sizeof(track_head)); split_track->name = g_strdup_printf("%s-%03d", track->name, pcount); split_track->type = track->type; split_track->show = TRUE; split_track->track = NULL; split_track->xmin = WORLD_SIZE_UNITS; split_track->ymin = WORLD_SIZE_UNITS; split_track->xmax = 0; split_track->ymax = 0; while ((lp = g_list_next(lp))) { tdata = (trackdata *)lp->data; split_track_data = (trackdata *)malloc(sizeof(trackdata)); memcpy(split_track_data, tdata, sizeof(trackdata)); unit2latlon(tdata->unitx, tdata->unity, plat, plong); pdist = dist_ell(olat, olong, plat, plong, WGS84); pdist *= NAUTICAL_MILE * 1000.; if (pdist > dist) { pcount++; track_add_new_to_main(Mapper, split_track); split_track = (track_head *)malloc(sizeof(track_head)); split_track->name = g_strdup_printf("%s-%03d", track->name, pcount); split_track->type = track->type; split_track->show = TRUE; split_track->track = NULL; split_track->xmin = WORLD_SIZE_UNITS; split_track->ymin = WORLD_SIZE_UNITS; split_track->xmax = 0; split_track->ymax = 0; split_track->track = g_list_prepend(split_track->track, split_track_data); } else { split_track->track = g_list_prepend(split_track->track, split_track_data); } olat = plat; olong = plong; } track_add_new_to_main(Mapper, split_track); // fprintf(stderr, "would split into %d new tracks\n", pcount); return 0; } void track_find_minmax(gpointer data, gpointer user_data) { trackdata *tdata = (trackdata *)data; track_head *ntrack = (track_head *)user_data; if (tdata->unitx > ntrack->xmax) ntrack->xmax = tdata->unitx; if (tdata->unitx < ntrack->xmin) ntrack->xmin = tdata->unitx; if (tdata->unity > ntrack->ymax) ntrack->ymax = tdata->unity; if (tdata->unity < ntrack->ymin) ntrack->ymin = tdata->unity; } gboolean get_selected_track (GtkTreeModel **tree_model, GtkTreeIter *iter, mapwin *Mapper) { GtkTreeSelection *tree_selection; GtkWidget *dialog; tree_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(Mapper->track_treeview)); if (!gtk_tree_selection_get_selected(tree_selection, tree_model, iter)) { // gtk_widget_destroy(dialog); dialog = gtk_message_dialog_new(GTK_WINDOW(gtk_widget_get_toplevel(Mapper->mainwin)), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", "No track selected,\nselect a track first."); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_hide (dialog); gtk_widget_destroy (dialog); return FALSE; } return TRUE; } static gint do_auto_split_dialog(mapwin *Mapper, track_head **track, guint *dist) { GtkWidget *dialog; GtkWidget *hbox; GtkWidget *stock; GtkWidget *table; GtkWidget *spinb_split_dist; GtkWidget *label; GtkWidget *w; gint result; GtkTreeModel *tree_model; GtkTreeIter iter; gchar *tname; track_head *trackh; gdouble mean_dist; if (!get_selected_track(&tree_model, &iter, Mapper)) return -1; dialog = gtk_dialog_new_with_buttons("Auto Split Track", GTK_WINDOW(gtk_widget_get_toplevel(Mapper->mainwin)), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); hbox = gtk_hbox_new (FALSE, 8); gtk_container_set_border_width (GTK_CONTAINER (hbox), 8); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, FALSE, FALSE, 0); stock = gtk_image_new_from_stock (GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG); gtk_box_pack_start (GTK_BOX (hbox), stock, FALSE, FALSE, 0); table = gtk_table_new (2, 4, FALSE); gtk_table_set_row_spacings (GTK_TABLE (table), 4); gtk_table_set_col_spacings (GTK_TABLE (table), 4); gtk_table_set_homogeneous(GTK_TABLE (table), FALSE); gtk_box_pack_start (GTK_BOX (hbox), table, TRUE, TRUE, 0); label = gtk_label_new ("Track to split"); gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 0, 1); w = gtk_entry_new(); gtk_table_attach_defaults (GTK_TABLE (table), w, 1, 2, 0, 1); gtk_entry_set_editable(GTK_ENTRY(w), FALSE); gtk_tree_model_get(tree_model, &iter, 1, &tname, 2, &trackh, -1); gtk_entry_set_text(GTK_ENTRY(w), tname); gtk_entry_set_max_length(GTK_ENTRY(w), strlen(tname) + 1); gtk_entry_set_width_chars(GTK_ENTRY(w), strlen(tname) + 1); mean_dist = track_get_mean_pointdistance(trackh); label = gtk_label_new ("Split Distance (m)\n(default: mean distance)"); gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 1, 2); spinb_split_dist = gtk_spin_button_new_with_range(0, 9999999, 1); gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinb_split_dist), mean_dist); gtk_table_attach_defaults (GTK_TABLE (table), spinb_split_dist, 1, 2, 1, 2); gtk_widget_show_all (hbox); result = gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_hide (dialog); switch (result) { case GTK_RESPONSE_ACCEPT: *dist = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinb_split_dist)); *track = trackh; break; default: break; } gtk_widget_destroy (dialog); return result; } void on_auto_split (GtkButton *widget, gpointer user_data) { mapwin *Mapper = (mapwin *)user_data; track_head *track; guint dist; gint result; track = NULL; dist = 0; result = do_auto_split_dialog(Mapper, &track, &dist); if (result == -1 || result == GTK_RESPONSE_REJECT) return; // fprintf(stderr, "split: track '%s' if dist > %dm\n", track->name, dist); do_auto_split_track(Mapper, track, dist); } /* split the selected track at the selected postion * into one new track and the rest of the other * split-point will be duplicated, original track will be preserved */ void on_split_track (GtkToolButton *widget, gpointer user_data) { mapwin *Mapper = (mapwin *)user_data; track_head *track; GtkTreeModel *tree_model; GtkTreeIter iter; gchar *tname; trackdata *tdata; GList *lp; gint pcount=0; track_head *split_track; trackdata *split_track_data; if (!get_selected_track(&tree_model, &iter, Mapper)) return; gtk_tree_model_get(tree_model, &iter, 1, &tname, 2, &track, -1); lp = track->track; while ((lp = g_list_next(lp))) { tdata = (trackdata *)lp->data; if (tdata->selected) break; } if (!lp) { GtkWidget *dialog; dialog = gtk_message_dialog_new(GTK_WINDOW(gtk_widget_get_toplevel(Mapper->mainwin)), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", "No selected split-point,\non selected track."); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_hide (dialog); gtk_widget_destroy (dialog); return; } lp = track->track; tdata = (trackdata *)lp->data; split_track = (track_head *)malloc(sizeof(track_head)); split_track->name = g_strdup_printf("%s-%03d", track->name, pcount); split_track->type = track->type; split_track->show = TRUE; split_track->track = NULL; split_track->xmin = WORLD_SIZE_UNITS; split_track->ymin = WORLD_SIZE_UNITS; split_track->xmax = 0; split_track->ymax = 0; while ((lp = g_list_next(lp))) { tdata = (trackdata *)lp->data; split_track_data = (trackdata *)malloc(sizeof(trackdata)); memcpy(split_track_data, tdata, sizeof(trackdata)); if (tdata->selected) { tdata->selected = FALSE; pcount++; split_track->track = g_list_prepend(split_track->track, split_track_data); track_add_new_to_main(Mapper, split_track); split_track = (track_head *)malloc(sizeof(track_head)); split_track->name = g_strdup_printf("%s-%03d", track->name, pcount); split_track->type = track->type; split_track->show = TRUE; split_track->track = NULL; split_track->xmin = WORLD_SIZE_UNITS; split_track->ymin = WORLD_SIZE_UNITS; split_track->xmax = 0; split_track->ymax = 0; split_track_data = (trackdata *)malloc(sizeof(trackdata)); memcpy(split_track_data, tdata, sizeof(trackdata)); split_track->track = g_list_prepend(split_track->track, split_track_data); } else { split_track->track = g_list_prepend(split_track->track, split_track_data); } } track_add_new_to_main(Mapper, split_track); // fprintf(stderr, "would split into %d new tracks\n", pcount); } static void delete_track_data(gpointer data, gpointer user_data) { trackdata *tdata = (trackdata *)data; g_free(tdata); } /* the calculated distance is not at all exact but serves * the purpose for greater, lower comparison */ static inline guint dist_point_to_point(gint x1, gint y1, gint x2, gint y2) { guint dist; dist = ABS((gint)x2-(gint)x1) + ABS((gint)y2-(gint)y1); return dist; } void do_select_trackpoint(gint sx, gint sy, gpointer user_data) { mapwin *Mapper = (mapwin *)user_data; GList *tracks, *track; track_head *trackh; trackdata *tdata, *least_tdata; guint dist, least_dist; gint x,y; /* find nearest trackpoint to pointer */ least_dist = WORLD_SIZE_UNITS; least_tdata = NULL; if (Mapper->tracklist != NULL) { for (tracks=Mapper->tracklist; tracks; tracks=g_list_next(tracks)) { trackh = (track_head *)tracks->data; if (trackh->show) { if (trackh->xmax < Mapper->xoffset || trackh->ymax < Mapper->yoffset || trackh->xmin > (Mapper->xoffset + (Mapper->canvas_x / Mapper->scale)) || trackh->ymin > (Mapper->yoffset + (Mapper->canvas_y / Mapper->scale))) { // fprintf(stderr, "not drawing track '%s'\n", trackh->name); } else { /* now search in this track */ for (track=trackh->track; track; track=g_list_next(track)) { tdata = (trackdata *)track->data; dist = dist_point_to_point(sx, sy, tdata->unitx, tdata->unity); if (dist < least_dist) { least_dist = dist; least_tdata = tdata; } } } } } } if (least_tdata != NULL) { least_tdata->selected = ! least_tdata->selected; // draw_tracks(Mapper); x = (least_tdata->unitx - Mapper->xoffset) * Mapper->scale; y = (least_tdata->unity - Mapper->yoffset) * Mapper->scale; draw_marker(x, y, Mapper->marker_size, Mapper->marker_type | (least_tdata->selected ? MARKER_SELECTED : 0), Mapper); gtk_widget_queue_draw(Mapper->drawarea); } } void do_trackpoint_region_select(gint sx1, gint sy1, guint sx2, guint sy2, gpointer user_data) { mapwin *Mapper = (mapwin *)user_data; GList *tracks, *track; track_head *trackh; trackdata *tdata; gint x,y; if (sx1 > sx2) { x=sx1; sx1=sx2; sx2=x; } if (sy1 > sy2) { y=sy1; sy1=sy2; sy2=y; } /* find trackpoints in boundary box */ if (Mapper->tracklist != NULL) { for (tracks=Mapper->tracklist; tracks; tracks=g_list_next(tracks)) { trackh = (track_head *)tracks->data; if (trackh->show) { if (trackh->xmax < Mapper->xoffset || trackh->ymax < Mapper->yoffset || trackh->xmin > (Mapper->xoffset + (Mapper->canvas_x / Mapper->scale)) || trackh->ymin > (Mapper->yoffset + (Mapper->canvas_y / Mapper->scale))) { // fprintf(stderr, "not drawing track '%s'\n", trackh->name); } else { /* now search in this track */ for (track=trackh->track; track; track=g_list_next(track)) { tdata = (trackdata *)track->data; if (tdata->unitx >= sx1 && tdata->unitx <= sx2 && tdata->unity >= sy1 && tdata->unity <= sy2) { tdata->selected = ! tdata->selected; x = (tdata->unitx - Mapper->xoffset) * Mapper->scale; y = (tdata->unity - Mapper->yoffset) * Mapper->scale; /*XXX*/ draw_marker(x, y, Mapper->marker_size, Mapper->marker_type | (tdata->selected ? MARKER_SELECTED : 0), Mapper); gtk_widget_queue_draw(Mapper->drawarea); } } } } } } } void do_move_trackpoint(gint sx, gint sy, gint ex, gint ey, gpointer user_data) { mapwin *Mapper = (mapwin *)user_data; gint moffset_x, moffset_y; GList *tracks, *track; track_head *trackh; trackdata *tdata; moffset_x = ex - sx; moffset_y = ey - sy; if (Mapper->tracklist != NULL) { for (tracks=Mapper->tracklist; tracks; tracks=g_list_next(tracks)) { trackh = (track_head *)tracks->data; if (trackh->show) { if (trackh->xmax < Mapper->xoffset || trackh->ymax < Mapper->yoffset || trackh->xmin > (Mapper->xoffset + (Mapper->canvas_x / Mapper->scale)) || trackh->ymin > (Mapper->yoffset + (Mapper->canvas_y / Mapper->scale))) { // fprintf(stderr, "not drawing track '%s'\n", trackh->name); } else { /* now search in this track */ for (track=trackh->track; track; track=g_list_next(track)) { tdata = (trackdata *)track->data; if (tdata->selected) { tdata->unitx += moffset_x; tdata->unity += moffset_y; } } } } } } draw_tracks(Mapper); } void on_select_trackpoint_cb(GtkToolButton *toolbutton, gpointer user_data) { mapwin *Mapper = (mapwin *)user_data; Mapper->toolmode = MAPPIX_TOOL_SELECT_TRACKPOINT; } void on_select_region_cb(GtkToolButton *toolbutton, gpointer user_data) { mapwin *Mapper = (mapwin *)user_data; Mapper->toolmode = MAPPIX_TOOL_SELECT_REGION; } void on_move_trackpoint_cb(GtkToolButton *toolbutton, gpointer user_data) { mapwin *Mapper = (mapwin *)user_data; Mapper->toolmode = MAPPIX_TOOL_MOVE_TRACKPOINT; } void on_track_delete(GtkToolButton *toolbutton, gpointer user_data) { mapwin *Mapper = (mapwin *)user_data; GtkWidget *dialog; GtkTreeModel *tree_model; GtkTreeIter iter; track_head *trackh; gchar *tname; gint res; if (!get_selected_track(&tree_model, &iter, Mapper)) return; gtk_tree_model_get(tree_model, &iter, 1, &tname, 2, &trackh, -1); dialog = gtk_message_dialog_new(GTK_WINDOW(gtk_widget_get_toplevel(Mapper->mainwin)), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, "Are you sure you want to delete track\n%s", tname); res = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_hide (dialog); gtk_widget_destroy (dialog); if (res != GTK_RESPONSE_YES) { return; } // fprintf(stderr, "would delete %s\n", tname); gtk_list_store_remove(GTK_LIST_STORE(tree_model), &iter); g_list_foreach(trackh->track, delete_track_data, NULL); g_list_free(trackh->track); Mapper->tracklist = g_list_remove(Mapper->tracklist, trackh); do_redraw(Mapper); } static void track_delete_selected_nodes(track_head *trackh) { GList *track; trackdata *tdata; for (track=trackh->track; track; track=g_list_next(track)) { tdata = (trackdata *)track->data; if (tdata->selected) { //track = g_list_remove(track, tdata); g_free(track->data); trackh->track = g_list_delete_link(trackh->track, track); track=trackh->track; } } } void on_delete_nodes_cb(GtkToolButton *toolbutton, gpointer user_data) { mapwin *Mapper = (mapwin *)user_data; GList *tracks; //, *track; track_head *trackh; // trackdata *tdata; if (Mapper->tracklist != NULL) { for (tracks=Mapper->tracklist; tracks; tracks=g_list_next(tracks)) { trackh = (track_head *)tracks->data; if (trackh->show) { if (trackh->xmax < Mapper->xoffset || trackh->ymax < Mapper->yoffset || trackh->xmin > (Mapper->xoffset + (Mapper->canvas_x / Mapper->scale)) || trackh->ymin > (Mapper->yoffset + (Mapper->canvas_y / Mapper->scale))) { // fprintf(stderr, "not drawing track '%s'\n", trackh->name); } else { /* now search in this track */ track_delete_selected_nodes(trackh); } } } } do_redraw(Mapper); } /* calculate distance from point to next point, * if actual distance > dist then create new track: * cut old track at current position, * end old track here, * start new track at next point */ static int do_thin_track(mapwin *Mapper, track_head *track, gint dist) { double olat, olong; double plat, plong; trackdata *tdata; GList *lp; double pdist=0.; track_head *split_track; trackdata *split_track_data; gint pkept, pskip; GtkWidget *dialog; lp = track->track; tdata = (trackdata *)lp->data; split_track = (track_head *)malloc(sizeof(track_head)); split_track->name = g_strdup_printf("%s-thin-%d", track->name, dist); split_track->show = TRUE; split_track->track = NULL; split_track->xmin = WORLD_SIZE_UNITS; split_track->ymin = WORLD_SIZE_UNITS; split_track->xmax = 0; split_track->ymax = 0; pkept=0; pskip=0; split_track_data = (trackdata *)malloc(sizeof(trackdata)); memcpy(split_track_data, tdata, sizeof(trackdata)); split_track->track = g_list_prepend(split_track->track, split_track_data); unit2latlon(tdata->unitx, tdata->unity, olat, olong); lp = g_list_next(lp); while ((lp = g_list_next(lp))) { tdata = (trackdata *)lp->data; unit2latlon(tdata->unitx, tdata->unity, plat, plong); pdist = dist_ell(olat, olong, plat, plong, WGS84); pdist *= NAUTICAL_MILE * 1000.; if (pdist > dist) { split_track_data = (trackdata *)malloc(sizeof(trackdata)); memcpy(split_track_data, tdata, sizeof(trackdata)); split_track->track = g_list_prepend(split_track->track, split_track_data); olat = plat; olong = plong; pkept++; } else pskip++; } track_add_new_to_main(Mapper, split_track); // fprintf(stderr, "would split into %d new tracks\n", pcount); // g_message("kept %d points, skipped %d points", pkept, pskip); dialog = gtk_message_dialog_new(GTK_WINDOW(gtk_widget_get_toplevel(Mapper->mainwin)), GTK_DIALOG_MODAL| GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "Kept %d points,\nskipped %d points", pkept, pskip); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_hide(dialog); gtk_widget_destroy(dialog); return 0; } static gint do_auto_thin_dialog(mapwin *Mapper, track_head **track, gint *dist) { GtkWidget *dialog; GtkWidget *hbox; GtkWidget *stock; GtkWidget *table; GtkWidget *spinb_split_dist; GtkWidget *label; GtkWidget *w; gint result; GtkTreeModel *tree_model; GtkTreeIter iter; gchar *tname; track_head *trackh; gdouble mean_dist; if (!get_selected_track(&tree_model, &iter, Mapper)) return -1; gtk_tree_model_get(tree_model, &iter, 1, &tname, 2, &trackh, -1); dialog = gtk_dialog_new_with_buttons("Auto Thin Track", GTK_WINDOW(gtk_widget_get_toplevel(Mapper->mainwin)), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); hbox = gtk_hbox_new (FALSE, 8); gtk_container_set_border_width (GTK_CONTAINER (hbox), 8); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, FALSE, FALSE, 0); stock = gtk_image_new_from_stock (GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG); gtk_box_pack_start (GTK_BOX (hbox), stock, FALSE, FALSE, 0); table = gtk_table_new (2, 4, FALSE); gtk_table_set_row_spacings (GTK_TABLE (table), 4); gtk_table_set_col_spacings (GTK_TABLE (table), 4); gtk_table_set_homogeneous(GTK_TABLE (table), FALSE); gtk_box_pack_start (GTK_BOX (hbox), table, TRUE, TRUE, 0); label = gtk_label_new ("Track to thin-out"); gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 0, 1); w = gtk_entry_new(); gtk_table_attach_defaults (GTK_TABLE (table), w, 1, 2, 0, 1); gtk_entry_set_editable(GTK_ENTRY(w), FALSE); //gtk_combo_box_append_text(GTK_COMBO_BOX(track_combo), tname); gtk_entry_set_text(GTK_ENTRY(w), tname); gtk_entry_set_max_length(GTK_ENTRY(w), strlen(tname) + 1); gtk_entry_set_width_chars(GTK_ENTRY(w), strlen(tname) + 1); mean_dist = track_get_mean_pointdistance(trackh); label = gtk_label_new ("Minimum Distance (m)\n(default: mean distance)"); gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 1, 2); spinb_split_dist = gtk_spin_button_new_with_range(0, 9999, 1); gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinb_split_dist), mean_dist); gtk_table_attach_defaults (GTK_TABLE (table), spinb_split_dist, 1, 2, 1, 2); gtk_widget_show_all (hbox); result = gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_hide (dialog); switch (result) { case GTK_RESPONSE_ACCEPT: *dist = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinb_split_dist)); *track = trackh; break; default: break; } gtk_widget_destroy (dialog); return result; } void on_auto_thin (GtkButton *widget, gpointer user_data) { mapwin *Mapper = (mapwin *)user_data; track_head *track; gint dist; gint result; track = NULL; dist = 0; result = do_auto_thin_dialog(Mapper, &track, &dist); if (result == -1 || result == GTK_RESPONSE_REJECT) return; // fprintf(stderr, "split: track '%s' if dist > %dm\n", track->name, dist); do_thin_track(Mapper, track, dist); } void on_properties_track (GtkButton *widget, gpointer user_data) { mapwin *Mapper = (mapwin *)user_data; GtkWidget *dialog, *hbox, *stock, *table, *label, *entry_name, *entry_type; GtkTreeModel *tree_model; GtkTreeIter iter; track_head *trackh; gchar *tname; const gchar *tmp_name; gint result; if (!get_selected_track(&tree_model, &iter, Mapper)) return; gtk_tree_model_get(tree_model, &iter, 1, &tname, 2, &trackh, -1); dialog = gtk_dialog_new_with_buttons("Rename track", GTK_WINDOW(gtk_widget_get_toplevel(Mapper->mainwin)), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); hbox = gtk_hbox_new (FALSE, 8); gtk_container_set_border_width (GTK_CONTAINER (hbox), 8); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, FALSE, FALSE, 0); stock = gtk_image_new_from_stock (GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG); gtk_box_pack_start (GTK_BOX (hbox), stock, FALSE, FALSE, 0); table = gtk_table_new (3, 4, FALSE); gtk_table_set_row_spacings (GTK_TABLE (table), 4); gtk_table_set_col_spacings (GTK_TABLE (table), 4); gtk_table_set_homogeneous(GTK_TABLE (table), FALSE); gtk_box_pack_start (GTK_BOX (hbox), table, TRUE, TRUE, 0); label = gtk_label_new ("Track name"); gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 0, 1); // track_combo = gtk_combo_box_new_text(); entry_name = gtk_entry_new(); gtk_entry_set_text(GTK_ENTRY(entry_name), tname); gtk_entry_set_editable(GTK_ENTRY(entry_name), TRUE); gtk_table_attach_defaults (GTK_TABLE (table), entry_name, 1, 2, 0, 1); label = gtk_label_new ("Track type"); gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 1, 2); // track_combo = gtk_combo_box_new_text(); entry_type = gtk_combo_box_new_text(); gtk_combo_box_append_text (GTK_COMBO_BOX(entry_type), "Unknown"); gtk_combo_box_append_text (GTK_COMBO_BOX(entry_type), "Outline / Border"); gtk_combo_box_append_text (GTK_COMBO_BOX(entry_type), "River"); gtk_combo_box_append_text (GTK_COMBO_BOX(entry_type), "Footpath"); gtk_combo_box_append_text (GTK_COMBO_BOX(entry_type), "Agricultural"); gtk_combo_box_append_text (GTK_COMBO_BOX(entry_type), "City"); gtk_combo_box_append_text (GTK_COMBO_BOX(entry_type), "Urban"); gtk_combo_box_append_text (GTK_COMBO_BOX(entry_type), "Motorway"); gtk_combo_box_append_text (GTK_COMBO_BOX(entry_type), "Highway"); gtk_combo_box_set_active (GTK_COMBO_BOX(entry_type), trackh->type); gtk_table_attach_defaults (GTK_TABLE (table), entry_type, 1, 2, 1, 2); gtk_widget_show_all (hbox); result = gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_hide (dialog); switch (result) { case GTK_RESPONSE_ACCEPT: tmp_name = gtk_entry_get_text(GTK_ENTRY(entry_name)); g_free(trackh->name); trackh->name = g_strdup(tmp_name); gtk_list_store_set(GTK_LIST_STORE(tree_model), &iter, 1, trackh->name, -1); if (trackh->type != gtk_combo_box_get_active(GTK_COMBO_BOX(entry_type))) { trackh->type = gtk_combo_box_get_active(GTK_COMBO_BOX(entry_type)); draw_tracks(Mapper); } break; default: break; } gtk_widget_destroy (dialog); } /* * Simplify a track by judging the angle variation between trackpoints * takes head of a track, minimum angle difference, main Mapper struct * does not return a value * * Idea of operation: * We do not need to record trackpoints which are "lineary" aligned on a track. * That means if the direction of a track section does not change by more than a * minimum angle we assume that the previous direction continues and we can drop * the current trackpoint. * Assume four consequent trackpoints A, B, C and D. * We calculate the angle of the track between points A and B. * We do the same for the angle between points B and C. * If the difference of the angles is less than min_angle we assume only a minimal change * and ignore (delete) point B. Next we calculate the angles from A to C and C to D and do * the same comparison, etc. * If the angle is greater than min_angle then we have a certain "twist" in direction and * the trackpoint should be kept, i.e. we move the triple forward one point and calculate * the next angles from B to C and C to D, etc. * The result will pretty well take care of curves and even light circles while eliminating * quite useless points from almost straight lines. * Good results can be expected for angle differences around 1.0 degree. */ void track_calc_angles(track_head *track, gdouble min_angle, mapwin *Mapper) { GtkWidget *dialog; trackdata *tdata1; trackdata *tdata2; trackdata *tdata3; GList *lp1, *lp2,*lp3; gdouble angle1=0.; gdouble angle2=0.; gdouble angle_diff; gint dx1, dy1; gint dx2, dy2; gint pkept=0, pskipped=0; lp1 = track->track; lp2 = g_list_next(lp1); if (!lp2) return; lp3 = g_list_next(lp2); if (!lp3) return; while (lp3) { tdata1 = (trackdata *)lp1->data; tdata2 = (trackdata *)lp2->data; tdata3 = (trackdata *)lp3->data; dx1 = tdata1->unitx - tdata2->unitx; dy1 = tdata1->unity - tdata2->unity; dx2 = tdata2->unitx - tdata3->unitx; dy2 = tdata2->unity - tdata3->unity; angle1 = atan2((gdouble)dx1, (gdouble)dy1); angle2 = atan2((gdouble)dx2, (gdouble)dy2); angle_diff = angle1 - angle2; if (fabs(angle_diff) < min_angle) { /* OK, zap point 2 ! */ tdata2->selected = TRUE; lp2 = g_list_next(lp2); lp3 = g_list_next(lp3); pskipped++; } else { do { lp1 = g_list_next(lp1); if (lp1) tdata1 = (trackdata *)lp1->data; } while (lp1 && tdata1->selected); lp2 = g_list_next(lp1); if (!lp2) break; lp3 = g_list_next(lp2); if (!lp3) break; pkept++; } } track_delete_selected_nodes(track); draw_tracks(Mapper); dialog = gtk_message_dialog_new(GTK_WINDOW(gtk_widget_get_toplevel(Mapper->mainwin)), GTK_DIALOG_MODAL| GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "Kept %d points,\nskipped %d points", pkept, pskipped); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_hide(dialog); gtk_widget_destroy(dialog); } void on_track_angle_simplify (GtkButton *widget, gpointer user_data) { mapwin *Mapper = (mapwin *)user_data; GtkWidget *dialog, *hbox, *stock, *table, *label, *spinb_angle; GtkTreeModel *tree_model; GtkTreeIter iter; track_head *trackh; gchar *tname; gdouble min_angle; gint result; if (!get_selected_track(&tree_model, &iter, Mapper)) return; gtk_tree_model_get(tree_model, &iter, 1, &tname, 2, &trackh, -1); dialog = gtk_dialog_new_with_buttons("Angle Simplify", GTK_WINDOW(gtk_widget_get_toplevel(Mapper->mainwin)), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); hbox = gtk_hbox_new (FALSE, 8); gtk_container_set_border_width (GTK_CONTAINER (hbox), 8); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, FALSE, FALSE, 0); stock = gtk_image_new_from_stock (GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG); gtk_box_pack_start (GTK_BOX (hbox), stock, FALSE, FALSE, 0); table = gtk_table_new (2, 4, FALSE); gtk_table_set_row_spacings (GTK_TABLE (table), 4); gtk_table_set_col_spacings (GTK_TABLE (table), 4); gtk_table_set_homogeneous(GTK_TABLE (table), FALSE); gtk_box_pack_start (GTK_BOX (hbox), table, TRUE, TRUE, 0); label = gtk_label_new ("Minimum angle (degree)"); gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 0, 1); spinb_angle = gtk_spin_button_new_with_range(0, 2 * M_PI, .01); gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinb_angle), 4.0); gtk_table_attach_defaults (GTK_TABLE (table), spinb_angle, 1, 2, 0, 1); gtk_widget_show_all (hbox); result = gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_hide (dialog); switch (result) { case GTK_RESPONSE_ACCEPT: min_angle = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinb_angle)); min_angle = ((2. * M_PI) / 360.) * min_angle; track_calc_angles(trackh, min_angle, Mapper); break; default: break; } gtk_widget_destroy (dialog); } void on_track_length(GtkWidget *widget, gpointer user_data) { mapwin *Mapper = (mapwin *)user_data; GtkWidget *dialog; GtkTreeModel *tree_model; GtkTreeIter iter; gchar *tname; track_head *trackh; double length=0.; if (!get_selected_track(&tree_model, &iter, Mapper)) return; gtk_tree_model_get(tree_model, &iter, 1, &tname, 2, &trackh, -1); length = track_get_length(trackh); // gtk_widget_destroy(dialog); dialog = gtk_message_dialog_new(GTK_WINDOW(gtk_widget_get_toplevel(Mapper->mainwin)), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "Track '%s' length is\n %lfkm", tname, length); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_hide (dialog); gtk_widget_destroy (dialog); }