mappix/mappix-trackedit.c

1124 lines
32 KiB
C

/*
* mappix-trackedit.c
*
* Copyright (C) 2006 Nils Faerber <nils.faerber@kernelconcepts.de>
*
*
* 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 <stdlib.h>
#include <string.h>
#include <math.h>
#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);
}