Friday 29 March 2013

Bite-sized free geocoding

            It took me some time to read (or skim through) all the TOUs of different geocoding providers. Most of them are free for personal use (Yahoo maps/Google maps and Bing maps geocoders). Only one that I found is free for commercial use (although with some heavy constraints but still). It's called Nominatim and it is used by OpenStreetMap project. I wrote this short snippet, someone might find helpful. First, we need to create the request url. The method below might not be an example of good coding practices, but it's clear enough. What you want to get as the return value is an url that looks something like this:
                http://nominatim.openstreetmap.org/search?city=London&format=xml
  1 public string BuildRequest(string building, string street, string city,
  2  string country) 
  3  { 
  4     bool streetAdded, cityAdded, countryAdded;
  5     streetAdded = cityAdded = countryAdded = false;
  6   
  7     StringBuilder sb = 
  8     new StringBuilder(@"http://nominatim.openstreetmap.org/search?"); 
  9  
 10     if (!String.IsNullOrEmpty(street)) 
 11     { 
 12         sb.Append("street="); 
 13         if (!String.IsNullOrEmpty(building))
 14         { 
 15             sb.AppendFormat("{0} ", building); 
 16         } 
 17         sb.Append(street); 
 18         streetAdded = true; 
 19     } 
 20  
 21     if (!String.IsNullOrEmpty(city)) 
 22     { 
 23         if (streetAdded) 
 24             sb.Append("&"); 
 25  
 26         sb.AppendFormat("city={0}", city); 
 27         cityAdded = true; 
 28     } 
 29  
 30     if (!String.IsNullOrEmpty(country)) 
 31     { 
 32         if (streetAdded | cityAdded) 
 33             sb.Append("&"); 
 34  
 35         sb.AppendFormat("country={0}", country); 
 36         countryAdded = true; 
 37     } 
 38  
 39     if (streetAdded | cityAdded | countryAdded)
 40         sb.AppendFormat("&"); 
 41  
 42     sb.AppendFormat("format=xml"); 
 43     return sb.ToString();  
 44  }

             After formulating the request, we can pass it into DownloadString() method. What we'll get in return will be xml in string form. Each <place> node is a separate place which matches the address we've given. There are several formats we can choose from when working with Nominatim provider, I've chosen xml.




  1 public Point GetCoordinates(string building, string street, string city,
  2  string country) 
  3 {     
  4     WebClient webClient = new WebClient();    
  5     string request = BuildRequest(building, street, city, country); 
  6     string result = webClient.DownloadString(request);
  7  
  8     XmlDocument xml = new XmlDocument(); 
  9     xml.LoadXml(result); 
 10  
 11     var nodes = xml.SelectNodes("/searchresults/place"); 
 12  
 13     if (nodes != null && nodes.Count > 0)
 14     { 
 15         var lat = nodes[0].Attributes["lat"].InnerText; 
 16         var lon = nodes[0].Attributes["lon"].InnerText; 
 17  
 18         double latitude = Double.Parse(lat, CultureInfo.InvariantCulture);
 19         double longitude = Double.Parse(lon, CultureInfo.InvariantCulture);
 20         return new Point(latitude, longitude);
 21     } 
 22     return null;
 23 } 


         To use the WebClient class, you need to add reference to System.Net. To get more information about the xml data returned by the provider, and the data you can pass in the request you should visit: http://wiki.openstreetmap.org/wiki/Nominatim To see the exact usage policy check: http://wiki.openstreetmap.org/wiki/Nominatim_usage_policy As you can see this is a really easy way, if you want to implement some simple geocoding in your app.

Wednesday 6 March 2013

Checkbox header column using AttachedProperty

        During our project some time ago, we've got a new requirement introduced. The requirement was to extend datagrids across the application, by adding a selection column, checkbox selection column. It's a boring and daunting task to change every view and viewmodel to support this new scenerio. Hence I opted for a more elegant, simple and automated way of doing things. I chose attached properties as the method of choice, bear in mind you can make this functionality work in a completely different fashion. Whatever suits you. I'd like to also add, that I do use indentation and curly braces in most of my code. Here for the sake of brevity I've let myself be a little bit less strict with the style of coding.

Code Snippet
  1. public static class SelectionColumnExtension
  2. {
  3.  
  4. public static bool GetSelectionColumn(DependencyObject obj)
  5. {
  6.     return (bool)obj.GetValue(SelectionColumnProperty);
  7. }
  8.  
  9. public static void SetSelectionColumn(DependencyObject obj, bool value)
  10. {
  11.     obj.SetValue(SelectionColumnProperty, value);
  12. }
  13.  
  14. // Selected column dependency property
  15. public static readonly DependencyProperty SelectionColumnProperty =
  16.     DependencyProperty.RegisterAttached("SelectionColumn",
  17.     typeof(bool),
  18.     typeof(SelectionColumnExtension),
  19.     new UIPropertyMetadata(false, OnSelectionColumnSet));

        Above is the attached property (added quickly with the good old "propa" visual studio snippet) which will be settable/bindable from XAML as the switch to add selection checkbox column to the datagrid.

Code Snippet
  1. // Selected column setup
  2. public static void OnSelectionColumnSet(DependencyObject obj,
  3.             DependencyPropertyChangedEventArgs args)
  4. {
  5.     var dataGrid = obj as DataGrid;
  6.     if (dataGrid != null && (bool)args.NewValue == true)
  7.     {
  8.         DataGridCheckBoxColumn column = new DataGridCheckBoxColumn();
  9.         column.Binding = new Binding("IsChecked");
  10.         DataTemplate dataTemplate = GetHeaderTemplate(dataGrid);
  11.         column.HeaderTemplate = dataTemplate;
  12.         dataGrid.Columns.Add(column);
  13.     }
  14. }

        Next is the callback method, that gets called every time "SelectionColumn" property changes. As you can see I didn't implement column removal without the need of such requirements. It is quite an easy task though and might get implemented later on.
       Binding constructor is set to "IsChecked" property by default - this is the property in your ViewModel to which checkboxes in the checkbox column will bind.

Code Snippet
  1. public static DataTemplate GetHeaderTemplate(DataGrid datagrid){
  2.     DataTemplate dataTemplate = new DataTemplate();
  3.     FrameworkElementFactory factory =
  4.         new FrameworkElementFactory(typeof(Grid));
  5.     FrameworkElementFactory cbFactory =
  6.         new FrameworkElementFactory(typeof(CheckBox));
  7.     Binding checkBoxBinding =
  8.         new Binding("GlobalIsChecked");
  9.     cbFactory.SetBinding(CheckBox.IsCheckedProperty, checkBoxBinding);
  10.     cbFactory.AddHandler(CheckBox.CheckedEvent,
  11.         new RoutedEventHandler(OnGlobalChecked));
  12.     cbFactory.AddHandler(CheckBox.UncheckedEvent,
  13.         new RoutedEventHandler(OnGlobalUnchecked));
  14.     cbFactory.SetValue(CheckBox.TagProperty, datagrid.Items);
  15.     factory.AppendChild(cbFactory);
  16.     dataTemplate.VisualTree = factory;
  17.     return dataTemplate;
  18. }

       We need a DataTemplate for the Checkbox column. We can create visuals in C#  code using FrameworkElementFactory. The most important part of this method is hooking up Checked and Unchecked event handling for the header checkbox and putting Datagrid.Items collection in the Tag of the checkbox for later operations.
       Note that Items property of the DataGrid is of ItemsCollection type and is actually a CollectionView. Because of that by refering this property through Checkbox's tag, we will have constant access to the current list of items in our datagrid without the need to follow changes in the collection. Lastly we need to add Checked and Unchecked events handling, which is pretty straightforward:

Code Snippet
  1. // Changing checked state of checkboxes according to the global checkbox
  2. public static void ChangeCheckboxStateTo(CheckBox cb , bool state)
  3. {          
  4.     if (cb != null)
  5.     {
  6.         ItemCollection ic = cb.Tag as ItemCollection;
  7.         foreach (var item in ic)
  8.         {
  9.             var property = item.GetType().GetProperty("IsChecked");                     
  10.             if (property != null)                    
  11.                 property.SetValue(item, state, null);
  12.         }
  13.     }
  14. }
  15. public static void OnGlobalUnchecked(object sender, RoutedEventArgs e)
  16. {
  17.     CheckBox cb = sender as CheckBox;
  18.     ChangeCheckboxStateTo(cb, false);
  19. }
  20. public static void OnGlobalChecked(object sender, RoutedEventArgs e)
  21. {
  22.     CheckBox cb = sender as CheckBox;
  23.     ChangeCheckboxStateTo(cb, true);
  24. }

Use in xaml:

Code Snippet
  1. <DataGrid ItemsSource="{Binding List3,UpdateSourceTrigger=PropertyChanged}"
  2.           SelectedItem="{Binding Item3}"                              
  3.           Ext:SelectionColumnExtension.SelectionColumn="True">
  4.     <DataGrid.Columns>
  5.         <DataGridTextColumn Binding="{Binding Data1}" Header="Data1"/>
  6.         <DataGridTextColumn Binding="{Binding Data2}" Header="Data2"/>
  7.         <DataGridTextColumn Binding="{Binding Data3}" Header="Data3"/>
  8.     </DataGrid.Columns>
  9. </DataGrid>

That's all you need to do to get this: