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 DownloadMapOptions 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);
684 GDateTime *local = g_date_time_to_timezone ( utc, tz );
686 g_date_time_unref ( utc );
689 gchar *str = g_date_time_format ( local, format );
691 g_date_time_unref ( local );
692 g_date_time_unref ( utc );
696 #define VIK_SETTINGS_NEAREST_TZ_FACTOR "utils_nearest_tz_factor"
698 * vu_get_tz_at_location:
700 * @vc: Position for which the time zone is desired
702 * Returns: TimeZone string of the nearest known location. String may be NULL.
704 * Use the k-d tree method (http://en.wikipedia.org/wiki/Kd-tree) to quickly retreive
705 * the nearest location to the given position.
707 gchar* vu_get_tz_at_location ( const VikCoord* vc )
714 vik_coord_to_latlon ( vc, &ll );
715 double pt[2] = { ll.lat, ll.lon };
718 if ( !a_settings_get_double(VIK_SETTINGS_NEAREST_TZ_FACTOR, &nearest) )
721 struct kdres *presults = kd_nearest_range ( kd, pt, nearest );
722 while( !kd_res_end( presults ) ) {
724 gchar *ans = (gchar*)kd_res_item ( presults, pos );
725 // compute the distance of the current result from the pt
726 double dist = sqrt( dist_sq( pt, pos, 2 ) );
727 if ( dist < nearest ) {
728 //printf( "NEARER node at (%.3f, %.3f, %.3f) is %.3f away is %s\n", pos[0], pos[1], pos[2], dist, ans );
732 kd_res_next ( presults );
734 g_debug ( "TZ lookup found %d results - picked %s", kd_res_size(presults), tz );
735 kd_res_free ( presults );
741 * vu_get_time_string:
743 * @time_t: The time of which the string is wanted
744 * @format The format of the time string - such as "%c"
745 * @vc: Position of object for the time output - maybe NULL
746 * (only applicable for VIK_TIME_REF_WORLD)
747 * @tz: TimeZone string - maybe NULL.
748 * (only applicable for VIK_TIME_REF_WORLD)
749 * Useful to pass in the cached value from vu_get_tz_at_location() to save looking it up again for the same position
751 * Returns: A string of the time according to the time display property
753 gchar* vu_get_time_string ( time_t *time, const gchar *format, const VikCoord* vc, const gchar *tz )
755 if ( !format ) return NULL;
757 switch ( a_vik_get_time_ref_frame() ) {
758 case VIK_TIME_REF_UTC:
759 str = g_malloc ( 64 );
760 strftime ( str, 64, format, gmtime(time) ); // Always 'GMT'
762 case VIK_TIME_REF_WORLD:
764 // No timezone specified so work it out
765 gchar *mytz = vu_get_tz_at_location ( vc );
767 GTimeZone *gtz = g_time_zone_new ( mytz );
768 str = time_string_tz ( time, format, gtz );
769 g_time_zone_unref ( gtz );
772 // No results (e.g. could be in the middle of a sea)
773 // Fallback to simplistic method that doesn't take into account Timezones of countries.
775 vik_coord_to_latlon ( vc, &ll );
776 str = time_string_adjusted ( time, round ( ll.lon / 15.0 ) * 3600 );
780 // Use specified timezone
781 GTimeZone *gtz = g_time_zone_new ( tz );
782 str = time_string_tz ( time, format, gtz );
783 g_time_zone_unref ( gtz );
786 default: // VIK_TIME_REF_LOCALE
787 str = g_malloc ( 64 );
788 strftime ( str, 64, format, localtime(time) );
797 * Apply any startup values that have been specified from the command line
798 * Values are defaulted in such a manner not to be applied when they haven't been specified
801 void vu_command_line ( VikWindow *vw, gdouble latitude, gdouble longitude, gint zoom_osm_level, gint map_id )
806 VikViewport *vvp = vik_window_viewport(vw);
808 if ( latitude != 0.0 || longitude != 0.0 ) {
812 vik_viewport_set_center_latlon ( vvp, &ll, TRUE );
815 if ( zoom_osm_level >= 0 ) {
816 // Convert OSM zoom level into Viking zoom level
817 gdouble mpp = exp ( (17-zoom_osm_level) * log(2) );
820 vik_viewport_set_zoom ( vvp, mpp );
824 guint my_map_id = map_id;
825 if ( my_map_id == 0 )
826 my_map_id = vik_maps_layer_get_default_map_type ();
828 // Don't add map layer if one already exists
829 GList *vmls = vik_layers_panel_get_all_layers_of_type(vik_window_layers_panel(vw), VIK_LAYER_MAPS, TRUE);
830 int num_maps = g_list_length(vmls);
831 gboolean add_map = TRUE;
833 for (int i = 0; i < num_maps; i++) {
834 VikMapsLayer *vml = (VikMapsLayer*)(vmls->data);
835 gint id = vik_maps_layer_get_map_type(vml);
836 if ( my_map_id == id ) {
844 VikMapsLayer *vml = VIK_MAPS_LAYER ( vik_layer_create(VIK_LAYER_MAPS, vvp, FALSE) );
845 vik_maps_layer_set_map_type ( vml, my_map_id );
846 vik_layer_rename ( VIK_LAYER(vml), _("Map") );
847 vik_aggregate_layer_add_layer ( vik_layers_panel_get_top_layer(vik_window_layers_panel(vw)), VIK_LAYER(vml), TRUE );
848 vik_layer_emit_update ( VIK_LAYER(vml) );
854 * Copy the displayed text of a widget (should be a GtkButton ATM)
856 static void vu_copy_label ( GtkWidget *widget )
858 a_clipboard_copy (VIK_CLIPBOARD_DATA_TEXT, 0, 0, 0, gtk_button_get_label(GTK_BUTTON(widget)), NULL );
862 * Generate a single entry menu to allow copying the displayed text of a widget (should be a GtkButton ATM)
864 void vu_copy_label_menu ( GtkWidget *widget, guint button )
866 GtkWidget *menu = gtk_menu_new();
867 GtkWidget *item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
868 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(vu_copy_label), widget );
869 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
870 gtk_widget_show ( item );
871 gtk_menu_popup ( GTK_MENU(menu), NULL, NULL, NULL, NULL, button, gtk_get_current_event_time() );
875 * Work out the best zoom level for the LatLon area and set the viewport to that zoom level
877 void vu_zoom_to_show_latlons ( VikCoordMode mode, VikViewport *vvp, struct LatLon maxmin[2] )
879 /* First set the center [in case previously viewing from elsewhere] */
880 /* Then loop through zoom levels until provided positions are in view */
881 /* This method is not particularly fast - but should work well enough */
882 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
884 vik_coord_load_from_latlon ( &coord, mode, &average );
885 vik_viewport_set_center_coord ( vvp, &coord, TRUE );
887 /* Convert into definite 'smallest' and 'largest' positions */
888 struct LatLon minmin;
889 if ( maxmin[0].lat < maxmin[1].lat )
890 minmin.lat = maxmin[0].lat;
892 minmin.lat = maxmin[1].lat;
894 struct LatLon maxmax;
895 if ( maxmin[0].lon > maxmin[1].lon )
896 maxmax.lon = maxmin[0].lon;
898 maxmax.lon = maxmin[1].lon;
900 /* Never zoom in too far - generally not that useful, as too close ! */
901 /* Always recalculate the 'best' zoom level */
903 vik_viewport_set_zoom ( vvp, zoom );
905 gdouble min_lat, max_lat, min_lon, max_lon;
906 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
907 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
908 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
909 /* NB I think the logic used in this test to determine if the bounds is within view
910 fails if track goes across 180 degrees longitude.
911 Hopefully that situation is not too common...
912 Mind you viking doesn't really do edge locations to well anyway */
913 if ( min_lat < minmin.lat &&
914 max_lat > minmin.lat &&
915 min_lon < maxmax.lon &&
916 max_lon > maxmax.lon )
917 /* Found within zoom level */
922 vik_viewport_set_zoom ( vvp, zoom );