2 * This file is part of mapper
4 * Copyright (C) 2008 Kaj-Michael Lang
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
34 struct sql_select_stmt {
35 sqlite3_stmt *select_paths;
36 sqlite3_stmt *select_path_nodes;
38 sqlite3_stmt *insert_path;
39 sqlite3_stmt *insert_path_node;
41 sqlite3_stmt *delete_path;
42 sqlite3_stmt *delete_path_nodes;
44 static struct sql_select_stmt sql;
46 #define PATH_TABLE_PATHS "create table IF NOT EXISTS paths ( \
47 nid int primary key, \
52 #define PATH_TABLE_NODES "create table IF NOT EXISTS path_nodes ( \
62 name text default null, \
66 NEW_POINT, /* A new point was appended to track track */
67 NEW_BREAK, /* A break was appended to the track */
68 NEW_WAYPOINT, /* A new waypoint/marker has been added */
69 NEAR_WAYPOINT, /* We are near the next route waypoint, announce it */
70 REACHED_WAYPOINT, /* We have reached the next route waypoint, announce it */
71 REACHED_DESTINATION, /* We have reached the last route waypoint */
72 CLEARED, /* Path was cleared */
75 static guint32 signals[LAST_SIGNAL] = {0};
77 G_DEFINE_TYPE(Path, path, G_TYPE_OBJECT);
79 #define GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), PATH_TYPE, PathPrivate))
82 path_dispose(GObject *object)
84 g_debug("path_dispose");
86 G_OBJECT_CLASS(path_parent_class)->dispose(object);
90 path_finalize(GObject *object)
92 Path *path=PATH(object);
94 g_debug("path_finalize");
95 MACRO_PATH_FREE(*path);
102 g_free(path->author);
104 g_free(path->keywords);
106 g_free(path->copyright);
112 path_class_init(PathClass *klass)
114 GObjectClass *object_class=G_OBJECT_CLASS(klass);
116 g_debug("path_class_init");
118 object_class->dispose=path_dispose;
119 object_class->finalize=path_finalize;
120 /* g_type_class_add_private (klass, sizeof(PathPrivate)); */
122 signals[NEW_POINT]=g_signal_new("new-point", G_OBJECT_CLASS_TYPE(object_class),
123 G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(PathClass, new_point),
124 NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL);
125 signals[NEW_BREAK]=g_signal_new("new-break", G_OBJECT_CLASS_TYPE(object_class),
126 G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(PathClass, new_break),
127 NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL);
128 signals[NEW_WAYPOINT]=g_signal_new("new-waypoint", G_OBJECT_CLASS_TYPE(object_class),
129 G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(PathClass, new_waypoint),
130 NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL);
134 path_init(Path *path)
136 g_debug("path_init");
137 MACRO_PATH_INIT(*path);
141 path_new(PathType type, guint id)
145 p=g_object_new(PATH_TYPE, NULL);
153 path_free(Path *path)
155 g_return_if_fail(path);
156 g_object_unref(path);
160 path_clear(Path *path)
162 g_return_if_fail(path);
163 MACRO_PATH_FREE(*path);
164 path->length=path->avgspeed=0.0;
169 path_resize(Path *path, guint size)
171 g_return_val_if_fail(path, FALSE);
173 if (path->head + size != path->cap) {
175 Point *old_head = path->head;
177 path->head = g_renew(Point, old_head, size);
178 g_assert(path->head);
179 path->cap = path->head + size;
180 if (path->head != old_head) {
181 path->tail = path->head + (path->tail - old_head);
183 /* Adjust all of the waypoints. */
184 for (curr = path->whead - 1; curr++ != path->wtail;)
185 curr->point = path->head + (curr->point - old_head);
193 path_wresize(Path *path, guint wsize)
195 g_return_val_if_fail(path, FALSE);
197 if (path->whead + wsize != path->wcap) {
198 WayPoint *old_whead = path->whead;
200 path->whead = g_renew(WayPoint, old_whead, wsize);
201 path->wtail = path->whead + (path->wtail - old_whead);
202 path->wcap = path->whead + wsize;
216 * Append a waypoint to path at given lat, lon with description desc
221 path_add_waypoint(Path *path, gdouble lat, gdouble lon, gchar *desc)
225 latlon2unit(lat, lon, unitx, unity);
226 MACRO_PATH_INCREMENT_TAIL(*path);
227 path->tail->unitx=unitx;
228 path->tail->unity=unity;
230 path->tail->altitude=NAN;
232 MACRO_PATH_INCREMENT_WTAIL(*path);
233 path->wtail->point=path->tail;
234 path->wtail->desc=desc;
236 path_find_nearest_point(path);
238 g_signal_emit(G_OBJECT(path), signals[NEW_WAYPOINT], 0, NULL);
248 * Append a point using given gps data. Adds a path break if gps is not valid.
250 * Returns: TRUE if the new point was added. FALSE is returned if new point distance was under sensitivity setting.
253 path_add_point(Path *path, GpsData *gps)
257 g_return_val_if_fail(path, FALSE);
260 path_add_break(path);
264 return path_add_latlon(path, gps->lat, gps->lon, gps->time, gps->speed, gps->altitude);
276 * Append a point with given lat,lon,ptime,speed and altitude to given path.
278 * Returns: TRUE if the new point was added. FALSE is returned if new point distance was under sensitivity setting.
281 path_add_latlon(Path *path, gdouble lat, gdouble lon, time_t ptime, gfloat speed, gfloat altitude)
285 latlon2unit(lat, lon, unitx, unity);
287 if (abs((gint)unitx-path->tail->unitx) > path->sensitivity || abs((gint)unity-path->tail->unity) > path->sensitivity) {
288 if (path->tail->unity && path->tail->unitx) {
291 unit2latlon(path->tail->unitx, path->tail->unity, lat, lon);
292 path->length+=calculate_distance(plat, plon, lat, lon);
294 MACRO_PATH_INCREMENT_TAIL(*path);
295 path->tail->unitx=unitx;
296 path->tail->unity=unity;
297 path->tail->time=ptime;
298 path->tail->altitude=altitude;
299 if (speed>path->maxspeed)
300 path->maxspeed=speed;
302 path->avgspeed=(path->points>0) ? path->tspeed/path->points : 0.0;
304 g_debug("TRACK: %f %f (%d)", path->length, path->avgspeed, path->points);
306 g_signal_emit(G_OBJECT(path), signals[NEW_POINT], 0, NULL);
318 * Add a break point to the path.
320 * Returns: TRUE if break was added, FALSE if last point was a break already.
323 path_add_break(Path *path)
325 g_return_val_if_fail(path, FALSE);
326 g_return_val_if_fail(path->tail, FALSE);
328 if (path->tail->unity && path->tail->unitx) {
329 /* To mark a "break" in a track, we'll add a (0, 0) point and then
330 another instance of the most recent track point. */
332 MACRO_PATH_INCREMENT_TAIL(*path);
333 *path->tail=_point_null;
334 MACRO_PATH_INCREMENT_TAIL(*path);
335 *path->tail=path->tail[-2];
337 g_signal_emit(G_OBJECT(path), signals[NEW_BREAK], 0, NULL);
348 * Checks if given path has any path points
350 * Returns: TRUE if there are points, FALSE otherwise
353 path_has_points(Path *path)
355 g_return_val_if_fail(path, FALSE);
356 return path->head==path->tail ? FALSE : TRUE;
360 * path_has_waypoints:
362 * Checks if given path has any waypoints
364 * Returns: TRUE if there are waypoints, FALSE otherwise
367 path_has_waypoints(Path *path)
369 g_return_val_if_fail(path, FALSE);
370 return path->whead==path->wtail ? FALSE : TRUE;
374 * path_find_last_point:
377 * Returns: The last path point or NULL if path is empty
380 path_find_last_point(Path *path)
384 g_return_val_if_fail(path, NULL);
386 if (!path_has_points(path))
389 for (p=path->tail; !p->unity; p--) {
395 * Updates near_point, next_way, and next_wpt. If quick is FALSE (as
396 * it is when this function is called from route_find_nearest_point), then
397 * the entire list (starting from near_point) is searched. Otherwise, we
398 * stop searching when we find a point that is farther away.
401 path_update_nears(Path *path, Point *point, gboolean quick)
403 gboolean ret = FALSE;
405 WayPoint *wcurr, *wnext;
406 guint64 near_dist_squared;
408 g_return_val_if_fail(path, FALSE);
410 /* If we have waypoints (_next_way != NULL), then determine the "next
411 * waypoint", which is defined as the waypoint after the nearest point,
412 * UNLESS we've passed that waypoint, in which case the waypoint after
413 * that waypoint becomes the "next" waypoint. */
414 if (path->next_way) {
415 /* First, set near_dist_squared with the new distance from
417 near = path->near_point;
418 near_dist_squared = DISTANCE_SQUARED(*point, *near);
420 /* Now, search path for a closer point. If quick is TRUE, then we'll
421 * only search forward, only as long as we keep finding closer points.
423 for (curr = path->near_point; curr++ != path->tail;) {
425 guint dist_squared = DISTANCE_SQUARED(*point, *curr);
426 if (dist_squared <= near_dist_squared) {
428 near_dist_squared = dist_squared;
434 /* Update _near_point. */
435 path->near_point = near;
436 path->near_point_dist_squared = near_dist_squared;
438 for (wnext = wcurr = path->next_way; wcurr != path->wtail; wcurr++) {
439 if (wcurr->point < near || (wcurr->point == near && quick
440 && (path->next_wpt && (DISTANCE_SQUARED(*point, *near) > path->next_way_dist_squared
441 && DISTANCE_SQUARED(*point, *path->next_wpt) < path->next_wpt_dist_squared))))
442 /* Okay, this else if expression warrants explanation. If the
443 * nearest track point happens to be a waypoint, then we want to
444 * check if we have "passed" that waypoint. To check this, we
445 * test the distance from _gps to the waypoint and from _gps to
446 * _next_wpt, and if the former is increasing and the latter is
447 * decreasing, then we have passed the waypoint, and thus we
448 * should skip it. Note that if there is no _next_wpt, then
449 * there is no next waypoint, so we do not skip it in that case. */
455 if (wnext == path->wtail && (wnext->point < near || (wnext->point == near && quick
456 && (path->next_wpt && (DISTANCE_SQUARED (*point, *near) > path->next_way_dist_squared
457 && DISTANCE_SQUARED(*point, *path->next_wpt) < path->next_wpt_dist_squared)))))
459 path->next_way = NULL;
460 path->next_wpt = NULL;
461 path->next_way_dist_squared = -1;
462 path->next_wpt_dist_squared = -1;
465 /* Only update next_way (and consequently _next_wpt) if _next_way is
466 * different, and record that fact for return. */
468 if (!quick || path->next_way != wnext) {
469 path->next_way = wnext;
470 path->next_wpt = wnext->point;
471 if (path->next_wpt == path->tail)
472 path->next_wpt = NULL;
474 while (!(++path->next_wpt)->unity) {
475 if (path->next_wpt == path->tail) {
476 path->next_wpt = NULL;
483 path->next_way_dist_squared = DISTANCE_SQUARED(*point, *wnext->point);
485 path->next_wpt_dist_squared = DISTANCE_SQUARED(*point, *path->next_wpt);
492 * path_find_nearest_point:
494 * Reset the near_point data by searching the entire path for the nearest point and waypoint.
497 path_find_nearest_point(Path *path)
499 g_return_if_fail(path);
501 /* Initialize near_point to first non-zero point. */
502 path->near_point=path->head;
503 while (!path->near_point->unity && path->near_point != path->tail)
506 /* Initialize next_way */
507 if (path->wtail==path->whead)
510 path->next_way=path->whead;
511 path->next_way_dist_squared=-1;
513 /* Initialize next_wpt */
515 path->next_wpt_dist_squared=-1;
520 * path_find_nearest_waypoint:
524 path_find_nearest_waypoint(Path *path, guint unitx, guint unity)
528 guint64 nearest_squared;
529 Point pos = { unitx, unity, 0, NAN };
531 g_return_val_if_fail(path, NULL);
533 wcurr=wnear=path->whead;
534 if (wcurr && wcurr->point && wcurr!=path->wtail) {
535 nearest_squared=DISTANCE_SQUARED(pos, *(wcurr->point));
537 while (wcurr++!=path->wtail) {
538 guint64 test_squared=DISTANCE_SQUARED(pos, *(wcurr->point));
539 if (test_squared < nearest_squared) {
541 nearest_squared=test_squared;
546 if (wnear && wnear->point) {
547 if (abs(unitx - wnear->point->unitx) < path->sensitivity && abs(unity - wnear->point->unity) < path->sensitivity)
555 * path_check_waypoint_announce:
559 * Check if we should announce upcoming waypoint.
563 path_check_waypoint_announce(Path *path, GpsData *gps)
565 guint a_thres_near, a_thres_at;
567 g_return_if_fail(path);
568 g_return_if_fail(gps);
573 a_thres_near=(20+(guint)gps->speed)*path->announce_notice_ratio*3;
574 a_thres_at=(20+(guint)gps->speed);
576 if (path->next_way_dist_squared<(a_thres_near * a_thres_near) && path->next_way!=path->announced_waypoint) {
577 g_signal_emit(G_OBJECT(path), signals[NEAR_WAYPOINT], 0, NULL);
578 path->announced_waypoint=path->next_way;
579 } else if (path->next_way_dist_squared<(a_thres_at * a_thres_at) && path->next_way!=path->announced_waypoint) {
580 g_signal_emit(G_OBJECT(path), signals[REACHED_WAYPOINT], 0, NULL);
581 path->announced_waypoint=path->next_way;
585 /******************************************************************************/
588 path_get_distance_to(Path *path, Point *point, gdouble lat, gdouble lon)
590 gdouble lat1, lon1, lat2, lon2;
593 g_return_val_if_fail(path, 0.0);
595 /* If point is NULL, use the next waypoint. */
596 if (point == NULL && path->next_way)
597 point = path->next_way->point;
599 /* If point is still NULL, return an error. */
606 if (point > path->near_point) {
608 /* Skip _near_point in case we have already passed it. */
609 for (curr = path->near_point + 1; curr <= point; ++curr) {
611 unit2latlon(curr->unitx, curr->unity, lat2, lon2);
612 sum += calculate_distance(lat1, lon1, lat2, lon2);
617 } else if (point < path->near_point) {
619 /* Skip near_point in case we have already passed it. */
620 for (curr = path->near_point - 1; curr >= point; --curr) {
622 unit2latlon(curr->unitx, curr->unity, lat2, lon2);
623 sum += calculate_distance(lat1, lon1, lat2, lon2);
629 /* Waypoint _is_ the nearest point. */
630 unit2latlon(path->near_point->unitx, path->near_point->unity, lat2, lon2);
631 sum += calculate_distance(lat1, lon1, lat2, lon2);
636 /******************************************************************************/
639 path_load(Path *path, const gchar *config_dir, const gchar *file)
645 g_return_val_if_fail(path, FALSE);
646 g_return_val_if_fail(config_dir, FALSE);
647 g_return_val_if_fail(file, FALSE);
649 pfile = gnome_vfs_uri_make_full_from_relative(config_dir, file);
650 if (gnome_vfs_read_entire_file(pfile, &size, &bytes)==GNOME_VFS_OK)
651 gpx_parse(path, bytes, size, GPX_PATH_NEW);
657 path_save(Path *path, const gchar *config_dir, const gchar *file)
659 GnomeVFSHandle *handle;
662 g_return_val_if_fail(path, FALSE);
663 g_return_val_if_fail(config_dir, FALSE);
664 g_return_val_if_fail(file, FALSE);
666 tfile=gnome_vfs_uri_make_full_from_relative(config_dir, file);
667 if (gnome_vfs_create(&handle, tfile, GNOME_VFS_OPEN_WRITE, FALSE, 0600)==GNOME_VFS_OK) {
668 gpx_write(path, handle);
669 gnome_vfs_close(handle);
675 /******************************************************************************/
678 path_set_destination_from_last(Path *path, Position *pos)
683 g_return_val_if_fail(path, FALSE);
684 g_return_val_if_fail(pos, FALSE);
686 if (path->head==path->tail) {
687 position_set(pos, FALSE, NAN, NAN, NAN);
691 p=path_find_last_point(path);
693 unit2latlon(p->unitx, p->unity, &lat, &lon);
694 position_set(pos, TRUE, lat, lon, 0);
696 position_set(pos, FALSE, NAN, NAN, NAN);
703 * path_get_waypoint_latlon:
708 * Get the real latitude and longitude of given waypoint
710 * Returns: TRUE if waypoint was valid, FALSE otherwise.
713 path_get_waypoint_latlon(WayPoint *way, gdouble *lat, gdouble *lon)
717 g_return_val_if_fail(way, FALSE);
718 g_return_val_if_fail(way->point, FALSE);
719 g_return_val_if_fail(lat, FALSE);
720 g_return_val_if_fail(lon, FALSE);
722 unit2latlon(way->point->unitx, way->point->unity, tlat, tlon);
731 * path_insert_mark_text:
735 * Add a new waypoint to path with given text.
739 path_insert_mark_text(Path *path, gchar *text)
741 g_return_if_fail(path);
742 MACRO_PATH_INCREMENT_WTAIL(*path);
743 path->wtail->point=path->tail;
745 path->wtail->desc=text;
748 path->wtail->desc=g_strdup_printf("WPT:#%u", path->wpcnt);
750 g_signal_emit(G_OBJECT(path), signals[NEW_WAYPOINT], 0, NULL);
754 * path_insert_mark_autonumber:
757 * Add autonumbered waypoint to given path
761 path_insert_mark_autonumber(Path *path)
763 path_insert_mark_text(path, NULL);
766 /******************************************************************************/
769 path_store_append_waypoint(GtkListStore *, WayPoint *w)
771 gchar tmp1[16], tmp2[16];
774 unit2latlon(wcurr->point->unitx, wcurr->point->unity, lat2, lon2);
775 lat_format(_degformat, lat2, tmp1);
776 lon_format(_degformat, lon2, tmp2);
778 g_snprintf(buffer1, sizeof(buffer1), "%s,%s", tmp1, tmp2);
779 sum += calculate_distance(lat1, lon1, lat2, lon2);
780 g_snprintf(buffer2, sizeof(buffer2), "%.02f %s", sum * UNITS_CONVERT[_units], UNITS_TEXT[_units]);
782 gtk_list_store_append(store, &iter);
783 gtk_list_store_set(store, &iter,
784 PATH_LATLON, buffer1,
785 PATH_DISTANCE, buffer2,
786 PATH_WAYPOINT, wcurr->desc,
797 * Generate a GtkListStore with information about path waypoints (location, distance)
800 path_get_waypoints_store(Path *path)
807 gchar tmp1[16], tmp2[16];
808 gdouble lat1, lon1, lat2, lon2;
811 g_return_val_if_fail(path!=NULL, NULL);
815 g_return_val_if_fail(wcurr!=NULL, NULL);
816 g_return_val_if_fail(wcurr->point!=NULL, NULL);
817 if (!path_has_waypoints(path))
820 store=gtk_list_store_new(PATH_NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
822 unit2latlon(wcurr->point->unitx, wcurr->point->unity, lat1, lon1);
824 while (wcurr!=path->wtail) {
829 g_debug("PSTORE: No point for waypoint (%s) skipping", wcurr->desc);
834 unit2latlon(wcurr->point->unitx, wcurr->point->unity, lat2, lon2);
835 lat_format(_degformat, lat2, tmp1);
836 lon_format(_degformat, lon2, tmp2);
838 g_snprintf(buffer1, sizeof(buffer1), "%s,%s", tmp1, tmp2);
839 sum += calculate_distance(lat1, lon1, lat2, lon2);
840 g_snprintf(buffer2, sizeof(buffer2), "%.02f %s", sum * UNITS_CONVERT[_units], UNITS_TEXT[_units]);
842 gtk_list_store_append(store, &iter);
843 gtk_list_store_set(store, &iter,
844 PATH_LATLON, buffer1,
845 PATH_DISTANCE, buffer2,
846 PATH_WAYPOINT, wcurr->desc,