diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0b1fa96 --- /dev/null +++ b/Makefile @@ -0,0 +1,70 @@ +# +# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# + +# prefix for installation and search path (like icons) +PREFIX = /usr/local/ + +# for normal desktop GTK+ +CCFLAGS = -Wall -O2 -g +GTKCFLAGS = `pkg-config --cflags gtk+-2.0` + +SQLITECFLAGS = `pkg-config --cflags sqlite3` + +CFLAGS = $(CCFLAGS) $(GTKCFLAGS) $(SQLITECFLAGS) + +#CLDFLAGS = +GTKLDFLAGS = `pkg-config --libs gtk+-2.0` + +SQLITELDFLAGS = `pkg-config --libs sqlite3` + +## for Maemo/Hildon +#CCFLAGS = -Wall -Os -g -DHILDON +#GTKCFLAGS = `pkg-config --cflags gtk+-2.0` `pkg-config --cflags dbus-1` + +#CFLAGS = $(CCFLAGS) $(GTKCFLAGS) + +##CLDFLAGS = +#GTKLDFLAGS = `pkg-config --libs gtk+-2.0` -lhildonlgpl -losso + + + +# no need to change anything below this line +# ------------------------------------------ + +.SUFFIXES: .d .c + +CFLAGS += -MD -DPREFIX=\"$(PREFIX)\" +LDFLAGS = $(CLDFLAGS) $(GTKLDFLAGS) $(SQLITELDFLAGS) + +MAPPIX_MEMBERS = mappix mappix-ui mappix-trackdraw mappix-gpxreader geo-dist \ + mappix-trackedit mappix-trackrecord nmeap01 mappix-fileio \ + mappix-waypoints +SOURCES = $(patsubst %,%.c,$(MAPPIX_MEMBERS)) +OBJS = $(patsubst %,%.o,$(MAPPIX_MEMBERS)) +DEPS = $(patsubst %,%.d,$(MAPPIX_MEMBERS)) + +all: mappix + +mappix: $(OBJS) + $(CC) -o $@ $^ $(LDFLAGS) + +clean: + rm -f *.o *.d mappix + +-include $(DEPS) diff --git a/NOTES b/NOTES new file mode 100644 index 0000000..cb21126 --- /dev/null +++ b/NOTES @@ -0,0 +1,74 @@ + + +GIOChannel* g_io_channel_unix_new (int fd); +g_io_channel_set_encoding() function with NULL +GIOStatus g_io_channel_set_encoding (GIOChannel *channel, + const gchar *encoding, + GError **error); +guint g_io_add_watch (GIOChannel *channel, + GIOCondition condition, + GIOFunc func, + gpointer user_data); +gboolean (*GIOFunc) (GIOChannel *source, + GIOCondition condition, + gpointer data); + FALSE for end watch + + +Distance Point To Line: +(for Peuker) + +d = |v^^.r| = (|(x_2-x_1)(y_1-y_0)-(x_1-x_0)(y_2-y_1)|) + / + (sqrt((x_2-x_1)^2+(y_2-y_1)^2)) + +- Ausgleichskurve + Ausgleichsrechnung + Interpolation + +* Peuker line simplification +* auto-split tracks + if track has "gaps" then split track into two tracks. + A gap is when in a group of four points A, B, C, D the distance + from A to B and C to D is about the same but B to C is "a lot" more. +* spike filter + calculate mean deviation of all points of one track, then + delete all points that have deviation > delta, + recalculate mean deviation and redo +* mean of two tracks A, B + for each point of track A calculate distance to + track B and create a new point on a new track in the middle of the + distance. Next do the same process for all point of track B. + The new track lies in the middle between A and B. + Requires component-wise distance function with direction, i.e. e.g. + distance of point is x=-5 and y=3 to track. +* merge of two tracks + for each point of track A find nearest corresponding point on + track B, then calculate the average between both points and record + this as new track point + Repeat this for each point of track B. + Limit the averaging to a maximum distance. + + +SQLite tables +------------- +tracks track headers (streets, boundaries, rivers, ...) + idx pkey index + tid track id, id common to all data points of one track + name track-/streetname + ttype track-/streettype + latmin bbox latitude min + lonmin + latmax + lonmax +trackpts the actual track (all tracks combined in one table?) + lat + lon + ele + dtim + nrsat + hdop + vdop + pdop +points POIs +cities name, center location, "size" diff --git a/README b/README new file mode 100644 index 0000000..6bec5dc --- /dev/null +++ b/README @@ -0,0 +1,39 @@ + + Mappix +======== + +Boilerplate: + + Copyright (C) 2006,2007 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + + +0. Introduction +--------------- + +Mappix is a to-become street map editor. +The idea is to record GPS data and edit and annotate the recorded tracks and +waypoints later. The longterm goal is to create a community of contributors. + + + +References +---------- + +GPX Specification + http://www.topografix.com/GPX/1/1/ diff --git a/cities.db b/cities.db new file mode 100644 index 0000000..3ef2360 Binary files /dev/null and b/cities.db differ diff --git a/geo-dist.c b/geo-dist.c new file mode 100644 index 0000000..fdd844c --- /dev/null +++ b/geo-dist.c @@ -0,0 +1,212 @@ +/* + * geo-dist + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +/* + * Calculate distances on Earth surface given geogrphic locations + */ + +#include +#include + +#include "geo-dist.h" + +// #define DEBUG + + +/* + * Earth models + * Remember, Earth is not a simple ball ;) + */ +struct ellipse_s { + char *name; + double a; + double invf; +}; + +static struct ellipse_s ellipse[] = { + { + "WGS84", + 6378.137 / 1.852, + 298.257223563 + }, { + "NAD27", + 6378.2064 / 1.852, + 294.9786982138 + }, { + "International", + 6378.388 / 1.852, + 297.0 + }, { + "Krasovsky", + 6378.245 / 1.852, + 298.3 + }, { + "Bessel", + 6377.397155 / 1.852, + 299.1528 + }, { + "WGS72", + 6378.135 / 1.852, + 298.26 + }, { + "WGS66", + 6378.145 / 1.852, + 298.25 + }, { + "FAI sphere", + 6371.0 / 1.852, + 1000000000. + } +}; + + +/* + * math helper + */ +#if 0 +double atan2(double y, double x) +{ +double out=0.; + + if (x < 0) { + out = atan(y/x) + M_PI; + } else + if ((x > 0) && (y >= 0)) { + out = atan(y/x); + } else + if ((x > 0) && (y < 0)) { + out = atan(y/x) + 2. * M_PI; + } else + if ((x == 0) && (y > 0)) { + out = M_PI / 2.; + } else + if ((x == 0) && (y < 0)) { + out = 3. * M_PI / 2.; + } else + if ((x == 0) && (y == 0)) { + out= 0.; + } + +return out; +} +#endif + +/* + * glat1, glat2: geodetic latitude in radians, N positive + * glon1, glon2: geodetic longitude in radians E positive + * Coordinates in radians means + * 50°50.32N ^= 50 degree, 50 minutes and 32 seconds + * ^= 50 + (50 / 60) + (32 / 3600) + * ^= 50.84222 + * + * returns: distance in nm (nautical miles), factor of 1.852 gives km + */ +double dist_ell(double glat1, double glon1, double glat2, double glon2, EllipsoidType e_model) +{ +double a = ellipse[e_model].a; // 6378.137/1.852 +double invf = ellipse[e_model].invf; // 298.257223563 +double f = 1. / invf; +double r, tu1, tu2, cu1, su1, cu2, s1, b1, f1; +double x, sx, cx, sy=0, cy=0, y=0, sa, c2a=0, cz=0, e=0, c, d; +double EPS = 0.00000000005; +double s; +double l_iter=1; +double MAXITER=100; + +#ifdef DEBUG + printf(" calc dist from %.04lf %.04lf to %.04lf %.04lf\n", glat1, glon1, glat2, glon2); +#endif + glat1 = (M_PI / 180.) * glat1; + glon1 = (M_PI / 180.) * -1. * glon1; + glat2 = (M_PI / 180.) * glat2; + glon2 = (M_PI / 180.) * -1. * glon2; + +#ifdef DEBUG + printf("Using ellipse model %s (%.04lf %.04lf)\n", ellipse[e_model].name, a, invf); + printf(" calc dist from %.04lf %.04lf to %.04lf %.04lf\n", glat1, glon1, glat2, glon2); +#endif + + if ((glat1+glat2==0.) && (fabs(glon1-glon2)==M_PI)) { +#ifdef DEBUG + printf("Warning: Course and distance between antipodal points is undefined\n"); +#endif + glat1=glat1+0.00001; // allow algorithm to complete + } + + if (glat1==glat2 && (glon1==glon2 || fabs(fabs(glon1-glon2)-2*M_PI) < EPS)) { +#ifdef DEBUG + printf("Points 1 and 2 are identical- course undefined\n"); +#endif + return 0; + } + + r = 1. - f; + tu1 = r * tan (glat1); + tu2 = r * tan (glat2); + cu1 = 1. / sqrt (1. + tu1 * tu1); + su1 = cu1 * tu1; + cu2 = 1. / sqrt (1. + tu2 * tu2); + s1 = cu1 * cu2; + b1 = s1 * tu2; + f1 = b1 * tu1; + x = glon2 - glon1; + d = x + 1.; // force one pass + + while ((fabs(d - x) > EPS) && (l_iter < MAXITER)) { + l_iter = l_iter + 1; + sx = sin (x); + cx = cos (x); + tu1 = cu2 * sx; + tu2 = b1 - su1 * cu2 * cx; + sy = sqrt (tu1 * tu1 + tu2 * tu2); + cy = s1 * cx + f1; + y = atan2 (sy, cy); + sa = s1 * sx / sy; + c2a = 1. - sa * sa; + cz = f1 + f1; + if (c2a > 0.) + cz = cy - cz / c2a; + e = cz * cz * 2. - 1.; + c = ((-3. * c2a + 4.) * f + 4.) * c2a * f / 16.; + d = x; + x = ((e * cy * c + cz) * sy * c + y) * sa; + x = (1. - c) * x * f + glon2 - glon1; + } + + x = sqrt ((1. / (r * r) - 1) * c2a + 1.); + x += 1.; + x = (x - 2.) / x; + c = 1. - x; + c = (x * x / 4. + 1.) / c; + d = (0.375 * x * x - 1.) * x; + x = e * cy; + s = ((((sy*sy*4.-3.)*(1.-e-e)*cz*d/6.-x)*d/4.+cz)*sy*d+y)*c*a*r; +#ifdef DEBUG + if (fabs (l_iter - MAXITER) < EPS) { + printf ("Algorithm did not converge\n"); + } else + fprintf(stderr,"%s = %.4f\n", __FUNCTION__, s); +#endif + +return s; +} diff --git a/geo-dist.h b/geo-dist.h new file mode 100644 index 0000000..f31e525 --- /dev/null +++ b/geo-dist.h @@ -0,0 +1,66 @@ +/* + * + * geo-dist + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef _GEO_DIST_H +#define _GEO_DIST_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* + * Calculate distances on Earth surface given geographic locations + */ + +typedef enum { + WGS84, + NAD27, + International, + Krasovsky, + Bessel, + WGS72, + WGS66, + FAIsphere +} EllipsoidType; + +/* nautical in kilometer */ +#define NAUTICAL_MILE 1.852 + +/* + * glat1, glat2: geodetic latitude in radians, N positive + * glon1, glon2: geodetic longitude in radians E positive + * Coordinates in radians means + * 50°50.32N ^= 50 degree, 50 minutes and 32 seconds + * ^= 50 + (50 / 60) + (32 / 3600) + * ^= 50.84222 + * + * returns: distance in nm (nautical miles), factor of 1.852 gives km + */ +double dist_ell(double, double, double, double, EllipsoidType); + +#ifdef __cplusplus +} +#endif + +#endif /* _GEO_DIST_H */ diff --git a/geo-tools.h b/geo-tools.h new file mode 100644 index 0000000..87e858e --- /dev/null +++ b/geo-tools.h @@ -0,0 +1,133 @@ +/* + * geo-tools.h + * + * Copyright (C) 2006,2007 Nils Faerber + * + * parts taken from Maemo-Mapper (GPL) + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef _GEO_TOOLS_H +#define _GEO_TOOLS_H + +/* from GNU glibc math.h - since we do not need __USE_GNU else + * we copy it here + */ +#ifndef M_PIl +#define M_PIl 3.1415926535897932384626433832795029L +#endif + +/* + * the following unit-latlpon conversions are + * shamelessly stolen^Wborrowed from Maemo-Mapper ;) + * ...and will be returned in time + * Some minor modifications have been done... + */ + +#define pixel2unit(pixel) ((pixel) << _zoom) +#define unit2pixel(pixel) ((pixel) >> _zoom) + + +/* use 31 bits for best accuracy */ +#define WORLD_SIZE_UNITS (1 << 30 /*26*/) +#define MERCATOR_SPAN (-6.28318377773622f) +#define MERCATOR_TOP (3.14159188886811f) +#define latlon2unit(lat, lon, unitx, unity) { \ + gdouble tmp; \ + unitx = (lon + 180.f) * (WORLD_SIZE_UNITS / 360.f) + 0.5f; \ + tmp = sin(lat * (M_PIl / 180.f)); \ + unity = 0.5f + (WORLD_SIZE_UNITS / MERCATOR_SPAN) \ + * (log((1.f + tmp) / (1.f - tmp)) * 0.5f - MERCATOR_TOP); \ +} + +#define unit2latlon(unitx, unity, lat, lon) { \ + (lon) = ((unitx) * (360.f / WORLD_SIZE_UNITS)) - 180.f; \ + (lat) = (360.f * (atan(exp(((unity) \ + * (MERCATOR_SPAN / WORLD_SIZE_UNITS)) \ + + MERCATOR_TOP)))) * (1.f / M_PIl) - 90.f; \ +} + +#define MACRO_RECALC_OFFSET() { \ + _offsetx = grid2pixel( \ + unit2grid(_center.unitx) \ + - _screen_grids_halfwidth \ + - tile2grid(_base_tilex)); \ + _offsety = grid2pixel( \ + unit2grid(_center.unity) \ + - _screen_grids_halfheight \ + - tile2grid(_base_tiley)); \ +} + +#define MACRO_RECALC_FOCUS_BASE() { \ + _focus.unitx = x2unit(_screen_width_pixels * _center_ratio / 20); \ + _focus.unity = y2unit(_screen_height_pixels * _center_ratio / 20); \ +} + +#define MACRO_RECALC_FOCUS_SIZE() { \ + _focus_unitwidth = pixel2unit( \ + (10 - _center_ratio) * _screen_width_pixels / 10); \ + _focus_unitheight = pixel2unit( \ + (10 - _center_ratio) * _screen_height_pixels / 10); \ +} + +#define MACRO_RECALC_CENTER_BOUNDS() { \ + _min_center.unitx = pixel2unit(grid2pixel(_screen_grids_halfwidth)); \ + _min_center.unity = pixel2unit(grid2pixel(_screen_grids_halfheight)); \ + _max_center.unitx = WORLD_SIZE_UNITS-grid2unit(_screen_grids_halfwidth) - 1; \ + _max_center.unity = WORLD_SIZE_UNITS-grid2unit(_screen_grids_halfheight)- 1; \ +} + +#define MACRO_INIT_TRACK(track) { \ + (track).head = (track).tail = g_new(TrackPoint, ARRAY_CHUNK_SIZE); \ + (track).tail->unitx = (track).tail->unity = 0; \ + (track).cap = (track).head + ARRAY_CHUNK_SIZE; \ + (track).whead = g_new(WayPoint, ARRAY_CHUNK_SIZE); \ + (track).wtail = (track).whead - 1; \ + (track).wcap = (track).whead + ARRAY_CHUNK_SIZE; \ +} + +#define MACRO_CLEAR_TRACK(track) if((track).head) { \ + WayPoint *curr; \ + g_free((track).head); \ + (track).head = (track).tail = (track).cap = NULL; \ + for(curr = (track).whead - 1; curr++ != (track).wtail; ) \ + g_free(curr->desc); \ + g_free((track).whead); \ + (track).whead = (track).wtail = (track).wcap = NULL; \ +} + +#define DISTANCE_ROUGH(point) \ + (abs((gint)((point).unitx) - _pos.unitx) \ + + abs((gint)((point).unity) - _pos.unity)) + +#define MACRO_QUEUE_DRAW_AREA() \ + gtk_widget_queue_draw_area( \ + _map_widget, \ + 0, 0, \ + _screen_width_pixels, \ + _screen_height_pixels) + +#define KEEP_DISPLAY_ON() { \ + /* Note that the flag means keep on ONLY when fullscreen. */ \ + if(_always_keep_on || _fullscreen) \ + { \ + osso_display_state_on(_osso); \ + osso_display_blanking_pause(_osso); \ + } \ +} + +#endif diff --git a/mappix-fileio.c b/mappix-fileio.c new file mode 100644 index 0000000..117216e --- /dev/null +++ b/mappix-fileio.c @@ -0,0 +1,134 @@ +/* + * + * mappix-fileio + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include +#include + +#include "mappix.h" +#include "geo-tools.h" + + +static void +write_trackpoint(gpointer data, gpointer user_data) +{ +trackdata *tdata = (trackdata *)data; +FILE *mfile = (FILE *)user_data; +gfloat llat, llong; +gchar dstr[G_ASCII_DTOSTR_BUF_SIZE]; + + if (tdata != NULL) { + g_fprintf(mfile, "unitx, tdata->unity, llat, llong); + g_ascii_dtostr(dstr, G_ASCII_DTOSTR_BUF_SIZE, llat); + g_fprintf(mfile, "lat=\"%s\" ", dstr); + g_ascii_dtostr(dstr, G_ASCII_DTOSTR_BUF_SIZE, llong); + g_fprintf(mfile, "long=\"%s\">\n", dstr); + g_fprintf(mfile, "%.6f\n", tdata->elevation); +#if 0 +// if (tdata->datetime != NULL) +// g_fprintf(mfile, "\n", tdata->datetime); + g_fprintf(mfile, "%d\n", tdata->nrsat); + g_fprintf(mfile, "%f\n", tdata->hdop); + g_fprintf(mfile, "%f\n", tdata->vdop); + g_fprintf(mfile, "%f\n", tdata->pdop); +#endif + g_fprintf(mfile, "\n"); + } +} + + +static void +write_track(gpointer data, gpointer user_data) +{ +track_head *thead = (track_head *)data; +FILE *mfile = (FILE *)user_data; +gfloat minlat, minlon, maxlat, maxlon; + + if (thead != NULL) { + g_fprintf(mfile, "\n"); + g_fprintf(mfile, "%s\n", thead->name); + g_fprintf(mfile, "%d\n", thead->type); /* NOT GPX CONFORMANT */ + g_fprintf(mfile, "%s\n", thead->show ? "true" : "false"); /* NOT GPX CONFORMANT */ + unit2latlon(thead->xmin, thead->ymin, minlat, minlon); + unit2latlon(thead->xmax, thead->ymax, maxlat, maxlon); + g_fprintf(mfile, "\n", minlat, minlon, maxlat, maxlon); + g_fprintf(mfile, "\n"); + if (thead->track != NULL) + g_list_foreach(thead->track, write_trackpoint, mfile); + g_fprintf(mfile, "\n"); + g_fprintf(mfile, "\n"); + } +} + + +static void +write_waypoint(gpointer data, gpointer user_data) +{ +waypoint *tdata = (waypoint *)data; +FILE *mfile = (FILE *)user_data; +gfloat llat, llong; +gchar dstr[G_ASCII_DTOSTR_BUF_SIZE]; + + if (tdata != NULL) { + g_fprintf(mfile, "unitx, tdata->unity, llat, llong); + g_ascii_dtostr(dstr, G_ASCII_DTOSTR_BUF_SIZE, llat); + g_fprintf(mfile, "lat=\"%s\" ", dstr); + g_ascii_dtostr(dstr, G_ASCII_DTOSTR_BUF_SIZE, llong); + g_fprintf(mfile, "long=\"%s\">\n", dstr); + g_fprintf(mfile, "%s\n", tdata->name); + g_fprintf(mfile, "%.6f\n", tdata->elevation); +#if 0 // not yet used + if (tdata->datetime != NULL) + g_fprintf(mfile, "\n", tdata->datetime); + g_fprintf(mfile, "%d\n", tdata->nrsat); + g_fprintf(mfile, "%f\n", tdata->hdop); + g_fprintf(mfile, "%f\n", tdata->vdop); + g_fprintf(mfile, "%f\n", tdata->pdop); +#endif + g_fprintf(mfile, "\n"); + } +} + + +gint +save_project_to_file(gchar *filename, mapwin *Mapper) +{ +FILE *mfile; + + mfile = g_fopen(filename, "w"); + + g_fprintf(mfile, "\n\n"); + if (Mapper->tracklist) + g_list_foreach(Mapper->tracklist, write_track, mfile); + if (Mapper->waypoints) + g_list_foreach(Mapper->waypoints, write_waypoint, mfile); + g_fprintf(mfile, "\n"); + + fclose(mfile); + +return 0; +} + diff --git a/mappix-fileio.h b/mappix-fileio.h new file mode 100644 index 0000000..b47c440 --- /dev/null +++ b/mappix-fileio.h @@ -0,0 +1,24 @@ +/* + * + * mappix-fileio + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +gint save_project_to_file(gchar *, mapwin *); diff --git a/mappix-gpxreader.c b/mappix-gpxreader.c new file mode 100644 index 0000000..5362b77 --- /dev/null +++ b/mappix-gpxreader.c @@ -0,0 +1,344 @@ +/* + * gpxreader + * read GPX files + * + * Copyright (C) 2006, 2007 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include +#include +#include + +#include +#include + +#include "mappix.h" + +#include "geo-tools.h" + +#define BUFFSIZE 8192 + + +/* oh well ... we have to remember in which tag we are... */ +#define CurrentIsTrackMask (guint)0x000000ff +#define CurrentIsTrack (guint)1 << 0 +#define CurrentIsTrackName (guint)1 << 1 +#define CurrentIsTrackType (guint)1 << 2 +#define CurrentIsTrackShow (guint)1 << 3 +#define CurrentIsTrackpointMask (guint)0x0000ff00 +#define CurrentIsTrackpoint (guint)1 << 8 +#define CurrentIsTrackpointEle (guint)1 << 9 +#define CurrentIsTrackpointNRSAT (guint)1 << 10 +#define CurrentIsTrackpointHDOP (guint)1 << 11 +#define CurrentIsTrackpointPDOP (guint)1 << 12 +#define CurrentIsWaypointMask (guint)0x00ff0000 +#define CurrentIsWaypoint (guint)1 << 16 +#define CurrentIsWaypointEle (guint)1 << 17 +#define CurrentIsWaypointName (guint)1 << 18 + +/* for passing data around the callbacks */ +typedef struct { + mapwin *Mapper; + guint current; + GList *track; + GList *waypoints; + trackdata *tdata; + waypoint *wpt; + gchar *fname; + gchar *tname; + gboolean show; + tracktype type; +} udt; + + +void finish_track(udt *ltdata) +{ +track_head *new_track; + + if (ltdata->track != NULL) { + /* done reading the track */ + new_track = (track_head *)malloc(sizeof(track_head)); + new_track->xmax = -1; + new_track->ymax = -1; + new_track->xmin = WORLD_SIZE_UNITS; + new_track->ymin = WORLD_SIZE_UNITS; + new_track->track = g_list_reverse(ltdata->track); + new_track->show = ltdata->show; + new_track->type = ltdata->type; + if (ltdata->tname != NULL) + new_track->name = g_strdup(ltdata->tname); + else + new_track->name = g_path_get_basename(ltdata->fname); + // fprintf(stderr,"track '%s' has bbox latmin=%lf longmin=%lf latmax=%lf longmax=%lf\n", new_track->name, new_track->latmin, new_track->longmin, new_track->latmax, new_track->longmax); + track_add_new_to_main(ltdata->Mapper, new_track); + } +} + + +static void XMLCALL +startElement(void *userData, const char *name, const char **atts) +{ +udt *ud = (udt *)userData; +gdouble rlat, rlong; + + if (strncmp(name, "trkpt", 5) == 0) { + ud->tdata = (trackdata *)malloc(sizeof(trackdata)); + memset(ud->tdata, 0, sizeof(trackdata)); + if (ud->tdata != NULL) { + ud->current |= CurrentIsTrackpoint; + rlat = g_ascii_strtod (atts[1], NULL); + rlong = g_ascii_strtod (atts[3], NULL); + latlon2unit(rlat, rlong, ud->tdata->unitx, ud->tdata->unity); + ud->tdata->selected = FALSE; + } + //fprintf(stderr,"lat=%lf long=%lf\n", tdata->latitude, tdata->longitude); + } else if (strncmp(name, "trkseg", 6) == 0) { + /* not yet supported */ + } else if (strncmp(name, "trk", 3) == 0) { + // g_message("new track"); + ud->current |= CurrentIsTrack; + if (ud->tname != NULL) { + g_free(ud->tname); + ud->tname = NULL; + }; + ud->type = UNKNOWN; + ud->show = FALSE; + } else if (strncmp(name, "wpt", 3) == 0) { + ud->wpt = (waypoint *)malloc(sizeof(waypoint)); + memset(ud->wpt, 0, sizeof(waypoint)); + if (ud->wpt != NULL) { + ud->current |= CurrentIsWaypoint; + rlat = g_ascii_strtod (atts[1], NULL); + rlong = g_ascii_strtod (atts[3], NULL); + latlon2unit(rlat, rlong, ud->wpt->unitx, ud->wpt->unity); + ud->wpt->name = NULL; + } + } else { + // fprintf(stderr,"xml unkn: '%s'\n", name); + if (ud->current & CurrentIsTrackpoint) { + //fprintf(stderr, "is trackpoint\n"); + if (strncmp(name, "ele", 3) == 0) + ud->current |= CurrentIsTrackpointEle; + if (strncmp(name, "sat", 3) == 0) + ud->current |= CurrentIsTrackpointNRSAT; + if (strncmp(name, "hdop", 4) == 0) + ud->current |= CurrentIsTrackpointHDOP; + if (strncmp(name, "pdop", 4) == 0) + ud->current |= CurrentIsTrackpointPDOP; + } else if (ud->current & CurrentIsWaypoint) { + //fprintf(stderr, "is waypoint\n"); + if (strncmp(name, "name", 4) == 0) + ud->current |= CurrentIsWaypointName; + if (strncmp(name, "ele", 3) == 0) + ud->current |= CurrentIsWaypointEle; + } else if (ud->current & CurrentIsTrack) { + //fprintf(stderr, "is track\n"); + if (strncmp(name, "name", 4) == 0) { + ud->current |= CurrentIsTrackName; + //fprintf(stderr,"name start\n"); + } + if (strncmp(name, "type", 4) == 0) { + ud->current |= CurrentIsTrackType; + //fprintf(stderr,"type start\n"); + } + if (strncmp(name, "show", 4) == 0) { + ud->current |= CurrentIsTrackShow; + //fprintf(stderr,"show start\n"); + } + } + } +} + + +static void XMLCALL +endElement(void *userData, const char *name) +{ +udt *ud = (udt *)userData; +//mapwin *Mapper = (mapwin *)userData; + + //fprintf(stderr,"xml endelem: '%s'\n", name); + + /* trackpoints */ + if (strncmp(name, "trkpt", 5) == 0) { + ud->current &= ~CurrentIsTrackpointMask; + ud->track = g_list_prepend(ud->track, ud->tdata); + } else if (strncmp(name, "trkseg", 6) == 0) { + } else if (strncmp(name, "trk", 3) == 0) { + finish_track(ud); + ud->current = 0; + ud->track = NULL; + ud->current &= ~CurrentIsTrackMask; + } + + if (ud->current & CurrentIsTrackName) { + if (strncmp(name, "name", 4) == 0) { + //fprintf(stderr, "bnamee 0x%04x ", ud->current); + ud->current ^= CurrentIsTrackName; + //fprintf(stderr, "anamee 0x%04x\n", ud->current); + } + } + + if (ud->current & CurrentIsTrackpoint) { + if (strncmp(name, "ele", 3) == 0) + ud->current ^= CurrentIsTrackpointEle; + } + + /* waypoints */ + if (strncmp(name, "wpt", 3) == 0) { + ud->current &= ~CurrentIsWaypointMask; + //ud->Mapper->waypoints = g_list_prepend(ud->Mapper->waypoints, ud->wpt); + ud->waypoints = g_list_prepend(ud->waypoints, ud->wpt); + } + if (ud->current & CurrentIsWaypoint) { + if (strncmp(name, "name", 4) == 0) + ud->current ^= CurrentIsWaypointName; + } + + if (ud->current & CurrentIsTrack) { + if (CurrentIsTrackType && strncmp(name, "type", 4) == 0) + ud->current ^= CurrentIsTrackType; + if (CurrentIsTrackShow && strncmp(name, "show", 4) == 0) + ud->current ^= CurrentIsTrackShow; + } +} + + +static void XMLCALL +CharData (void *userData, const XML_Char *s, int len) +{ +udt *ud = (udt *)userData; +gchar *tmp; + + /* string is not null terminated */ + tmp = (gchar *)malloc(2+len*sizeof(gchar)); + memcpy(tmp,s,len); + tmp[len] = 0; + // fprintf(stderr,"char-data: '%s'\n", tmp); + + /* track head */ + if (ud->current & CurrentIsTrackName) { + if (ud->tname == NULL) + ud->tname = g_strdup(tmp); + else + ud->tname = g_strconcat(ud->tname, tmp, NULL); + } + if (ud->current & CurrentIsTrackType) { + //fprintf(stderr, "type = '%s'\n", tmp); + gint val; + sscanf(tmp, "%d", &val); + ud->type = val; + } + if (ud->current & CurrentIsTrackShow) { + //fprintf(stderr, "show = '%s'\n", tmp); + if (len > 0 && g_ascii_strncasecmp("true", tmp, 4)==0 ) + ud->show = TRUE; + else + ud->show = FALSE; + } + + /* trackpoints */ + if (ud->current & CurrentIsTrackpointEle) { + sscanf(tmp, "%f", &ud->tdata->elevation); + } + if (ud->current & CurrentIsTrackpointNRSAT) { + gint val; + sscanf(tmp, "%d", &val); + ud->tdata->nrsat = val; + } + if (ud->current & CurrentIsTrackpointHDOP) { + sscanf(tmp, "%f", &ud->tdata->hdop); + } + if (ud->current & CurrentIsTrackpointPDOP) { + sscanf(tmp, "%f", &ud->tdata->pdop); + } + + /* waypoints */ + if (ud->current & CurrentIsWaypointName) { + if (ud->wpt->name == NULL) + ud->wpt->name = g_strdup(tmp); + else + ud->wpt->name = g_strconcat(ud->wpt->name, tmp, NULL); + } + if (ud->current & CurrentIsWaypointEle) { + sscanf(tmp, "%f", &ud->wpt->elevation); + } + + free(tmp); +} + +#ifdef DEBUG_WP +void print_waypoints(gpointer data, gpointer user_data) +{ +waypoint *wpnt = (waypoint *)data; + + if (data != NULL) { + fprintf(stderr,"wpt: lat=%lf long=%lf name='%s'\n", wpnt->unitx, wpnt->unity, wpnt->name); + } +} +#endif + + +gint gpxread(gchar *fname, mapwin *Mapper) +{ +char buf[BUFFSIZE]; +XML_Parser parser = XML_ParserCreate(NULL); +int done; +FILE *inpfile; +udt ldata; +// track_head *new_track; + + inpfile=fopen(fname, "r"); + if (inpfile == NULL) + return -1; + + ldata.fname = fname; + ldata.current = 0; + ldata.Mapper = Mapper; + ldata.track = NULL; + ldata.tname = NULL; + ldata.waypoints = NULL; + XML_SetUserData(parser, &ldata); + XML_SetElementHandler(parser, startElement, endElement); + XML_SetCharacterDataHandler(parser, CharData); + + do { + size_t len = fread(buf, 1, sizeof(buf), inpfile); + done = len < sizeof(buf); + if (XML_Parse(parser, buf, len, done) == XML_STATUS_ERROR) { + fprintf(stderr, "%s at line %d\n", XML_ErrorString(XML_GetErrorCode(parser)), XML_GetCurrentLineNumber(parser)); + return -1; + } + } while (!done); + + XML_ParserFree (parser); + fclose (inpfile); + + if (ldata.waypoints) { + ldata.waypoints = g_list_reverse(ldata.waypoints); + Mapper->waypoints = g_list_concat(Mapper->waypoints, ldata.waypoints); + } +#ifdef DEBUG_WP + if (Mapper->waypoints != NULL) { + g_list_foreach(Mapper->waypoints, print_waypoints, NULL); + } +#endif + +return 0; +} diff --git a/mappix-gpxreader.h b/mappix-gpxreader.h new file mode 100644 index 0000000..31b0aa6 --- /dev/null +++ b/mappix-gpxreader.h @@ -0,0 +1,37 @@ +/* + * mappix-gpxreader.h + * + * 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. + */ + +#ifndef _MAPPIX_GPXREADER_H +#define _MAPPIX_GPXREADER_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +gint gpxread(gchar *, mapwin *); + + +#ifdef __cplusplus +} +#endif + +#endif /* _MAPPIX_GPXREADER_H */ diff --git a/mappix-trackdraw.c b/mappix-trackdraw.c new file mode 100644 index 0000000..51d16b9 --- /dev/null +++ b/mappix-trackdraw.c @@ -0,0 +1,744 @@ +/* + * trackdraw + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include + +#include +#include + +#include "mappix.h" +#include "geo-tools.h" +#include "geo-dist.h" + + + +void +draw_waypoint_label (mapwin *Mapper, gint x, gint y, gchar *text_label) +{ +PangoLayout *playout; +GdkColor fgc, bgc; + + playout = gtk_widget_create_pango_layout (Mapper->drawarea, text_label); + gdk_color_parse("black", &fgc); + gdk_color_parse("yellow", &bgc); + gdk_draw_layout_with_colors(Mapper->mappix, Mapper->drawarea->style->black_gc, x+10, y-20, playout, &fgc, &bgc); + gdk_gc_set_line_attributes(Mapper->drawarea->style->black_gc, 0, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER); + gdk_draw_line(Mapper->mappix, Mapper->drawarea->style->black_gc, x+10, y-10, x, y); + gtk_widget_queue_draw(Mapper->drawarea); +} + + +static void +draw_city_name(mapwin *Mapper, gchar *city_name, guint x, guint y) +{ +PangoLayout *playout; +GdkColor fgc; + + // fprintf(stderr, "city %s\n", city_name); + playout = gtk_widget_create_pango_layout (Mapper->drawarea, city_name); + gdk_color_parse("black", &fgc); + gdk_draw_layout_with_colors(Mapper->mappix, Mapper->drawarea->style->black_gc, x, y, playout, &fgc, NULL); + gtk_widget_queue_draw(Mapper->drawarea); +} + + +static int +city_db_callback(void *user_data, int argc, char **argv, char **azColName) +{ +mapwin *Mapper = (mapwin *)user_data; +double lat, lon; +guint unitx, unity; + + // str argv[0] + lat = g_ascii_strtod(argv[1] , NULL); + lon = g_ascii_strtod(argv[2] , NULL); + latlon2unit(lat, lon, unitx, unity); + unitx = (unitx - Mapper->xoffset) * Mapper->scale; + unity = (unity - Mapper->yoffset) * Mapper->scale; + draw_city_name(Mapper, argv[0], unitx, unity); + +return 0; +} + + +void +draw_city_names(mapwin *Mapper) +{ +sqlite3 *db; + + /* re-scan for cities */ + if (Mapper->show_cities && !sqlite3_open("cities.db", &db)) { + char *zErrMsg = 0; + gchar sql[256]; + double lat_min, lon_min; + double lat_max, lon_max; + gchar lamax[32], lamin[32], lomax[32], lomin[32]; + + unit2latlon(Mapper->xmin, Mapper->ymin, lat_min, lon_min); + unit2latlon(Mapper->xmax, Mapper->ymax, lat_max, lon_max); + g_ascii_dtostr(lamax, 32, lat_max); + g_ascii_dtostr(lamin, 32, lat_min); + g_ascii_dtostr(lomax, 32, lon_max); + g_ascii_dtostr(lomin, 32, lon_min); + g_sprintf(sql, "SELECT city, lat, long FROM staedte WHERE lat > %s AND lat < %s AND long > %s AND long < %s;", + lamax, lamin, lomin, lomax); + sqlite3_exec(db, sql, city_db_callback, Mapper, &zErrMsg); + // fprintf(stderr, "sql: '%s'\n", sql); + sqlite3_close(db); + } +} + + +void +draw_scale(mapwin *Mapper) +{ +PangoLayout *playout; +GdkColor fgc; +guint x1,x2,y; +double lat1, lat2, lon1, lon2, dist; +gchar scale_text[64]; + + // assuming we have 50px in the lower right corner + // fprintf(stderr, "city %s\n", city_name); + x1 = Mapper->xoffset; + x2 = Mapper->xoffset + (100. / Mapper->scale); + y = Mapper->yoffset; + unit2latlon(x1, y, lat1, lon1); + unit2latlon(x2, y, lat2, lon2); + //fprintf(stderr, "x=%d x2=%d y=%d lat1=%lf lat2=%lf lon1=%lf lon2=%lf\n", x1,x2,y,lat1,lat2,lon1,lon2); + + dist = dist_ell(lat1, lon1, lat2, lon2, WGS84) * NAUTICAL_MILE; + if (dist > 1.0) + g_snprintf(scale_text, 64, "%.02lfkm", dist); + else { + g_snprintf(scale_text, 64, "%.00lfm", dist*100.); + } + + playout = gtk_widget_create_pango_layout (Mapper->drawarea, scale_text); + gdk_color_parse("black", &fgc); + gdk_draw_layout_with_colors(Mapper->mappix, Mapper->drawarea->style->black_gc, Mapper->canvas_x - 80, Mapper->canvas_y - 20, playout, &fgc, NULL); + gdk_gc_set_line_attributes(Mapper->drawarea->style->black_gc, 0, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER); + gdk_draw_line(Mapper->mappix, Mapper->drawarea->style->black_gc, Mapper->canvas_x-110, Mapper->canvas_y - 20, Mapper->canvas_x-10, Mapper->canvas_y - 20); + gdk_draw_line(Mapper->mappix, Mapper->drawarea->style->black_gc, Mapper->canvas_x-110, Mapper->canvas_y - 25, Mapper->canvas_x-110, Mapper->canvas_y - 15); + gdk_draw_line(Mapper->mappix, Mapper->drawarea->style->black_gc, Mapper->canvas_x-10, Mapper->canvas_y - 25, Mapper->canvas_x-10, Mapper->canvas_y - 15); + + for (x1 = (Mapper->canvas_x-100); x1 < (Mapper->canvas_x-10); x1+=10) + gdk_draw_line(Mapper->mappix, Mapper->drawarea->style->black_gc, x1, Mapper->canvas_y - 25, x1, Mapper->canvas_y - 20); + + gtk_widget_queue_draw(Mapper->drawarea); +} + + +void +do_redraw(mapwin *Mapper) +{ + draw_tracks(Mapper); + if (Mapper->show_cities) + draw_city_names(Mapper); + if (Mapper->show_waypoints) + draw_waypoints(Mapper); + if (Mapper->show_scale) + draw_scale(Mapper); + gtk_widget_queue_draw(Mapper->drawarea); +} + + +void +change_scale(mapwin *Mapper, double new_scale) +{ + Mapper->scale = new_scale; + + do_redraw(Mapper); +} + + +void +zoom_in_cb (GtkSpinButton *widget, gpointer user_data) +{ +mapwin *Mapper = (mapwin *)user_data; +gdouble scale_inc; +guint x,y; + + scale_inc = Mapper->scale / 10.; + x = (Mapper->canvas_x / 2) / Mapper->scale; + y = (Mapper->canvas_y / 2) / Mapper->scale; + Mapper->scale += scale_inc; + Mapper->xoffset += x - ((Mapper->canvas_x / 2) / Mapper->scale); + Mapper->yoffset += y - ((Mapper->canvas_y / 2) / Mapper->scale); + Mapper->xmin = Mapper->xoffset; + Mapper->ymin = Mapper->yoffset; + Mapper->xmax = Mapper->xmin + (Mapper->canvas_x / Mapper->scale); + Mapper->ymax = Mapper->ymin + (Mapper->canvas_y / Mapper->scale); + change_scale(Mapper, Mapper->scale); +} + + +void +zoom_out_cb (GtkSpinButton *widget, gpointer user_data) +{ +mapwin *Mapper = (mapwin *)user_data; +gdouble scale_inc; +guint x,y; + + scale_inc = Mapper->scale / 10.; + if (scale_inc < 0) + scale_inc = 0; + x = (Mapper->canvas_x / 2) / Mapper->scale; + y = (Mapper->canvas_y / 2) / Mapper->scale; + Mapper->scale -= scale_inc; + Mapper->xoffset += x - ((Mapper->canvas_x / 2) / Mapper->scale); + Mapper->yoffset += y - ((Mapper->canvas_y / 2) / Mapper->scale); + Mapper->xmin = Mapper->xoffset; + Mapper->ymin = Mapper->yoffset; + Mapper->xmax = Mapper->xmin + (Mapper->canvas_x / Mapper->scale); + Mapper->ymax = Mapper->ymin + (Mapper->canvas_y / Mapper->scale); + change_scale(Mapper, Mapper->scale); +} + + +void +zoom_area_cb (GtkRadioToolButton *widget, gpointer user_data) +{ +mapwin *Mapper = (mapwin *)user_data; + + Mapper->toolmode = MAPPIX_TOOL_ZOOM_REGION; +} + + +void +move_cb (GtkRadioToolButton *widget, gpointer user_data) +{ +mapwin *Mapper = (mapwin *)user_data; + + Mapper->toolmode = MAPPIX_TOOL_MOVE; +} + + +void +auto_scale (GtkButton *widget, gpointer user_data) +{ +mapwin *Mapper = (mapwin *)user_data; + + change_scale(Mapper, calc_scale(Mapper)); +} + + +void +draw_line(gint x1, gint y1, gint x2, gint y2, gint linewidth, gpointer user_data) +{ +mapwin *Mapper = (mapwin *)user_data; + + gdk_gc_set_line_attributes(Mapper->drawarea->style->black_gc, linewidth, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_MITER); + gdk_draw_line(Mapper->mappix, Mapper->drawarea->style->black_gc, x1, y1, x2, y2); +} + + +void +draw_marker(gint x1, gint y1, gint psize, gint marker_type, gpointer user_data) +{ +mapwin *Mapper = (mapwin *)user_data; +GdkGC *marker_gc; + + if (!Mapper->show_marker) + return; + if (marker_type & MARKER_SELECTED) { + marker_gc = Mapper->blue_gc; + marker_type &= ~MARKER_SELECTED; + } else + marker_gc = Mapper->red_gc; + if (marker_type != MARKER_TYPE_NONE) { + if (marker_type == MARKER_TYPE_FILLED_SQUARE) { + gdk_draw_rectangle(Mapper->mappix, marker_gc, TRUE, x1 - (psize/2), y1 - (psize/2), psize, psize); + } else if (marker_type == MARKER_TYPE_SQUARE) { + gdk_draw_rectangle(Mapper->mappix, marker_gc, FALSE, x1 - (psize/2), y1 - (psize/2), psize, psize); + } else if (marker_type == MARKER_TYPE_CROSS) { + psize = psize / 2; + gdk_gc_set_line_attributes(marker_gc, 0 /*linewidth*/, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER); + gdk_draw_line(Mapper->mappix, marker_gc, x1-psize, y1-psize, x1+psize, y1+psize); + gdk_draw_line(Mapper->mappix, marker_gc, x1+psize, y1-psize, x1-psize, y1+psize); + } + } +} + +void +draw_markers(track_head *trackh, double bbxmin, double bbxmax, double bbymin, double bbymax, mapwin *Mapper, cairo_t *cr) +{ +GList *track = trackh->track; +trackdata *tdata; +GList *lp; +gboolean start_newline = TRUE; + + if (Mapper->show_marker && Mapper->scale > 0.0009) { + double ox, oy; + double angle; + + ox = 0.; oy = 0.; + cairo_set_line_width(cr, 1.5 / Mapper->scale); + for (lp = track; lp; lp=g_list_next(lp)) { + tdata = (trackdata *)lp->data; + if ((tdata->unitx < bbxmin || tdata->unitx > bbxmax) || + (tdata->unity < bbymin || tdata->unity > bbymax)) { + start_newline = TRUE; + } else { + if (start_newline) { + ox = (double)tdata->unitx; + oy = (double)tdata->unity; + } else { + angle = -1. * atan2((double)tdata->unitx - ox, + (double)tdata->unity - oy); + cairo_move_to(cr, (double)tdata->unitx, (double)tdata->unity); + /* draw marker arrow */ + cairo_save (cr); + if (tdata->selected) + cairo_set_source_rgb (cr, .0,.0,.9); + else + cairo_set_source_rgb (cr, .0,.0,.0); + // cairo_set_line_width(cr, 1.5 / Mapper->scale); + cairo_rotate(cr, angle); + cairo_rel_move_to (cr, -5./ Mapper->scale, -5./ Mapper->scale); + cairo_rel_line_to (cr, 5./ Mapper->scale,5. / Mapper->scale); + cairo_rel_line_to (cr, 5./ Mapper->scale,-5. / Mapper->scale); + cairo_stroke (cr); + cairo_restore (cr); + ox = (double)tdata->unitx; + oy = (double)tdata->unity; + } + start_newline = FALSE; + } + } + } +} + +void +draw_line_with_marker(gint x1, gint y1, gint x2, gint y2, gint psize, gint linewidth, gint marker_type, gpointer user_data) +{ + draw_marker(x1, y1, psize, marker_type, user_data); + draw_marker(x2, y2, psize, marker_type, user_data); + draw_line(x1, y1, x2, y2, linewidth, user_data); +} + + +double +do_calc_scale(mapwin *Mapper) +{ +double xscale, yscale; +double scale; + + /* we calculate both scales and choose the smaller scale factor so that all data will fit */ + xscale = Mapper->canvas_x / (gdouble)(Mapper->xmax - Mapper->xmin); + yscale = Mapper->canvas_y / (gdouble)(Mapper->ymax - Mapper->ymin); + + if (xscale < yscale) + scale = xscale; + else + scale = yscale; + + /* this should bring the tracks to the lower left corner */ + Mapper->xoffset = Mapper->xmin; + Mapper->yoffset = Mapper->ymin; + + // g_message("xmax=%d ymax=%d xmin=%d ymax=%d xscale=%f yscale=%f scale=%f", Mapper->xmax, Mapper->ymax, Mapper->xmin, Mapper->ymin, Mapper->xscale, Mapper->yscale, Mapper->scale); + return scale; +} + + +double +calc_scale(mapwin *Mapper) +{ +trackdata *tdata; +track_head *thead; +GList *trackh, *trackp; + + // fprintf(stderr,"cs\n"); + Mapper->xmin=WORLD_SIZE_UNITS; + Mapper->xmax=0; + Mapper->ymin=WORLD_SIZE_UNITS; + Mapper->ymax=0; + for (trackh=Mapper->tracklist; trackh; trackh=g_list_next(trackh)) { + // fprintf(stderr," +cs\n"); + thead = (track_head *)trackh->data; + if (thead->show) { + for (trackp=thead->track; trackp; trackp=g_list_next(trackp)) { + tdata = (trackdata *)trackp->data; + // fprintf(stderr,"latmin=%lf latmax=%lf longmin=%lf longmax=%lf\n", latmin, latmax, longmin, longmax); + if (tdata->unitx < Mapper->xmin) + Mapper->xmin = tdata->unitx; + else if (tdata->unitx > Mapper->xmax) + Mapper->xmax = tdata->unitx; + if (tdata->unity < Mapper->ymin) + Mapper->ymin = tdata->unity; + else if (tdata->unity > Mapper->ymax) + Mapper->ymax = tdata->unity; + } + } + } + return do_calc_scale(Mapper); +} + + +void +draw_waypoints(mapwin *Mapper) +{ +GList *lp; +waypoint *wpdata; + + if (!Mapper->show_waypoints) + return; + for (lp = Mapper->waypoints; lp; lp=g_list_next(lp)) { + double x,y; + wpdata = (waypoint *)lp->data; + x = (wpdata->unitx - Mapper->xoffset) * Mapper->scale; + y = (wpdata->unity - Mapper->yoffset) * Mapper->scale; + // fprintf(stderr,"x=%lf y=%lf sc=%lf\n", tdata->longitude, tdata->latitude, Mapper->scale); + if (x < 0 || y < 0) + continue; + if (x > Mapper->canvas_x) + continue; + if (y > Mapper->canvas_y) + continue; + draw_waypoint_label (Mapper, x, y, wpdata->name); + } +} + + +#if 0 +static void +print_matrix(cairo_t *cr) +{ +cairo_matrix_t cmatrix; + + cairo_get_matrix(cr, &cmatrix); + fprintf(stderr, "xx=%lf xy=%lf x0=%lf\n", cmatrix.xx, cmatrix.xy, cmatrix.x0); + fprintf(stderr, "yx=%lf yy=%lf y0=%lf\n", cmatrix.yx, cmatrix.yy, cmatrix.y0); +} +#endif + +void +draw_track(track_head *trackh, gpointer user_data) +{ +mapwin *Mapper = (mapwin *)user_data; +GList *track = trackh->track; +trackdata *tdata; +gboolean start_newline = TRUE; +GList *lp; +cairo_t *cr; +cairo_matrix_t cmatrix; +double bbxmin, bbxmax; +double bbymin, bbymax; +// double TRACK_FACTOR = (1. / (2. * Mapper->scale)) + (100000. * Mapper->scale); +double TRACK_FACTOR = (4000. * (Mapper->scale * Mapper->scale)) * (1. / Mapper->scale); + + if (TRACK_FACTOR < (0.8 / Mapper->scale)) + TRACK_FACTOR = 0.8 / Mapper->scale; + if (track == NULL) + return; + cr = gdk_cairo_create (GDK_DRAWABLE(Mapper->mappix)); + + cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND); + cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); + + /* first set drawing for the outline of track or for non-outlined */ + if (Mapper->show_marker) { + cairo_set_source_rgb (cr, 0,0,0); + cairo_set_line_width(cr, 1.0 / Mapper->scale); + } else { + switch (trackh->type) { + case UNKNOWN: + case OUTLINE: /* always visible in 1.0pt */ + cairo_set_source_rgb (cr, 0,0,0); + cairo_set_line_width(cr, 1.0 / Mapper->scale); + break; + case FOOTPATH: + cairo_set_source_rgb (cr, 0,0,0); + cairo_set_line_width(cr, 1.0 * TRACK_FACTOR); + break; + case RIVER: + cairo_set_source_rgb (cr, 0,0,.9); + cairo_set_line_width(cr, 1.3 * TRACK_FACTOR); + break; + case AGRI: + cairo_set_source_rgb (cr, 0.4,0.4,0.4); + cairo_set_line_width(cr, 1. * TRACK_FACTOR); + break; + case CITY: + cairo_set_source_rgb (cr, .5,.5,.5); + cairo_set_line_width(cr, 1. * TRACK_FACTOR); + break; + case URBAN: + cairo_set_source_rgb (cr, .3,.3,.3); + cairo_set_line_width(cr, 1.5 * TRACK_FACTOR); + break; + case MOTORWAY: + cairo_set_source_rgb (cr, .3,.3,.3); + cairo_set_line_width(cr, 3.5 * TRACK_FACTOR); + break; + case HIGHWAY: + cairo_set_source_rgb (cr, .1,.1,.1); + cairo_set_line_width(cr, 4.5 * TRACK_FACTOR); + break; + default: + cairo_set_line_width(cr, 1.0 * TRACK_FACTOR); + break; + } + } + // fprintf(stderr, "scale is: %lf\n", Mapper->scale); + + cairo_matrix_init_identity(&cmatrix); + cairo_matrix_init_scale(&cmatrix, Mapper->scale, Mapper->scale); + cairo_matrix_translate(&cmatrix, -((double)Mapper->xoffset), + -((double)Mapper->yoffset)); + cairo_set_matrix(cr, &cmatrix); + + bbxmin = Mapper->xoffset; + bbxmax = bbxmin + ((double)Mapper->canvas_x / Mapper->scale); + bbymin = Mapper->yoffset; + bbymax = bbymin + ((double)Mapper->canvas_y / Mapper->scale); + // fprintf(stderr, "xl=%lf xh=%lf yl=%lf yh=%lf\n", bbxmin, bbxmax, bbymin, bbymax); + + for (lp = track; lp; lp=g_list_next(lp)) { + tdata = (trackdata *)lp->data; + if ((tdata->unitx < bbxmin || tdata->unitx > bbxmax) || + (tdata->unity < bbymin || tdata->unity > bbymax)) { + /* first finish this line, even if the target is outside */ + if (!start_newline) + cairo_line_to (cr, (double)tdata->unitx, (double)tdata->unity); + start_newline = TRUE; + } else { + //draw_line_with_marker(x, y, x2, y2, Mapper->marker_size, Mapper->linewidth, Mapper->marker_type, Mapper); + if (start_newline) { + // fprintf(stderr, "mt\n"); + cairo_move_to (cr, (double)tdata->unitx, (double)tdata->unity); + } else { + // fprintf(stderr, "lt\n"); + cairo_line_to (cr, (double)tdata->unitx, (double)tdata->unity); + } + start_newline = FALSE; + } + } + // fprintf(stderr, "xl=%lf xh=%lf yl=%lf yh=%lf\n", bbxmin, bbxmax, bbymin, bbymax); + // print_matrix(cr); + cairo_stroke (cr); + + if (!(Mapper->show_marker || trackh->type == UNKNOWN || trackh->type == OUTLINE + || trackh->type == RIVER || trackh->type == FOOTPATH + || trackh->type == AGRI)) { + cairo_set_source_rgb (cr, 0.7,0.7,0.7); + cairo_set_line_width(cr, (double)Mapper->linewidth / Mapper->scale); + + /* second set drawing for the inner colored track */ + switch (trackh->type) { + case CITY: + cairo_set_source_rgb (cr, 1.,1.,1.); + cairo_set_line_width(cr, .5 * TRACK_FACTOR); + break; + case URBAN: + cairo_set_source_rgb (cr, 1.,1.,.7); + cairo_set_line_width(cr, 1. * TRACK_FACTOR); + break; + case MOTORWAY: + cairo_set_source_rgb (cr, 1.,1.,.5); + cairo_set_line_width(cr, 2.5 * TRACK_FACTOR); + break; + case HIGHWAY: + cairo_set_source_rgb (cr, 9.,0.,0.); + cairo_set_line_width(cr, 3.5 * TRACK_FACTOR); + break; + default: + cairo_set_line_width(cr, 1.0 * TRACK_FACTOR); + break; + } + + for (lp = track; lp; lp=g_list_next(lp)) { + tdata = (trackdata *)lp->data; + // x = (tdata->unitx - Mapper->xoffset) * Mapper->scale; + // y = (tdata->unity - Mapper->yoffset) * Mapper->scale; + // fprintf(stderr,"x=%lf y=%lf sc=%lf\n", (double)tdata->unitx, (double)tdata->unity, Mapper->scale); + // fprintf(stderr,"x=%lf y=%lf\n", x, y); + if ((tdata->unitx < bbxmin || tdata->unitx > bbxmax) || + (tdata->unity < bbymin || tdata->unity > bbymax)) { + // fprintf(stderr, "nl\n"); + /* first finish this line, even if the target is outside */ + if (!start_newline) + cairo_line_to (cr, (double)tdata->unitx, (double)tdata->unity); + start_newline = TRUE; + } else { + //draw_line_with_marker(x, y, x2, y2, Mapper->marker_size, Mapper->linewidth, Mapper->marker_type, Mapper); + if (start_newline) { + // fprintf(stderr, "mt\n"); + cairo_move_to (cr, (double)tdata->unitx, (double)tdata->unity); + } else { + // fprintf(stderr, "lt\n"); + cairo_line_to (cr, (double)tdata->unitx, (double)tdata->unity); + } + start_newline = FALSE; + } + } + cairo_stroke (cr); + } + + if (Mapper->show_marker) + draw_markers(trackh, bbxmin, bbxmax, bbymin, bbymax, Mapper, cr); + + // fprintf(stderr, "xl=%lf xh=%lf yl=%lf yh=%lf\n", bbxmin, bbxmax, bbymin, bbymax); + // print_matrix(cr); + cairo_stroke (cr); + + cairo_destroy (cr); +} + + +static gboolean +draw_idle(gpointer user_data) +{ +mapwin *Mapper = (mapwin *)user_data; +static GList *track=NULL; +static track_head *trackh=NULL; +trackdata *tdata; +double x,y; +static double x2=-1,y2=-1; +static GList *lp=NULL; + + if (Mapper->tracklist == NULL) { + // fprintf(stderr,"tracklist==NULL -> end\n"); + /* erase drawing area */ + gdk_draw_rectangle(Mapper->mappix, Mapper->drawarea->style->white_gc, 1, 0, 0, Mapper->canvas_x, Mapper->canvas_y); + gtk_widget_queue_draw(Mapper->drawarea); + Mapper->draw_idle_active = FALSE; + track = NULL; + lp = NULL; + x2 = -1; + y2 = -1; + return FALSE; + } + + // start new drawing + if (!Mapper->draw_idle_active) { + // fprintf(stderr,"new drawing -> starting\n"); + Mapper->draw_idle_active = TRUE; + track = Mapper->tracklist; + /* erase drawing area */ + gdk_draw_rectangle(Mapper->mappix, Mapper->drawarea->style->white_gc, 1, 0, 0, Mapper->canvas_x, Mapper->canvas_y); + lp = NULL; + x2 = -1; y2 = -1; + } + + trackh=(track_head *)track->data; + 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 if (trackh->track != NULL && trackh->show) { + if (lp == NULL) + lp = trackh->track; + tdata = (trackdata *)lp->data; + x = (tdata->unitx - Mapper->xoffset) * Mapper->scale; + y = (tdata->unity - Mapper->yoffset) * Mapper->scale; + // fprintf(stderr,"x=%lf y=%lf sc=%lf\n", tdata->longitude, tdata->latitude, Mapper->scale); + /* if the next point is the same as the previous we do not see a line so we do not paint it */ + if ((guint)x != (guint)x2 || (guint)y != (guint)y2) { + if (x2 < 0 && y2 < 0) { + x2 = x; y2 = y; + draw_marker(x,y,Mapper->marker_size, Mapper->marker_type | (tdata->selected ? MARKER_SELECTED : 0), Mapper); + } else { + if ((x < 0 || x > Mapper->canvas_x) || (y < 0 || y > Mapper->canvas_y)) { + x2 = -1; y2 = -1; + } else { + draw_marker(x,y,Mapper->marker_size, Mapper->marker_type | (tdata->selected ? MARKER_SELECTED : 0), Mapper); + draw_line(x, y, x2, y2, Mapper->linewidth, Mapper); + x2 = x; + y2 = y; + } + } + } + } + + // OK, done with this track + lp = g_list_next(lp); + if (lp == NULL) { + // fprintf(stderr,"track end -> next track\n"); + gtk_widget_queue_draw(Mapper->drawarea); + track = g_list_next(track); + if (track != NULL) { + trackh = (track_head *)track->data; + lp = trackh->track; + x2 = -1; y2 = -1; + } + } + + if (track == NULL) { + // fprintf(stderr,"end of tracks -> end\n"); + draw_waypoints(Mapper); + Mapper->draw_idle_active = FALSE; + gtk_widget_queue_draw(Mapper->drawarea); + return FALSE; + } + +return TRUE; +} + + +void +draw_tracks(mapwin *Mapper) +{ + if (Mapper->use_idledraw) { + if (Mapper->draw_idle_active) { + Mapper->draw_idle_active = FALSE; + g_idle_remove_by_data(Mapper); + } + g_idle_add_full(G_PRIORITY_HIGH_IDLE + 20, draw_idle, Mapper, NULL); + } else { + GList *track; + track_head *trackh; + + /* erase drawing area */ + gdk_draw_rectangle(Mapper->mappix, Mapper->drawarea->style->white_gc, 1, 0, 0, Mapper->canvas_x, Mapper->canvas_y); + gtk_widget_queue_draw(Mapper->drawarea); + if (Mapper->tracklist != NULL) { + for (track=Mapper->tracklist; track; track=g_list_next(track)) { + trackh = (track_head *)track->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 + draw_track(trackh, Mapper); + } + } + } + } +} + + +void +draw_filename (gchar *fname, mapwin *Mapper) +{ + if (gpxread(fname, Mapper) < 0) + return; + draw_tracks(Mapper); + draw_city_names(Mapper); + draw_waypoints(Mapper); + gtk_widget_queue_draw(Mapper->drawarea); +} diff --git a/mappix-trackdraw.h b/mappix-trackdraw.h new file mode 100644 index 0000000..6726c34 --- /dev/null +++ b/mappix-trackdraw.h @@ -0,0 +1,54 @@ +/* + * mappix-trackdraw.h + * + * 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. + */ + +#ifndef _MAPPIX_TRACKDRAW_H +#define _MAPPIX_TRACKDRAW_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +void draw_waypoint_label (mapwin *, gint, gint, gchar *); +void draw_pointed_line(gint, gint, gint, gint, gint, gint, gpointer); +void draw_lines_cb (GtkButton *, gpointer); +double do_calc_scale(mapwin *); +double calc_scale(mapwin *); +void do_redraw(mapwin *); +void change_scale(mapwin *, double); +void draw_marker(gint, gint, gint, gint, gpointer); +void draw_track(track_head *, gpointer); +void draw_tracks(mapwin *); +void draw_filename (gchar *, mapwin *); +void zoom_in_cb (GtkSpinButton *, gpointer); +void zoom_out_cb (GtkSpinButton *, gpointer); +void zoom_area_cb (GtkRadioToolButton *, gpointer); +void move_cb (GtkRadioToolButton *, gpointer); +void auto_scale (GtkButton *, gpointer); +void draw_waypoints(mapwin *); +void draw_city_names(mapwin *); +void draw_scale(mapwin *); + +#ifdef __cplusplus +} +#endif + +#endif /* _MAPPIX_TRACKDRAW_H */ diff --git a/mappix-trackedit.c b/mappix-trackedit.c new file mode 100644 index 0000000..19d8078 --- /dev/null +++ b/mappix-trackedit.c @@ -0,0 +1,1124 @@ +/* + * 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); +} + diff --git a/mappix-trackedit.h b/mappix-trackedit.h new file mode 100644 index 0000000..2b1ab08 --- /dev/null +++ b/mappix-trackedit.h @@ -0,0 +1,51 @@ +/* + * mappix-trackedit.h + * + * Copyright (C) 2006,2007 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. + */ + +#ifndef _MAPPIX_TRACKEDIT_H +#define _MAPPIX_TRACKEDIT_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +void on_auto_split (GtkButton *, gpointer); +void on_auto_thin (GtkButton *, gpointer); +void on_properties_track (GtkButton *, gpointer); +void on_track_angle_simplify (GtkButton *, gpointer); +void on_track_delete (GtkToolButton *, gpointer); +void on_select_trackpoint_cb(GtkToolButton *, gpointer); +void on_select_region_cb(GtkToolButton *, gpointer); +void do_trackpoint_region_select(gint, gint, guint, guint, gpointer); +void on_move_trackpoint_cb(GtkToolButton *, gpointer); +void do_select_trackpoint(gint, gint, gpointer); +void do_move_trackpoint(gint, gint, gint, gint, gpointer); +void track_find_minmax(gpointer, gpointer); +void track_add_new_to_main(mapwin *, track_head *); +void on_delete_nodes_cb(GtkToolButton *, gpointer); +void on_split_track(GtkToolButton *, gpointer); +void on_track_length(GtkWidget *, gpointer); + +#ifdef __cplusplus +} +#endif + +#endif /* _MAPPIX_TRACKEDIT_H */ diff --git a/mappix-trackrecord.c b/mappix-trackrecord.c new file mode 100644 index 0000000..9f94704 --- /dev/null +++ b/mappix-trackrecord.c @@ -0,0 +1,210 @@ +#include "mappix.h" +#include +#include +#include +#include +#include +#include +#include + +#include "nmeap.h" +#include "geo-tools.h" + +#define GPS_DEVICE "/dev/rfcomm0" + + +struct iochandata { + track_head *track; + GtkWidget *dialog; + GtkWidget *lat_label; + GtkWidget *long_label; + GtkWidget *nrpnt_label; + gboolean end_record; + gint nrpoints; + nmeap_context_t nmeap; + nmeap_gga_t gga; +}; + + +static gboolean +gps_ch_inev (GIOChannel *source, GIOCondition condition, gpointer data) +{ +struct iochandata *iodata = (struct iochandata *)data; +GError *merr; +static gchar buf[256], label[128]; +gsize rread; +gint offset, remain, status; +trackdata *trackpoint; + + if (condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) { + /* something bad happened on the channel, close it */ + g_message("error on gps_ch"); + return FALSE; + } + + merr = NULL; + if (g_io_channel_read_chars (source, buf, 255, &rread, &merr) != G_IO_STATUS_NORMAL) + return FALSE; + + // buf[255] = 0; + if (rread == 0) + return TRUE; + // g_message("chan read %d", rread); + + /* FIXME: use nmeap.sf.net instead */ + remain = rread; + offset = 0; + while (remain > 0) { + status = nmeap_parseBuffer(&iodata->nmeap, &buf[offset], &remain); + offset += (rread - remain); + switch(status) { + case NMEAP_GPGGA: + g_message("found GPGGA message %.6f %.6f %.0f %lu %d %d %f %f\n", iodata->gga.latitude , iodata->gga.longitude, iodata->gga.altitude, iodata->gga.time, + iodata->gga.satellites, iodata->gga.quality, iodata->gga.hdop, iodata->gga.geoid); + if (iodata->gga.quality > 0) { + if (iodata->gga.latitude > 360 || iodata->gga.longitude > 360) + return TRUE; + iodata->nrpoints++; + //g_message("lat=%f long=%f %s, fixtype %d", iodata->nms->latitude, iodata->nms->longitude, iodata->nms->satsvalid ? "valid" : "not valid", iodata->nms->fixtype); +#if 0 + for (i=0; i<12; i++) { + g_message("sat #%02d", i); + g_message("\tin use:\t%s", iodata->nms->satellites[i].used ? "yes" : "no"); + g_message("\tno#:\t%d", iodata->nms->satellites[i].number); + g_message("\tele:\t%d", iodata->nms->satellites[i].elevation); + g_message("\tazim:\t%d", iodata->nms->satellites[i].azimuth); + g_message("\ts/n ratio:\t%d", iodata->nms->satellites[i].snratio); + } +#endif + g_sprintf(label, "%d", iodata->nrpoints); + gtk_label_set_text(GTK_LABEL(iodata->nrpnt_label), label); + g_sprintf(label, "%f", iodata->gga.latitude); + gtk_label_set_text(GTK_LABEL(iodata->lat_label), label); + g_sprintf(label, "%f", iodata->gga.longitude); + gtk_label_set_text(GTK_LABEL(iodata->long_label), label); + + trackpoint = (trackdata *)g_malloc(sizeof(trackdata)); + latlon2unit(iodata->gga.latitude, iodata->gga.longitude, trackpoint->unitx, trackpoint->unity); + trackpoint->elevation = iodata->gga.altitude; + trackpoint->selected = FALSE; + iodata->track->track = g_list_prepend(iodata->track->track, trackpoint); + } + break; + case NMEAP_GPRMC: + break; + default: + break; + } + } + if (merr != NULL) + g_error_free(merr); + + if (iodata->end_record) + return FALSE; + else + return TRUE; +} + + +void +do_record_track (mapwin *Mapper) +{ +gint fd; +GIOChannel *gps_ch; +GtkWidget *dialog; +GtkWidget *table, *w; +GtkWidget *long_label; +GtkWidget *lat_label; +GtkWidget *nrpnt_label; +gint evsrcid, res; +struct iochandata iodata; + + +#ifdef HILDON + gtk_infoprintf(GTK_WINDOW(gtk_widget_get_toplevel(Mapper->mainwin)), + "Connecting to GPS..."); + while (gtk_events_pending()) + gtk_main_iteration(); +#endif + fd = g_open(GPS_DEVICE, O_RDONLY | O_NONBLOCK); + if (fd < 0) { + return; + } + gps_ch = g_io_channel_unix_new(fd); + + dialog = gtk_dialog_new_with_buttons("Recording Track", GTK_WINDOW(gtk_widget_get_toplevel(Mapper->mainwin)), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_ADD, 1 /* response add */, + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); + + table = gtk_table_new (2, 3, 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), TRUE); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), table, FALSE, FALSE, 0); + + w = gtk_label_new("Latitude"); + gtk_label_set_use_markup(GTK_LABEL(w), TRUE); + lat_label = gtk_label_new("-.-X"); + gtk_table_attach_defaults (GTK_TABLE (table), w, 0, 1, 0, 1); + gtk_table_attach_defaults (GTK_TABLE (table), lat_label, 1, 2, 0, 1); + + w = gtk_label_new("Longitude"); + gtk_label_set_use_markup(GTK_LABEL(w), TRUE); + long_label = gtk_label_new("-.-X"); + gtk_table_attach_defaults (GTK_TABLE (table), w, 0, 1, 1, 2); + gtk_table_attach_defaults (GTK_TABLE (table), long_label, 1, 2, 1, 2); + + w = gtk_label_new("points #"); + gtk_label_set_use_markup(GTK_LABEL(w), TRUE); + nrpnt_label = gtk_label_new("0"); + gtk_table_attach_defaults (GTK_TABLE (table), w, 0, 1, 2, 3); + gtk_table_attach_defaults (GTK_TABLE (table), nrpnt_label, 1, 2, 2, 3); + + gtk_widget_show_all(dialog); + + iodata.end_record = FALSE; + iodata.nrpoints = 0; + iodata.dialog = dialog; + iodata.lat_label = lat_label; + iodata.long_label = long_label; + iodata.nrpnt_label = nrpnt_label; + + iodata.track = (track_head *)g_malloc(sizeof(track_head)); + iodata.track->name = g_strdup("RecTrack"); + iodata.track->show = TRUE; + + //nmea_parse_line("", &nms); + if (nmeap_init(&iodata.nmeap, NULL) != 0) + return; + if (nmeap_addParser(&iodata.nmeap, "GPGGA", nmeap_gpgga, NULL, &iodata.gga) != 0) + return; + + evsrcid = g_io_add_watch(gps_ch, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + gps_ch_inev, &iodata); + do { + res = gtk_dialog_run(GTK_DIALOG(dialog)); + if (res == 1) { + g_message("would add waypoint"); + } + } while (res == 1); /* anything than 1 will leave */ + + iodata.end_record = TRUE; + g_io_channel_shutdown(gps_ch, FALSE, NULL); + close(fd); + + track_add_new_to_main(Mapper, iodata.track); + + if (iodata.dialog != NULL) { + gtk_widget_hide(iodata.dialog); + gtk_widget_destroy(iodata.dialog); + } +} + +void +on_record_track (GtkButton *widget, gpointer user_data) +{ +mapwin *Mapper = (mapwin *)user_data; + + do_record_track(Mapper); +} diff --git a/mappix-trackrecord.h b/mappix-trackrecord.h new file mode 100644 index 0000000..462fe78 --- /dev/null +++ b/mappix-trackrecord.h @@ -0,0 +1,6 @@ +#ifndef _MAPPIX_TRACKRECORD_H +#define _MAPPIX_TRACKRECORD_H + +void on_record_track (GtkButton *widget, gpointer user_data); + +#endif /* _MAPPIX_TRACKRECORD_H */ diff --git a/mappix-ui.c b/mappix-ui.c new file mode 100644 index 0000000..bec9e38 --- /dev/null +++ b/mappix-ui.c @@ -0,0 +1,1074 @@ +/* + * mappix-ui + * + * Copyright (C) 2006,2007 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "mappix.h" + +#include "geo-tools.h" + +#define DEFAULT_WINDOW_WIDTH 640 +#define DEFAULT_WINDOW_HEIGHT 480 + + + +gint +do_preference_dialog (GtkWidget *pw, gpointer userdata) +{ +mapwin *Mapper = (mapwin *)userdata; +GtkWidget *dialog; +GtkWidget *hbox; +GtkWidget *stock; +GtkWidget *table; +GtkWidget *mtype[4]; +GtkWidget *spinb_marker_size; +GtkWidget *spinb_line_width; +GtkWidget *idledraw_toggle; +GtkWidget *label; +GtkWidget *w; +gint result; + + dialog = gtk_dialog_new_with_buttons("Preferences", 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_INFO, 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 ("Marker mode"); + gtk_table_attach_defaults (GTK_TABLE (table), + label, + 0, 1, 0, 1); + w = gtk_vbox_new(FALSE, 2); + gtk_table_attach_defaults (GTK_TABLE (table), w, 1, 2, 0, 1); + mtype[0] = gtk_radio_button_new_with_label(NULL, "None"); + mtype[1] = gtk_radio_button_new_with_label(gtk_radio_button_get_group(GTK_RADIO_BUTTON(mtype[0])), "Filled Square"); + mtype[2] = gtk_radio_button_new_with_label(gtk_radio_button_get_group(GTK_RADIO_BUTTON(mtype[0])), "Square"); + mtype[3] = gtk_radio_button_new_with_label(gtk_radio_button_get_group(GTK_RADIO_BUTTON(mtype[0])), "Cross"); + gtk_container_add(GTK_CONTAINER(w), mtype[0]); + gtk_container_add(GTK_CONTAINER(w), mtype[1]); + gtk_container_add(GTK_CONTAINER(w), mtype[2]); + gtk_container_add(GTK_CONTAINER(w), mtype[3]); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mtype[Mapper->marker_type]), TRUE); + + label = gtk_label_new ("Marker size"); + gtk_table_attach_defaults (GTK_TABLE (table), + label, + 0, 1, 1, 2); + + spinb_marker_size = gtk_spin_button_new_with_range(0, 100, 1); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinb_marker_size), Mapper->marker_size); + gtk_table_attach_defaults (GTK_TABLE (table), spinb_marker_size, 1, 2, 1, 2); + + label = gtk_label_new ("Line width"); + gtk_table_attach_defaults (GTK_TABLE (table), + label, + 0, 1, 2, 3); + + spinb_line_width = gtk_spin_button_new_with_range(0, 100, 1); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinb_line_width), Mapper->linewidth); + gtk_table_attach_defaults (GTK_TABLE (table), spinb_line_width, 1, 2, 2, 3); + + label = gtk_label_new ("Draw in background"); + gtk_table_attach_defaults (GTK_TABLE (table), + label, + 0, 1, 3, 4); + + idledraw_toggle = gtk_check_button_new(); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(idledraw_toggle), Mapper->use_idledraw); + gtk_table_attach_defaults (GTK_TABLE (table), idledraw_toggle, 1, 2, 3, 4); + + gtk_widget_show_all (hbox); + + result = gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_hide (dialog); + + switch (result) { + gint i; + case GTK_RESPONSE_ACCEPT: + /* one of three MUST be selected */ + for (i=0; i<=3; i++) + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(mtype[i]))) + Mapper->marker_type = i; + Mapper->marker_size = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinb_marker_size)); + Mapper->linewidth = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinb_line_width)); + Mapper->use_idledraw = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(idledraw_toggle)); + draw_tracks(Mapper); + break; + default: + break; + } + gtk_widget_destroy (dialog); + + +return result; +} + + +gint close_application(GtkWidget *widget, GdkEvent *event, gpointer userdata) +{ +mapwin *Mapper = (mapwin *)userdata; + + if (Mapper->modified) { + /* ask for exit confirmation */ + } + gtk_main_quit (); + +return FALSE; +} + + +gboolean +mappix_configure(GtkWidget *w, GdkEventConfigure *event, gpointer userdata) +{ +mapwin *Mapper = (mapwin *)userdata; +GdkRectangle grect; + + // fprintf(stderr,"conf w:%d h:%d\n", event->width, event->height); + + if (Mapper->mappix != NULL) { + g_object_unref(Mapper->mappix); + g_object_unref(Mapper->mappix_blank); + } + Mapper->canvas_x = event->width; + Mapper->canvas_y = event->height; + Mapper->mappix = gdk_pixmap_new(GDK_DRAWABLE(w->window), Mapper->canvas_x, Mapper->canvas_y, -1); + Mapper->mappix_blank = gdk_pixmap_new(GDK_DRAWABLE(w->window), Mapper->canvas_x, Mapper->canvas_y, -1); + + gdk_draw_rectangle(Mapper->mappix, w->style->white_gc, 1, 0, 0, Mapper->canvas_x, Mapper->canvas_y); + gdk_draw_rectangle(Mapper->mappix_blank, w->style->white_gc, 1, 0, 0, Mapper->canvas_x, Mapper->canvas_y); + + grect.x = 0; + grect.y = 0; + grect.width = Mapper->canvas_x-1; + grect.height = Mapper->canvas_y-1; + gdk_gc_set_clip_rectangle(Mapper->red_gc, &grect); + gdk_gc_set_clip_origin(Mapper->red_gc, 0, 0); + gdk_gc_set_clip_rectangle(Mapper->blue_gc, &grect); + gdk_gc_set_clip_origin(Mapper->blue_gc, 0, 0); + gdk_gc_set_clip_rectangle(Mapper->select_gc, &grect); + gdk_gc_set_clip_origin(Mapper->select_gc, 0, 0); + gdk_gc_set_clip_rectangle(Mapper->drawarea->style->black_gc, &grect); + gdk_gc_set_clip_origin(Mapper->drawarea->style->black_gc, 0, 0); + + do_redraw(Mapper); + +return TRUE; +} + + +gboolean +mappix_expose(GtkWidget *w, GdkEventExpose *event, gpointer userdata) +{ +mapwin *Mapper = (mapwin *)userdata; +gint areax, areay, width, height; + + // fprintf(stderr,"exp\n"); + + if (Mapper->move_selection) { + return TRUE; + } + areax = event->area.x; + areay = event->area.y; + width = event->area.width; + height = event->area.height; + // fprintf(stderr,"exp x,y,w,h: %d, %d, %d, %d\n", areax, areay, width, height); + gdk_draw_drawable(w->window, w->style->fg_gc[GTK_WIDGET_STATE(w)], Mapper->mappix, + areax, areay, + areax, areay, + -1 /*width*/, -1 /*height*/); + +return TRUE; +} + + +gboolean +mappix_drfocus_in(GtkWidget *w, GdkEventCrossing *event, gpointer user_data) +{ +mapwin *Mapper = (mapwin *)user_data; + + // fprintf(stderr,"drawing-area enter\n"); + switch (Mapper->toolmode) { + case MAPPIX_TOOL_NONE: + gdk_window_set_cursor(Mapper->mainwin->window, Mapper->cursor_normal); + break; + case MAPPIX_TOOL_ZOOM_REGION: + case MAPPIX_TOOL_SELECT_REGION: + gdk_window_set_cursor(Mapper->mainwin->window, Mapper->cursor_crosshair); + break; + case MAPPIX_TOOL_MOVE: + gdk_window_set_cursor(Mapper->mainwin->window, Mapper->cursor_mover); + break; + case MAPPIX_TOOL_SELECT_TRACKPOINT: + gdk_window_set_cursor(Mapper->mainwin->window, Mapper->cursor_select); + case MAPPIX_TOOL_MOVE_TRACKPOINT: + gdk_window_set_cursor(Mapper->mainwin->window, Mapper->cursor_select); + default: + break; + } + +return TRUE; +} + + +gboolean +mappix_drfocus_out(GtkWidget *w, GdkEventCrossing *event, gpointer user_data) +{ +mapwin *Mapper = (mapwin *)user_data; + + // fprintf(stderr,"drawing-area leave\n"); + gdk_window_set_cursor(Mapper->mainwin->window, Mapper->cursor_normal); + +return TRUE; +} + + +void +create_file_selection (GtkButton *widget, gpointer user_data) +{ +mapwin *Mapper = (mapwin *)user_data; +GtkWidget *fchooser; +GtkFileFilter *filter; + + fchooser = gtk_file_chooser_dialog_new("Open GPX file", + GTK_WINDOW(Mapper->mainwin), GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + NULL); + filter = gtk_file_filter_new(); + gtk_file_filter_add_pattern(filter, "*.gpx"); + gtk_file_filter_set_name(filter, "GPX file"); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fchooser), filter); + filter = gtk_file_filter_new(); + gtk_file_filter_add_pattern(filter, "*"); + gtk_file_filter_set_name(filter, "All files"); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fchooser), filter); + + /* Display that dialog */ + gtk_widget_show (fchooser); + if (gtk_dialog_run (GTK_DIALOG(fchooser)) == GTK_RESPONSE_ACCEPT) { + draw_filename(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (fchooser)), Mapper); + } + gtk_widget_destroy(fchooser); +} + + +void +on_save_gpxfile (GtkButton *widget, gpointer user_data) +{ +mapwin *Mapper = (mapwin *)user_data; +GtkWidget *fchooser; +GtkFileFilter *filter; + + fchooser = gtk_file_chooser_dialog_new("Save project to GPX file", + GTK_WINDOW(Mapper->mainwin), GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + filter = gtk_file_filter_new(); + gtk_file_filter_add_pattern(filter, "*.gpx"); + gtk_file_filter_set_name(filter, "GPX file"); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fchooser), filter); + filter = gtk_file_filter_new(); + gtk_file_filter_add_pattern(filter, "*"); + gtk_file_filter_set_name(filter, "All files"); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fchooser), filter); + + /* Display that dialog */ + gtk_widget_show (fchooser); + if (gtk_dialog_run (GTK_DIALOG(fchooser)) == GTK_RESPONSE_ACCEPT) { + save_project_to_file(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (fchooser)), Mapper); + } + gtk_widget_destroy(fchooser); +} + + +void +draw_selectbox(mapwin *Mapper, gint x, gint y, gboolean update, gboolean start_new) +{ +static gint origin_x, origin_y; +static gint old_x, old_y; + + if (start_new) { + origin_x = -1; + origin_y = -1; + old_x = -1; + old_y = -1; + return; + } + if (update) { + if (origin_x == -1 && origin_y == -1) { + origin_x = x; + origin_y = y; + return; + } + if (old_x != -1 && old_y != -1) + gdk_draw_rectangle(Mapper->mappix, Mapper->select_gc, FALSE, MIN(origin_x, old_x), MIN(origin_y, old_y), ABS(old_x - origin_x), ABS(old_y - origin_y)); + gdk_draw_rectangle(Mapper->mappix, Mapper->select_gc, FALSE, MIN(origin_x, x), MIN(origin_y, y), ABS(x - origin_x), ABS(y - origin_y)); + gtk_widget_queue_draw(Mapper->drawarea); + old_x = x; + old_y = y; + return; + } + if (old_x != -1 && old_y != -1) + gdk_draw_rectangle(Mapper->mappix, Mapper->select_gc, FALSE, MIN(origin_x, old_x), MIN(origin_y, old_y), ABS(old_x - origin_x), ABS(old_y - origin_y)); +} + + +static gboolean +button_press_event(GtkWidget *widget, GdkEventButton *event, gpointer user_data) +{ +mapwin *Mapper = (mapwin *)user_data; +static guint press_x, press_y; +static guint release_x, release_y; + + // we only want button 1 + if (event->button != 1) + return TRUE; + switch (Mapper->toolmode) { + case MAPPIX_TOOL_SELECT_REGION: + if (event->type == GDK_BUTTON_PRESS) { + // fprintf(stderr,"press: "); + press_x = Mapper->pointer_x; + press_y = Mapper->pointer_y; + draw_selectbox(Mapper, 0, 0, FALSE, TRUE); + Mapper->draw_selection = TRUE; + } + if (event->type == GDK_BUTTON_RELEASE) { + // fprintf(stderr,"release: "); + release_x = Mapper->pointer_x; + release_y = Mapper->pointer_y; + draw_selectbox(Mapper, 0, 0, FALSE, FALSE); + Mapper->draw_selection = FALSE; + gtk_widget_queue_draw(Mapper->drawarea); + do_trackpoint_region_select(press_x, press_y, release_x, release_y, Mapper); + } + // fprintf(stderr, "%d %d %lf %lf\n", event->button, event->state, Mapper->pointer_long, Mapper->pointer_lat); + break; + case MAPPIX_TOOL_ZOOM_REGION: + if (event->type == GDK_BUTTON_PRESS) { + // fprintf(stderr,"press: "); + press_x = Mapper->pointer_x; + press_y = Mapper->pointer_y; + draw_selectbox(Mapper, 0, 0, FALSE, TRUE); + Mapper->draw_selection = TRUE; + } + if (event->type == GDK_BUTTON_RELEASE) { + // fprintf(stderr,"release: "); + release_x = Mapper->pointer_x; + release_y = Mapper->pointer_y; + if (release_x > press_x) { + Mapper->xmin = press_x; + Mapper->xmax = release_x; + } else { + Mapper->xmin = release_x; + Mapper->xmax = press_x; + } + if (release_y > press_y) { + Mapper->ymin = press_y; + Mapper->ymax = release_y; + } else { + Mapper->ymin = release_y; + Mapper->ymax = press_y; + } + // calculate new scale factor for zoomed square + draw_selectbox(Mapper, 0, 0, FALSE, FALSE); + change_scale(Mapper, do_calc_scale(Mapper)); + // draw_tracks(Mapper); + // gtk_widget_queue_draw(Mapper->drawarea); + Mapper->draw_selection = FALSE; + } + // fprintf(stderr, "%d %d %lf %lf\n", event->button, event->state, Mapper->pointer_long, Mapper->pointer_lat); + break; + case MAPPIX_TOOL_MOVE: + if (event->type == GDK_BUTTON_PRESS) { + press_x = Mapper->pointer_x; + press_y = Mapper->pointer_y; + Mapper->mouse_px = Mapper->mouse_x; + Mapper->mouse_py = Mapper->mouse_y; + Mapper->move_selection = TRUE; + } + if (event->type == GDK_BUTTON_RELEASE) { + Mapper->move_selection = FALSE; + release_x = Mapper->pointer_x; + release_y = Mapper->pointer_y; + Mapper->xoffset = Mapper->xoffset + (press_x - release_x); + Mapper->yoffset = Mapper->yoffset + (press_y - release_y); + Mapper->xmin = Mapper->xoffset; + Mapper->ymin = Mapper->yoffset; + Mapper->xmax = Mapper->xmin + (Mapper->canvas_x / Mapper->scale); + Mapper->ymax = Mapper->ymin + (Mapper->canvas_y / Mapper->scale); + change_scale(Mapper, Mapper->scale); + } + break; + case MAPPIX_TOOL_SELECT_TRACKPOINT: + if (event->type == GDK_BUTTON_RELEASE) { + release_x = Mapper->pointer_x; + release_y = Mapper->pointer_y; + do_select_trackpoint(release_x, release_y, Mapper); + } + break; + case MAPPIX_TOOL_MOVE_TRACKPOINT: + if (event->type == GDK_BUTTON_PRESS) { + press_x = Mapper->pointer_x; + press_y = Mapper->pointer_y; + } + if (event->type == GDK_BUTTON_RELEASE) { + release_x = Mapper->pointer_x; + release_y = Mapper->pointer_y; + do_move_trackpoint(press_x, press_y, release_x, release_y, Mapper); + } + break; + default: + break; + } + +return TRUE; +} + + +static gboolean +motion_notify_event(GtkWidget *widget, GdkEventMotion *event, gpointer user_data) +{ +mapwin *Mapper = (mapwin *)user_data; +gint x, y; +GdkModifierType state; +static gchar statlabel[256]; +static guint ox, oy; + + if (event->is_hint) + gdk_window_get_pointer (event->window, &x, &y, &state); + else { + x = event->x; + y = event->y; + state = event->state; + } + + /* if scale is not yet set we cannot scale */ + if (Mapper->scale) { + Mapper->pointer_x = (x / Mapper->scale) + Mapper->xoffset; + Mapper->pointer_y = (y / Mapper->scale) + Mapper->yoffset; + } + Mapper->mouse_x = x; + Mapper->mouse_y = y; + + if (Mapper->draw_selection) + draw_selectbox(Mapper, x, y, TRUE, FALSE); + + if (Mapper->move_selection) { + if (ox != x || oy != y) { + guint delta_x, delta_y; + delta_x = x - Mapper->mouse_px; + delta_y = y - Mapper->mouse_py; + gdk_draw_drawable(Mapper->drawarea->window, Mapper->drawarea->style->fg_gc[GTK_WIDGET_STATE(Mapper->drawarea)], Mapper->mappix_blank, + 0, 0, + 0, 0, + -1 /*width*/, -1 /*height*/); + gdk_draw_drawable(Mapper->drawarea->window, Mapper->drawarea->style->fg_gc[GTK_WIDGET_STATE(Mapper->drawarea)], Mapper->mappix, + 0, 0, + delta_x, delta_y, + -1 /*width*/, -1 /*height*/); + //gtk_widget_queue_draw(Mapper->drawarea); + } + } + + ox=x; oy=y; + + sprintf(statlabel, "lat: %d long: %d scale: %lf", Mapper->pointer_x, Mapper->pointer_y, Mapper->scale); + gtk_statusbar_pop(GTK_STATUSBAR(Mapper->statbar), Mapper->lstatid); + Mapper->lstatid = gtk_statusbar_get_context_id(GTK_STATUSBAR(Mapper->statbar), "pcoord"); + gtk_statusbar_push(GTK_STATUSBAR(Mapper->statbar), Mapper->lstatid, statlabel); + +return TRUE; +} + + +static void +track_show_toggled (GtkCellRendererToggle *cell, gchar *path_str, gpointer user_data) +{ +mapwin *Mapper = (mapwin *)user_data; +GtkTreeModel *model; // = (GtkTreeModel *)data; +GtkTreeIter iter; +GtkTreePath *path = gtk_tree_path_new_from_string (path_str); +// gboolean show; +track_head *trackh; + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(Mapper->track_treeview)); + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, 2, &trackh, -1); + trackh->show ^= 1; + gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, trackh->show, -1); + gtk_tree_path_free (path); + do_redraw(Mapper); +} + + +static void +do_show_waypoints_toggled(GtkCheckMenuItem *checkmenuitem, gpointer user_data) +{ +mapwin *Mapper = (mapwin *)user_data; + + Mapper->show_waypoints = gtk_check_menu_item_get_active(checkmenuitem); + if (Mapper->show_waypoints) + draw_waypoints(Mapper); + else + do_redraw(Mapper); +} + + +static void +do_show_cities_toggled(GtkCheckMenuItem *checkmenuitem, gpointer user_data) +{ +mapwin *Mapper = (mapwin *)user_data; + + Mapper->show_cities = gtk_check_menu_item_get_active(checkmenuitem); + if (Mapper->show_cities) + draw_city_names(Mapper); + else + do_redraw(Mapper); +} + + +static void +do_show_markers_toggled(GtkCheckMenuItem *checkmenuitem, gpointer user_data) +{ +mapwin *Mapper = (mapwin *)user_data; + + Mapper->show_marker = gtk_check_menu_item_get_active(checkmenuitem); + do_redraw(Mapper); +} + + +static void +do_show_scale_toggled(GtkCheckMenuItem *checkmenuitem, gpointer user_data) +{ +mapwin *Mapper = (mapwin *)user_data; + + Mapper->show_scale = gtk_check_menu_item_get_active(checkmenuitem); + if (Mapper->show_scale) + draw_scale(Mapper); + else + do_redraw(Mapper); +} + + +static void +add_columns (GtkTreeView *treeview, mapwin *Mapper) +{ +GtkCellRenderer *renderer; +GtkTreeViewColumn *column; +// GtkTreeModel *model; + + // model = gtk_tree_view_get_model(Mapper->track_treeview); + renderer = gtk_cell_renderer_toggle_new (); + g_signal_connect (renderer, "toggled", G_CALLBACK (track_show_toggled), Mapper); + column = gtk_tree_view_column_new_with_attributes ("Show", renderer, "active", 0, NULL); + gtk_tree_view_column_set_sizing (GTK_TREE_VIEW_COLUMN (column), GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width (GTK_TREE_VIEW_COLUMN (column), 50); + gtk_tree_view_append_column (treeview, column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Name", renderer, "text", 1, NULL); + // gtk_tree_view_column_set_sort_column_id (column, 1); + gtk_tree_view_set_search_column (treeview, 1); + gtk_tree_view_append_column (treeview, column); +} + + +static GtkWidget +*load_icon(gchar *filename) +{ +GdkPixbuf *gpbuf; +gchar *sfname1, *sfname2; + + sfname1 = g_strdup(PREFIX); + sfname2 = g_strconcat(sfname1, filename, NULL); + gpbuf = gdk_pixbuf_new_from_file(sfname2, NULL); + g_free(sfname1); + g_free(sfname2); + if (gpbuf == NULL) { + gpbuf = gdk_pixbuf_new_from_file(filename, NULL); + } + if (gpbuf == NULL) + return NULL; + +return gtk_image_new_from_pixbuf(gpbuf); +} + + +/* OK, this will do the nasty work of creating the UI of the main window + * Be careful when changing since order does matter! + */ +int +create_main_window(mapwin *Mapper) +{ +GtkWidget *mainmenu; +GtkWidget *mvb, *mhb, *w; +GtkWidget *filemenu, *editmenu, *algomenu, *viewmenu, *toolsmenu, *mitem; +GtkWidget *tb; // toolbar +GtkToolItem *ti; // tool-item +GtkTooltips *tt; // tooltip +GtkWidget *mim; // menu item +GtkStyle *style; +GtkTreeModel *tree_model; +GdkColor color; + + + /* initialize some private data to sane defaults */ +#ifdef HILDON + Mapper->hildon_app = NULL; + Mapper->hildon_mainview = NULL; +#endif + /* pixmap for drawing */ + Mapper->mappix = NULL; + /* default canvas size */ + Mapper->canvas_x = 640; + Mapper->canvas_y = 480; + /* default line drawing parameters */ + Mapper->linewidth = 1; + Mapper->marker_type = 1; // square (faster) + Mapper->marker_size = 3; + /* default scale factors */ + Mapper->xscale = 0; + Mapper->yscale = 0; + Mapper->scale = 0; + Mapper->xoffset = 0; + Mapper->yoffset = 0; + Mapper->xmin=WORLD_SIZE_UNITS; + Mapper->xmax=0; + Mapper->ymin=WORLD_SIZE_UNITS; + Mapper->ymax=0; + Mapper->toolmode = MAPPIX_TOOL_ZOOM_REGION; + Mapper->modified = FALSE; + Mapper->use_idledraw = FALSE; + Mapper->draw_selection = FALSE; + Mapper->move_selection = FALSE; + Mapper->show_marker = FALSE; + Mapper->show_waypoints = TRUE; + Mapper->show_cities = TRUE; + Mapper->show_scale = TRUE; + +#ifdef HILDON + Mapper->hildon_app = HILDON_APP(hildon_app_new()); + Mapper->hildon_mainview = HILDON_APPVIEW(hildon_appview_new("main_view")); + hildon_app_set_appview(Mapper->hildon_app, Mapper->hildon_mainview); + hildon_app_set_title(Mapper->hildon_app, "Mappix"); + Mapper->mainwin = GTK_WIDGET(Mapper->hildon_mainview); +#else + Mapper->mainwin = gtk_window_new (GTK_WINDOW_TOPLEVEL); + g_signal_connect (G_OBJECT (Mapper->mainwin), "delete_event", + G_CALLBACK (close_application), NULL); + gtk_container_set_border_width (GTK_CONTAINER (Mapper->mainwin), 1); + gtk_window_set_default_size(GTK_WINDOW(gtk_widget_get_toplevel(Mapper->mainwin)), DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT); +#endif + Mapper->cursor_normal = gdk_cursor_new_for_display(gtk_widget_get_display(Mapper->mainwin), GDK_LEFT_PTR); + Mapper->cursor_mover = gdk_cursor_new_for_display(gtk_widget_get_display(Mapper->mainwin), GDK_FLEUR); + Mapper->cursor_crosshair = gdk_cursor_new_for_display(gtk_widget_get_display(Mapper->mainwin), GDK_CROSSHAIR); + Mapper->cursor_hand = gdk_cursor_new_for_display(gtk_widget_get_display(Mapper->mainwin), GDK_HAND2); + Mapper->cursor_select = gdk_cursor_new_for_display(gtk_widget_get_display(Mapper->mainwin), GDK_RIGHT_PTR); + + style = gtk_widget_get_style (Mapper->mainwin); + + mvb = gtk_vbox_new(FALSE, 0); + gtk_container_add (GTK_CONTAINER (Mapper->mainwin), mvb); + gtk_widget_show(mvb); + +#ifdef HILDON + mainmenu = GTK_WIDGET(hildon_appview_get_menu( HILDON_APPVIEW(Mapper->hildon_mainview))); +#else + mainmenu = gtk_menu_bar_new(); + gtk_container_add (GTK_CONTAINER (mvb), mainmenu); + gtk_box_set_child_packing(GTK_BOX(mvb), mainmenu, FALSE, FALSE, 2, GTK_PACK_START); + gtk_widget_show(mainmenu); +#endif + + // menu "File" + w=gtk_menu_item_new_with_label ("File"); + gtk_menu_shell_append (GTK_MENU_SHELL (mainmenu), w); + gtk_widget_show (w); + filemenu = gtk_menu_new(); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), filemenu); + // item "Import track" + mitem = gtk_image_menu_item_new_with_label("Open GPX file"); + mim = gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mitem), mim); + gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), mitem); + gtk_widget_show (mitem); + g_signal_connect(G_OBJECT(mitem), "activate", G_CALLBACK(create_file_selection), Mapper); + // item "Import track" + mitem = gtk_image_menu_item_new_with_label("Save GPX file"); + mim = gtk_image_new_from_stock(GTK_STOCK_SAVE, GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mitem), mim); + gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), mitem); + gtk_widget_show (mitem); + g_signal_connect(G_OBJECT(mitem), "activate", G_CALLBACK(on_save_gpxfile), Mapper); + // item seperator + mitem = gtk_separator_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), mitem); + gtk_widget_show (mitem); + // item "Exit" + mitem = gtk_image_menu_item_new_with_label("Exit"); + mim = gtk_image_new_from_stock(GTK_STOCK_QUIT, GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mitem), mim); + gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), mitem); + gtk_widget_show (mitem); + g_signal_connect(G_OBJECT(mitem), "activate", G_CALLBACK(close_application), Mapper); + // menu "Edit" + w=gtk_menu_item_new_with_label ("Edit"); + gtk_menu_shell_append (GTK_MENU_SHELL (mainmenu), w); + gtk_widget_show (w); + editmenu = gtk_menu_new(); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), editmenu); + + // submenu "Algorithms" + algomenu = gtk_menu_new(); + w = gtk_menu_item_new_with_label ("Algorithms"); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(w), algomenu); + gtk_menu_shell_append (GTK_MENU_SHELL (editmenu), w); + gtk_widget_show (algomenu); + gtk_widget_show (w); + + // item "Autoscale" + mitem = gtk_image_menu_item_new_with_label("Auto-Scale"); + mim = gtk_image_new_from_stock(GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mitem), mim); + gtk_menu_shell_append(GTK_MENU_SHELL(algomenu), mitem); + gtk_widget_show (mitem); + g_signal_connect(G_OBJECT(mitem), "activate", G_CALLBACK(auto_scale), Mapper); + // item "Autosplit" + mitem = gtk_image_menu_item_new_with_label("Auto-Split"); + mim = gtk_image_new_from_stock(GTK_STOCK_CUT, GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mitem), mim); + gtk_menu_shell_append(GTK_MENU_SHELL(algomenu), mitem); + gtk_widget_show (mitem); + g_signal_connect(G_OBJECT(mitem), "activate", G_CALLBACK(on_auto_split), Mapper); + // item "Autothin" + mitem = gtk_image_menu_item_new_with_label("Auto-Thin"); + //mim = gtk_image_new_from_stock(GTK_STOCK_CUT, GTK_ICON_SIZE_MENU); + //gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mitem), mim); + gtk_menu_shell_append(GTK_MENU_SHELL(algomenu), mitem); + gtk_widget_show (mitem); + g_signal_connect(G_OBJECT(mitem), "activate", G_CALLBACK(on_auto_thin), Mapper); + // item "Angle simplify" + mitem = gtk_image_menu_item_new_with_label("Auto-angle-simple"); + //mim = gtk_image_new_from_stock(GTK_STOCK_CUT, GTK_ICON_SIZE_MENU); + //gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mitem), mim); + gtk_menu_shell_append(GTK_MENU_SHELL(algomenu), mitem); + gtk_widget_show (mitem); + g_signal_connect(G_OBJECT(mitem), "activate", G_CALLBACK(on_track_angle_simplify), Mapper); + + // item "Properties" + mitem = gtk_image_menu_item_new_with_label("Track Properties"); + //mim = gtk_image_new_from_stock(GTK_STOCK_CUT, GTK_ICON_SIZE_MENU); + //gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mitem), mim); + gtk_menu_shell_append(GTK_MENU_SHELL(editmenu), mitem); + gtk_widget_show (mitem); + g_signal_connect(G_OBJECT(mitem), "activate", G_CALLBACK(on_properties_track), Mapper); + // item "Edit/View waypoints" + mitem = gtk_image_menu_item_new_with_label("Edit/View waypoints"); + //mim = gtk_image_new_from_stock(GTK_STOCK_CUT, GTK_ICON_SIZE_MENU); + //gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mitem), mim); + gtk_menu_shell_append(GTK_MENU_SHELL(editmenu), mitem); + gtk_widget_show (mitem); + g_signal_connect(G_OBJECT(mitem), "activate", G_CALLBACK(on_editview_waypoints), Mapper); + // item seperator + mitem = gtk_separator_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(editmenu), mitem); + gtk_widget_show (mitem); + // item "Record" + mitem = gtk_image_menu_item_new_with_label("Record"); + //mim = gtk_image_new_from_stock(GTK_STOCK_CUT, GTK_ICON_SIZE_MENU); + //gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mitem), mim); + gtk_menu_shell_append(GTK_MENU_SHELL(editmenu), mitem); + gtk_widget_show (mitem); + g_signal_connect(G_OBJECT(mitem), "activate", G_CALLBACK(on_record_track), Mapper); + // item seperator + mitem = gtk_separator_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(editmenu), mitem); + gtk_widget_show (mitem); + // item "Preferences" + mitem = gtk_image_menu_item_new_with_label("Preferences"); + mim = gtk_image_new_from_stock(GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mitem), mim); + gtk_menu_shell_append(GTK_MENU_SHELL(editmenu), mitem); + gtk_widget_show (mitem); + g_signal_connect(G_OBJECT(mitem), "activate", G_CALLBACK(do_preference_dialog), Mapper); + + // menu "View" + w=gtk_menu_item_new_with_label ("View"); + gtk_menu_shell_append (GTK_MENU_SHELL (mainmenu), w); + gtk_widget_show (w); + viewmenu = gtk_menu_new(); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), viewmenu); + // item "Show cities" + mitem = gtk_check_menu_item_new_with_label("Show cities"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mitem), TRUE); + gtk_menu_shell_append (GTK_MENU_SHELL (viewmenu), mitem); + gtk_widget_show (mitem); + g_signal_connect(G_OBJECT(mitem), "toggled", G_CALLBACK(do_show_cities_toggled), Mapper); + // item "Show waypoints" + mitem = gtk_check_menu_item_new_with_label("Show waypoints"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mitem), TRUE); + gtk_menu_shell_append (GTK_MENU_SHELL (viewmenu), mitem); + gtk_widget_show (mitem); + g_signal_connect(G_OBJECT(mitem), "toggled", G_CALLBACK(do_show_waypoints_toggled), Mapper); + // item "Show markers" + mitem = gtk_check_menu_item_new_with_label("Show markers"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mitem), FALSE); + gtk_menu_shell_append (GTK_MENU_SHELL (viewmenu), mitem); + gtk_widget_show (mitem); + g_signal_connect(G_OBJECT(mitem), "toggled", G_CALLBACK(do_show_markers_toggled), Mapper); + // item "Show scale" + mitem = gtk_check_menu_item_new_with_label("Show scale"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mitem), TRUE); + gtk_menu_shell_append (GTK_MENU_SHELL (viewmenu), mitem); + gtk_widget_show (mitem); + g_signal_connect(G_OBJECT(mitem), "toggled", G_CALLBACK(do_show_scale_toggled), Mapper); + + // menu "Tools" + w=gtk_menu_item_new_with_label ("Tools"); + gtk_menu_shell_append (GTK_MENU_SHELL (mainmenu), w); + gtk_widget_show (w); + toolsmenu = gtk_menu_new(); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (w), toolsmenu); + // item "Measure Distance" + mitem = gtk_image_menu_item_new_with_label("Measure distance"); + gtk_menu_shell_append (GTK_MENU_SHELL (toolsmenu), mitem); + gtk_widget_show (mitem); + //g_signal_connect(G_OBJECT(mitem), "activate", G_CALLBACK(do_preference_dialog), Mapper); + // item "Track length (total)" + mitem = gtk_image_menu_item_new_with_label("Track length"); + gtk_menu_shell_append (GTK_MENU_SHELL (toolsmenu), mitem); + gtk_widget_show (mitem); + g_signal_connect(G_OBJECT(mitem), "activate", G_CALLBACK(on_track_length), Mapper); + // item "Trackpoint distance" + mitem = gtk_image_menu_item_new_with_label("Trackpoint distance"); + gtk_menu_shell_append (GTK_MENU_SHELL (toolsmenu), mitem); + gtk_widget_show (mitem); + //g_signal_connect(G_OBJECT(mitem), "activate", G_CALLBACK(do_preference_dialog), Mapper); + + /* toolbar and buttons */ + tb = gtk_toolbar_new(); + tt = gtk_tooltips_new(); + gtk_container_add (GTK_CONTAINER (mvb), tb); + gtk_box_set_child_packing(GTK_BOX(mvb), tb, FALSE, FALSE, 2, GTK_PACK_START); + gtk_widget_show(tb); + /* auto zoom */ + w = load_icon("pixmaps/stock_zoom-page-width.png"); + gtk_widget_show(w); + ti = gtk_tool_button_new(w, "Auto Zoom"); + gtk_toolbar_insert(GTK_TOOLBAR(tb), ti, -1); + gtk_widget_show(GTK_WIDGET(ti)); + g_signal_connect(G_OBJECT(ti), "clicked", G_CALLBACK(auto_scale), Mapper); + gtk_tool_item_set_tooltip (ti, tt, "Auto Zoom", "Auto Zoom complete data to view area"); + /* zoom in */ + w = load_icon("pixmaps/stock_zoom-in.png"); + gtk_widget_show(w); + ti = gtk_tool_button_new(w, "Zoom In"); + gtk_toolbar_insert(GTK_TOOLBAR(tb), ti, -1); + gtk_widget_show(GTK_WIDGET(ti)); + g_signal_connect(G_OBJECT(ti), "clicked", G_CALLBACK(zoom_in_cb), Mapper); + gtk_tool_item_set_tooltip (ti, tt, "Zoom in", "Zoom in, make it larger"); + /* zoom out */ + w = load_icon("pixmaps/stock_zoom-out.png"); + gtk_widget_show(w); + ti = gtk_tool_button_new(w, "Zoom Out"); + gtk_toolbar_insert(GTK_TOOLBAR(tb), ti, -1); + gtk_widget_show(GTK_WIDGET(ti)); + g_signal_connect(G_OBJECT(ti), "clicked", G_CALLBACK(zoom_out_cb), Mapper); + gtk_tool_item_set_tooltip (ti, tt, "Zoom out", "Zoom in, make it smaller"); + /* zoom selection */ + w = load_icon("pixmaps/stock_zoom-object.png"); + gtk_widget_show(w); + ti = gtk_radio_tool_button_new(NULL); + gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(ti), w); + gtk_tool_button_set_label(GTK_TOOL_BUTTON(ti), "Zoom Selection"); + gtk_toolbar_insert(GTK_TOOLBAR(tb), ti, -1); + gtk_widget_show(GTK_WIDGET(ti)); + g_signal_connect(G_OBJECT(ti), "clicked", G_CALLBACK(zoom_area_cb), Mapper); + Mapper->zoom_area_toggle_button = GTK_RADIO_TOOL_BUTTON(ti); + gtk_tool_item_set_tooltip (ti, tt, "Zoom selection", "Zoom selected area to maxmum view size"); + /* move */ + w = load_icon("pixmaps/stock-tool-move-22.png"); + gtk_widget_show(w); + ti = gtk_radio_tool_button_new(gtk_radio_tool_button_get_group(GTK_RADIO_TOOL_BUTTON(ti))); + gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(ti), w); + gtk_tool_button_set_label(GTK_TOOL_BUTTON(ti), "Move"); + gtk_toolbar_insert(GTK_TOOLBAR(tb), ti, -1); + gtk_widget_show(GTK_WIDGET(ti)); + g_signal_connect(G_OBJECT(ti), "clicked", G_CALLBACK(move_cb), Mapper); + Mapper->move_toggle_button = GTK_RADIO_TOOL_BUTTON(ti); + gtk_tool_item_set_tooltip (ti, tt, "Move", "Move the viewing area"); + /* seperator */ + ti = gtk_separator_tool_item_new(); + gtk_toolbar_insert(GTK_TOOLBAR(tb), ti, -1); + gtk_widget_show(GTK_WIDGET(ti)); + /* select trackpoint */ + w = load_icon("pixmaps/stock_draw-selection.png"); + gtk_widget_show(w); + ti = gtk_radio_tool_button_new(gtk_radio_tool_button_get_group(GTK_RADIO_TOOL_BUTTON(Mapper->move_toggle_button))); + gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(ti), w); + gtk_tool_button_set_label(GTK_TOOL_BUTTON(ti), "Select Trackpoint"); + gtk_toolbar_insert(GTK_TOOLBAR(tb), ti, -1); + gtk_widget_show(GTK_WIDGET(ti)); + g_signal_connect(G_OBJECT(ti), "clicked", G_CALLBACK(on_select_trackpoint_cb), Mapper); + Mapper->move_toggle_button = GTK_RADIO_TOOL_BUTTON(ti); + gtk_tool_item_set_tooltip (ti, tt, "Select point(s)", "Select track point(s)"); + /* select region */ + w = load_icon("pixmaps/stock_frame.png"); + gtk_widget_show(w); + ti = gtk_radio_tool_button_new(gtk_radio_tool_button_get_group(GTK_RADIO_TOOL_BUTTON(Mapper->move_toggle_button))); + gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(ti), w); + gtk_tool_button_set_label(GTK_TOOL_BUTTON(ti), "Select Region"); + gtk_toolbar_insert(GTK_TOOLBAR(tb), ti, -1); + gtk_widget_show(GTK_WIDGET(ti)); + g_signal_connect(G_OBJECT(ti), "clicked", G_CALLBACK(on_select_region_cb), Mapper); + Mapper->move_toggle_button = GTK_RADIO_TOOL_BUTTON(ti); + gtk_tool_item_set_tooltip (ti, tt, "Select point(s)", "Select point(s) in region"); + /* move trackpoint */ + w = load_icon("pixmaps/stock_node-move.png"); + gtk_widget_show(w); + ti = gtk_radio_tool_button_new(gtk_radio_tool_button_get_group(GTK_RADIO_TOOL_BUTTON(Mapper->move_toggle_button))); + gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(ti), w); + gtk_tool_button_set_label(GTK_TOOL_BUTTON(ti), "Move Trackpoint"); + gtk_toolbar_insert(GTK_TOOLBAR(tb), ti, -1); + gtk_widget_show(GTK_WIDGET(ti)); + g_signal_connect(G_OBJECT(ti), "clicked", G_CALLBACK(on_move_trackpoint_cb), Mapper); + Mapper->move_toggle_button = GTK_RADIO_TOOL_BUTTON(ti); + gtk_tool_item_set_tooltip (ti, tt, "Move point(s)", "Move selected track point(s)"); + /* split track at selected nodes */ + w = load_icon("pixmaps/stock_node-curve-split.png"); + gtk_widget_show(w); + ti = gtk_tool_button_new(w, "Split Track"); + gtk_toolbar_insert(GTK_TOOLBAR(tb), ti, -1); + gtk_widget_show(GTK_WIDGET(ti)); + g_signal_connect(G_OBJECT(ti), "clicked", G_CALLBACK(on_split_track), Mapper); + gtk_tool_item_set_tooltip (ti, tt, "Split at selection(s)", "Split track at selected track point(s)"); + /* delete selected nodes */ + w = load_icon("pixmaps/stock_node-delete.png"); + gtk_widget_show(w); + ti = gtk_tool_button_new(w, "Delete Node(s)"); + gtk_toolbar_insert(GTK_TOOLBAR(tb), ti, -1); + gtk_widget_show(GTK_WIDGET(ti)); + g_signal_connect(G_OBJECT(ti), "clicked", G_CALLBACK(on_delete_nodes_cb), Mapper); + gtk_tool_item_set_tooltip (ti, tt, "Delete node(s)", "Delete selected track point(s)"); + /* seperator */ + ti = gtk_separator_tool_item_new(); + gtk_toolbar_insert(GTK_TOOLBAR(tb), ti, -1); + gtk_widget_show(GTK_WIDGET(ti)); + /* delete track */ + w = load_icon("pixmaps/stock_delete.png"); + gtk_widget_show(w); + ti = gtk_tool_button_new(w, "Delete Track"); + gtk_toolbar_insert(GTK_TOOLBAR(tb), ti, -1); + gtk_widget_show(GTK_WIDGET(ti)); + g_signal_connect(G_OBJECT(ti), "clicked", G_CALLBACK(on_track_delete), Mapper); + gtk_tool_item_set_tooltip (ti, tt, "Delete track", "Delete selected track"); + + gtk_tooltips_enable(tt); + + mhb = gtk_hbox_new(FALSE, 0); + gtk_container_add (GTK_CONTAINER(mvb), mhb); + gtk_widget_show(mhb); + + /* track list in treeview */ + w = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (w), GTK_SHADOW_ETCHED_IN); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_box_pack_start (GTK_BOX (mhb), w, FALSE, FALSE, 0); + gtk_widget_show(w); + tree_model = GTK_TREE_MODEL(gtk_list_store_new (3, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER)); + Mapper->track_treeview = gtk_tree_view_new_with_model (tree_model); +#ifdef HILDON + g_object_set(G_OBJECT(Mapper->track_treeview), "allow-checkbox-mode", FALSE, NULL); + gtk_tree_view_set_hover_selection(GTK_TREE_VIEW(Mapper->track_treeview), FALSE); +#endif + gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (Mapper->track_treeview), TRUE); + g_object_unref (tree_model); + gtk_container_add (GTK_CONTAINER (w), Mapper->track_treeview); + gtk_widget_show(Mapper->track_treeview); + add_columns(GTK_TREE_VIEW (Mapper->track_treeview), Mapper); + + /* drawing area for drawing the tracks */ + Mapper->drawarea = gtk_drawing_area_new(); + gtk_container_add (GTK_CONTAINER (mhb), Mapper->drawarea); + gtk_box_set_child_packing(GTK_BOX(mhb), Mapper->drawarea, TRUE, TRUE, 2, GTK_PACK_START); + gtk_widget_show(Mapper->drawarea); + gtk_widget_set_events (Mapper->drawarea, (GDK_EXPOSURE_MASK)); + g_signal_connect(G_OBJECT(Mapper->drawarea), "expose-event", G_CALLBACK(mappix_expose), Mapper); + g_signal_connect(G_OBJECT(Mapper->drawarea), "enter-notify-event", G_CALLBACK(mappix_drfocus_in), Mapper); + g_signal_connect(G_OBJECT(Mapper->drawarea), "leave-notify-event", G_CALLBACK(mappix_drfocus_out), Mapper); + gtk_widget_realize(Mapper->drawarea); + Mapper->red_gc = gdk_gc_new (Mapper->drawarea->window); + color.red = 65535; // full red + color.green = 0; + color.blue = 0; + gdk_gc_set_rgb_fg_color (Mapper->red_gc, &color); + Mapper->blue_gc = gdk_gc_new (Mapper->drawarea->window); + color.red = 0; + color.green = 0; + color.blue = 65535; + gdk_gc_set_rgb_fg_color (Mapper->blue_gc, &color); + Mapper->select_gc = gdk_gc_new (Mapper->drawarea->window); + color.red = 65535; + color.green = 65535; + color.blue = 0; + gdk_gc_set_rgb_fg_color (Mapper->select_gc, &color); + gdk_gc_set_function(Mapper->select_gc, GDK_XOR); + g_signal_connect(G_OBJECT(Mapper->drawarea), "configure-event", G_CALLBACK(mappix_configure), Mapper); + + Mapper->statbar = gtk_statusbar_new(); + Mapper->lstatid = 0; + gtk_container_add (GTK_CONTAINER (mvb), Mapper->statbar); + gtk_widget_show(Mapper->statbar); + gtk_box_set_child_packing(GTK_BOX(mvb), Mapper->statbar, FALSE, FALSE, 2, GTK_PACK_START); + + Mapper->lstatid = gtk_statusbar_get_context_id(GTK_STATUSBAR(Mapper->statbar), "pcoord"); + gtk_statusbar_push(GTK_STATUSBAR(Mapper->statbar), Mapper->lstatid, "none"); + + g_signal_connect (G_OBJECT (Mapper->drawarea), "motion_notify_event", G_CALLBACK (motion_notify_event), Mapper); + g_signal_connect (G_OBJECT (Mapper->drawarea), "button_press_event", G_CALLBACK (button_press_event), Mapper); + g_signal_connect (G_OBJECT (Mapper->drawarea), "button_release_event", G_CALLBACK (button_press_event), Mapper); + gtk_widget_add_events (Mapper->drawarea, GDK_EXPOSURE_MASK + | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK | GDK_BUTTON_PRESS_MASK + | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_RELEASE_MASK); + + gtk_widget_show (Mapper->mainwin); +#ifdef HILDON + gtk_widget_show(GTK_WIDGET(Mapper->hildon_app)); +#endif + +return 0; +} diff --git a/mappix-ui.h b/mappix-ui.h new file mode 100644 index 0000000..eb0d39d --- /dev/null +++ b/mappix-ui.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * mappix-ui.h + * + * Sun Jun 4 19:06:50 2006 + * Copyright 2006 User + * Email + ****************************************************************************/ + +/* + * 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. + */ + +#ifndef _MAPPIX_UI_H +#define _MAPPIX_UI_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +int create_main_window(mapwin *); +gint close_application( GtkWidget *, GdkEvent *, gpointer); + + +#ifdef __cplusplus +} +#endif + +#endif /* _MAPPIX_UI_H */ diff --git a/mappix-waypoints.c b/mappix-waypoints.c new file mode 100644 index 0000000..e98c728 --- /dev/null +++ b/mappix-waypoints.c @@ -0,0 +1,365 @@ +/* + * mappix-waypoints + * + * Copyright (C) 2006,2007 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "mappix.h" +#include "geo-tools.h" + + +static void +fill_liststore(GtkListStore *store, GList *waypoints) +{ +GtkTreeIter iter; +GList *lp; +waypoint *wpdata; +gdouble lat, lon; + + if (waypoints == NULL) + return; + if (waypoints->data == NULL) + return; + + lp = waypoints; + do { + wpdata = (waypoint *)lp->data; + unit2latlon(wpdata->unitx, wpdata->unity, lat, lon); + gtk_list_store_append(store, &iter); + gtk_list_store_set (store, &iter, 0, wpdata->name, 1, lat, 2, lon, 3, lp, -1); + } while ((lp = g_list_next(lp))); +} + + +static void +wp_tv_add_columns (GtkTreeView *treeview, mapwin *Mapper) +{ +GtkCellRenderer *renderer; +GtkTreeViewColumn *column; +// GtkTreeModel *model; + + // model = gtk_tree_view_get_model(Mapper->track_treeview); + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Name", renderer, "text", 0, NULL); + //gtk_tree_view_column_set_sizing (GTK_TREE_VIEW_COLUMN (column), GTK_TREE_VIEW_COLUMN_FIXED); + //gtk_tree_view_column_set_fixed_width (GTK_TREE_VIEW_COLUMN (column), 50); + gtk_tree_view_append_column (treeview, column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Latitude", renderer, "text", 1, NULL); + // gtk_tree_view_column_set_sort_column_id (column, 1); + gtk_tree_view_append_column (treeview, column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Longitude", renderer, "text", 2, NULL); + // gtk_tree_view_column_set_sort_column_id (column, 1); + gtk_tree_view_append_column (treeview, column); + + gtk_tree_view_set_search_column (treeview, 0); +} + + +static void +delete_waypoint (GtkTreeView *treeview, GtkListStore *store, GtkWidget *parent, gpointer user_data) +{ +mapwin *Mapper = (mapwin *)user_data; +waypoint *wpdata; +GtkTreeSelection *tree_selection; +GtkTreeIter iter; +GList *lp; +gint res; +GtkWidget *dialog; + + tree_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + if (!gtk_tree_selection_get_selected(tree_selection, NULL, &iter)) + return; + gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 3, &lp, -1); + wpdata = lp->data; + + dialog = gtk_message_dialog_new(GTK_WINDOW(gtk_widget_get_toplevel(parent)), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_YES_NO, + "Are you sure you want to delete waypoint\n%s", wpdata->name); + 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", wpdata->name); + gtk_list_store_remove(GTK_LIST_STORE(store), &iter); + + Mapper->waypoints = g_list_remove(lp, wpdata); + g_free(wpdata); +} + + +static void +edit_waypoint (GtkTreeView *treeview, GtkListStore *store, GtkWidget *parent, gpointer user_data) +{ +// mapwin *Mapper = (mapwin *)user_data; +waypoint *wpdata; +GtkTreeSelection *tree_selection; +GtkTreeIter iter; +GList *lp; +gint result; +GtkWidget *dialog, *hbox, *stock, *table, *label; +GtkWidget *entry_name, *entry_lat, *entry_lon; +gdouble lat, lon; +const gchar *tmp_name; + + tree_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + if (!gtk_tree_selection_get_selected(tree_selection, NULL, &iter)) + return; + gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 3, &lp, -1); + wpdata = lp->data; + unit2latlon(wpdata->unitx, wpdata->unity, lat, lon); + + dialog = gtk_dialog_new_with_buttons("Edit waypoint", GTK_WINDOW(gtk_widget_get_toplevel(parent)), + 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 ("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), wpdata->name); + 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 ("Latitude"); + gtk_table_attach_defaults (GTK_TABLE (table), + label, + 0, 1, 1, 2); + entry_lat = gtk_spin_button_new_with_range(0.0, 360.0, 0.00000001); + gtk_spin_button_set_digits(GTK_SPIN_BUTTON(entry_lat), 8); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(entry_lat), lat); + gtk_table_attach_defaults (GTK_TABLE (table), entry_lat, 1, 2, 1, 2); + + label = gtk_label_new ("Longitude"); + gtk_table_attach_defaults (GTK_TABLE (table), + label, + 0, 1, 2, 3); + entry_lon = gtk_spin_button_new_with_range(0.0, 360.0, 0.00000001); + gtk_spin_button_set_digits(GTK_SPIN_BUTTON(entry_lon), 8); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(entry_lon), lon); + gtk_table_attach_defaults (GTK_TABLE (table), entry_lon, 1, 2, 2, 3); + + 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(wpdata->name); + wpdata->name = g_strdup(tmp_name); + lat = gtk_spin_button_get_value(GTK_SPIN_BUTTON(entry_lat)); + lon = gtk_spin_button_get_value(GTK_SPIN_BUTTON(entry_lon)); + latlon2unit(lat, lon, wpdata->unitx, wpdata->unity) + gtk_list_store_set(GTK_LIST_STORE(store), &iter, 0, wpdata->name, 1, lat, 2, lon, -1); + break; + default: + break; + } + gtk_widget_destroy (dialog); +} + + +static void +new_waypoint (GtkTreeView *treeview, GtkListStore *store, GtkWidget *parent, gpointer user_data) +{ +mapwin *Mapper = (mapwin *)user_data; +waypoint *wpdata; +GtkTreeIter iter; +gint result; +GtkWidget *dialog, *hbox, *stock, *table, *label; +GtkWidget *entry_name, *entry_lat, *entry_lon; +gdouble lat, lon; +const gchar *tmp_name; + + dialog = gtk_dialog_new_with_buttons("Edit waypoint", GTK_WINDOW(gtk_widget_get_toplevel(parent)), + 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); + + unit2latlon(Mapper->xmin, Mapper->ymin, lat, lon); + + label = gtk_label_new ("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), "Label"); + 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 ("Latitude"); + gtk_table_attach_defaults (GTK_TABLE (table), + label, + 0, 1, 1, 2); + entry_lat = gtk_spin_button_new_with_range(0.0, 360.0, 0.00000001); + gtk_spin_button_set_digits(GTK_SPIN_BUTTON(entry_lat), 8); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(entry_lat), lat); + gtk_table_attach_defaults (GTK_TABLE (table), entry_lat, 1, 2, 1, 2); + + label = gtk_label_new ("Longitude"); + gtk_table_attach_defaults (GTK_TABLE (table), + label, + 0, 1, 2, 3); + entry_lon = gtk_spin_button_new_with_range(0.0, 360.0, 0.00000001); + gtk_spin_button_set_digits(GTK_SPIN_BUTTON(entry_lon), 8); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(entry_lon), lon); + gtk_table_attach_defaults (GTK_TABLE (table), entry_lon, 1, 2, 2, 3); + + 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)); + wpdata = (waypoint *)g_malloc(sizeof(waypoint)); + wpdata->name = g_strdup(tmp_name); + lat = gtk_spin_button_get_value(GTK_SPIN_BUTTON(entry_lat)); + lon = gtk_spin_button_get_value(GTK_SPIN_BUTTON(entry_lon)); + latlon2unit(lat, lon, wpdata->unitx, wpdata->unity) + Mapper->waypoints = g_list_prepend(Mapper->waypoints, wpdata); + gtk_list_store_append(store, &iter); + gtk_list_store_set(GTK_LIST_STORE(store), &iter, 0, wpdata->name, 1, lat, 2, lon, -1); + break; + default: + break; + } + gtk_widget_destroy (dialog); +} + + +void +on_editview_waypoints (GtkWidget *widget, gpointer user_data) +{ +mapwin *Mapper = (mapwin *)user_data; +GtkWidget *dialog; +gint dresult; +GtkWidget *w; +GtkWidget *wp_treeview; +GtkTreeModel *tree_model; +gboolean changed=FALSE; + + dialog = gtk_dialog_new_with_buttons ("My dialog", GTK_WINDOW(gtk_widget_get_toplevel(Mapper->mainwin)), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + "Edit", 1, + "Delete", 2, + "New", 3, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + NULL); + + /* track list in treeview */ + w = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (w), GTK_SHADOW_ETCHED_IN); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), w, TRUE, TRUE, 0); + + gtk_widget_show(w); + tree_model = GTK_TREE_MODEL(gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_POINTER)); + wp_treeview = gtk_tree_view_new_with_model (tree_model); +#if 0 +#ifdef HILDON + g_object_set(G_OBJECT(Mapper->track_treeview), "allow-checkbox-mode", FALSE, NULL); + gtk_tree_view_set_hover_selection(GTK_TREE_VIEW(Mapper->track_treeview), FALSE); +#endif +#endif + gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (wp_treeview), TRUE); + g_object_unref (tree_model); + gtk_container_add (GTK_CONTAINER (w), wp_treeview); + gtk_widget_show(wp_treeview); + wp_tv_add_columns(GTK_TREE_VIEW (wp_treeview), Mapper); + + fill_liststore(GTK_LIST_STORE(tree_model), Mapper->waypoints); + + gtk_widget_show_all (dialog); + + dresult = 0; + while (dresult >= 0) { + dresult = gtk_dialog_run(GTK_DIALOG(dialog)); + + switch (dresult) { + case 1: + edit_waypoint(GTK_TREE_VIEW(wp_treeview), GTK_LIST_STORE(tree_model), dialog, Mapper); + changed = TRUE; + break; + case 2: + delete_waypoint(GTK_TREE_VIEW(wp_treeview), GTK_LIST_STORE(tree_model), dialog, Mapper); + changed = TRUE; + break; + case 3: + new_waypoint(GTK_TREE_VIEW(wp_treeview), GTK_LIST_STORE(tree_model), dialog, Mapper); + changed = TRUE; + break; + default: + break; + } + } + + gtk_widget_hide(dialog); + + gtk_widget_destroy(dialog); + if (changed) + do_redraw(Mapper); +} diff --git a/mappix-waypoints.h b/mappix-waypoints.h new file mode 100644 index 0000000..42102bf --- /dev/null +++ b/mappix-waypoints.h @@ -0,0 +1,27 @@ +/* + * mappix-waypoints.h + * + * 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. + */ + +#ifndef _MAPPIX_WAYPOINTS_H +#define _MAPPIX_WAYPOINTS_H + +void on_editview_waypoints (GtkWidget *, gpointer ); + +#endif diff --git a/mappix.c b/mappix.c new file mode 100644 index 0000000..0a99c4d --- /dev/null +++ b/mappix.c @@ -0,0 +1,53 @@ +/* + * + * mappix + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +/* we don't do much here, mainly fire up the UI */ + +#include + +#include "mappix.h" + +int main(int argc, char **argv) +{ +mapwin Mapper; +#ifdef HILDON +osso_context_t *context; +#endif + + gtk_init (&argc, &argv); +#ifdef HILDON + context = osso_initialize (OSSO_SERVICE_NAME, APP_VER, TRUE, NULL); + g_assert(context); +#endif + + Mapper.tracklist = NULL; + Mapper.waypoints = NULL; + Mapper.cities = NULL; + + create_main_window(&Mapper); + + gtk_main(); + +return 0; +} diff --git a/mappix.h b/mappix.h new file mode 100644 index 0000000..c667a61 --- /dev/null +++ b/mappix.h @@ -0,0 +1,133 @@ +/* + * mappix + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef _MAPPIX_H +#define _MAPPIX_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + +#include +#include +#include +#include + +#ifdef HILDON +#include +#include +#include +#include + + +#define APP_NAME "mappix" +#define APP_VER "0.1-1" +#define OSSO_SERVICE_NAME "mappix" +#endif + +#include "trackdata.h" + +/* what are we doing right now? */ +typedef enum { + MAPPIX_TOOL_NONE, + MAPPIX_TOOL_ZOOM_REGION, + MAPPIX_TOOL_SELECT_REGION, + MAPPIX_TOOL_MOVE, + MAPPIX_TOOL_SELECT_TRACKPOINT, + MAPPIX_TOOL_MOVE_TRACKPOINT +} MappixToolmode; + +typedef enum { + MARKER_TYPE_NONE, + MARKER_TYPE_FILLED_SQUARE, + MARKER_TYPE_SQUARE, + MARKER_TYPE_CROSS, + MARKER_SELECTED = 0x80 +} MappixMarkerType; + +// to avoid globals we pass around a monster-struct +typedef struct { + GtkWidget *mainwin; +#ifdef HILDON + HildonApp *hildon_app; + HildonAppView *hildon_mainview; +#endif + GdkCursor *cursor_normal, *cursor_mover, *cursor_crosshair, *cursor_hand, *cursor_select; + GtkWidget *track_treeview; + GtkWidget *drawarea; + GdkGC *red_gc; + GdkGC *blue_gc; + GdkGC *select_gc; + GdkPixmap *mappix; + GdkPixmap *mappix_blank; + GtkWidget *statbar; + GtkRadioToolButton *zoom_area_toggle_button, *move_toggle_button; + gint linewidth; + MappixMarkerType marker_type; // 0=no marker, 1=filled square, 2=non-filled square, 3=cross + gint marker_size; + gboolean show_marker; + guint lstatid; // ID of last statusbar message + gint canvas_x; + gint canvas_y; + double xscale; + double yscale; + double scale; + gint xoffset; + gint yoffset; + guint pointer_x, pointer_y; // scaled coordinates + guint mouse_x, mouse_y; // unscaled coordinates + guint mouse_px, mouse_py; // unscaled coordinates of last mouse button press + gint xmin, xmax; + gint ymin, ymax; + MappixToolmode toolmode; + gboolean modified; + gboolean use_idledraw; + gboolean draw_idle_active; // we redraw the tracks in idle loop + // gboolean use_ellipse_projection; + gboolean draw_selection; + gboolean move_selection; + GList *tracklist; + GList *waypoints; + GList *cities; + gboolean show_waypoints; + gboolean show_cities; + gboolean show_scale; +} mapwin; + + +#include "mappix-ui.h" +#include "mappix-trackdraw.h" +#include "mappix-trackedit.h" +#include "mappix-gpxreader.h" +#include "mappix-trackrecord.h" +#include "mappix-fileio.h" +#include "mappix-waypoints.h" + +#ifdef __cplusplus +} +#endif + +#endif /* _MAPPIX_H */ diff --git a/nmeap.h b/nmeap.h new file mode 100644 index 0000000..b08e764 --- /dev/null +++ b/nmeap.h @@ -0,0 +1,218 @@ +/* +Copyright (c) 2005, David M Howard (daveh at dmh2000.com) +All rights reserved. + +This product is licensed for use and distribution under the BSD Open Source License. +see the file COPYING for more details. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef __NMEAP_H__ +#define __NMEAP_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define NDEBUG 1 + +/* +============================================ +COMPILE TIME CONFIGURATION CONSTANTS +============================================ +*/ + +/* these constants affect the size of the context object. tweak them as desired but know what you are doing */ + +/** maximum number of sentence parsers supported */ +#define NMEAP_MAX_SENTENCES 8 +/** length of sentence name. leave this at 5 unless you really know what you are doing */ +#define NMEAP_MAX_SENTENCE_NAME_LENGTH 5 +/** max length of a complete sentence. the standard says 82 bytes, but its probably better to go at least 128 since + * some units don't adhere to the 82 bytes especially for proprietary sentences */ +#define NMEAP_MAX_SENTENCE_LENGTH 255 +/** max tokens in one sentence. 24 is enough for any standard sentence */ +#define NMEAP_MAX_TOKENS 24 + +/* predefined message ID's */ + +/* GGA MESSAGE ID */ +#define NMEAP_GPGGA 1 +/* RMC MESSAGE ID */ +#define NMEAP_GPRMC 2 + +/** user defined parsers should make ID numbers using NMEAP_USER as the base value, plus some increment */ +#define NMEAP_USER 100 + +/* forward references */ +struct nmeap_context; +struct nmeap_sentence; + +/* +============================================ +CALLOUTS +============================================ +*/ + +/** + * sentence callout function type + * a callout is fired for each registered sentence type + * the callout gets the object context and a pointer to sentence specific data. + * the callout must cast the 'sentence_data' to the appropriate type for that callout + * @param context nmea object context + * @param sentence_data sentence specific data +*/ +typedef void (*nmeap_callout_t)(struct nmeap_context *context,void *sentence_data,void *user_data); + +/** + * sentence parser function type + * stored in the object context and called internally when the sentence name matches + * the specified value + * the callout gets the object context and a pointer to sentence specific data. + * the callout must cast the 'sentence_data' to the appropriate type for that callout + * @param context nmea object context + * @param sentence_data sentence specific data + * @return id of sentence (each sentence parser knows its own ID) +*/ +typedef int (*nmeap_sentence_parser_t)(struct nmeap_context *context,struct nmeap_sentence *sentence); + + +/* ==== opaque types === */ +#include "nmeap_def.h" + + +/* +============================================ +STANDARD SENTENCE DATA STRUCTURES +============================================ +*/ + +/** extracted data from a GGA message */ +struct nmeap_gga { + double latitude; + double longitude; + double altitude; + unsigned long time; + int satellites; + int quality; + double hdop; + double geoid; +}; +typedef struct nmeap_gga nmeap_gga_t; + +/** extracted data from an RMC message */ +struct nmeap_rmc { + unsigned long time; + char warn; + double latitude; + double longitude; + double speed; + double course; + unsigned long date; + double magvar; +}; + +typedef struct nmeap_rmc nmeap_rmc_t; + +/* +============================================ +METHODS +============================================ +*/ + +/** + * initialize an NMEA parser. call this function to initialize a user allocated context object + * @param context nmea object context. allocated by user statically or dynamically. + * @param user_data pointer to user defined data + * @return 0 if ok, -1 if initialization failed + */ +int nmeap_init(nmeap_context_t *context,void *user_data); + +/** + * register an NMEA sentence parser + * @param context nmea object context + * @param sentence_name string matching the sentence name for this parser. e.g. "GPGGA". not including the '$' + * @param sentence_parser parser function for this sentence + * @param sentence_callout callout triggered when this sentence is received and parsed. + * if null, no callout is triggered for this sentence + * @param sentence_data user allocated sentence specific data defined by the application. the parser uses + this data item to store the extracted data. This data object needs to persist over the life + of the parser, so be careful if allocated on the stack. + * @return 0 if registered ok, -1 if registration failed + */ +int nmeap_addParser(nmeap_context_t *context, + const char *sentence_name, + nmeap_sentence_parser_t sentence_parser, + nmeap_callout_t sentence_callout, + void *sentence_data + ); + +/** + * parse a buffer of nmea data. + * @param context nmea object context + * @param buffer buffer of input characters + * @param length [in,out] pointer to length of buffer. on return, contains number of characters not used for + * the current sentence + * @return -1 if error, 0 if the data did not complete a sentence, sentence code if a sentence was found in the stream + */ +int nmeap_parseBuffer(nmeap_context_t *context,const char *buffer,int *length); + +/** + * parse one character of nmea data. + * @param context nmea object context + * @param ch input character + * @return -1 if error, 0 if the data did not complete a sentence, sentence code if a sentence was found in the stream + */ +int nmeap_parse(nmeap_context_t *context,char ch); + + +/** + * built-in parser for GGA sentences. + * @param context nmea object context + * @param sentence sentence object for this parser + */ +int nmeap_gpgga(nmeap_context_t *context,nmeap_sentence_t *sentence); + +/** + * built-in parser for RMC sentences. + * @param context nmea object context + * @param sentence sentence object for this parser + */ +int nmeap_gprmc(nmeap_context_t *context,nmeap_sentence_t *sentence); + +/** + * extract latitude from 2 tokens in ddmm.mmmm,h format. + * @param plat pointer to token with numerical latitude + * @param phem pointer to token with hemisphere + * @return latitude in degrees and fractional degrees + */ +double nmeap_latitude(const char *plat,const char *phem); + + +/** + * extract longitude from 2 tokens in ddmm.mmmm,h format. + * @param plat pointer to token with numerical longitude + * @param phem pointer to token with hemisphere + * @return longitude in degrees and fractional degrees + */ +double nmeap_longitude(const char *plat,const char *phem); + +#ifdef __cplusplus +} // extern C +#endif + + +#endif + diff --git a/nmeap01.c b/nmeap01.c new file mode 100644 index 0000000..6080ef1 --- /dev/null +++ b/nmeap01.c @@ -0,0 +1,621 @@ +/* +Copyright (c) 2005, David M Howard (daveh at dmh2000.com) +All rights reserved. + +This product is licensed for use and distribution under the BSD Open Source License. +see the file COPYING for more details. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +/** + * nmeap01.c + * nmeap gps data parser + * + * see the file COPYING for terms of the licnese +*/ + + +#include +#include +#include +#include +#include +#include "nmeap.h" + +/* this only works if you are sure you have an upper case hex digit */ +#define HEXTOBIN(ch) ((ch <= '9') ? ch - '0' : ch - ('A' - 10)) + +/* forward references */ +int nmeap_init(nmeap_context_t *context,void *user_data); +int nmeap_addParser(nmeap_context_t *context, + const char *sentence_name, + nmeap_sentence_parser_t sentence_parser, + nmeap_callout_t sentence_callout, + void *sentence_data + ); +int nmeap_tokenize(nmeap_context_t *context); +int nmeap_process(nmeap_context_t *context); +int nmeap_parse(nmeap_context_t *context,char ch); +int nmeap_parseBuffer(nmeap_context_t *context,const char *buffer,int *length); + +/** + * get a latitude out of a pair of nmea tokens + */ +double nmeap_latitude(const char *plat,const char *phem) +{ + double lat; + int deg; + double min; + int ns; + + assert(plat != 0); + assert(phem != 0); + + if (*plat == 0) { + return 0.0; + } + if (*phem == 0) { + return 0.0; + } + + /* north lat is +, south lat is - */ + if (*phem == 'N') { + ns = 1; + } + else { + ns = -1; + } + + /* latitude is degrees, minutes, fractional minutes */ + /* no validation is performed on the token. it better be good.*/ + /* if it comes back 0.0 then probably the token was bad */ + lat = atof(plat); + + /* extract the degree part */ + deg = (int)(lat / 100.0); + + /* mask out the degrees */ + min = lat - (deg * 100.0); + + /* compute the actual latitude in degrees.decimal-degrees */ + lat = (deg + (min / 60.0)) * ns; + + return lat; +} + +/** + * get a longitude out of a pair of nmea tokens + */ +double nmeap_longitude(const char *plon,const char *phem) +{ + double lon; + int deg; + double min; + int ew; + + assert(plon != 0); + assert(phem != 0); + + if (*plon == 0) { + return 0.0; + } + if (*phem == 0) { + return 0.0; + } + + /* west long is negative, east long is positive */ + if (*phem == 'E') { + ew = 1; + } + else { + ew = -1; + } + + /* longitude is degrees, minutes, fractional minutes */ + /* no validation is performed on the token. it better be good.*/ + /* if it comes back 0.0 then probably the token was bad */ + lon = atof(plon); + + /* extract the degree part */ + deg = (int)(lon / 100.0); + + /* mask out the degrees */ + min = lon - (deg * 100.0); + + /* compute the actual lonitude in degrees.decimal-degrees */ + lon = (deg + (min / 60.0)) * ew; + + + return lon; +} + +/** + * get an altitude longitude out of a pair of nmea tokens + * ALTITUDE is returned in METERS + */ +double nmeap_altitude(const char *palt,const char *punits) +{ + double alt; + + if (*palt == 0) { + return 0.0; + } + + /* convert with no error checking */ + alt = atof(palt); + + if (*punits == 'M') { + /* already in meters */ + } + else if (*punits == 'F') { + /* convert to feet */ + alt = alt * 3.2808399; + } + + return alt; +} + +/** + * initialize an NMEA parser + */ +int nmeap_init(nmeap_context_t *context,void *user_data) +{ + assert(context != 0); + + memset(context,0,sizeof(*context)); + + context->user_data = user_data; + + return 0; +} + + +/** + * register an NMEA sentence parser + */ +int nmeap_addParser(nmeap_context_t *context, + const char *sentence_name, + nmeap_sentence_parser_t sentence_parser, + nmeap_callout_t sentence_callout, + void *sentence_data + ) +{ + nmeap_sentence_t *s = 0; + + /* runtime error */ + assert(context != 0); + + /* sentence capacity overflow */ + if (context->sentence_count >= NMEAP_MAX_SENTENCES) { + return -1; + } + + /* point at next empty sentence buffer */ + s = &context->sentence[context->sentence_count]; + + /* advance sentence data count */ + context->sentence_count++; + + /* clear the sentence data */ + memset(s,0,sizeof(*s)); + + /* name */ + strncpy(s->name,sentence_name,NMEAP_MAX_SENTENCE_NAME_LENGTH); + + /* parser */ + s->parser = sentence_parser; + + /* callout */ + s->callout = sentence_callout; + + /* data */ + s->data = sentence_data; + + return 0; +} + +/** + * tokenize a buffer + */ +int nmeap_tokenize(nmeap_context_t *context) +{ + char *s; + int tokens; + int state; + + /* first token is header. assume it is there */ + tokens = 0; + s = context->input; + context->token[tokens] = s; + + /* get rest of tokens */ + tokens = 1; + state = 0; + while((*s != 0)&&(tokens < NMEAP_MAX_TOKENS)) { + switch(state) { + case 0: + /* looking for end of a token */ + if (*s == ',') { + /* delimit at the comma */ + *s = 0; + /* new token */ + state = 1; + } + break; + case 1: + /* start of next token, might be another comma */ + context->token[tokens++] = s; + if (*s == ',') { + /* delimit at the comma */ + *s = 0; + } + else { + /* not a comma */ + state = 0; + } + break; + default: + state = 0; + break; + } + + // next character + s++; + } + return tokens; +} + +/** + * process a sentence + */ +int nmeap_process(nmeap_context_t *context) +{ + int id=0; + int i; + nmeap_sentence_t *s; + + /* copy the input to a debug buffer */ + /* remove debug_input when everything is working. */ + strncpy(context->debug_input,context->input,sizeof(context->debug_input)); + + /* tokenize the input */ + context->tokens = nmeap_tokenize(context); + + /* try to find a matching sentence parser */ + /* this search is O(n). it has a lot of potential for optimization, at the expense of complexity, if you have a lot of sentences */ + /* binary search instead of linear (have to keep sentences in sorted order) O(NlogN) */ + /* OR, when sentences are added, create a TRIE structure to find the names with a constant time search O(5) */ + for(i=0;isentence_count;i++) { + s = &context->sentence[i]; + assert(s != 0); + if (strncmp(context->input_name,s->name,5) == 0) { + /* found a match, call its parser */ + id = (*context->sentence[i].parser)(context,s); + if (id > 0) { + break; + } + } + } + + return id; +} + +/** + +-5-+ +---+ + v | v | + +------+ +------+ +------+ +------+ +------+ + | 0 |--$--> |1-hdr |--alnum--> |2-data|----\r-->| 6-LF |---\n--->| done |--> 0 + +------+ +------+ +------+ +------+ +------+ + | ^ + * +--------\r-------+ + V | + +------+ +------+ +------+ + |3-cks |--xdigit-->|4-cks |-xdigit->| 5-CR | + +------+ +------+ +------+ + +return to start conditions: +1. buffer overflow +2. invalid character for state + +checksum calculation +two hex digits represent the XOR of all characters between, but not +including, the "$" and "*". A checksum is required on some +sentences. + +*/ +int nmeap_parse(nmeap_context_t *context,char ch) +{ + int status = 0; + + /* check for input buffer overrun first to avoid duplicating code in the + individual states + */ + if (context->input_count >= (sizeof(context->input)-1)) { + /* input buffer overrun, restart state machine */ + context->input_state = 0; + /* reset input count */ + context->input_count = 0; + } + + /* store the byte */ + context->input[context->input_count] = ch; + + /* next buffer position */ + context->input_count++; + + /* run it through the lexical scanner */ + switch(context->input_state) { + /* LOOKING FOR $ */ + case 0: + if (ch == '$') { + /*look for id */ + context->input_state = 1; + context->ccks = 0; + context->icks = 0; + } + else { + /* header error, start over */ + context->err_hdr++; + context->input_state = 0; + context->input_count = 0; + } + break; + /* LOOKING FOR 5 CHARACTER SENTENCE ID */ + case 1: + /* allow numbers even though it isn't usually done */ + /* a proprietary id might have a numeral */ + if (isalnum(ch)) { + /* store name separately */ + context->input_name[context->input_count - 2] = ch; + /* checksum */ + context->ccks ^= ch; + /* end of header? */ + if (context->input_count >= 6) { + /* yes, get body */ + context->input_state = 2; + } + } + else { + /* bad character, start over */ + context->err_id++; + context->input_state = 0; + context->input_count = 0; + } + break; + /* LOOKING FOR CR OR CHECKSUM INDICATOR */ + case 2: + if (ch == '*') { + /* this sentence has a checksum */ + context->input_state = 3; + } + else if (ch == '\r') { + /* carriage return, no checksum, force a match */ + context->icks = 0; + context->ccks = 0; + context->input_state = 6; + } + else { + /* continue accumulating data */ + /* checksum */ + context->ccks ^= ch; + } + break; + /* LOOKING FOR FIRST CHECKSUM CHARACTER */ + case 3: + /* must be upper case hex digit */ + if (isxdigit(ch) && (ch <= 'F')) { + /* got first checksum byte */ + context->input_state = 4; + context->icks = HEXTOBIN(ch) << 4; + } + else { + /* input error, restart */ + context->err_cks++; + context->input_state = 0; + context->input_count = 0; + } + break; + /* LOOKING FOR SECOND CHECKSUM CHARACTER */ + case 4: + /* must be upper case hex digit */ + if (isxdigit(ch) && (ch <= 'F')) { + /* got second checksum byte */ + context->input_state = 5; + context->icks += HEXTOBIN(ch); + } + else { + /* input error, restart */ + context->err_cks++; + context->input_state = 0; + context->input_count = 0; + } + break; + /* LOOKING FOR CR */ + case 5: + if (ch == '\r') { + /* carriage return */ + context->input_state = 6; + } + else { + /* input error, restart */ + context->err_crl++; + context->input_state = 0; + context->input_count = 0; + } + break; + /* LOOKING FOR LINE FEED */ + case 6: + if (ch == '\n') { + /* linefeed, line complete */ + + /* delimit buffer */ + context->input[context->input_count] = 0; + + /* if the checksums match, process the sentence */ + if (context->ccks == context->icks) { + /* process */ + status = nmeap_process(context); + + /* count good messages */ + context->msgs++; + } + else { + /* count checksum errors */ + context->err_cks++; + } + + /* restart next time */ + context->input_state = 0; + context->input_count = 0; + } + else { + /* input error, restart */ + context->err_crl++; + context->input_state = 0; + context->input_count = 0; + } + break; + default: + context->err_unk++; + context->input_state = 0; + break; + } + + return status; +} + +/** + * parse a buffer of nmea data + */ +int nmeap_parseBuffer(nmeap_context_t *context,const char *buffer,int *length) +{ + int i; + int status; + int rem; + int tlen; + + tlen = *length; + rem = *length; + status = 0; + /* for each byte in the buffer */ + for(i=0;idata; + + /* if there is a data element, extract data from the tokens */ + if (gga != 0) { + gga->latitude = nmeap_latitude(context->token[2],context->token[3]); + gga->longitude = nmeap_longitude(context->token[4],context->token[5]); + gga->altitude = nmeap_altitude(context->token[9],context->token[10]); + gga->time = atoi(context->token[1]); + gga->satellites = atoi(context->token[7]); + gga->quality = atoi(context->token[6]); + gga->hdop = atof(context->token[8]); + gga->geoid = nmeap_altitude(context->token[11],context->token[12]); + } + +#ifndef NDEBUG + /* print raw input string */ + printf("%s",context->debug_input); + + /* print some validation data */ + printf("%s==%s %02x==%02x\n",context->input_name,sentence->name,context->icks,context->ccks); + + /* print the tokens */ + for(i=0;itokens;i++) { + printf("%d:%s\n",i,context->token[i]); + } +#endif + + /* if the sentence has a callout, call it */ + if (sentence->callout != 0) { + (*sentence->callout)(context,gga,context->user_data); + } + + return NMEAP_GPGGA; +} + +/** + * standard GPRMCntence parser + */ +int nmeap_gprmc(nmeap_context_t *context,nmeap_sentence_t *sentence) +{ +#ifndef NDEBUG + int i; +#endif + + /* get pointer to sentence data */ + nmeap_rmc_t *rmc = (nmeap_rmc_t *)sentence->data; + + /* if there is a data element, use it */ + if (rmc != 0) { + /* extract data from the tokens */ + rmc->time = atoi(context->token[1]); + rmc->warn = *context->token[2]; + rmc->latitude = nmeap_latitude(context->token[3],context->token[4]); + rmc->longitude = nmeap_longitude(context->token[5],context->token[6]); + rmc->speed = atof(context->token[7]); + rmc->course = atof(context->token[8]); + rmc->date = atoi(context->token[9]); + rmc->magvar = atof(context->token[10]); + } + +#ifndef NDEBUG + /* print raw input string */ + printf("%s",context->debug_input); + + /* print some validation data */ + printf("%s==%s %02x==%02x\n",context->input_name,sentence->name,context->icks,context->ccks); + + /* print the tokens */ + for(i=0;itokens;i++) { + printf("%d:%s\n",i,context->token[i]); + } +#endif + + /* if the sentence has a callout, call it */ + if (sentence->callout != 0) { + (*sentence->callout)(context,rmc,context->user_data); + } + + return NMEAP_GPRMC; +} + + diff --git a/nmeap_def.h b/nmeap_def.h new file mode 100644 index 0000000..c9d1a3c --- /dev/null +++ b/nmeap_def.h @@ -0,0 +1,72 @@ +/* +Copyright (c) 2005, David M Howard (daveh at dmh2000.com) +All rights reserved. + +This product is licensed for use and distribution under the BSD Open Source License. +see the file COPYING for more details. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef __NMEAP_DEF_H__ +#define __NMEAP_DEF_H__ + +/** + * context for a single sentence + */ +typedef struct nmeap_sentence { + char name[NMEAP_MAX_SENTENCE_NAME_LENGTH + 1]; + int id; + nmeap_sentence_parser_t parser; + nmeap_callout_t callout; + void *data; +} nmeap_sentence_t; + +/** + * parser context + */ +struct nmeap_context { + /** support up to 8 sentences */ + nmeap_sentence_t sentence[NMEAP_MAX_SENTENCES]; /* sentence descriptors */ + int sentence_count; /* number of initialized descriptors */ + + /** sentence input buffer */ + char input[NMEAP_MAX_SENTENCE_LENGTH + 1]; /* input line buffer */ + int input_count; /* index into 'input */ + int input_state; /* current lexical scanner state */ + char input_name[6]; /* sentence name */ + char icks; /* input checksum */ + char ccks; /* computed checksum */ + + /* tokenization */ + char *token[NMEAP_MAX_TOKENS]; /* list of delimited tokens */ + int tokens; /* list of tokens */ + + /** errors and debug. optimize these as desired */ + unsigned long msgs; /* count of good messages */ + unsigned long err_hdr; /* header error */ + unsigned long err_ovr; /* overrun error */ + unsigned long err_unk; /* unknown error */ + unsigned long err_id; /* bad character in id */ + unsigned long err_cks; /* bad checksum */ + unsigned long err_crl; /* expecting cr or lf, got something else */ + char debug_input[NMEAP_MAX_SENTENCE_LENGTH + 1]; /* input line buffer for debug */ + + /** opaque user data */ + void *user_data; +}; + +typedef struct nmeap_context nmeap_context_t; + +#endif /* __NMEAP_DEF_H__ */ diff --git a/pixmaps/stock-curve-free-16.png b/pixmaps/stock-curve-free-16.png new file mode 100644 index 0000000..995049f Binary files /dev/null and b/pixmaps/stock-curve-free-16.png differ diff --git a/pixmaps/stock-curve-smooth-16.png b/pixmaps/stock-curve-smooth-16.png new file mode 100644 index 0000000..fc5313d Binary files /dev/null and b/pixmaps/stock-curve-smooth-16.png differ diff --git a/pixmaps/stock-layer-to-imagesize-16.png b/pixmaps/stock-layer-to-imagesize-16.png new file mode 100644 index 0000000..7cbd755 Binary files /dev/null and b/pixmaps/stock-layer-to-imagesize-16.png differ diff --git a/pixmaps/stock-tool-iscissors-22.png b/pixmaps/stock-tool-iscissors-22.png new file mode 100644 index 0000000..fc29ae9 Binary files /dev/null and b/pixmaps/stock-tool-iscissors-22.png differ diff --git a/pixmaps/stock-tool-measure-22.png b/pixmaps/stock-tool-measure-22.png new file mode 100644 index 0000000..4c7f4d7 Binary files /dev/null and b/pixmaps/stock-tool-measure-22.png differ diff --git a/pixmaps/stock-tool-move-22.png b/pixmaps/stock-tool-move-22.png new file mode 100644 index 0000000..75da34d Binary files /dev/null and b/pixmaps/stock-tool-move-22.png differ diff --git a/pixmaps/stock_delete.png b/pixmaps/stock_delete.png new file mode 100644 index 0000000..6b638c5 Binary files /dev/null and b/pixmaps/stock_delete.png differ diff --git a/pixmaps/stock_draw-selection.png b/pixmaps/stock_draw-selection.png new file mode 100644 index 0000000..3ac980b Binary files /dev/null and b/pixmaps/stock_draw-selection.png differ diff --git a/pixmaps/stock_frame.png b/pixmaps/stock_frame.png new file mode 100644 index 0000000..bd9a3d5 Binary files /dev/null and b/pixmaps/stock_frame.png differ diff --git a/pixmaps/stock_node-add.png b/pixmaps/stock_node-add.png new file mode 100644 index 0000000..fbb4e96 Binary files /dev/null and b/pixmaps/stock_node-add.png differ diff --git a/pixmaps/stock_node-close-path.png b/pixmaps/stock_node-close-path.png new file mode 100644 index 0000000..460e1cc Binary files /dev/null and b/pixmaps/stock_node-close-path.png differ diff --git a/pixmaps/stock_node-convert.png b/pixmaps/stock_node-convert.png new file mode 100644 index 0000000..c048f5e Binary files /dev/null and b/pixmaps/stock_node-convert.png differ diff --git a/pixmaps/stock_node-corner-to-smooth.png b/pixmaps/stock_node-corner-to-smooth.png new file mode 100644 index 0000000..a7fabc3 Binary files /dev/null and b/pixmaps/stock_node-corner-to-smooth.png differ diff --git a/pixmaps/stock_node-corner.png b/pixmaps/stock_node-corner.png new file mode 100644 index 0000000..8825e0f Binary files /dev/null and b/pixmaps/stock_node-corner.png differ diff --git a/pixmaps/stock_node-curve-split.png b/pixmaps/stock_node-curve-split.png new file mode 100644 index 0000000..48b6ec4 Binary files /dev/null and b/pixmaps/stock_node-curve-split.png differ diff --git a/pixmaps/stock_node-delete.png b/pixmaps/stock_node-delete.png new file mode 100644 index 0000000..2298e08 Binary files /dev/null and b/pixmaps/stock_node-delete.png differ diff --git a/pixmaps/stock_node-mark-for-deletion.png b/pixmaps/stock_node-mark-for-deletion.png new file mode 100644 index 0000000..328d824 Binary files /dev/null and b/pixmaps/stock_node-mark-for-deletion.png differ diff --git a/pixmaps/stock_node-move.png b/pixmaps/stock_node-move.png new file mode 100644 index 0000000..bf39b82 Binary files /dev/null and b/pixmaps/stock_node-move.png differ diff --git a/pixmaps/stock_node-smooth-to-symmetrical.png b/pixmaps/stock_node-smooth-to-symmetrical.png new file mode 100644 index 0000000..3c3a63d Binary files /dev/null and b/pixmaps/stock_node-smooth-to-symmetrical.png differ diff --git a/pixmaps/stock_zoom-1.png b/pixmaps/stock_zoom-1.png new file mode 100644 index 0000000..8265f27 Binary files /dev/null and b/pixmaps/stock_zoom-1.png differ diff --git a/pixmaps/stock_zoom-in.png b/pixmaps/stock_zoom-in.png new file mode 100644 index 0000000..1ac4864 Binary files /dev/null and b/pixmaps/stock_zoom-in.png differ diff --git a/pixmaps/stock_zoom-next.png b/pixmaps/stock_zoom-next.png new file mode 100644 index 0000000..6ad66e6 Binary files /dev/null and b/pixmaps/stock_zoom-next.png differ diff --git a/pixmaps/stock_zoom-object.png b/pixmaps/stock_zoom-object.png new file mode 100644 index 0000000..0499416 Binary files /dev/null and b/pixmaps/stock_zoom-object.png differ diff --git a/pixmaps/stock_zoom-optimal.png b/pixmaps/stock_zoom-optimal.png new file mode 100644 index 0000000..cabc662 Binary files /dev/null and b/pixmaps/stock_zoom-optimal.png differ diff --git a/pixmaps/stock_zoom-out.png b/pixmaps/stock_zoom-out.png new file mode 100644 index 0000000..d67a87d Binary files /dev/null and b/pixmaps/stock_zoom-out.png differ diff --git a/pixmaps/stock_zoom-page-width.png b/pixmaps/stock_zoom-page-width.png new file mode 100644 index 0000000..0a54907 Binary files /dev/null and b/pixmaps/stock_zoom-page-width.png differ diff --git a/pixmaps/stock_zoom-page.png b/pixmaps/stock_zoom-page.png new file mode 100644 index 0000000..4ac8ca4 Binary files /dev/null and b/pixmaps/stock_zoom-page.png differ diff --git a/pixmaps/stock_zoom-previous.png b/pixmaps/stock_zoom-previous.png new file mode 100644 index 0000000..a484513 Binary files /dev/null and b/pixmaps/stock_zoom-previous.png differ diff --git a/pixmaps/stock_zoom-shift.png b/pixmaps/stock_zoom-shift.png new file mode 100644 index 0000000..8300f3c Binary files /dev/null and b/pixmaps/stock_zoom-shift.png differ diff --git a/pixmaps/stock_zoom.png b/pixmaps/stock_zoom.png new file mode 100644 index 0000000..5ba0ab1 Binary files /dev/null and b/pixmaps/stock_zoom.png differ diff --git a/trackdata.h b/trackdata.h new file mode 100644 index 0000000..c823263 --- /dev/null +++ b/trackdata.h @@ -0,0 +1,88 @@ +/* + * trackdata + * + * Copyright (C) 2006,2007 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef _TRACKDATA_H_ +#define _TRACKDATA_H_ + +/* track element */ +typedef struct { + // data + gint unitx; + gint unity; + gfloat elevation; + gchar *datetime; + gchar nrsat; // number of satellites in view + gfloat hdop; // horizontal delution + gfloat vdop; // vertital delution + gfloat pdop; // ??? other dilution + // attributes for UI + gboolean selected; +} trackdata; + +/* track types, LANEs are no. of lanes per direction */ +typedef enum { + UNKNOWN, + OUTLINE, // like borders + RIVER, + FOOTPATH, + AGRI, + CITY, + URBAN, + MOTORWAY, + HIGHWAY, +} tracktype; + + +/* tracklist element */ +typedef struct { + GList *track; // list of trackdata trackpoints + gboolean show; // if this track is to be drawn + gchar *name; // visible name of this track + gint xmax, ymax; // bounding box of this track + gint xmin, ymin; + tracktype type; +} track_head; + + +/* waypoint / POI */ +typedef struct { + gint unitx; + gint unity; + gfloat elevation; + gchar *name; +} waypoint; + + +/* + * cities + * Cities are stored in database, internal GList if cities only stores + * the cities inside the current view + */ + +typedef struct { + gint unitx; + gint unity; + gchar *name; + guint population; +} city; + +#endif