1.
Introduction
As we see in the ArcGis Desktop application
(ArcMap), the use of the legend consists of more than just displaying the
rendering information. Behind the legend extra functionality is exposed through
context menus. In the Silverlight desktop application I wrote some of this
functionality was added to the Silverlight API Legend. This extra functionality
was done through extending the Silverlight API legend control with extra
methods and attached in the XAML through commands. The Microsoft Silverlight framework
and MVVM makes this possible.
In the JavaScript API however, the legend
control has only limited functionality making it very difficult to create extended
functionality. To solve this issue, a custom legend control must be created. A
solution could be creating the legend with the help of the Dojo library, but
for a generic solution this is not a simple job. Another solution is using jQuery
templates. The jQuery templates is an extension created by Microsoft and added to the community. The interesting
aspect is the use of html templates to define how a control is shown using all the
power of CSS and a build-in processing syntax. There was an interesting session
at ESRI in 2011 explaining this
technique of building a custom legend control. To have a flexible solution, the template is
saved on the server in a file that is downloaded and added to the legend
region. The initial template I used has the following contents.
<li>
<input type="checkbox"
style="float:left" id="${$item.getCheckboxID()}"
checked="${$item.getChecked()}" /><h3 class="agsLegendOpen" style="margin:0 0 0 25px;padding:0;" >${title}</h3>
{{if serviceType == 4}}
{{if renderer.type=="simple"}}
{{if renderer.symbol.type=='esriPMS'}}
<div style="display:table; margin:10px 5px;" >
<img style="padding-right:10px;
float:left;
display:inline-block;
vertical-align:middle;"
src="data:${renderer.symbol.contentType};base64,${renderer.symbol.imageData}"/>
</div>
{{else renderer.symbol.type=='esriSFS'}}
{{if renderer.symbol.color == null}}
<div style="display:table-cell;
vertical-align:middle;
width:20px;
height:20px;
background-color:rgba(151,219,242,255);
border:${$item.getBorder(renderer.symbol.outline)};">
</div>
{{else}}
<div style="display:table-cell;
vertical-align:middle;
width:20px;
height:20px;
background-color:${$item.getColor(renderer.symbol.color)};
border:${$item.getBorder(renderer.symbol.outline)};">
</div>
{{/if}}
{{else renderer.symbol.type=='esriSMS'}}
{{if renderer.symbol.style=="esriSMSCircle"}}
{{/if}}
{{/if}}
{{else renderer.type=='uniqueValue'}}
<ul>
{{each renderer.uniqueValueInfos}}
{{if $value.symbol.type=='esriPMS'}}
<div style="display:table;margin:5px 5px;">
<img src="data:${$value.symbol.contentType};base64,${$value.symbol.imageData}" style="vertical-align:middle;"/>
<span style="padding:0px 5px 0px 0px;display:table-cell;vertical-align:middle;">${$value.label}</span>
</div>
{{else $value.symbol.type=='esriSFS'}}
<li style="display:table; margin:5px 5px;" >
<div style="display:table-cell;vertical-align:middle;width:20px;height:20px;background:${$item.getColor($value.symbol.color)};border:${$item.getBorder($value.symbol.outline)};" ></div>
<span style="padding:0px 5px 0px 5px;display:table-cell;vertical-align:middle;">${$value.label}</span>
</li>
{{else $value.symbol.type=='esriSLS'}}
<li style="display:table;" >
<div style="margin:8px 5px -15px 5px;
width:20px;
height:${$value.symbol.width}px;
background:${$item.getColor($value.symbol.color)};">
</div>
<span
style="padding:0px 5px 0px 5px;
display:table-cell;vertical-align:middle;">
${$value.label}
</span>
</li>
{{/if}}
{{/each}}
</ul>
{{/if}}
{{/if}}
</li>
<div style="clear:both"/>
As you can see, there are conditional statements and loop statements. To finalize this approach I encapsulate the template class into a custom jQuery plugin. All these classes makes part of the legend module.
2. Legend in action
var divLegend;
var layersCount;
var layersTotal
function legendBuild(eventName, eventData, tagData) {
…}
return {
initLegend: function (divElement) {
this.divLegend = divElement;
iEventAggregator.subscribe("mapLoaded", legendBuild, divElement);
}
}
Because the retrieving of all the legend
information coming from the REST services is asynchronous, I use a simple
counter to know when the last information is retrieved and all the legend
information is available to be applied in the template. The code below shows
how the build of the legend is started after the last legend has been
processed. We simply called the custom legend jQuery plug-in
function buildLegend() { if (layersCount == layersTotal) {
var legend = $("#legendRegion").agsLegend({ });
}
To activate the templates, this can be done
through this method in the jQuery plug-in.
$.fn.agsLegend = function (options) { opts = $.extend({}, $.fn.agsLegend.defaults, options);
if (opts.autoLoadTemplates) {
getTemplate();
}
return this.each(function () {
$this = $(this);
$this.delegate("input[type='checkbox']", "click", checkLayerVisibility);
$this.delegate("h3","mouseup",rightClick);
if (opts.isCollapsible) { makeCollapsible(this); }
}
Upload of the template from the server is
done by this code through a AJAX call.
function getTemplate() { $.ajax({
url: opts.templateFileURL,
success: opts.onTemplateLoaded || handleTemplateResponse
});
}
To apply the template, you can do this by this function below. You simply insert the template into a script section of the head section. Using the tmpl method of the jquery template plug-in the data is inserted into the template.
function handleTemplateResponse(data) {
script = document.createElement("script");
script.style.display = "none";
script.type = "text/x-jquery-tmpl";
script.id = opts.templateDOMId;
script.text = data;
$('head').append(script);
$("#"+opts.templateDOMId).tmpl(iGisOperation.GetLayersLegendInfos(),
{
getColor: getColor,
getBorder: getBorder,
getChecked: getChecked,
getCheckboxID: getCheckboxID
}).appendTo($this);
}
Some methods are needed to format the
values in the template.The final result of all this, is a legend that looks
like this.
In the coming weeks I will add a toolbar to
the application to allow different GIS operations on the map as zooming and
selecting.
Johnny this looks very cool, and thank you for contributing to the community.
ReplyDeleteDid you post a demo site showing this Legend code in action, or your site in general? This would be great to see in action.
Hi, Great.. Tutorial is just awesome..It is really helpful for a newbie like me..
ReplyDeleteI am a regular follower of your blog. Really very informative post you shared here.
Kindly keep blogging. If anyone wants to become a Front end developer learn from Javascript Training in Chennai .
or Javascript Training in Chennai.
Nowadays JavaScript has tons of job opportunities on various vertical industry. ES6 Training in Chennai