Create legend from scratch

3044
8
09-24-2013 04:40 AM
CurtWalker
New Contributor
I need to develop a legend/TOC that has both layer toggling and the symbols for each layer, just like you would see in ArcGIS Desktop.  I've already looked at the 3rd-party TOC widget and while it's a neat solution it doesn't fully satisfy our needs.  What I'd really like to learn is how that widget and also the legend dijit are mining the symbols for each layer in a map service. 

I did some digging and I have found the image URL for point layers in REST.  That one was easy.  After some experimenting I was able to harvest those images using the URL and inject them into my own legend pane.  But there does not appear to be symbol URLs for polygon or polyline layers in REST.  Instead I found the JSON where fill and outline properties are given, but this only applies to polygon layers.  I was able to dynamically build my own polygon symbol from the JSON using canvas in HTML 5.

So that leaves polylines and for whatever reason the JSON for polylines in REST is worthless.  I have a line layer that is a wide yellow stroke with a narrower black stroke in the middle, but you'd never know that looking at the JSON which says the line is a simple black stroke only.

Does anyone know how the legend dijit and TOC widgets are mining the graphics for each map service layer?  Is it somewhere in REST or what?
0 Kudos
8 Replies
CurtWalker
New Contributor
May have answered my own question...

Previously I was getting the JSON for each layer in a map service this way:

http://<yourserver>/arcgis/rest/services/<servicename>/MapServer/6?f=pjson

Only point symbol URLs are exposed this way though.  Apparently the full legend is exposed here:

http://<yourserver>/arcgis/rest/services/<servicename>/MapServer/legend

...and the JSON for the legend can be reached here:

http://<yourserver>/arcgis/rest/services/<servicename>/MapServer/legend?f=json

Still need to figure out how to target a specific layer ID amongst within the JSON and get back the URL.
0 Kudos
ZachLiu1
Occasional Contributor II
Do you mean the url of the icon? each layer should have one with the url ending with:

MapServer/id/images/urlfromJson

you also get binary image data
0 Kudos
CurtWalker
New Contributor
Zach, yeah that's exactly what I was after.  Experimented some more and got it to work.  Using the response from my ajax call to the JSON in a loop that iterates through each sublayer id as i:

result.layers.legend[0].url

My custom legend/TOC is working with this approach.  Thanks.
0 Kudos
JasonZou
Occasional Contributor III
Hi Curt,

Instead of using the icon url, why not using imageData directly to save some client-server round traffic? For your reference, below is the excerpt of the JSON string returned from the legend request.

var legendResponse = {
 "layers": [{
   "layerId": 0,
   "layerName": "ROW",
   "layerType": "Feature Layer",
   "minScale": 1000000,
   "maxScale": 0,
   "legend": [{
     "label": "",
     "url": "393c5a8cc17bc07be57e6aedb7519a27",
     "imageData": "iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IB2cksfwAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAENJREFUOI1jYaAyYGFgYGAoWMHwnxqGTYhgYGShhkHIAMXACREMjOQYguxD2rpw1MBRA0cNHB4GUqNcpI0LyS0HsQEAa34JUaZbYooAAAAASUVORK5CYII=",
     "contentType": "image/png"
    }]
  },


Here is the code to build an image node.
var layerLegend = legendResponse.layers[0].legend;
legendImgNode = domConstruct.create("img", {
    src: "data:" + layerLegend.contentType + ";base64," + layerLegend.imageData
});
0 Kudos
MattDriscoll
Esri Contributor
If you're looking for something simple, I'm working on something similar that uses a legend widget for each layer and can be styled.

http://driskull.github.io/arcgis-dijit-layer-legend-js/
0 Kudos
VinayBansal
Occasional Contributor II
You can generate legend using this code
function getLegendJSON() {
    var divLegend = dojo.byId("legendDiv");
    if (divLegend.innerHTML == "") {
        divLegend.innerHTML = "Creating Legend...";
        var url = <<Your_map_Service_Url>> + "/legend";
        var requestHandle = esri.request({
            "url": url,
            "content": {
                "f": "json"
            },
            "callbackParamName": "callback"
        });

        requestHandle.then(requestSucceeded, requestFailed);
    }
    
}
function requestSucceeded(response, io) {
    var lyr;
    var htmlString = "<table>";
    var divLegend = dojo.byId("legendDiv");
    var mapURL = <<Your_map_Service_Url>>;
    if (response != null && response.layers.length > 0) {
        for (var iCnt = 0; iCnt < response.layers.length; iCnt++) {
            lyr = response.layers[iCnt];
         
                if (lyr.legend.length > 1) {
                    htmlString += "<tr><td colspan='2' style='font-weight:bold;'>" + lyr.layerName + "</td></tr>";
                    for (var jCnt = 0; jCnt < lyr.legend.length; jCnt++) {
                        var src = mapURL + "/" + lyr.layerId + "/images/" + lyr.legend[jCnt].url;
                        var strlbl = lyr.legend[jCnt].label.replace("<Null>", "Null");
                        htmlString += "<tr><td align='left'><img src=\"" + src + "\" alt ='' /></td><td>" + strlbl + "</td></tr>";
                    }
                }
                else {
                    htmlString += "<tr><td colspan='2' class='tdLayerHeader' style='font-weight:bold;'>" + lyr.layerName + "</td></tr>";
                    var src = mapURL + "/" + lyr.layerId + "/images/" + lyr.legend[0].url;
                    htmlString += "<tr><td colspan='2' ><img src=\"" + src + "\" alt ='' /></td></tr>";
                }
            

        }
        htmlString += "</table>";
    }
    divLegend.innerHTML = htmlString;


}
0 Kudos
NigelAlford
New Contributor III
Thanks for this thread, I was able to create an ideal dynamic layer legend solution beautifully from your snippets, this was the perfect nudge I needed.
:cool:
0 Kudos
BrettGreenfield__DNR_
Occasional Contributor II
Sorry for bumping an old thread, but I'm working on this myself right now...

I'm able to get the legend symbols fine for services we're hosting on our own server, but for my web map I'm also pulling in a couple of services from an external source.  When I go to http://<mapservice>/legend for these external services, I'm taken to a page that says "Invalid URL".  Going through documentation of the legend API reference, I noticed this:

If the layers are version 10 or lower the legend is created using the ArcGIS.com legend service. In order to use the ArcGIS.com legend service your map service needs to be publicly accessible and your application must be able to access ArcGIS.com.


I'm not sure what version of ArcServer this source is running, but I'm assuming this has something to do with my issue (I am able to create a legend for these services via the legend dijit).  If they are running something before 10.0 SP1, does that mean I can't get the legend symbol for these layers via esri.request?  Is there still a way I can obtain the legend symbol?
0 Kudos