sgriebgeo

Dynamic Custom Labeling with ArcGIS for Javascript and FontAwesome

Blog Post created by sgriebgeo on May 6, 2015

Recently I was given the task to implement functionality within an application that would allow map layers to use icons from the FontAwesome library to provide custom symbology.

 

I found a great blog post here which got me pretty far along the way, and I would suggest reading that post before continuing this one, or you may be a bit lost.

 

That post was great, and provided a baseline for what I wanted to do, but there was one part that I could tell was going to be a problem:

(taken from the other post)

 

  1. // you can use the cheatseeht to copy paste the font symbol 
  2.   // http://fortawesome.github.io/Font-Awesome/cheatsheet/ 
  3.   var sym = new TextSymbol("", font, treeColor);

 

Hold on, any time I want to set a custom symbol, I need to go to the FontAwesome cheatsheet, copy the actual symbol, then put it in my code?  That doesn't seem like a very "programmer" thing to do, there must be a better way!  Well, there is, and I'm going to show you how.

 

So the first step in this process is to let the user select an Icon.  I'm sure there's a library that'll do that for me, this is javascript after all.

 

justin-lau/ui-iconpicker · GitHub

 

Beautiful, my app is an angular app and this works great.  Ok, but that is giving me things like:

 

fa fa-lg fa-glass

 

I think I can work with that...This next part took a bit of learning about FontAwesome and how it works.  We need to somehow get from that string with our css class to that weird unicode thing.  So the way FontAwesome and similar libraries work is that they use the ::before selector and just insert the Character Code for the icon before the HTML element with that class assigned.  Ok, so maybe I can get the styles being applied to the page, and find that one, and figure out what content it's adding.

 

Of course, someone has done that before, and I was able to use the snippet below to do exactly that:

 

$scope.getCssVal = function(style, selector, sheet) {
   var sheets = typeof sheet !== 'undefined' ? [sheet] : document.styleSheets;
   for (var i = 0, l = sheets.length; i < l; i++) {
   var sheet = sheets[i];
   if( !sheet.cssRules ) { continue; }
   for (var j = 0, k = sheet.cssRules.length; j < k; j++) {
   var rule = sheet.cssRules[j];
   if (rule.selectorText && rule.selectorText.split(',').indexOf(selector) !== -1) {
   return rule.style[style];
  }
  }
  }
   return null;
};

 

 

OK, so I can use that function like this:

//this.iconClass looks like "fa fa-lg fa-glass"
//the last class attribute is the one we care about
var iconStr = this.iconClass.substring(this.iconClass.lastIndexOf(" ")+1);
//get the value of the "content" rule for our class
var symbolVal = $scope.getCssVal('content', '.'+ iconStr +'::before');
//since the css rule has the value in quotes, we use this Regex to remove the double quotes
//if you don't do this, all of your icons will just look like little squares
symbolVal = symbolVal.replace(/['"]+/g, '');

 

Great! Now we have our symbol.  All we have to do is pass it into the Font object using the code from the post linked above, and you've got a way to select an icon for your layer.  Now you could just implement a colorpicker, and a selector for the size, and you might get something like this:

 

 

And then when you pass those params into your Font for your renderer, and add your Layer, you'd get something like this:

 

Amazing! Hope this helps anyone interested in doing something similar.  Please feel free to comment.

Outcomes