GeoNames Find
FindDockWindow.cs
// Copyright 2011 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.ComponentModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Windows.Forms;
using System.Xml;

using ESRI.ArcGISExplorer.Mapping;

namespace GeoNamesFind
{
    /// <summary>
    /// Implements a custom find DockWindow.
    /// </summary>
    /// <remarks>
    /// Uses the geonames web service to search for placenames. The web service takes
    /// a URL query and returns an XML file with the results.
    /// The results are parsed and Graphics are created from them. The Graphics are attached
    /// to the Tag property of the TreeNode and are drawn on the map when the node is checked.
    /// </remarks>
    public partial class FindDockWindow : ESRI.ArcGISExplorer.Application.DockWindow
    {
        WebClient _webClient;  // used to download results from the web service
        string _resultFile;    // temp file that contains the result xml

        GraphicCollection _graphics = null;


        /// <summary>
        /// Creates a new FindDockWindow instance.
        /// </summary>
        public FindDockWindow()
        {
            InitializeComponent();

            _webClient = new WebClient();
            _webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(_webClient_DownloadCompleted);

            _graphics = ESRI.ArcGISExplorer.Application.Application.ActiveMapDisplay.Graphics;
        }

        #region Button Events

        /// <summary>
        /// Raised when the Clear button is clicked. 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonClear_Click(object sender, EventArgs e)
        {
            ClearResults();
        }

        /// <summary>
        /// Clears the results from the tree and graphics from the map.
        /// </summary>
        void ClearResults()
        {
            foreach (TreeNode node in treeView1.Nodes)
            {
                _graphics.Remove(node.Tag as Graphic);
            }
            treeView1.Nodes.Clear();
        }

        /// <summary>
        /// Enables/disables the controls on the DockWindow.
        /// </summary>
        /// <param name="enable"></param>
        void EnableControls(bool enable)
        {
            buttonFind.Enabled = enable;
            buttonClear.Enabled = enable;
            textBox1.Enabled = enable;

            // if we are enabling the controls, select the search string 
            // and give the TextBox focus
            //
            if (enable)
            {
                textBox1.SelectAll();
                textBox1.Focus();
            }
        }

        /// <summary>
        /// Raised when the Find button is clicked. Issues the query to the web service.
        /// </summary>
        private void buttonFind_Click(object sender, EventArgs e)
        {
            ClearResults();
            EnableControls(false);

            // generate a temporary filename to hold the result
            //
            _resultFile = Path.GetTempFileName();

            // build the url and make the async call to execute the query (just get the first 10 matches)
            //
            string url = "http://ws.geonames.org/search?q=" + textBox1.Text + "&maxRows=10&style=full";
            _webClient.DownloadFileAsync(new Uri(url), _resultFile);
        }

        #endregion

        #region WebClient Events

        /// <summary>
        /// Raised when the xml result file has been downloaded.
        /// </summary>
        void _webClient_DownloadCompleted(object sender, AsyncCompletedEventArgs e)
        {
            // read the xml file
            //
            XmlDocument doc = new XmlDocument();
            doc.Load(_resultFile);

            foreach (XmlNode xmlNode in doc.SelectNodes("/geonames/geoname"))
            {
                double lat = double.Parse(xmlNode.SelectSingleNode("lat").InnerText);
                double lon = double.Parse(xmlNode.SelectSingleNode("lng").InnerText);
                double elev = double.NaN;

                double.TryParse(xmlNode.SelectSingleNode("elevation").InnerText, out elev);

                Graphic graphic = CreateGraphic(xmlNode.SelectSingleNode("name").InnerText,
                                                xmlNode.SelectSingleNode("adminName1").InnerText,
                                                xmlNode.SelectSingleNode("countryName").InnerText,
                                                lat, lon, elev);

                treeView1.Nodes.Add(new TreeNode(graphic.Label) { Tag = graphic });
            }

            EnableControls(true);

            // zoom to the first node in the tree
            //
            if (treeView1.Nodes.Count > 0)
                ZoomTo(treeView1.Nodes[0]);
        }

        /// <summary>
        /// Zooms to the location that corresponds to the specified TreeNode and makes it visible.
        /// </summary>
        /// <param name="item"></param>
        void ZoomTo(TreeNode node)
        {
            node.Checked = true;
            this.Update();  // force redraw of the DockView before the flying to the item

            this.Cursor = Cursors.AppStarting;

            Graphic g = node.Tag as Graphic;
            if (g != null)
                ESRI.ArcGISExplorer.Application.Application.ActiveMapDisplay.ZoomTo(g.Geometry);
            this.Cursor = Cursors.Default;
        }

        /// <summary>
        /// Creates a Graphic using the specified names and location.
        /// </summary>
        Graphic CreateGraphic(string name, string adminName, string countryName, double lat, double lon, double elev)
        {
            Graphic graphic = new Graphic(new ESRI.ArcGISExplorer.Geometry.Point(lon, lat, elev), Symbol.Marker.Stickpin.White);

            if (double.IsNaN(elev)) // is the elevation undefined?
                graphic.Placement3D = Placement3D.AttachToSurface;
            else
                graphic.Placement3D = Placement3D.AbsoluteElevation;

            if (!string.IsNullOrEmpty(adminName))
                name += ", " + adminName;

            if (!string.IsNullOrEmpty(countryName))
                name += ", " + countryName;

            graphic.Label = name;

            return graphic;
        }

        #endregion

        #region TreeView Events

        /// <summary>
        /// Raised when a TreeNode is checked or unchecked.
        /// </summary>
        private void treeView1_AfterCheck(object sender, TreeViewEventArgs e)
        {
            Graphic g = e.Node.Tag as Graphic;
            if (g == null) return;

            // if the node is checked, show the graphic on the map, otherwise, hide it.
            if (_graphics.Contains(g))
            {
                g.Visible = (e.Node.Checked);
            }
            else
            {
                // If this is the first time that the graphic is shown, then add it to the Map.
                // It will be visible by default.
                _graphics.Add(g);
            }
        }

        /// <summary>
        /// Raised when an tree node is double-clicked.
        /// </summary>

        private void treeView1_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            ZoomTo(e.Node);
        }

        #endregion

        #region TextBox Events
        /// <summary>
        /// Raised when a key is pressed in the TextBox control.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void textBox1_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Return)
            {
                e.Handled = true;
                buttonFind_Click(this, EventArgs.Empty);
            }
        }

        #endregion

        #region Context Menu Events

        /// <summary>
        /// Delete context menu item.
        /// </summary>
        private void deleteMenuItem_Click(object sender, EventArgs e)
        {
            if (treeView1.SelectedNode == null) return;

            // Remove tree node and associated graphic from the map.
            Graphic g = treeView1.SelectedNode.Tag as Graphic;
            if (g != null) _graphics.Remove(g);
            treeView1.SelectedNode.Remove();
        }

        /// <summary>
        /// Move To Map context menu item - creates a note for the selected
        /// TreeNode and adds it to the map.
        /// </summary>
        private void moveToMapMenuItem_Click(object sender, EventArgs e)
        {
            if (treeView1.SelectedNode == null) return;

            Map map = ESRI.ArcGISExplorer.Application.Application.ActiveMapDisplay.Map;

            Graphic g = treeView1.SelectedNode.Tag as Graphic;

            treeView1.SelectedNode.Checked = false;  // causes the associated graphic to be removed from the map
         
            _graphics.Remove(g);

            if (g != null)
            {
                Note note = new Note(g.Label, g.Geometry, Symbol.Marker.Stickpin.Orange);  // change the symbol
                note.Viewpoint = new Viewpoint(g.Geometry.GetEnvelope());

                map.ChildItems.Insert(0, note); // add the note to the map
                ESRI.ArcGISExplorer.Application.Application.SelectedItems.Select(note);
            }


   treeView1.SelectedNode.Remove();

        }

        /// <summary>
        /// Delete All context menu item.
        /// </summary>
        private void deleteAllMenuItem_Click(object sender, EventArgs e)
        {
            ClearResults();
        }

        /// <summary>
        /// Raised before the context menu is shown - enable/disable items.
        /// </summary>
        private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
        {
            // enable/disable the context menu items
            deleteMenuItem.Enabled = treeView1.SelectedNode != null;
            moveToMapMenuItem.Enabled = treeView1.SelectedNode != null;
            deleteAllMenuItem.Enabled = treeView1.Nodes.Count > 0;
        }
        #endregion

    }

}