Saturday, April 24, 2010

Adding better Maptips to the ESRI Silverlight API Clusterer!

At work we have been having this issue.  We are building a Silverligh App with ESRI's API and we have several features that due to being in the same shopping complex stack on top of each other.  However, if using  ESRI's out of the box clusterer (flare or simple) the Maptip does not contain any information about the individual features that are in the cluster.  Originally, we put a custom symbol on each flare of the flare clusterer, but due to issues with how the objets are programmed we could not access the correct information to get a tooltip service to work.  However, since Friday I have been working to solve this issue for my specific project.  I noticed today while working on a custom maptip from the toolkit I decided to implement a custom graphics clusterer.  With a little assistance from a co-worker (Tater), I decided to take this approach, the code below is the override for the OnCreateGraphics method of the custom clusterer.  To get access to all the attributes of each graphic you need to add an attribute to the cluster graphic that you will then assign the list of graphics too:

protected override Graphic OnCreateGraphic(GraphicCollection cluster, MapPoint point, int maxClusterCount) 
{
  if (cluster.Count == 1)
  {
    return cluster[0]; 
  }
  Graphic graphic = null; 
  double sum = 0; 
  double size = (sum + 450) / 30; 
  size = (Math.Log(sum * SymbolScale / 10) * 10 + 20); 
  if (size < 12)
  {
    size = 12;
  } 
  graphic = new Graphic() { Symbol = new SimpleMarkerSymbol() { Size = 15 }, Geometry = point }; 
  graphic.Attributes.Add("Color", InterpolateColor(size - 12, 100)); 

  //Create a list to hold your graphics
  List GraphicsList = new List(); 
  
  foreach (Graphic g in cluster) 
  {
    //Loop through all the graphics in the cluster and add to your list
    GraphicsList.Add(g);    
  }
  //Now create a new attribute to hold your graphics list
  graphic.Attributes.Add("Graphics", GraphicsList); 
  
  return graphic; 
}


The second step after creating a List of graphics to pass to the maptip, the next problem arises.  After trying to bind to the datagrid with no luck.  I started thinking how does ESRI bind the collection of graphics to the Feature Data Grid in the toolkit.  After looking around ESRI makes an IEnumerable of Attribute Dictionaries, then use an extension method to then convert those dictionaries in to something the datagrid will bind to, the example is below you will need to use the CreateDataSource.cs file for the extension methods:
private void graphicsLayer_MouseEnter(object sender, Graphic graphic, MouseEventArgs args) 
{
  mouseIsOver = true; 
  if (currentFeature != graphic) //Mouse entered a new feature 
  {
    this.expanded = true; 
    currentFeature = graphic;
    Point p = args.GetPosition(null); 
    SetValue(Canvas.LeftProperty, p.X + HorizontalOffset); 
    SetValue(Canvas.TopProperty, p.Y + (VerticalOffset - 1000));
    
    //Check to see if a sinlge maptip or cluster was passed to the maptip 
    if (graphic.Attributes.ContainsKey("Graphics")) 
    {
      //Pull out your list of graphics
      List graphicsList = graphic.Attributes["Graphics"] as List; 
      
      //Select the attribute of each graphic and place in a list of dictionaries
      IEnumerable> dictList = (from a in graphicsList select a.Attributes).AsEnumerable>(); 
      
      //Use the ToDataSource extension method from the FeatureDataGrid in the ESRI Silverlight toolkit
      //and set that to your ItemsSource
      this.DataContext = this.ItemsSource = dictList.ToDataSource(out objectType); 
    }
    else 
    {
      this.DataContext = this.ItemsSource = graphic.Attributes; 
    }
    if (!string.IsNullOrEmpty(TitleMember)) 
    {
      object title = null; 
      if (graphic.Attributes.ContainsKey(TitleMember))
      { 
        title = string.Format("{0}", graphic.Attributes[TitleMember]);
      } 
      else 
      {
        string firstKey = null; 
        foreach (string key in graphic.Attributes.Keys) 
        {
          if (firstKey == null) firstKey = key; 
          if (graphic.Attributes[key].GetType() == typeof(string)) 
          {
            title = graphic.Attributes[key] as string; 
            break; 
          }
      }
      if (title == null && !string.IsNullOrEmpty(firstKey))
      { 
        title = string.Format("{0}", graphic.Attributes[firstKey]);
      } 
    }
    this.Title = title; 
  }
  ChangeVisualState(false); 
  Visibility = Visibility.Collapsed; 
  }
  if (Visibility == Visibility.Collapsed) 
  {
    timer.Start(); 
    //Delay showing maptip 
  }
}

Tuesday, April 13, 2010

The spark of inspiration!

A couple of weeks ago I attend the ESRI Developer's Summit and attended the talk by Dave Bouwman about developing a GIS app using ArcGIS Sever and Rails.  Dave was talking about just the culture of Ruby and even mentioned Why's (Poignant) Guide to Ruby.  I was so intrigued with the whole topic that I started ready the guide.  All I have to say it is not for everyone, but I connected.  Today by accident I happened to find a link to a website that has Why's Estate.  As I am writing this I am listening to the soundtrack to Why's Guide.  As I promised I will be getting the Webclient code posted up, but I want to wait until I can deliver everyone some nice copy/paste and syntax highlighting.  I have a few more gems (yep, I took it) coming that I hope everyone will find useful.

Sunday, April 11, 2010

Posting code on blogger.

In an attempt to follow up to my original post, I ran into issues posting up the C# examples that I had.  However, after a little research I found this great post on how to add syntax highlighting to blogger.  Hopefully, I will be following up with the Webclient code soon.

Thursday, April 8, 2010

First Post

This is the first post and I hope I am able to keep this blog and the code that I post funky fresh for you all.  The next post is going to be a quick and dirty guide for using the .NET WebClient class.