1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
3 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
5 * Copyright (C) 2013, Rob Norris <rw_norris@hotmail.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 * Dependencies in this file can be on anything.
24 * For functions with simple system dependencies put it in util.c
27 #include <glib/gstdio.h>
28 #include <glib/gi18n.h>
35 #include "preferences.h"
36 #include "vikmapslayer.h"
40 #include "misc/kdtree.h"
42 #define FMT_MAX_NUMBER_CODES 9
45 * vu_trackpoint_formatted_message:
46 * @format_code: String describing the message to generate
47 * @trkpt: The trackpoint for which the message is generated about
48 * @trkpt_prev: A trackpoint (presumed previous) for interpolating values with the other trackpoint (such as speed)
49 * @trk: The track in which the trackpoints reside
50 * @climb: Vertical speed (Out of band (i.e. not in a trackpoint) value for display currently only for GPSD usage)
52 * TODO: One day replace this cryptic format code with some kind of tokenizer parsing
53 * thus would make it more user friendly and maybe even GUI controlable.
54 * However for now at least there is some semblance of user control
56 gchar* vu_trackpoint_formatted_message ( gchar *format_code, VikTrackpoint *trkpt, VikTrackpoint *trkpt_prev, VikTrack *trk, gdouble climb )
63 len = strlen ( format_code );
64 if ( len > FMT_MAX_NUMBER_CODES )
65 len = FMT_MAX_NUMBER_CODES;
67 gchar* values[FMT_MAX_NUMBER_CODES];
69 for ( i = 0; i < FMT_MAX_NUMBER_CODES; i++ ) {
73 gchar *speed_units_str = NULL;
74 vik_units_speed_t speed_units = a_vik_get_units_speed ();
75 switch (speed_units) {
76 case VIK_UNITS_SPEED_MILES_PER_HOUR:
77 speed_units_str = g_strdup ( _("mph") );
79 case VIK_UNITS_SPEED_METRES_PER_SECOND:
80 speed_units_str = g_strdup ( _("m/s") );
82 case VIK_UNITS_SPEED_KNOTS:
83 speed_units_str = g_strdup ( _("knots") );
86 // VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
87 speed_units_str = g_strdup ( _("km/h") );
91 gchar *separator = g_strdup ( " | " );
93 for ( i = 0; i < len; i++ ) {
94 switch ( g_ascii_toupper ( format_code[i] ) ) {
95 case 'G': values[i] = g_strdup ( _("GPSD") ); break; // GPS Preamble
96 case 'K': values[i] = g_strdup ( _("Trkpt") ); break; // Trkpt Preamble
100 gchar *speedtype = NULL;
101 if ( isnan(trkpt->speed) && trkpt_prev ) {
102 if ( trkpt->has_timestamp && trkpt_prev->has_timestamp ) {
103 if ( trkpt->timestamp != trkpt_prev->timestamp ) {
105 // Work out from previous trackpoint location and time difference
106 speed = vik_coord_diff(&(trkpt->coord), &(trkpt_prev->coord)) / ABS(trkpt->timestamp - trkpt_prev->timestamp);
107 speedtype = g_strdup ( "*" ); // Interpolated
110 speedtype = g_strdup ( "**" );
113 speedtype = g_strdup ( "**" );
116 speed = trkpt->speed;
117 speedtype = g_strdup ( "" );
119 switch (speed_units) {
120 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
121 speed = VIK_MPS_TO_KPH(speed);
123 case VIK_UNITS_SPEED_MILES_PER_HOUR:
124 speed = VIK_MPS_TO_MPH(speed);
126 case VIK_UNITS_SPEED_KNOTS:
127 speed = VIK_MPS_TO_KNOTS(speed);
130 // VIK_UNITS_SPEED_METRES_PER_SECOND:
131 // Already in m/s so nothing to do
135 values[i] = g_strdup_printf ( _("%sSpeed%s %.1f%s"), separator, speedtype, speed, speed_units_str );
136 g_free ( speedtype );
142 gchar *speedtype = NULL;
143 if ( isnan(climb) && trkpt_prev ) {
144 if ( trkpt->has_timestamp && trkpt_prev->has_timestamp ) {
145 if ( trkpt->timestamp != trkpt_prev->timestamp ) {
146 // Work out from previous trackpoint altitudes and time difference
147 // 'speed' can be negative if going downhill
148 speed = (trkpt->altitude - trkpt_prev->altitude) / ABS(trkpt->timestamp - trkpt_prev->timestamp);
149 speedtype = g_strdup ( "*" ); // Interpolated
152 speedtype = g_strdup ( "**" ); // Unavailable
155 speedtype = g_strdup ( "**" );
159 speedtype = g_strdup ( "" );
161 switch (speed_units) {
162 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
163 speed = VIK_MPS_TO_KPH(speed);
165 case VIK_UNITS_SPEED_MILES_PER_HOUR:
166 speed = VIK_MPS_TO_MPH(speed);
168 case VIK_UNITS_SPEED_KNOTS:
169 speed = VIK_MPS_TO_KNOTS(speed);
172 // VIK_UNITS_SPEED_METRES_PER_SECOND:
173 // Already in m/s so nothing to do
176 // Go for 2dp as expect low values for vertical speeds
177 values[i] = g_strdup_printf ( _("%sClimb%s %.2f%s"), separator, speedtype, speed, speed_units_str );
178 g_free ( speedtype );
183 vik_units_height_t height_units = a_vik_get_units_height ();
184 switch (height_units) {
185 case VIK_UNITS_HEIGHT_FEET:
186 values[i] = g_strdup_printf ( _("%sAlt %dfeet"), separator, (int)round(VIK_METERS_TO_FEET(trkpt->altitude)) );
189 //VIK_UNITS_HEIGHT_METRES:
190 values[i] = g_strdup_printf ( _("%sAlt %dm"), separator, (int)round(trkpt->altitude) );
197 gint heading = isnan(trkpt->course) ? 0 : (gint)round(trkpt->course);
198 values[i] = g_strdup_printf ( _("%sCourse %03d\302\260" ), separator, heading );
204 gint diff = (gint) round ( vik_coord_diff ( &(trkpt->coord), &(trkpt_prev->coord) ) );
206 gchar *dist_units_str = NULL;
207 vik_units_distance_t dist_units = a_vik_get_units_distance ();
208 // expect the difference between track points to be small hence use metres or yards
209 switch (dist_units) {
210 case VIK_UNITS_DISTANCE_MILES:
211 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
212 dist_units_str = g_strdup ( _("yards") );
215 // VIK_UNITS_DISTANCE_KILOMETRES:
216 dist_units_str = g_strdup ( _("m") );
220 values[i] = g_strdup_printf ( _("%sDistance diff %d%s"), separator, diff, dist_units_str );
222 g_free ( dist_units_str );
229 if ( trkpt->has_timestamp ) {
230 // Compact date time format
231 msg = vu_get_time_string ( &(trkpt->timestamp), "%x %X", &(trkpt->coord), NULL );
234 msg = g_strdup ("--");
235 values[i] = g_strdup_printf ( _("%sTime %s"), separator, msg );
242 if ( trkpt->has_timestamp && trkpt_prev->has_timestamp ) {
243 time_t t_diff = trkpt->timestamp - trkpt_prev->timestamp;
244 values[i] = g_strdup_printf ( _("%sTime diff %lds"), separator, t_diff );
250 case 'X': values[i] = g_strdup_printf ( _("%sNo. of Sats %d"), separator, trkpt->nsats ); break;
254 // Distance to the end 'Finish' (along the track)
255 gdouble distd = vik_track_get_length_to_trackpoint (trk, trkpt);
256 gdouble diste = vik_track_get_length_including_gaps ( trk );
257 gdouble dist = diste - distd;
258 gchar *dist_units_str = NULL;
259 vik_units_distance_t dist_units = a_vik_get_units_distance ();
260 switch (dist_units) {
261 case VIK_UNITS_DISTANCE_MILES:
262 dist_units_str = g_strdup ( _("miles") );
263 dist = VIK_METERS_TO_MILES(dist);
265 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
266 dist_units_str = g_strdup ( _("NM") );
267 dist = VIK_METERS_TO_NAUTICAL_MILES(dist);
270 // VIK_UNITS_DISTANCE_KILOMETRES:
271 dist_units_str = g_strdup ( _("km") );
272 dist = dist / 1000.0;
275 values[i] = g_strdup_printf ( _("%sTo End %.2f%s"), separator, dist, dist_units_str );
276 g_free ( dist_units_str );
283 // Distance from start (along the track)
284 gdouble distd = vik_track_get_length_to_trackpoint (trk, trkpt);
285 gchar *dist_units_str = NULL;
286 vik_units_distance_t dist_units = a_vik_get_units_distance ();
287 switch (dist_units) {
288 case VIK_UNITS_DISTANCE_MILES:
289 dist_units_str = g_strdup ( _("miles") );
290 distd = VIK_METERS_TO_MILES(distd);
292 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
293 dist_units_str = g_strdup ( _("NM") );
294 distd = VIK_METERS_TO_NAUTICAL_MILES(distd);
297 // VIK_UNITS_DISTANCE_KILOMETRES:
298 dist_units_str = g_strdup ( _("km") );
299 distd = distd / 1000.0;
302 values[i] = g_strdup_printf ( _("%sDistance along %.2f%s"), separator, distd, dist_units_str );
303 g_free ( dist_units_str );
309 // Location (Lat/Long)
310 gchar *lat = NULL, *lon = NULL;
312 vik_coord_to_latlon (&(trkpt->coord), &ll);
313 a_coords_latlon_to_string ( &ll, &lat, &lon );
314 values[i] = g_strdup_printf ( "%s%s %s", separator, lat, lon );
320 case 'N': // Name of track
322 values[i] = g_strdup_printf ( _("%sTrack: %s"), separator, trk->name );
325 case 'E': // Name of trackpoint if available
327 values[i] = g_strdup_printf ( "%s%s", separator, trkpt->name );
329 values[i] = g_strdup ( "" );
337 g_free ( separator );
338 g_free ( speed_units_str );
340 gchar *msg = g_strconcat ( values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7], values[8], NULL );
342 for ( i = 0; i < FMT_MAX_NUMBER_CODES; i++ ) {
343 if ( values[i] != '\0' )
344 g_free ( values[i] );
351 GtkWindow *window; // Layer needed for redrawing
352 gchar *version; // Image list
353 } new_version_thread_data;
355 static gboolean new_version_available_message ( new_version_thread_data *nvtd )
357 // Only a simple goto website option is offered
358 // Trying to do an installation update is platform specific
359 if ( a_dialog_yes_or_no ( nvtd->window,
360 _("There is a newer version of Viking available: %s\n\nDo you wish to go to Viking's website now?"), nvtd->version ) )
361 // NB 'VIKING_URL' redirects to the Wiki, here we want to go the main site.
362 open_url ( nvtd->window, "http://sourceforge.net/projects/viking/" );
364 g_free ( nvtd->version );
369 #define VIK_SETTINGS_VERSION_CHECKED_DATE "version_checked_date"
371 static void latest_version_thread ( GtkWindow *window )
373 // Need to allow a few redirects, as SF file is often served from different server
374 DownloadFileOptions options = { FALSE, FALSE, NULL, 5, NULL, NULL, NULL };
375 gchar *filename = a_download_uri_to_tmp_file ( "http://sourceforge.net/projects/viking/files/VERSION", &options );
376 //gchar *filename = g_strdup ( "VERSION" );
381 GMappedFile *mf = g_mapped_file_new ( filename, FALSE, NULL );
385 gchar *text = g_mapped_file_get_contents ( mf );
387 gint latest_version = viking_version_to_number ( text );
388 gint my_version = viking_version_to_number ( VIKING_VERSION );
390 g_debug ( "The lastest version is: %s", text );
392 if ( my_version < latest_version ) {
393 new_version_thread_data *nvtd = g_malloc ( sizeof(new_version_thread_data) );
394 nvtd->window = window;
395 nvtd->version = g_strdup ( text );
396 gdk_threads_add_idle ( (GSourceFunc) new_version_available_message, nvtd );
399 g_debug ( "Running the lastest version: %s", VIKING_VERSION );
401 g_mapped_file_unref ( mf );
403 g_remove ( filename );
407 // Update last checked time
409 g_get_current_time ( &time );
410 a_settings_set_string ( VIK_SETTINGS_VERSION_CHECKED_DATE, g_time_val_to_iso8601(&time) );
413 #define VIK_SETTINGS_VERSION_CHECK_PERIOD "version_check_period_days"
416 * vu_check_latest_version:
417 * @window: Somewhere where we may need use the display to inform the user about the version status
419 * Periodically checks the released latest VERSION file on the website to compare with the running version
422 void vu_check_latest_version ( GtkWindow *window )
424 if ( ! a_vik_get_check_version () )
427 gboolean do_check = FALSE;
430 if ( ! a_settings_get_integer ( VIK_SETTINGS_VERSION_CHECK_PERIOD, &check_period ) ) {
434 // Get last checked date...
435 GDate *gdate_last = g_date_new();
436 GDate *gdate_now = g_date_new();
438 gchar *last_checked_date = NULL;
440 // When no previous date available - set to do the version check
441 if ( a_settings_get_string ( VIK_SETTINGS_VERSION_CHECKED_DATE, &last_checked_date) ) {
442 if ( g_time_val_from_iso8601 ( last_checked_date, &time_last ) ) {
443 g_date_set_time_val ( gdate_last, &time_last );
452 g_get_current_time ( &time_now );
453 g_date_set_time_val ( gdate_now, &time_now );
456 // Dates available so do the comparison
457 g_date_add_days ( gdate_last, check_period );
458 if ( g_date_compare ( gdate_last, gdate_now ) < 0 )
462 g_date_free ( gdate_last );
463 g_date_free ( gdate_now );
466 #if GLIB_CHECK_VERSION (2, 32, 0)
467 g_thread_try_new ( "latest_version_thread", (GThreadFunc)latest_version_thread, window, NULL );
469 g_thread_create ( (GThreadFunc)latest_version_thread, window, FALSE, NULL );
475 * vu_set_auto_features_on_first_run:
477 * Ask the user's opinion to set some of Viking's default behaviour
479 void vu_set_auto_features_on_first_run ( void )
481 gboolean auto_features = FALSE;
482 gboolean set_defaults = FALSE;
484 if ( a_vik_very_first_run () ) {
486 GtkWidget *win = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
488 if ( a_dialog_yes_or_no ( GTK_WINDOW(win),
489 _("This appears to be Viking's very first run.\n\nDo you wish to enable automatic internet features?\n\nIndividual settings can be controlled in the Preferences."), NULL ) )
490 auto_features = TRUE;
492 // Default to more standard cache layout for new users (well new installs at least)
493 maps_layer_set_cache_default ( VIK_MAPS_CACHE_LAYOUT_OSM );
497 if ( auto_features ) {
498 // Set Maps to autodownload
499 // Ensure the default is true
500 maps_layer_set_autodownload_default ( TRUE );
503 // Simplistic repeat of preference settings
504 // Only the name & type are important for setting a preference via this 'external' way
506 // Enable auto add map +
508 VikLayerParam pref_add_map[] = { { VIK_LAYER_NUM_TYPES, VIKING_PREFERENCES_STARTUP_NAMESPACE "add_default_map_layer", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, NULL, VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, NULL, NULL, NULL, }, };
509 VikLayerParam pref_startup_method[] = { { VIK_LAYER_NUM_TYPES, VIKING_PREFERENCES_STARTUP_NAMESPACE "startup_method", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, NULL, VIK_LAYER_WIDGET_COMBOBOX, NULL, NULL, NULL, NULL, NULL, NULL}, };
511 VikLayerParamData vlp_data;
513 a_preferences_run_setparam ( vlp_data, pref_add_map );
515 vlp_data.u = VIK_STARTUP_METHOD_AUTO_LOCATION;
516 a_preferences_run_setparam ( vlp_data, pref_startup_method );
518 // Only on Windows make checking for the latest version on by default
519 // For other systems it's expected a Package manager or similar controls the installation, so leave it off
521 VikLayerParam pref_startup_version_check[] = { { VIK_LAYER_NUM_TYPES, VIKING_PREFERENCES_STARTUP_NAMESPACE "check_version", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, NULL, VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, NULL, }, };
523 a_preferences_run_setparam ( vlp_data, pref_startup_version_check );
526 // Ensure settings are saved for next time
527 a_preferences_save_to_file ();
530 // Ensure defaults are saved if changed
532 a_layer_defaults_save ();
536 * vu_get_canonical_filename:
538 * Returns: Canonical absolute filename
540 * Any time a path may contain a relative component, so need to prepend that directory it is relative to
541 * Then resolve the full path to get the normal canonical filename
543 gchar *vu_get_canonical_filename ( VikLayer *vl, const gchar *filename )
545 gchar *canonical = NULL;
549 if ( g_path_is_absolute ( filename ) )
550 canonical = g_strdup ( filename );
552 const gchar *vw_filename = vik_window_get_filename ( VIK_WINDOW_FROM_WIDGET (vl->vvp) );
553 gchar *dirpath = NULL;
555 dirpath = g_path_get_dirname ( vw_filename );
557 dirpath = g_get_current_dir(); // Fallback - if here then probably can't create the correct path
560 if ( g_path_is_absolute ( dirpath ) )
561 full = g_strconcat ( dirpath, G_DIR_SEPARATOR_S, filename, NULL );
563 full = g_strconcat ( g_get_current_dir(), G_DIR_SEPARATOR_S, dirpath, G_DIR_SEPARATOR_S, filename, NULL );
565 canonical = file_realpath_dup ( full ); // resolved
573 static struct kdtree *kd = NULL;
577 * @dir: The directory from which to load the latlontz.txt file
579 * Returns: The number of elements within the latlontz.txt loaded
581 static gint load_ll_tz_dir ( const gchar *dir )
584 gchar *lltz = g_build_filename ( dir, "latlontz.txt", NULL );
585 if ( g_access(lltz, R_OK) == 0 ) {
588 FILE *ff = g_fopen ( lltz, "r" );
590 while ( fgets ( buffer, 4096, ff ) ) {
592 gchar **components = g_strsplit (buffer, " ", 3);
593 guint nn = g_strv_length ( components );
595 double pt[2] = { g_ascii_strtod (components[0], NULL), g_ascii_strtod (components[1], NULL) };
596 gchar *timezone = g_strchomp ( components[2] );
597 if ( kd_insert ( kd, pt, timezone ) )
598 g_critical ( "Insertion problem of %s for line %ld of latlontz.txt", timezone, line_num );
601 // NB Don't free timezone as it's part of the kdtree data now
602 g_free ( components[0] );
603 g_free ( components[1] );
605 g_warning ( "Line %ld of latlontz.txt does not have 3 parts", line_num );
607 g_free ( components );
612 g_warning ( "%s: Could not open %s", __FUNCTION__, lltz);
621 * vu_setup_lat_lon_tz_lookup:
623 * Can be called multiple times but only initializes the lookup once
625 void vu_setup_lat_lon_tz_lookup ()
633 // Look in the directories of data path
634 gchar **data_dirs = a_get_viking_data_path();
636 // Process directories in reverse order for priority
637 guint n_data_dirs = g_strv_length ( data_dirs );
638 for (; n_data_dirs > 0; n_data_dirs--) {
639 loaded += load_ll_tz_dir(data_dirs[n_data_dirs-1]);
641 g_strfreev ( data_dirs );
643 g_debug ( "%s: Loaded %d elements", __FUNCTION__, loaded );
645 g_critical ( "%s: No lat/lon/timezones loaded", __FUNCTION__ );
649 * vu_finalize_lat_lon_tz_lookup:
651 * Clear memory used by the lookup.
652 * only call on program exit
654 void vu_finalize_lat_lon_tz_lookup ()
657 kd_data_destructor ( kd, g_free );
662 static double dist_sq( double *a1, double *a2, int dims ) {
663 double dist_sq = 0, diff;
664 while( --dims >= 0 ) {
665 diff = (a1[dims] - a2[dims]);
666 dist_sq += diff*diff;
671 static gchar* time_string_adjusted ( time_t *time, gint offset_s )
673 time_t *mytime = time;
674 *mytime = *mytime + offset_s;
675 gchar *str = g_malloc ( 64 );
676 // Append asterisks to indicate use of simplistic model (i.e. no TZ)
677 strftime ( str, 64, "%a %X %x **", gmtime(mytime) );
681 static gchar* time_string_tz ( time_t *time, const gchar *format, GTimeZone *tz )
683 GDateTime *utc = g_date_time_new_from_unix_utc (*time);
685 g_warning ( "%s: result from g_date_time_new_from_unix_utc() is NULL", __FUNCTION__ );
688 GDateTime *local = g_date_time_to_timezone ( utc, tz );
690 g_date_time_unref ( utc );
691 g_warning ( "%s: result from g_date_time_to_timezone() is NULL", __FUNCTION__ );
694 gchar *str = g_date_time_format ( local, format );
696 g_date_time_unref ( local );
697 g_date_time_unref ( utc );
701 #define VIK_SETTINGS_NEAREST_TZ_FACTOR "utils_nearest_tz_factor"
703 * vu_get_tz_at_location:
705 * @vc: Position for which the time zone is desired
707 * Returns: TimeZone string of the nearest known location. String may be NULL.
709 * Use the k-d tree method (http://en.wikipedia.org/wiki/Kd-tree) to quickly retreive
710 * the nearest location to the given position.
712 gchar* vu_get_tz_at_location ( const VikCoord* vc )
719 vik_coord_to_latlon ( vc, &ll );
720 double pt[2] = { ll.lat, ll.lon };
723 if ( !a_settings_get_double(VIK_SETTINGS_NEAREST_TZ_FACTOR, &nearest) )
726 struct kdres *presults = kd_nearest_range ( kd, pt, nearest );
727 while( !kd_res_end( presults ) ) {
729 gchar *ans = (gchar*)kd_res_item ( presults, pos );
730 // compute the distance of the current result from the pt
731 double dist = sqrt( dist_sq( pt, pos, 2 ) );
732 if ( dist < nearest ) {
733 //printf( "NEARER node at (%.3f, %.3f, %.3f) is %.3f away is %s\n", pos[0], pos[1], pos[2], dist, ans );
737 kd_res_next ( presults );
739 g_debug ( "TZ lookup found %d results - picked %s", kd_res_size(presults), tz );
740 kd_res_free ( presults );
746 * vu_get_time_string:
748 * @time_t: The time of which the string is wanted
749 * @format The format of the time string - such as "%c"
750 * @vc: Position of object for the time output - maybe NULL
751 * (only applicable for VIK_TIME_REF_WORLD)
752 * @tz: TimeZone string - maybe NULL.
753 * (only applicable for VIK_TIME_REF_WORLD)
754 * Useful to pass in the cached value from vu_get_tz_at_location() to save looking it up again for the same position
756 * Returns: A string of the time according to the time display property
758 gchar* vu_get_time_string ( time_t *time, const gchar *format, const VikCoord* vc, const gchar *tz )
760 if ( !format ) return NULL;
762 switch ( a_vik_get_time_ref_frame() ) {
763 case VIK_TIME_REF_UTC:
764 str = g_malloc ( 64 );
765 strftime ( str, 64, format, gmtime(time) ); // Always 'GMT'
767 case VIK_TIME_REF_WORLD:
769 // No timezone specified so work it out
770 gchar *mytz = vu_get_tz_at_location ( vc );
772 GTimeZone *gtz = g_time_zone_new ( mytz );
773 str = time_string_tz ( time, format, gtz );
774 g_time_zone_unref ( gtz );
777 // No results (e.g. could be in the middle of a sea)
778 // Fallback to simplistic method that doesn't take into account Timezones of countries.
780 vik_coord_to_latlon ( vc, &ll );
781 str = time_string_adjusted ( time, round ( ll.lon / 15.0 ) * 3600 );
785 // Use specified timezone
786 GTimeZone *gtz = g_time_zone_new ( tz );
787 str = time_string_tz ( time, format, gtz );
788 g_time_zone_unref ( gtz );
791 default: // VIK_TIME_REF_LOCALE
792 str = g_malloc ( 64 );
793 strftime ( str, 64, format, localtime(time) );
802 * Apply any startup values that have been specified from the command line
803 * Values are defaulted in such a manner not to be applied when they haven't been specified
806 void vu_command_line ( VikWindow *vw, gdouble latitude, gdouble longitude, gint zoom_osm_level, gint map_id )
811 VikViewport *vvp = vik_window_viewport(vw);
813 if ( latitude != 0.0 || longitude != 0.0 ) {
817 vik_viewport_set_center_latlon ( vvp, &ll, TRUE );
820 if ( zoom_osm_level >= 0 ) {
821 // Convert OSM zoom level into Viking zoom level
822 gdouble mpp = exp ( (17-zoom_osm_level) * log(2) );
825 vik_viewport_set_zoom ( vvp, mpp );
829 guint my_map_id = map_id;
830 if ( my_map_id == 0 )
831 my_map_id = vik_maps_layer_get_default_map_type ();
833 // Don't add map layer if one already exists
834 GList *vmls = vik_layers_panel_get_all_layers_of_type(vik_window_layers_panel(vw), VIK_LAYER_MAPS, TRUE);
835 int num_maps = g_list_length(vmls);
836 gboolean add_map = TRUE;
838 for (int i = 0; i < num_maps; i++) {
839 VikMapsLayer *vml = (VikMapsLayer*)(vmls->data);
840 gint id = vik_maps_layer_get_map_type(vml);
841 if ( my_map_id == id ) {
849 VikMapsLayer *vml = VIK_MAPS_LAYER ( vik_layer_create(VIK_LAYER_MAPS, vvp, FALSE) );
850 vik_maps_layer_set_map_type ( vml, my_map_id );
851 vik_layer_rename ( VIK_LAYER(vml), _("Map") );
852 vik_aggregate_layer_add_layer ( vik_layers_panel_get_top_layer(vik_window_layers_panel(vw)), VIK_LAYER(vml), TRUE );
853 vik_layer_emit_update ( VIK_LAYER(vml) );
859 * Copy the displayed text of a widget (should be a GtkButton ATM)
861 static void vu_copy_label ( GtkWidget *widget )
863 a_clipboard_copy (VIK_CLIPBOARD_DATA_TEXT, 0, 0, 0, gtk_button_get_label(GTK_BUTTON(widget)), NULL );
867 * Generate a single entry menu to allow copying the displayed text of a widget (should be a GtkButton ATM)
869 void vu_copy_label_menu ( GtkWidget *widget, guint button )
871 GtkWidget *menu = gtk_menu_new();
872 GtkWidget *item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
873 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(vu_copy_label), widget );
874 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
875 gtk_widget_show ( item );
876 gtk_menu_popup ( GTK_MENU(menu), NULL, NULL, NULL, NULL, button, gtk_get_current_event_time() );
880 * Work out the best zoom level for the LatLon area and set the viewport to that zoom level
882 void vu_zoom_to_show_latlons ( VikCoordMode mode, VikViewport *vvp, struct LatLon maxmin[2] )
884 /* First set the center [in case previously viewing from elsewhere] */
885 /* Then loop through zoom levels until provided positions are in view */
886 /* This method is not particularly fast - but should work well enough */
887 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
889 vik_coord_load_from_latlon ( &coord, mode, &average );
890 vik_viewport_set_center_coord ( vvp, &coord, TRUE );
892 /* Convert into definite 'smallest' and 'largest' positions */
893 struct LatLon minmin;
894 if ( maxmin[0].lat < maxmin[1].lat )
895 minmin.lat = maxmin[0].lat;
897 minmin.lat = maxmin[1].lat;
899 struct LatLon maxmax;
900 if ( maxmin[0].lon > maxmin[1].lon )
901 maxmax.lon = maxmin[0].lon;
903 maxmax.lon = maxmin[1].lon;
905 /* Never zoom in too far - generally not that useful, as too close ! */
906 /* Always recalculate the 'best' zoom level */
908 vik_viewport_set_zoom ( vvp, zoom );
910 gdouble min_lat, max_lat, min_lon, max_lon;
911 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
912 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
913 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
914 /* NB I think the logic used in this test to determine if the bounds is within view
915 fails if track goes across 180 degrees longitude.
916 Hopefully that situation is not too common...
917 Mind you viking doesn't really do edge locations to well anyway */
918 if ( min_lat < minmin.lat &&
919 max_lat > minmin.lat &&
920 min_lon < maxmax.lon &&
921 max_lon > maxmax.lon )
922 /* Found within zoom level */
927 vik_viewport_set_zoom ( vvp, zoom );