/**
 * Correlate the image to any track within the TrackWaypoint layer
 */
static void trw_layer_geotag_process ( geotag_options_t *options )
{
	if ( !options->vtl || !IS_VIK_LAYER(options->vtl) )
		return;

	if ( !options->image )
		return;

	gboolean has_gps_exif = FALSE;
	gchar* datetime = a_geotag_get_exif_date_from_file ( options->image, &has_gps_exif );

	if ( datetime ) {
	
		// If image already has gps info - don't attempt to change it.
		if ( !options->ov.overwrite_gps_exif && has_gps_exif ) {
			if ( options->ov.create_waypoints ) {
				// Create waypoint with file information
				gchar *name = NULL;
				VikWaypoint *wp = a_geotag_create_waypoint_from_file ( options->image, vik_trw_layer_get_coord_mode (options->vtl), &name );
				if ( !name )
					name = g_strdup ( a_file_basename ( options->image ) );
				vik_trw_layer_filein_add_waypoint ( options->vtl, name, wp );
				g_free ( name );
				
				// Mark for redraw
				options->redraw = TRUE;
			}
			g_free ( datetime );
			return;
		}

		options->PhotoTime = ConvertToUnixTime ( datetime, EXIF_DATE_FORMAT, options->ov.TimeZoneHours, options->ov.TimeZoneMins);
		g_free ( datetime );
		
		// Apply any offset
		options->PhotoTime = options->PhotoTime + options->ov.time_offset;

		options->found_match = FALSE;

		if ( options->track ) {
			// Single specified track
			// NB Doesn't care about track name
			trw_layer_geotag_track ( NULL, options->track, options );
		}
		else {
			// Try all tracks
			GHashTable *tracks = vik_trw_layer_get_tracks ( options->vtl );
			if ( g_hash_table_size (tracks) > 0 ) {
				g_hash_table_foreach ( tracks, (GHFunc) trw_layer_geotag_track, options );
			}
		}

		// Match found ?
		if ( options->found_match ) {

			if ( options->ov.create_waypoints ) {

				// Create waypoint with found position
				gchar *name = NULL;
				VikWaypoint *wp = a_geotag_create_waypoint_positioned ( options->image, options->coord, options->altitude, &name );
				if ( !name )
					name = g_strdup ( a_file_basename ( options->image ) );
				vik_trw_layer_filein_add_waypoint ( options->vtl, name, wp );
				g_free ( name );
				
				// Mark for redraw
				options->redraw = TRUE;
			}

			// Write EXIF if specified
			if ( options->ov.write_exif ) {
				a_geotag_write_exif_gps ( options->image, options->coord, options->altitude, options->ov.no_change_mtime );
			}
		}
	}
}
Beispiel #2
0
static void ExtractTrackPoints(xmlNodePtr Start)
{
	/* The pointer passed to us should be the start
	 * of a heap of trkpt's. So walk though them,
	 * extracting what we need. */
	xmlNodePtr Current = NULL;
	xmlNodePtr CCurrent = NULL;
	xmlAttrPtr Properties = NULL;
	const char* Lat;
	const char* Long;
	const char* Elev;
	const char* Time;

	for (Current = Start; Current; Current = Current->next)
	{
		if ((Current->type == XML_ELEMENT_NODE) &&
			(strcmp((const char *)Current->name, "trkpt") == 0))
		{
			/* This is indeed a trackpoint. Extract! */

			/* Reset the vars... so we don't get
			 * the data from last run. */
			Lat = NULL;
			Long = NULL;
			Elev = NULL;
			Time = NULL;
			
			/* To get the Lat and Long, we have to
			 * extract the properties... another 
			 * linked list to walk. */
			for (Properties = Current->properties;
					Properties;
					Properties = Properties->next)
			{
				if (strcmp((const char *)Properties->name, "lat") == 0)
				{
					Lat = (const char *)Properties->children->content;
				}
				if (strcmp((const char *)Properties->name, "lon") == 0)
				{
					Long = (const char *)Properties->children->content;
				}
			}

			/* Now, grab the elevation and time.
			 * These are children of trkpt. */
			/* Oh, and what's the deal with the
			 * Node->children->content thing? */
			for (CCurrent = Current->children;
					CCurrent;
					CCurrent = CCurrent->next)
			{
				if (strcmp((const char *)CCurrent->name, "ele") == 0)
				{
					if (CCurrent->children)
						Elev = (const char *)CCurrent->children->content;
				}
				if (strcmp((const char *)CCurrent->name, "time") == 0)
				{
					if (CCurrent->children)
						Time = (const char *)CCurrent->children->content;
				}
			}

			/* Check that we have all the data. If we're missing something,
			 * then skip this point... */
			if (Time == NULL || Long == NULL || Lat == NULL)
			{
				/* Missing some data. */
				/* TODO: Really should report this upstream... */
				continue;
			}

			/* Right, now we theoretically have all the data.
			 * Allocate ourselves some memory and go for it... */
			if (FirstPoint)
			{
				/* Ok, adding to the list... */
				LastPoint->Next = (struct GPSPoint*) malloc(sizeof(struct GPSPoint));
				LastPoint = LastPoint->Next;
				LastPoint->Next = NULL;
			} else {
				/* This is the first one. */
				FirstPoint = (struct GPSPoint*) malloc(sizeof(struct GPSPoint));
				FirstPoint->Next = NULL;
				LastPoint = FirstPoint;
			}

			/* Clear the structure first... */
			LastPoint->Lat = 0;
			LastPoint->LatDecimals = 0;
			LastPoint->Long = 0;
			LastPoint->LongDecimals = 0;
			LastPoint->Elev = 0;
			LastPoint->ElevDecimals = -1; // default meaning no altitude was found
			LastPoint->Time = 0;
			LastPoint->EndOfSegment = 0;

			/* Write the data into LastPoint, which should be a new point. */
			LastPoint->Lat = atof(Lat);
			LastPoint->LatDecimals = NumDecimals(Lat);
			LastPoint->Long = atof(Long);
			LastPoint->LongDecimals = NumDecimals(Long);
			if (Elev) {
				LastPoint->Elev = atof(Elev);
				LastPoint->ElevDecimals = NumDecimals(Elev);
			}
			LastPoint->Time = ConvertToUnixTime(Time, GPX_DATE_FORMAT, 0, 0);
			
			/* Debug...
			printf("TrackPoint. Lat %s (%f), Long %s (%f). Elev %s (%f), Time %d.\n",
					Lat, atof(Lat), Long, atof(Long), Elev, atof(Elev),
					ConvertToUnixTime(Time, GPX_DATE_FORMAT, 0, 0));
			printf("Decimals %d %d %d\n", LastPoint->LatDecimals, LastPoint->LongDecimals, LastPoint->ElevDecimals);
			*/
			
					
		}
	} /* End For. */

	/* Return control to the recursive function... */
}
Beispiel #3
0
struct GPSPoint* CorrelatePhoto(const char* Filename,
		struct CorrelateOptions* Options)
{
	/* Read out the timestamp from the EXIF data. */
	char* TimeTemp;
	int IncludesGPS = 0;
	TimeTemp = ReadExifDate(Filename, &IncludesGPS);
	if (!TimeTemp)
	{
		/* Error reading the time from the file. Abort. */
		/* If this was a read error, then a seperate message
		 * will appear on the console. Otherwise, we were
		 * returned here due to the lack of exif tags. */
		Options->Result = CORR_NOEXIFINPUT;
		return NULL;
	}
	if (IncludesGPS)
	{
		/* Already have GPS data in the file!
		 * So we can't do this again... */
		Options->Result = CORR_GPSDATAEXISTS;
		free(TimeTemp);
		return NULL;
	}
	if (Options->AutoTimeZone)
	{
		/* Use the local time zone as of the date of first picture
		 * as the time for correlating all the remainder. */
		time_t RealTime;

		/* PhotoTime isn't a true epoch time, but is rather out
		 * by the local offset from UTC */
		time_t PhotoTime =
			ConvertToUnixTime(TimeTemp, EXIF_DATE_FORMAT, 0, 0);

		/* Extract the component time values */
		struct tm *PhotoTm = gmtime(&PhotoTime);

		/* Then create a true epoch-based local time, including DST */
		PhotoTm->tm_isdst = -1;
		RealTime = mktime(PhotoTm);

		/* Finally, RealTime is the proper Epoch time of the photo.
		 * The difference from PhotoTime is the time zone offset. */
		Options->TimeZoneHours = (PhotoTime - RealTime) / 3600;
		Options->TimeZoneMins = ((PhotoTime - RealTime) % 3600) / 60;
		Options->AutoTimeZone = 0;
	}
	//printf("Using offset %02d:%02d\n", Options->TimeZoneHours, Options->TimeZoneMins);

	/* Now convert the time into Unixtime. */
	time_t PhotoTime =
		ConvertToUnixTime(TimeTemp, EXIF_DATE_FORMAT,
			Options->TimeZoneHours, Options->TimeZoneMins);

	/* Add the PhotoOffset time. This is to make the Photo time match
	 * the GPS time - ie, it is (GPS - Photo). */
	PhotoTime += Options->PhotoOffset;

	/* Free the memory for the time string - it won't otherwise
	 * be freed for us. */
	free(TimeTemp);

	/* Search the list of GPS tracks to find one containing the range
	 * we're interested in. Options points to an array with the last
	 * entry denoted by a NULL Points pointer. */
	int TrackNum;
	for (TrackNum = 0; Options->Track[TrackNum].Points; ++TrackNum)
	{
		/* Check that the photo is within the times that
		 * our tracks are for. Can't really match it if
		 * we were not logging when it was taken.
		 * Note: photos taken between logging sessions of the
		 * same file will still make it inside of this. In
		 * some cases, it won't matter, but if it does, then
		 * keep this in mind!! */
		if ((PhotoTime >= Options->Track[TrackNum].MinTime) &&
		    (PhotoTime <= Options->Track[TrackNum].MaxTime))
			break;
	}
	if (!Options->Track[TrackNum].Points) {
		/* All tracks were outside the time range. Abort. */
		Options->Result = CORR_NOMATCH;
		return NULL;
	}

	/* Time to run through the list, and see if our PhotoTime
	 * is in between two points. Alternately, it might be
	 * exactly on a point... even better... */
	const struct GPSPoint* Search;
	struct GPSPoint* Actual = (struct GPSPoint*) malloc(sizeof(struct GPSPoint));

	Options->Result = CORR_NOMATCH; /* For convenience later */

	for (Search = Options->Track[TrackNum].Points; Search; Search = Search->Next)
	{
		/* First test: is it exactly this point? */
		if (PhotoTime == Search->Time)
		{
			/* This is the point, exactly.
			 * Copy out the data and return that. */
			Actual->Lat = Search->Lat;
			Actual->LatDecimals = Search->LatDecimals;
			Actual->Long = Search->Long;
			Actual->LongDecimals = Search->LongDecimals;
			Actual->Elev = Search->Elev;
			Actual->ElevDecimals = Search->ElevDecimals;
			Actual->Time = Search->Time;

			Options->Result = CORR_OK;
			break;
		}

		/* Sanity check / track segment fix: is the photo time before
		 * the current point? If so, we've gone past it. Hrm. */
		if (Search->Time > PhotoTime)
		{
			Options->Result = CORR_NOMATCH;
			break;
		}

		/* Sanity check: we need to peek at the next point.
		 * Make sure we can. */
		if (Search->Next == NULL) break;
		/* Sanity check: does this point have the same
		 * timestamp as the next? If so, skip onward. */
		if (Search->Time == Search->Next->Time) continue;
		/* Sanity check: does this point have a later
		 * timestamp than the next point? If so, skip. */
		if (Search->Time > Search->Next->Time) continue;

		if (Options->DoBetweenTrkSeg)
		{
			/* Righto, we are interpolating between segments.
			 * So simply do nothing! Simple! */
		} else {
			/* Don't check between track segments.
			 * If the end of segment marker is set, then simply
			 * "jump" over this point. */
			if (Search->EndOfSegment)
			{
				continue;
			}
		}

		/* Sort of sanity check: is this photo inside our
		 * "feather" time? If not, abort. */
		if (Options->FeatherTime)
		{
			/* Is the point between these two? */
			if ((PhotoTime > Search->Time) &&
				(PhotoTime < Search->Next->Time))
			{
				/* It is. Now is it too far
				 * from these two? */
				if (((Search->Time + Options->FeatherTime) < PhotoTime) &&
					((Search->Next->Time - Options->FeatherTime) > PhotoTime))
				{ 
					/* We are inside the feather
					 * time between two points.
					 * Abort. */
					Options->Result = CORR_TOOFAR;
					free(Actual);
					return NULL;
				} 
			}
		} /* endif (Options->Feather) */
		
		/* Second test: is it between this and the
		 * next point? */
		if ((PhotoTime > Search->Time) &&
				(PhotoTime < Search->Next->Time))
		{
			/* It is between these points.
			 * Unless told otherwise, we interpolate.
			 * If not interpolating, we round to nearest.
			 * If points are equidistant, we round down. */
			if (Options->NoInterpolate)
			{
				/* No interpolation. Round. */
				Round(Search, Actual, PhotoTime);
				Options->Result = CORR_ROUND;
				break;
			} else {
				/* Interpolate away! */
				Interpolate(Search, Actual, PhotoTime);
				Options->Result = CORR_INTERPOLATED;
				break;
			}
		}
	} /* End for() loop to search. */

	/* Did we actually match it at all? */
	if (Options->Result == CORR_NOMATCH)
	{
		/* Nope, no match at all. */
		/* Return with nothing. */
		free(Actual);
		return NULL;
	}

	/* Write the data back into the Exif info. If we're allowed. */
	if (Options->NoWriteExif)
	{
		/* Don't write exif tags. Just return. */
		return Actual;
	} else {
		/* Do write the exif tags. And then return. */
		if (WriteGPSData(Filename, Actual, Options->Datum, Options->NoChangeMtime, Options->DegMinSecs))
		{
			/* All ok. Good! Return. */
			return Actual;
		} else {
			/* Not good. Return point, but note failure. */
			Options->Result = CORR_EXIFWRITEFAIL;
			return Actual;
		}
	}
	
	/* Looks like nothing matched. Free the prepared memory,
	 * and return nothing. */
	free(Actual);
	return NULL;
}