RSSWeatherGraphicTracker\RSSWeather.cs
// Copyright 2012 ESRI // // All rights reserved under the copyright laws of the United States // and applicable international laws, treaties, and conventions. // // You may freely redistribute and use this sample code, with or // without modification, provided you include the original copyright // notice and use restrictions. // // See the use restrictions. // using System; using System.Data; using System.Collections.Generic; using System.Text.RegularExpressions; using System.Xml; using System.IO; using System.Threading; using System.Timers; using System.Drawing; using System.Drawing.Imaging; using System.Windows.Forms; using System.Runtime.InteropServices; using ESRI.ArcGIS.ADF.BaseClasses; using ESRI.ArcGIS.ADF.CATIDs; using ESRI.ArcGIS.esriSystem; using ESRI.ArcGIS.Carto; using ESRI.ArcGIS.Geometry; using ESRI.ArcGIS.Display; using ESRI.ArcGIS.Controls; using ESRI.ArcGIS.EngineCore; using ESRI.ArcGIS.Geodatabase; using ESRI.ArcGIS.DataSourcesFile; namespace RSSWeatherGraphicTracker { #region WeatherItemEventArgs class members public sealed class WeatherItemEventArgs : EventArgs { private int m_iconId; private long m_zipCode; private double m_x; private double m_y; private int m_iconWidth; private int m_iconHeight; public WeatherItemEventArgs(int iconId, long zipCode, double X, double Y, int iconWidth, int iconHeight) { m_iconId = iconId; m_zipCode = zipCode; m_x = X; m_y = Y; m_iconWidth = iconWidth; m_iconHeight = iconHeight; } public int IconID { get { return m_iconId; } } public long ZipCode { get { return m_zipCode; } } public double mapY { get { return m_y; } } public double mapX { get { return m_x; } } } #endregion //declare delegates for the event handling public delegate void WeatherItemAdded(object sender, WeatherItemEventArgs args); public delegate void WeatherItemsUpdated(object sender, EventArgs args); class RSSWeather { private sealed class InvokeHelper : Control { //delegate used to pass the invoked method to the main thread public delegate void RefreshWeatherItemHelper(WeatherItemEventArgs weatherItemInfo); private RSSWeather m_weather = null; public InvokeHelper(RSSWeather rssWeather) { m_weather = rssWeather; CreateHandle(); CreateControl(); } public void RefreshWeatherItem(WeatherItemEventArgs weatherItemInfo) { try { // Invoke the RefreshInternal through its delegate if (!this.IsDisposed && this.IsHandleCreated) Invoke(new RefreshWeatherItemHelper(RefreshWeatherItemInvoked), new object[] { weatherItemInfo }); } catch (Exception ex) { System.Diagnostics.Trace.WriteLine(ex.Message); } } private void RefreshWeatherItemInvoked(WeatherItemEventArgs weatherItemInfo) { if (m_weather != null) m_weather.UpdateTracker(weatherItemInfo); } } #region class members private System.Timers.Timer m_timer = null; private Thread m_updateThread = null; private string m_iconFolder = string.Empty; private DataTable m_weatherItemTable = null; private DataTable m_symbolTable = null; private DataTable m_locations = null; private string m_dataFolder = string.Empty; private string m_installationFolder = string.Empty; private IPoint m_point = null; private ISpatialReference m_SRWGS84 = null; private IBasicMap m_mapOrGlobe = null; private ISimpleTextSymbol m_textSymbol = null; private IGraphicTracker m_graphicTracker = null; private InvokeHelper m_invokeHelper = null; //weather items events public event WeatherItemAdded OnWeatherItemAdded; public event WeatherItemsUpdated OnWeatherItemsUpdated; #endregion public RSSWeather() { //get the directory for the cache. If it does not exist, create it. m_dataFolder = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "RSSWeather"); if (!System.IO.Directory.Exists(m_dataFolder)) { System.IO.Directory.CreateDirectory(m_dataFolder); } m_iconFolder = m_dataFolder; m_installationFolder = ESRI.ArcGIS.RuntimeManager.ActiveRuntime.Path; } public void Init(IBasicMap mapOrGlobe) { System.Diagnostics.Trace.WriteLine("Init - Thread ID: " + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString()); if (mapOrGlobe == null) return; m_mapOrGlobe = mapOrGlobe; try { //initialize the tables (main table as well as the symbols table) InitializeTables(); //get the location list from a featureclass (US major cities) and synchronize it with the //cached information in case it exists. if (null == m_locations) InitializeLocations(); m_point = new PointClass(); m_SRWGS84 = CreateGeographicSpatialReference(); m_point.SpatialReference = m_SRWGS84; m_textSymbol = new TextSymbolClass() as ISimpleTextSymbol; m_textSymbol.Font = ToFontDisp(new Font("Tahoma", 10.0f, FontStyle.Bold)); m_textSymbol.Size = 10.0; m_textSymbol.Color = (IColor)ToRGBColor(Color.FromArgb(0, 255, 0)); m_textSymbol.XOffset = 0.0; m_textSymbol.YOffset = 16.0; m_graphicTracker = new GraphicTrackerClass(); m_graphicTracker.Initialize(mapOrGlobe as object); if (m_weatherItemTable.Rows.Count > 0) PopulateGraphicTracker(); m_invokeHelper = new InvokeHelper(this); this.OnWeatherItemAdded += new WeatherItemAdded(OnWeatherItemAddedEvent); //instantiate the timer for the weather update thread m_timer = new System.Timers.Timer(1000); m_timer.Elapsed += new ElapsedEventHandler(OnUpdateTimer); //enable the update timer m_timer.Enabled = true; } catch (Exception ex) { System.Diagnostics.Trace.WriteLine(ex.Message); } } public void Remove() { this.OnWeatherItemAdded -= new WeatherItemAdded(OnWeatherItemAddedEvent); m_invokeHelper = null; m_timer.Enabled = false; // wait for the update thread to exit m_updateThread.Join(); m_graphicTracker.RemoveAll(); m_graphicTracker = null; } public void UpdateTracker(WeatherItemEventArgs weatherItemInfo) { System.Diagnostics.Trace.WriteLine("UpdateTracker - Thread ID: " + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString()); if (m_graphicTracker == null) throw new Exception("Graphic tracker is not initialized!"); // 1. lock the m_weatherItemTable and get the record DataRow row; lock (m_weatherItemTable) { row = m_weatherItemTable.Rows.Find(weatherItemInfo.ZipCode); if (row == null) return; // 2. get the symbol for the item IGraphicTrackerSymbol symbol = GetSymbol(weatherItemInfo.IconID, Convert.ToString(row[7])); if (symbol == null) return; string label = string.Format("{0}, {1}?F", Convert.ToString(row[2]), Convert.ToString(row[5])); // 3. check whether it has a tracker ID (not equals -1) int trackerID = Convert.ToInt32(row[15]); //m_graphicTracker.SuspendUpdate = true; m_point.PutCoords(weatherItemInfo.mapX, weatherItemInfo.mapY); m_point.SpatialReference = m_SRWGS84; m_point.Project(m_mapOrGlobe.SpatialReference); if (trackerID == -1) // new tracker { trackerID = m_graphicTracker.Add(m_point as IGeometry, symbol); m_graphicTracker.SetTextSymbol(trackerID, m_textSymbol); row[15] = trackerID; } else // existing tracker { m_graphicTracker.MoveTo(trackerID, m_point.X, m_point.Y, 0); m_graphicTracker.SetSymbol(trackerID, symbol); } m_graphicTracker.SetLabel(trackerID, label); row.AcceptChanges(); //m_graphicTracker.SuspendUpdate = false; } } #region private utility methods void PopulateGraphicTracker() { m_graphicTracker.SuspendUpdate = true; foreach (DataRow row in m_weatherItemTable.Rows) { IGraphicTrackerSymbol symbol = GetSymbol(Convert.ToInt32(row[8]), Convert.ToString(row[7])); if (symbol == null) continue; string label = string.Format("{0}, {1}?F", Convert.ToString(row[2]), Convert.ToString(row[5])); m_point.PutCoords(Convert.ToDouble(row[4]), Convert.ToDouble(row[3])); int trackerID = m_graphicTracker.Add(m_point as IGeometry, symbol); m_graphicTracker.SetTextSymbol(trackerID, m_textSymbol); m_graphicTracker.SetScaleMode(trackerID, esriGTScale.esriGTScaleAuto); m_graphicTracker.SetOrientationMode(trackerID, esriGTOrientation.esriGTOrientationAutomatic); m_graphicTracker.SetElevationMode(trackerID, esriGTElevation.esriGTElevationClampToGround); m_graphicTracker.SetLabel(trackerID, label); row[15] = trackerID; row.AcceptChanges(); } m_graphicTracker.SuspendUpdate = false; } /// <summary> /// create a WGS1984 geographic coordinate system. /// In this case, the underlying data provided by the service is in WGS1984. /// </summary> /// <returns></returns> private ISpatialReference CreateGeographicSpatialReference() { ISpatialReferenceFactory spatialRefFatcory = new SpatialReferenceEnvironmentClass(); IGeographicCoordinateSystem geoCoordSys; geoCoordSys = spatialRefFatcory.CreateGeographicCoordinateSystem((int)esriSRGeoCSType.esriSRGeoCS_WGS1984); geoCoordSys.SetFalseOriginAndUnits(-180.0, -180.0, 5000000.0); geoCoordSys.SetZFalseOriginAndUnits(0.0, 100000.0); geoCoordSys.SetMFalseOriginAndUnits(0.0, 100000.0); return geoCoordSys as ISpatialReference; } /// <summary> /// run the thread that does the update of the weather data /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void OnUpdateTimer(object sender, ElapsedEventArgs e) { m_timer.Interval = 2700000; //(45 minutes) m_updateThread = new Thread(new ThreadStart(ThreadProc)); //run the update thread m_updateThread.Start(); } /// <summary> /// the main update thread for the data. /// </summary> /// <remarks>Since the information is coming from a web service which might /// take a while to respond, it is not logical to let the application hang while waiting /// for response. Therefore, running the request on a different thread frees the application to /// continue working while waiting for a response. /// Please note that in this case, synchronization of shared resources must be addressed, /// otherwise you might end up getting unexpected results.</remarks> private void ThreadProc() { try { long lZipCode; //iterate through all the records in the main table and update it against //the information from the website. foreach (DataRow r in m_locations.Rows) { //put the thread to sleep in order not to overwhelm the RSS website //System.Threading.Thread.Sleep(200); //get the zip code of the record (column #1) lZipCode = Convert.ToInt32(r[1]); //make the request and update the item AddWeatherItem(lZipCode, 0.0, 0.0); } //serialize the tables onto the local machine DataSet ds = new DataSet(); ds.Tables.Add(m_weatherItemTable); ds.WriteXml(System.IO.Path.Combine(m_dataFolder, "Weather.xml")); ds.Tables.Remove(m_weatherItemTable); ds.Dispose(); GC.Collect(); //fire an event to notify update of the weather items if(OnWeatherItemsUpdated != null) OnWeatherItemsUpdated(this, new EventArgs()); } catch (Exception ex) { System.Diagnostics.Trace.WriteLine(ex.Message); } } /// <summary> /// given a bitmap url, saves it on the local machine and returns its size /// </summary> /// <param name="iconPath"></param> /// <param name="width"></param> /// <param name="height"></param> private Bitmap DownloadIcon(string iconPath, out int width, out int height) { //if the icon does not exist on the local machine, get it from RSS site string iconFileName = System.IO.Path.Combine(m_iconFolder, System.IO.Path.GetFileNameWithoutExtension(iconPath) + ".png"); width = 0; height = 0; Bitmap bitmap = null; if (!File.Exists(iconFileName)) { using (System.Net.WebClient webClient = new System.Net.WebClient()) { //open a readable stream to download the bitmap using (System.IO.Stream stream = webClient.OpenRead(iconPath)) { bitmap = new Bitmap(stream, true); //save the image as a bitmap in the icons folder bitmap.Save(iconFileName, ImageFormat.Png); //get the bitmap's dimensions width = bitmap.Width; height = bitmap.Height; } } } else { //get the bitmap's dimensions { bitmap = new Bitmap(iconFileName); width = bitmap.Width; height = bitmap.Height; } } return bitmap; } /// <summary> /// get the specified symbol from the symbols table. /// </summary> /// <param name="iconCode"></param> /// <param name="dbr"></param> /// <returns></returns> private IGraphicTrackerSymbol GetSymbol(int iconCode, string iconPath) { IGraphicTrackerSymbol symbol; int iconWidth, iconHeight; Bitmap bitmap = null; //search for an existing symbol in the table DataRow r = m_symbolTable.Rows.Find(iconCode); if (r == null) //in case that the symbol does not exist in the table, create a new entry { r = m_symbolTable.NewRow(); r[1] = iconCode; //Initialize the picture marker symbol symbol = InitializeSymbol(iconPath, out iconWidth, out iconHeight, out bitmap); if (null == symbol) return null; //update the symbol table lock (m_symbolTable) { r[2] = symbol; r[3] = iconWidth; r[4] = iconHeight; r[5] = bitmap; m_symbolTable.Rows.Add(r); } } else { if (r[2] is DBNull) //in case that the record exists but the symbol hasn't been initialized { //Initialize the symbol symbol = InitializeSymbol(iconPath, out iconWidth, out iconHeight, out bitmap); if (null == symbol) return null; //update the symbol table lock (m_symbolTable) { r[2] = symbol; r[5] = bitmap; r.AcceptChanges(); } } else //the record exists in the table and the symbol has been initialized //get the symbol symbol = r[2] as IGraphicTrackerSymbol; } //return the requested symbol return symbol; } /// <summary> /// Initialize a character marker symbol for a given bitmap path /// </summary> /// <param name="iconPath"></param> /// <param name="iconWidth"></param> /// <param name="iconHeight"></param> /// <returns></returns> private IGraphicTrackerSymbol InitializeSymbol(string iconPath, out int iconWidth, out int iconHeight, out Bitmap bitmap) { iconWidth = iconHeight = 0; bitmap = null; try { //make sure that the icon exit on dist or else download it DownloadIcon(iconPath, out iconWidth, out iconHeight); string iconFileName = System.IO.Path.Combine(m_iconFolder, System.IO.Path.GetFileNameWithoutExtension(iconPath) + ".png"); if (!System.IO.File.Exists(iconFileName)) return null; IGraphicTrackerSymbol symbol = m_graphicTracker.CreateSymbolFromPath(iconFileName, iconFileName); return symbol; } catch { return null; } } /// <summary> /// Makes a request against RSS Weather service and add update the table /// </summary> /// <param name="zipCode"></param> /// <param name="Lat"></param> /// <param name="Lon"></param> private void AddWeatherItem(long zipCode, double Lat, double Lon) { try { string cityName; double lat, lon; int temp; string condition; string desc; string iconPath; string day; string date; int low; int high; int iconCode; int iconWidth = 52; //default values int iconHeight = 52; Bitmap bitmap = null; DataRow dbr = m_weatherItemTable.Rows.Find(zipCode); if (dbr != null) { // get the date DateTime updateDate = Convert.ToDateTime(dbr[14]); TimeSpan ts = DateTime.Now - updateDate; // if the item had been updated in the past 15 minutes, simply bail out. if (ts.TotalMinutes < 15) return; } //the base URL for the service string url = "http://xml.weather.yahoo.com/forecastrss?p="; //the RegEx used to extract the icon path from the HTML tag string regxQry = "(http://(\\\")?(.*?\\.gif))"; XmlTextReader reader = null; XmlDocument doc; XmlNode node; try { //make the request and get the result back into XmlReader reader = new XmlTextReader(url + zipCode.ToString()); } catch (Exception ex) { System.Diagnostics.Trace.WriteLine(ex.Message); return; } //load the XmlReader to an xml doc doc = new XmlDocument(); doc.Load(reader); //set an XmlNamespaceManager since we have to make explicit namespace searches XmlNamespaceManager xmlnsManager = new System.Xml.XmlNamespaceManager(doc.NameTable); //Add the namespaces used in the xml doc to the XmlNamespaceManager. xmlnsManager.AddNamespace("yweather", "http://xml.weather.yahoo.com/ns/rss/1.0"); xmlnsManager.AddNamespace("geo", "http://www.w3.org/2003/01/geo/wgs84_pos#"); //make sure that the node exists node = doc.DocumentElement.SelectSingleNode("/rss/channel/yweather:location/@city", xmlnsManager); if (null == node) return; //get the city name cityName = doc.DocumentElement.SelectSingleNode("/rss/channel/yweather:location/@city", xmlnsManager).InnerXml; if (Lat == 0.0 && Lon == 0.0) { //in case that the caller did not specify a coordinate, get the default coordinate from the service lat = Convert.ToDouble(doc.DocumentElement.SelectSingleNode("/rss/channel/item/geo:lat", xmlnsManager).InnerXml); lon = Convert.ToDouble(doc.DocumentElement.SelectSingleNode("/rss/channel/item/geo:long", xmlnsManager).InnerXml); } else { lat = Lat; lon = Lon; } //extract the rest of the information from the RSS response condition = doc.DocumentElement.SelectSingleNode("/rss/channel/item/yweather:condition/@text", xmlnsManager).InnerXml; iconCode = Convert.ToInt32(doc.DocumentElement.SelectSingleNode("/rss/channel/item/yweather:condition/@code", xmlnsManager).InnerXml); temp = Convert.ToInt32(doc.DocumentElement.SelectSingleNode("/rss/channel/item/yweather:condition/@temp", xmlnsManager).InnerXml); desc = doc.DocumentElement.SelectSingleNode("/rss/channel/item/description").InnerXml; day = doc.DocumentElement.SelectSingleNode("/rss/channel/item/yweather:forecast/@day", xmlnsManager).InnerXml; date = doc.DocumentElement.SelectSingleNode("/rss/channel/item/yweather:forecast/@date", xmlnsManager).InnerXml; low = Convert.ToInt32(doc.DocumentElement.SelectSingleNode("/rss/channel/item/yweather:forecast/@low", xmlnsManager).InnerXml); high = Convert.ToInt32(doc.DocumentElement.SelectSingleNode("/rss/channel/item/yweather:forecast/@high", xmlnsManager).InnerXml); //use regex in order to extract the icon name from the html script Match m = Regex.Match(desc, regxQry); if (m.Success) { iconPath = m.Value; //add the icon ID to the symbology table DataRow tr = m_symbolTable.Rows.Find(iconCode); if (null == tr) { //get the icon from the website bitmap = DownloadIcon(iconPath, out iconWidth, out iconHeight); //create a new record tr = m_symbolTable.NewRow(); tr[1] = iconCode; tr[3] = iconWidth; tr[4] = iconHeight; tr[5] = bitmap; //update the symbol table. The initialization of the symbol cannot take place in here, since //this code gets executed on a background thread. lock (m_symbolTable) { m_symbolTable.Rows.Add(tr); } } else //get the icon's dimensions from the table { //get the icon's dimensions from the table iconWidth = Convert.ToInt32(tr[3]); iconHeight = Convert.ToInt32(tr[4]); } } else { iconPath = ""; } //test whether the record already exists in the table. if (null == dbr) //in case that the record does not exist { //create a new record dbr = m_weatherItemTable.NewRow(); if (!m_weatherItemTable.Columns[0].AutoIncrement) dbr[0] = Convert.ToInt32(DateTime.Now.Millisecond); //add the item to the table lock (m_weatherItemTable) { dbr[1] = zipCode; dbr[2] = cityName; dbr[3] = lat; dbr[4] = lon; dbr[5] = temp; dbr[6] = condition; dbr[7] = iconPath; dbr[8] = iconCode; dbr[9] = day; dbr[10] = date; dbr[11] = low; dbr[12] = high; dbr[13] = false; dbr[14] = DateTime.Now; dbr[15] = -1; m_weatherItemTable.Rows.Add(dbr); } } else //in case that the record exists, just update it { //update the record lock (m_weatherItemTable) { dbr[5] = temp; dbr[6] = condition; dbr[7] = iconPath; dbr[8] = iconCode; dbr[9] = day; dbr[10] = date; dbr[11] = low; dbr[12] = high; dbr[14] = DateTime.Now; dbr.AcceptChanges(); } } //fire an event to notify the user that the item has been updated if (OnWeatherItemAdded != null) { WeatherItemEventArgs weatherItemEventArgs = new WeatherItemEventArgs(iconCode, zipCode, lon, lat, iconWidth, iconHeight); OnWeatherItemAdded(this, weatherItemEventArgs); } } catch (Exception ex) { System.Diagnostics.Trace.WriteLine("AddWeatherItem: " + ex.Message); } } private IRgbColor ToRGBColor(System.Drawing.Color color) { IRgbColor rgbColor = new RgbColorClass(); rgbColor.Red = color.R; rgbColor.Green = color.G; rgbColor.Blue = color.B; return rgbColor; } private stdole.IFontDisp ToFontDisp(System.Drawing.Font font) { stdole.IFont aFont; aFont = new stdole.StdFontClass(); aFont.Name = font.Name; aFont.Size = (decimal)font.Size; aFont.Bold = font.Bold; aFont.Italic = font.Italic; aFont.Strikethrough = font.Strikeout; aFont.Underline = font.Underline; return aFont as stdole.IFontDisp; } /// <summary> /// initialize the main table as well as the symbols table. /// The base class calls new on the table and adds a default ID field. /// </summary> private void InitializeTables() { string path = System.IO.Path.Combine(m_dataFolder, "Weather.xml"); //In case that there is no existing cache on the local machine, create the table. if (!System.IO.File.Exists(path)) { //create the table the table in addition to the default 'ID' and 'Geometry' m_weatherItemTable = new DataTable("RECORDS"); m_weatherItemTable.Columns.Add("ID", typeof(long)); //0 m_weatherItemTable.Columns.Add("ZIPCODE", typeof(long)); //1 m_weatherItemTable.Columns.Add("CITYNAME", typeof(string)); //2 m_weatherItemTable.Columns.Add("LAT", typeof(double)); //3 m_weatherItemTable.Columns.Add("LON", typeof(double)); //4 m_weatherItemTable.Columns.Add("TEMP", typeof(int)); //5 m_weatherItemTable.Columns.Add("CONDITION", typeof(string)); //6 m_weatherItemTable.Columns.Add("ICONNAME", typeof(string)); //7 m_weatherItemTable.Columns.Add("ICONID", typeof(int)); //8 m_weatherItemTable.Columns.Add("DAY", typeof(string)); //9 m_weatherItemTable.Columns.Add("DATE", typeof(string)); //10 m_weatherItemTable.Columns.Add("LOW", typeof(string)); //11 m_weatherItemTable.Columns.Add("HIGH", typeof(string)); //12 m_weatherItemTable.Columns.Add("SELECTED", typeof(bool)); //13 m_weatherItemTable.Columns.Add("UPDATEDATE", typeof(DateTime)); //14 m_weatherItemTable.Columns.Add("TRACKERID", typeof(long)); //15 //set the ID column to be auto increment m_weatherItemTable.Columns[0].AutoIncrement = true; m_weatherItemTable.Columns[0].ReadOnly = true; //the zipCode column must be the unique and nut allow null m_weatherItemTable.Columns[1].Unique = true; // set the ZIPCODE primary key for the table m_weatherItemTable.PrimaryKey = new DataColumn[] { m_weatherItemTable.Columns["ZIPCODE"] }; } else //in case that the local cache exists, simply load the tables from the cache. { DataSet ds = new DataSet(); ds.ReadXml(path); m_weatherItemTable = ds.Tables["RECORDS"]; if (null == m_weatherItemTable) throw new Exception("Cannot find 'RECORDS' table"); if (16 != m_weatherItemTable.Columns.Count) throw new Exception("Table 'RECORDS' does not have all required columns"); m_weatherItemTable.Columns[0].ReadOnly = true; // set the ZIPCODE primary key for the table m_weatherItemTable.PrimaryKey = new DataColumn[] { m_weatherItemTable.Columns["ZIPCODE"] }; //synchronize the locations table foreach (DataRow r in m_weatherItemTable.Rows) { try { //in case that the locations table does not exists, create and initialize it if (null == m_locations) InitializeLocations(); //get the zipcode for the record string zip = Convert.ToString(r[1]); //make sure that there is no existing record with that zipCode already in the //locations table. DataRow[] rows = m_locations.Select("ZIPCODE = " + zip); if (0 == rows.Length) { DataRow rec = m_locations.NewRow(); rec[1] = Convert.ToInt64(r[1]); //zip code rec[2] = Convert.ToString(r[2]); //city name //add the new record to the locations table lock (m_locations) { m_locations.Rows.Add(rec); } } } catch (Exception ex) { System.Diagnostics.Trace.WriteLine(ex.Message); } } //dispose the DS ds.Tables.Remove(m_weatherItemTable); ds.Dispose(); GC.Collect(); } //initialize the symbol map table m_symbolTable = new DataTable("Symbology"); //add the columns to the table m_symbolTable.Columns.Add("ID", typeof(int)); //0 m_symbolTable.Columns.Add("ICONID", typeof(int)); //1 m_symbolTable.Columns.Add("SYMBOL", typeof(IGraphicTrackerSymbol)); //2 m_symbolTable.Columns.Add("SYMBOLWIDTH", typeof(int)); //3 m_symbolTable.Columns.Add("SYMBOLHEIGHT", typeof(int)); //4 m_symbolTable.Columns.Add("BITMAP", typeof(Bitmap)); //5 //set the ID column to be auto increment m_symbolTable.Columns[0].AutoIncrement = true; m_symbolTable.Columns[0].ReadOnly = true; m_symbolTable.Columns[1].AllowDBNull = false; m_symbolTable.Columns[1].Unique = true; //set ICONID as the primary key for the table m_symbolTable.PrimaryKey = new DataColumn[] { m_symbolTable.Columns["ICONID"] }; } /// <summary> /// Initialize the location table. Gets the location from a featureclass /// </summary> private void InitializeLocations() { //create a new instance of the location table m_locations = new DataTable(); //add fields to the table m_locations.Columns.Add("ID", typeof(int)); m_locations.Columns.Add("ZIPCODE", typeof(long)); m_locations.Columns.Add("CITYNAME", typeof(string)); m_locations.Columns[0].AutoIncrement = true; m_locations.Columns[0].ReadOnly = true; //set ZIPCODE as the primary key for the table m_locations.PrimaryKey = new DataColumn[] { m_locations.Columns["ZIPCODE"] }; PopulateLocationsTable(); } /// <summary> /// Load the information from the MajorCities featureclass to the locations table /// </summary> private void PopulateLocationsTable() { string path = System.IO.Path.Combine(m_installationFolder + @"\..", @"DeveloperKit10.0\Samples\data\USZipCodeData\"); //open the featureclass IWorkspaceFactory wf = new ShapefileWorkspaceFactoryClass() as IWorkspaceFactory; IWorkspace ws = wf.OpenFromFile(path, 0); IFeatureWorkspace fw = ws as IFeatureWorkspace; IFeatureClass featureClass = fw.OpenFeatureClass("ZipCode_Boundaries_US_Major_Cities"); //map the name and zip fields int zipIndex = featureClass.FindField("ZIP"); int nameIndex = featureClass.FindField("NAME"); string cityName; long zip; try { //iterate through the features and add the information to the table IFeatureCursor fCursor = null; fCursor = featureClass.Search(null, true); IFeature feature = fCursor.NextFeature(); int index = 0; while (null != feature) { object obj = feature.get_Value(nameIndex); if (obj == null) continue; cityName = Convert.ToString(obj); obj = feature.get_Value(zipIndex); if (obj == null) continue; zip = long.Parse(Convert.ToString(obj)); if (zip <= 0) continue; //add the current location to the location table DataRow r = m_locations.Rows.Find(zip); if (null == r) { r = m_locations.NewRow(); r[1] = zip; r[2] = cityName; lock (m_locations) { m_locations.Rows.Add(r); } } feature = fCursor.NextFeature(); index++; } //release the feature cursor Marshal.ReleaseComObject(fCursor); } catch (Exception ex) { System.Diagnostics.Trace.WriteLine(ex.Message); } } /// <summary> /// weather ItemAdded event handler /// </summary> /// <param name="sender"></param> /// <param name="args"></param> /// <remarks>gets fired when an item is added to the table</remarks> private void OnWeatherItemAddedEvent(object sender, WeatherItemEventArgs args) { // use the invoke helper since this event gets fired on a different thread m_invokeHelper.RefreshWeatherItem(args); } #endregion } }