Select to view content in your preferred language

BasemapGallery - excluding certian ArcGIS stock basemaps

1625
8
02-01-2014 09:35 AM
EdMahon
Emerging Contributor
I'm looking at a basemap gallery that contains a user-defined subset of the basemaps returned when ArcGISBasemaps = true.

Rather than duplicate/re-mangage the internals of the gallery widget (i.e. maintain the data and manually load the subset of interest - a big YUK! for externally managed dependencies that can change), the obvious algorithm is to specify all ArcGIS basemaps and simply remove the exclusion set after their load completes.  I can think of three simple approaches (which I can't find any solid reference or discussions on are):

1. Let the basemap gallery load and on the 'load' event notification, remove the basemaps that aren't required.  Remove requires the basemap ID.

Issue:

A) The well-known basemap names (e.g. topo, national-geographic, etc.) are nowhere in the basemap data structures after loading to perform lookups for ID/removal.
B) The ids assigned are layer_0, layer_x,... layer_8 and no way to map back to the original well-known name.  I don't want to depend on free-range data (e.g. title fields) or an attribute that is not 110% deterministic for values.

2. Filter out the unwanted basemaps in the initial load.

Issue:

A) It appears to be an all-or-nothing.


3. Obtain the various basemap titles somehow and use that field to perform the lookup, to get the layer ID, to perform the removal.

Issue:

A) How to get the various stock basemap titles assigned by ArcGIS.

Any help or definitive guidance would be greatly appreciated.  Seems like an reasonable requirement that's been entirely overlooked...

Thanks in advance!

Ed
0 Kudos
8 Replies
JeffPace
MVP Alum
I agree that this is frustrating.  Instead of "showArcGISBasemap" being true/false I wish it would take an array of maps to include

As a work around, you can set it to false, and then manually build your Gallery.  Just create a Basemap (using the REST url of the basemap you want to include) for each and than manually load the Gallery.
0 Kudos
EdMahon
Emerging Contributor
I agree that this is frustrating.  Instead of "showArcGISBasemap" being true/false I wish it would take an array of maps to include

As a work around, you can set it to false, and then manually build your Gallery.  Just create a Basemap (using the REST url of the basemap you want to include) for each and than manually load the Gallery.


Yep - doesn't look like I'll have a choice... grrrr!!!  Really didn't want to duplicate the URLs/names/thumbnails, etc. - bad OO design because it's all controlled externally and now becomes a dependency coupling for the code.

API Design improvement: Accept a list of the well-known stock map names as the layers when showArcGISBasemap is true (or custom layers when false - avoid another "candy machine interface").

Maybe what I though should work will spawn other ideas - I did find a way to make the association, but the layers don't seem to load in the basemapgallery as expected..  I tried implementing an exclusion algorithm (e.g. want them all except 'streets') and set a flag on startup to work with the following:

Client:
var layer = 'streets';  // Basemap to exclude

map.setBasemap(layer);

Handler:

    toolBasemapGallery.on("load", function ()
    {
        var bmcHandler = map.on('basemap-change', function (evt)
        {
            var bFound = false;

            // Test each of the new basemap Layers
            evt.current.layers.forEach(function (layer)
            {
                // Test each of the gallery's GalleryBasemaps
                toolBasemapGallery.basemaps.forEach(function (basemap)
                {
                    // Test the GalleryBasemap's collection of Layers
                    var bmLayersArray = basemap.getLayers();  // +++EAM+++ WHY A PROMISE STRUCTURE???

                    if (Array.isArray(bmLayersArray))  // +++EAM+++ Never is... grrrr....
                    {
                        bmLayersArray.forEach(function (bmLayer)
                        {
                            if (!bFound && (bFound = (layer.url == bmLayer.url)))  // They are the same if the bmLayer was populated
                            {
                                toolBasemapGallery.remove(bmLayer.id);
                                map.removeLayer(map.getLayer(layer.id));
                            }
                        });
                    }
                });
            });
        });
0 Kudos
SteveCole
Honored Contributor
Am I missing the point of your issue? Here's a slightly modified snippit of ESRI sample code that I use to load my basemapGallery widget with the ESRI basemaps with the exception of the Oceans basemap:

 // Populate the basemap gallery widget with the list of ESRI basemaps
 dojo.connect(basemapGallery, 'onLoad', function () {
  //add the basemaps to the menu but exclude the "Oceans" basemap
  dojo.forEach(basemapGallery.basemaps, function (basemap) {
  if (basemap.title != 'Oceans') {
   dijit.byId("basemapMenu").addChild(new dijit.MenuItem({
     label: basemap.title,
     onClick: dojo.hitch(this, function () {this.basemapGallery.select(basemap.id);})
   }));
  }
  }); 
 });


It's in legacy code. Sorry.

Steve
0 Kudos
EdMahon
Emerging Contributor
Am I missing the point of your issue? Here's a slightly modified snippit of ESRI sample code that I use to load my basemapGallery widget with the ESRI basemaps with the exception of the Oceans basemap:

 // Populate the basemap gallery widget with the list of ESRI basemaps
 dojo.connect(basemapGallery, 'onLoad', function () {
  //add the basemaps to the menu but exclude the "Oceans" basemap
  dojo.forEach(basemapGallery.basemaps, function (basemap) {
  if (basemap.title != 'Oceans') {
   dijit.byId("basemapMenu").addChild(new dijit.MenuItem({
     label: basemap.title,
     onClick: dojo.hitch(this, function () {this.basemapGallery.select(basemap.id);})
   }));
  }
  }); 
 });


It's in legacy code. Sorry.

Steve


Thanks for the reply, Steve.

Yes, perhaps a slight misunderstanding; I want to let the basemap gallery load the stock basemaps as usual (without any intervention during the load) and remove the unwanted ones from the gallery before display.  Populating a menu or external html artifact is not the same scenario I'm trying to address.

An unfortunately, the basemap.title field does not correspond to the short form (e.g. topo, national-geographic) which is the well-known identification name that would be friendly to an admin for defining the inclusion/exclusion set.  I used the map.setBasemap because it does expect the friendly name (which I pull from the DB), but once you're into the basemap data structures, these friendly name no longer can be associated.  Still of the opinion that a proper API needs to be implemented to accommodate this...
0 Kudos
JeffPace
MVP Alum
Right.

I create my basemap, then call a method (sorry devs) called "hideDumbLayers", which hides (but does not remove because it breaks the gallery) the layers I do not want to show.  The problem  with this is "new exciting layers" show up automagically, and i do not want that.  So everytime a new layer is added I have to hardcode and remove it

I no londer do this its in an old version of our app, we moved away from the gallery completely due to inability to control its contents

 hideDumbLayers: function(){
            //if created
             if(dojo.byId("galleryNode_basemap_0")){
            //find current layer
            for(var j = 0; j < this.map.layerIds.length; j++) {
                var layer = this.map.getLayer(this.map.layerIds);

                    if(layer.visible==true&&dojo.indexOf(this.mcgisBasemaps,layer.id)>-1){
                        if(layer.id=="Base Map"){
                            this.initBasemap="MCGIS Base Map";
                        }else{
                        this.initBasemap=layer.id;
                        }
                    }
                   
                }
                var dumbBasemapIds=[];
                var ids={};
                var galleryContainer=dojo.byId("galleryNode_basemap_0").parentNode;
                dojo.forEach(galleryContainer.childNodes, dojo.hitch(this, function(galleryItem){
                  if(galleryItem.childNodes[1]){
                    var title = galleryItem.childNodes[1].childNodes[0].title;                   
                    if(title==="Oceans"||title==="Light Gray Canvas"||title==="Relief with Labels"||title==="Terrain"||title==="Terrain with Labels"||title==="Shaded Relief"){
                        dumbBasemapIds.push(galleryItem.id);
                    }else{
                        ids[title]=galleryItem.id;
                     if(title==this.initBasemap){
                         this.baseMapGallery.select(ids[title].replace("galleryNode_",""));
                         dojo.addClass(dojo.byId(ids[title]), "esriBasemapGallerySelectedNode");
                     }
                    }
                }}));
              this.layerIds=ids;
                dojo.forEach(dumbBasemapIds, dojo.hitch(function(id){
                    dojo.style(dojo.byId(id), "display", "none");
                }));

            }else{
          setTimeout(dojo.hitch(this, function() { this.hideDumbLayers(); } ), 50);
              
            }
        }
        
    }
0 Kudos
by Anonymous User
Not applicable
Right.

I create my basemap, then call a method (sorry devs) called "hideDumbLayers", which hides (but does not remove because it breaks the gallery) the layers I do not want to show. 


Jeff could you elaborate on why removing the default basemaps breaks the Gallery? I'm running into an issue where if I use 'showArcGISBasemaps : false' my dropdown menu won't populate with my custom list. It worked when I did it in a dojo title pane, but not working in a button. I'm a newb so probably it is forehead-slap kind of situation, but I am curious what is known to break.

Thanks!
0 Kudos
JeffPace
MVP Alum
Jeff could you elaborate on why removing the default basemaps breaks the Gallery? I'm running into an issue where if I use 'showArcGISBasemaps : false' my dropdown menu won't populate with my custom list. It worked when I did it in a dojo title pane, but not working in a button. I'm a newb so probably it is forehead-slap kind of situation, but I am curious what is known to break.

Thanks!


I havent used it in a few versions of the api.  But i was actually hiding/removing layers AFTER it was constructed by removing DOM elelements.  Expectedly that broke things

If you are just setting  'showArcGISBasemaps : false' you should not be breaking anything.

If you could show some code..
0 Kudos
by Anonymous User
Not applicable
Ah I see. I am at version 3.8, and actually was talking to Ken about this over at this thread. I put my snippet of code there, I honestly think I've done something pretty silly, maybe when I am loading modules...
0 Kudos