I'm planning on using TopoFusion for search and rescue dogs with the Garmin Astro collar.
Search dogs' ability to find victims is influenced by weather conditions. Therefore I'd like a weather "overlay" for trails. This is particularly relevant for changes in wind direction.
My plan is to use the Weather Underground API to pull weather data at regular intervals, e.g. 30 seconds to a minute, and then programmatically create jpegs with formated text information (black letters on white background). This should let me run my own program alongside TopoFusion and provide TopoFusion with data it can ingest via PhotoFusion.
My question is the following. When my team trains, we might have a half dozen or so members in the field at once. Will PhotoFusion have a problem with this, i.e. the situation where there are >1 GPS tracks at a given time, but only one image/weather data to share between them?
BTW a great feature for TopoFusion would be a custom waypoint annotator that ingests e.g. csv and displays this way, so that converting to an image would be unnecessary.
----------------
OK, update. Here is one of the images I'm producing.
I decided to use Yahoo's API rather that WU b/c the API is cleaner, has all the data I care about, and seems to be updated more regularly.
When I write the jpg, I'm reading it back in and modifying its exif data I assume that exif's date taken is the right parameter to set.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Net;
using System.Data;
namespace WeatherToPhotoFusion
{
public class YahooWeather
{
public string WindSpeed { get; set; }
public string WindDirection { get; set; }
public string Humidity { get; set; }
public string Pressure { get; set; }
public string PressureDirection { get; set; }
public string Temperature { get; set; }
public string WeatherCondition { get; set; }
public string PublishedTime { get; set; }
public DateTime ReceivedTime { get; set; }
public YahooWeather() { }
public static YahooWeather GetConditions(string location)
{
YahooWeather conditions = new YahooWeather();
XmlDocument xmlConditions = new XmlDocument();
xmlConditions.Load(string.Format("
http://weather.yahooapis.com/forecastrss?w={0}", location));
// Set up namespace manager for XPath
XmlNamespaceManager ns = new XmlNamespaceManager(xmlConditions.NameTable);
ns.AddNamespace("yweather", "
http://xml.weather.yahoo.com/ns/rss/1.0");
// Get forecast with XPath
XmlNode node = xmlConditions.SelectSingleNode("/rss/channel/yweather:wind", ns);
conditions.WindSpeed = node.Attributes["speed"].InnerText;
conditions.WindDirection = node.Attributes["direction"].InnerText;
node = xmlConditions.SelectSingleNode("/rss/channel/yweather:atmosphere", ns);
conditions.Humidity = node.Attributes["humidity"].InnerText;
conditions.Pressure = node.Attributes["pressure"].InnerText;
switch (node.Attributes["rising"].InnerText)
{
case "0": conditions.PressureDirection = "steady";
break;
case "1": conditions.PressureDirection = "rising";
break;
case "2": conditions.PressureDirection = "falling";
break;
}
node = xmlConditions.SelectSingleNode("/rss/channel/item/yweather:condition", ns);
conditions.WeatherCondition = node.Attributes["text"].InnerText;
conditions.Temperature = node.Attributes["temp"].InnerText;
node = xmlConditions.SelectSingleNode("/rss/channel/item/pubDate", ns);
conditions.PublishedTime = node.InnerText;
conditions.ReceivedTime = DateTime.Now;
return conditions;
}
}
}
---------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
namespace WeatherToPhotoFusion
{
public class TextJPEG
{
public TextJPEG() {}
public static void WriteFile( YahooWeather yahooWeather)
{
int width = 400, height = 400;
SolidBrush backgroundColor = new SolidBrush(Color.White);
Font font = new Font("Arial", 12,FontStyle.Bold );
System.Drawing.Bitmap bitmap = new Bitmap(width,height);
System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(bitmap);
graphics.FillRectangle( backgroundColor , 0, 0, width, height);
double radian = int.Parse(yahooWeather.WindDirection) * Math.PI / 180;
TextJPEG.DrawPolygon(5f, 200, Color.Red, (float)radian, graphics);
graphics.DrawString("Temperature:\t\t"+ yahooWeather.Temperature, font, SystemBrushes.WindowText, new Point( 10, 10 ) );
graphics.DrawString("Pressure:\t\t"+ yahooWeather.Pressure, font, SystemBrushes.WindowText, new Point(10, 50));
graphics.DrawString("PressureDirection:\t"+ yahooWeather.PressureDirection, font, SystemBrushes.WindowText, new Point(10, 100));
graphics.DrawString("Humidity:\t\t"+ yahooWeather.Humidity, font, SystemBrushes.WindowText, new Point(10, 150));
graphics.DrawString("WindSpeed:\t\t"+ yahooWeather.WindSpeed, font, SystemBrushes.WindowText, new Point(10, 200));
graphics.DrawString("WindDirection:\t\t"+ yahooWeather.WindDirection, font, SystemBrushes.WindowText, new Point(10, 250));
graphics.DrawString("WeatherCondition:\t"+ yahooWeather.WeatherCondition, font, SystemBrushes.WindowText, new Point(10, 300));
graphics.DrawString("PublishedTime:\t\t"+ yahooWeather.PublishedTime, font, SystemBrushes.WindowText, new Point(10, 350));
bitmap.Save("TEMP", ImageFormat.Jpeg);
//strangely we have to save this to get any property items to work with
Image image = Image.FromFile("TEMP");
PropertyItem[] items = image.PropertyItems;
PropertyItem property = items[0]; //any property will do b/c we don't have a constructor
string dateTimeString = yahooWeather.ReceivedTime.ToString(@"yyyy\:MM\:dd HH\:mm\:ss");
byte[] Data = Encoding.UTF8.GetBytes(dateTimeString + '\0');
property.Id = 0x9003;
property.Type = (Int16) 2;
property.Value = Data;
property.Len = Data.Length;
image.SetPropertyItem(property);
image.Save( yahooWeather.ReceivedTime.ToLongDateString().Replace(" ","_")+ "_"+
yahooWeather.ReceivedTime.Ticks.ToString() + ".JPG");
}
private static void DrawPolygon(float fThickness, float fLength, Color color,
float fRadians, Graphics graphics)
{
float fCenterX = 200, fCenterY = 200;
PointF A = new PointF((float)(fCenterX +
fThickness * 2 * System.Math.Cos(fRadians + Math.PI / 2)),
(float)(fCenterY -
fThickness * 2 * System.Math.Sin(fRadians + Math.PI / 2)));
PointF B = new PointF((float)(fCenterX +
fThickness * 2 * System.Math.Cos(fRadians - Math.PI / 2)),
(float)(fCenterY -
fThickness * 2 * System.Math.Sin(fRadians - Math.PI / 2)));
PointF C = new PointF((float)(fCenterX +
fLength * System.Math.Cos(fRadians)),
(float)(fCenterY -
fLength * System.Math.Sin(fRadians)));
PointF D = new PointF((float)(fCenterX -
fThickness * 4 * System.Math.Cos(fRadians)),
(float)(fCenterY +
fThickness * 4 * System.Math.Sin(fRadians)));
PointF[] points = { A, D, B, C };
graphics.FillPolygon(new SolidBrush(color), points);
}
}
}