Select to view content in your preferred language

Single checkbox for large and small scales of same feature

1299
11
11-01-2012 08:03 PM
DavidTreering
Emerging Contributor
I can't figure out how to have a single checkbox control the visibility of two layers, where one is the large scale version and one is the small scale version.
           var mapservice = "http://147.126.65.155/gis/rest/services/tft/tft_maine/MapServer";

           var superMarket1 = new esri.layers.FeatureLayer(
               mapservice+"/4",
               {
                   id : 'superMarket1',
                   mode : esri.layers.FeatureLayer.MODE_SNAPSHOT,
                   opacity : 1
               });
           var superMarket2 = new esri.layers.FeatureLayer(
               mapservice+"/5",
               {
                   id : 'superMarket2',
                   mode : esri.layers.FeatureLayer.MODE_SNAPSHOT,
                   opacity : 1
               });


[HTML]<li class="TOCThemeItem"><input class="list_item" id="superMarket1" value="superMarket1" onclick="layerVisibility();" type="checkbox">Supermarket</li>[/HTML]
0 Kudos
11 Replies
__Rich_
Deactivated User
What have you got in your layerVisibility() method so far?
0 Kudos
DavidTreering
Emerging Contributor
What have you got in your layerVisibility() method so far?


function layerVisibility(LayerID) {
               //Retrieved the list of Layer IDs, which need to be toggled
               for ( var i = 0; i < LayerID.length; i++) {
                       //Got the layer object from the map based on its ID
                       var layer = map.getLayer(LayerID);

                       //Checked its visibility
                       if (layer.visible) {
                               layer.hide();
                               
                               //alert("Visible");
                       } else {
                               layer.show();


                               //alert("Hidden");
                       }
               }
        }


So far....
0 Kudos
__Rich_
Deactivated User
function layerVisibility(LayerID) {
               //Retrieved the list of Layer IDs, which need to be toggled
               for ( var i = 0; i < LayerID.length; i++) {
                       //Got the layer object from the map based on its ID
                       var layer = map.getLayer(LayerID);

                       //Checked its visibility
                       if (layer.visible) {
                               layer.hide();
                               
                               //alert("Visible");
                       } else {
                               layer.show();


                               //alert("Hidden");
                       }
               }
        }


So far....

That code doesn't reflect the original requirement that you stated, what do you actually want to achieve?

Do you:
a) Want to toggle ALL layers in the map based on their current state?  (that's what it looks like you're trying to do in the above code)
b) Want to toggle between 2 specific layers? (that's what your first post suggests)
c) Something else 😉

Best we get the requirement straight before we start looking for ways to satisfy it 🙂
0 Kudos
DavidTreering
Emerging Contributor
That code doesn't reflect the original requirement that you stated, what do you actually want to achieve?

Do you:
a) Want to toggle ALL layers in the map based on their current state?  (that's what it looks like you're trying to do in the above code)
b) Want to toggle between 2 specific layers? (that's what your first post suggests)
c) Something else 😉

Best we get the requirement straight before we start looking for ways to satisfy it 🙂


Sorry about the confusion.  I have a single feature, supermarkets, that has scale dependent visibility for two separate layers.  One disappears at scale 17, in my map & feature service, and the other appears.  This is how I set it up in ArcGIS Desktop and published the service.

So when I toggle the layer with a checkbox, the html id is referring to only one of the two layers.  I need the supermarkets visibility to be controlled by one single checkbox, regardless of what scale the map is at.

Does this clarify my requirement?
Thanks!
0 Kudos
__Rich_
Deactivated User
Still a little hazy, but let's press on.

You realise that the server will always obey the visible scale rules that you have defined in your map document, regardless of what you do on the client-side?  (out of curiosity why do you want to try and override these rules?)

For mutual exclusivity between 2 options a radio button, rather than a checkbox, might be more intuitive for users?

Anyway...the crux of your issue is that at some point to change the visibility of a layer you need to obtain a reference to the layer object of interest.

There's a few ways to do this and also options when it comes to hooking the functionality up to GUI elements.

Looking at your original code, you have two variables for the layers, namely "superMarket1" and "superMarket2", if these variables are in scope when you want to do the toggling then you could simply do:

//Note that this is dependent on the initial state of the layers!
superMarket1.setVisibility(!superMarket1.visible);
superMarket2.setVisibility(!superMarket1.visible);  //Note that this means that the visibility of superMarket2 will always be the opposite of superMarket1.


If those variables are out of scope (and note I'm not suggesting you makes them global or anything...for that way lies laziness 😉 ) then you can ask the map to return you a reference if you know the id...which looking at your original code you have hard-coded to "superMarket1" and "superMarket2", then you could do:
//Assumes the map object is in scope
map.getLayer("superMarket1").show();
map.getLayer("superMarket2").hide();


You could drive this off a list of layer Ids that you maintain, you could loop through the array of map.layerIds...there's many ways to do this.

There's also patterns/practises/techniques/tools for controlling scope which might be of interest, see dojo.hitch for example.

Also note that event handlers for element events are passed event arguments...which you might use to get a reference to the element raising the event etc.
0 Kudos
DavidTreering
Emerging Contributor


You realise that the server will always obey the visible scale rules that you have defined in your map document, regardless of what you do on the client-side?  (out of curiosity why do you want to try and override these rules?)

For mutual exclusivity between 2 options a radio button, rather than a checkbox, might be more intuitive for users?

Anyway...the crux of your issue is that at some point to change the visibility of a layer you need to obtain a reference to the layer object of interest.


I'm still not helping you understand this problem.  It is simpler than you are making it. (but thank you for taking the time to go through the explanations and options.)

I understand the visible scale rules I set up and DO NOT want to change them or override anything.  On the GUI, I have a single checkbox, labeled Supermarkets.  This needs to control the visibility of BOTH supermarket layers.  So I have:
<input class="list_item" id="superMarket1" value="superMarkets" onclick="layerVisibility();" type="checkbox">Supermarket<br>

In this case, only the large scale, id= superMarket1 will be toggled.  When the map is zoomed out to a small scale, the other layer, with id = supermarket2, will not have been toggled to the same state as superMarket1.

I need to toggle "visibility" of both simultaneously, although only one is actually being drawn on the map at any one scale.

I thought I might try creating an Array of length 2 with both layers and toggle both from there, but haven't thought through how to do that yet.

var superMarket1 = new esri.layers.FeatureLayer(
               mapservice+"/4",
               {
                   id : 'superMarket1',
                   mode : esri.layers.FeatureLayer.MODE_SNAPSHOT,
                   opacity : 1
               });
           var superMarket2 = new esri.layers.FeatureLayer(
               mapservice+"/5",
               {
                   id : 'superMarket2',
                   mode : esri.layers.FeatureLayer.MODE_SNAPSHOT,
                   opacity : 1
               });
           var superMarkets = new Array();
             superMarkets[0] = superMarket1;
             superMarkets[1] = superMarket2;

Does this clarify?  Thanks again for your patience.
0 Kudos
__Rich_
Deactivated User
I (finally) see!

OK, here's one way:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Toggle Test</title>
    <link rel="stylesheet" type="text/css" href="http://serverapi.arcgisonline.com/jsapi/arcgis/3.2/js/dojo/dijit/themes/claro/claro.css" />
    <link rel="stylesheet" type="text/css" href="http://serverapi.arcgisonline.com/jsapi/arcgis/3.2/js/esri/css/esri.css" />
    <style>
        html, body
        {
            width: 100%;
            height: 100%;
            overflow: hidden;
            font-size:small;
        }
    </style>
</head>
<body class="claro">
    <script type="text/javascript" src="http://serverapi.arcgisonline.com/jsapi/arcgis/?v=3.2"></script>
    <script type="text/javascript">
        dojo.require("esri.map");
        dojo.require("esri.layers.FeatureLayer");

        var map;

        function init() {
            var extent = new esri.geometry.Extent({
                "xmin": -90.6063,
                "ymin": 38.3106,
                "xmax": -88.4764,
                "ymax": 38.3689,
                "spatialReference": { "wkid": 4269 }
            });
            map = new esri.Map("mapDiv",
                {
                    extent: esri.geometry.geographicToWebMercator(extent)
                }
                );

            dojo.connect(window, "resize", map, map.resize);

            map.addLayer(new esri.layers.ArcGISTiledMapServiceLayer("http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer"));

            //Roads
            map.addLayer(new esri.layers.FeatureLayer("http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Network/USA/MapServer/77",
                {
                id:"road1",
                mode: esri.layers.FeatureLayer.MODE_ONDEMAND,
                outFields: ["*"]
                }
            ));
            map.addLayer(new esri.layers.FeatureLayer("http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Network/USA/MapServer/78",
                {
                    id: "road2",
                    mode: esri.layers.FeatureLayer.MODE_ONDEMAND,
                    outFields: ["*"]
                }
            ));
            map.addLayer(new esri.layers.FeatureLayer("http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Network/USA/MapServer/74",
                {
                    id: "road3",
                    mode: esri.layers.FeatureLayer.MODE_ONDEMAND,
                    outFields: ["*"]
                }
            ));
            map.addLayer(new esri.layers.FeatureLayer("http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Network/USA/MapServer/75",
                {
                    id: "road4",
                    mode: esri.layers.FeatureLayer.MODE_ONDEMAND,
                    outFields: ["*"]
                }
            ));
            map.addLayer(new esri.layers.FeatureLayer("http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Network/USA/MapServer/71",
               {
                   id: "road5",
                   mode: esri.layers.FeatureLayer.MODE_ONDEMAND,
                   outFields: ["*"]
               }
           ));
            map.addLayer(new esri.layers.FeatureLayer("http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Network/USA/MapServer/72",
                {
                    id: "road6",
                    mode: esri.layers.FeatureLayer.MODE_ONDEMAND,
                    outFields: ["*"]
                }
            ));

            //Rivers & Lakes
            map.addLayer(new esri.layers.FeatureLayer("http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Network/USA/MapServer/95",
                {
                    id: "river1",
                    mode: esri.layers.FeatureLayer.MODE_ONDEMAND,
                    outFields: ["*"]
                }
            ));
            map.addLayer(new esri.layers.FeatureLayer("http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Network/USA/MapServer/96",
                {
                    id: "river2",
                    mode: esri.layers.FeatureLayer.MODE_ONDEMAND,
                    outFields: ["*"]
                }
            ));
            map.addLayer(new esri.layers.FeatureLayer("http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Network/USA/MapServer/97",
                {
                    id: "river3",
                    mode: esri.layers.FeatureLayer.MODE_ONDEMAND,
                    outFields: ["*"]
                }
            ));
            map.addLayer(new esri.layers.FeatureLayer("http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Network/USA/MapServer/98",
                {
                    id: "river4",
                    mode: esri.layers.FeatureLayer.MODE_ONDEMAND,
                    outFields: ["*"]
                }
            ));
            map.addLayer(new esri.layers.FeatureLayer("http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Network/USA/MapServer/99",
               {
                   id: "river5",
                   mode: esri.layers.FeatureLayer.MODE_ONDEMAND,
                   outFields: ["*"]
               }
           ));
            checkLayerState();
        }

        //Will set the visibility of each layer to the opposite of its current state i.e. toggle
        function layerToggle(/*Array of strings*/layerIds) {
            //Could use dojo.forEach here if you prefer
            for (var i = 0; i < layerIds.length; i++) {
                var layer = map.getLayer(layerIds);
                if (layer) {
                    //Set the layer's visibility to NOT its current visibility
                    layer.setVisibility(!layer.visible);
                }
            }
            checkLayerState();
        }

        function checkLayerState() {
            var div = document.getElementById("layerstate");
            var s = "<p>Non-Graphics Layers:";
            for (var i = 0; i < map.layerIds.length; i++) {
                var layer = map.getLayer(map.layerIds);
                s += "<br />" + layer.id + " : " + (layer.visible?"on":"off");
            }
            s += "</p><p>Graphics Layers:";
            for (var i = 0; i < map.graphicsLayerIds.length; i++) {
                var layer = map.getLayer(map.graphicsLayerIds);
                s += "<br />" + layer.id + " : " + (layer.visible ? "on" : "off");
            }
            div.innerHTML = "</p>" + s;
        }

        dojo.ready(init);
    </script>
    <div id="mapDiv" style="height:100%;width:100%;"></div>
    <div style="position:absolute;bottom:10px;left:10px;background-color:#000;color:#fff;">
        <input type="checkbox" checked="checked" onclick="layerToggle(['road1', 'road2', 'road3', 'road4', 'road5', 'road6']);" />Toggle All Roads<br />
        <input type="checkbox" checked="checked" onclick="layerToggle(['river1', 'river2', 'river3', 'river4', 'river5']);" />Toggle All Rivers<br />
    </div>
    <div id="layerstate" style="position:absolute;top:10px;right:10px;background-color:#000;color:#fff;"></div>
</body>
</html>

It's a bit rough and horribly hard-coded...but then it's only a (knocked-up in 5 mins) sample and hopefully the approach is clearly demonstrated 🙂

P.S. Did you see the PM I sent you?
0 Kudos
DavidTreering
Emerging Contributor
I (finally) see!


I am struggling with the use of this layerToggle([array]);

I have four layers that work: Convenience Stores, CSAs, Farmstands, and Farmers' Markets.  The rest of the layers using layerToggle, including Supermarkets, Roads and Cities , do not work.

I'm using updateLayerVisibility() to toggle some layers that have one layerID per checkbox, so don't let that confuse you.  That is not quite working either.

I wonder if you have any input for me on the drawing order on the map.  I want the first 7 layers to draw first, below the stores and boundaries and roads, etc.

I have attached my application code.
0 Kudos
__Rich_
Deactivated User
I am struggling with the use of this layerToggle([array]);

You just need to pass it an array of strings where each string is the Id of a layer in the map.

I have four layers that work: Convenience Stores, CSAs, Farmstands, and Farmers' Markets. The rest of the layers using layerToggle, including Supermarkets, Roads and Cities , do not work.

I added a little bit to the code on bold/red below:
function layerToggle(layerIds) {
            //Could use dojo.forEach here if you prefer
            for (var i = 0; i < layerIds.length; i++) {
                var layer = map.getLayer(layerIds);
                if (layer) {
                    //Set the layer's visibility to NOT its current visibility
                    layer.setVisibility(!layer.visible);
                }
                else {
 alert("layer [" + layerIds + "] not found");
 }
            }
            //checkLayerState();
        }

It's only a dirty/temporary bit of code to illustrate what's going wrong, don't leave it in when you go live, perhaps substitute the alert for a console.warn or something.

Anyway, the problem is you are passing, for example, 'supermarket1' and 'supermarket2' to the layerToggle function but in your JS file you're giving those layers Ids of 'superMarket1' and 'superMarket2' respectively - spot the difference? (clue: JS is case-sensitive)

I haven't gone through all the other layers - I'll leave that for you to do, that alert should help 🙂


I'm using updateLayerVisibility() to toggle some layers that have one layerID per checkbox, so don't let that confuse you. That is not quite working either. 

I'd use the same function to toggle all layers, that way you only have one path through and if you have a bug you only have to fix it in one place etc. etc. (good programming practice)

You can just pass the layerToggle method an array of 1 Id e.g. ['superMarket1']


I wonder if you have any input for me on the drawing order on the map. I want the first 7 layers to draw first, below the stores and boundaries and roads, etc. 

Layers are drawn in the order that you add them to the map, you can override that by passing a desired layer index to the addLayer method, addLayers doesn't have the specific index argument (it adds the layers in the order of the array that you pass) but you can re-order layers afterwards - probably best to consult the documentation/samples.
0 Kudos