Data Driven Pages in a Web Map or Specifying Extents for the PrintTask

4103
5
Jump to solution
09-01-2015 03:14 PM
JoshVan_Kylen
New Contributor III

We are trying to recreate the data driven pages process of ArcMap in a web map application.  So far we have a Geoprocessor service that the web map application calls to generate the GridIndex and adds graphics to the map indicating the tiles to be printed.   Next we would like to use this collection of extents to send to the PrintTask one at a time, but extents is not an accepted parameter of the PrintTask. 

We attempted  to set the web map to each set of extents and then send a print request but the loop is too fast for the setExtent method and we end up with multiple prints of the last set of extents. 

Does anyone else have a solution for producing Data Driven Pages in a web map with ESRI's JS API?

As far as I can tell the possible solutions are:

Option A: Modify the Geoprocessor that generates the Gird index to also either call the Export Web Map Task for each of the extents, or use another method to generate the PDFs and serve them to the user.  Better yet if it could combine the collection of PDFs into one multi page PDF.

Option B: Reverse engineer the JSON call produced by the PrintTask and loop through the collection of extents and send them to the Export Web Map Task.

Any help will be greatly appreciated.

0 Kudos
1 Solution

Accepted Solutions
KellyHutchins
Esri Frequent Contributor

If you don't need to display the map then I think Andrew's approach is the way to go. Here's an example that shows this in action - we create a new map for each extent then use that map with the print task. H

JS Bin - Collaborative JavaScript Debugging

View solution in original post

5 Replies
KellyHutchins
Esri Frequent Contributor

setExtent is asynchronous so you'll need to define a callback function using the then() method in order to know when the extent has been modified.

Here's an example that shows how this works:

        var extent = new Extent(-122.68,45.53,-122.45,45.60);

        map.setExtent(extent).then(function(){

          console.log("Extent has been set - ready to print");

        });

More details on how this works can be found in this excellent blog post:

Keeping Promises

0 Kudos
JoshVan_Kylen
New Contributor III

Thank You Kelly,

This has to be the correct approach, but after incorporating your suggestion the map is setting and printing only the last tile extent in the collection.  But at least it is no longer producing multiple prints of the last extent.

The for loop does not wait for the asynchronous setExtent to complete before moving onto the next extent.

I've read through the recommended "Keeping Promises" and a few other articles/threads ("Correct way to write loops for promise", "Dojo Deferreds and Promises") but I'm still not crystal clear how to achieve a loop effectively with asynchronous calls.  Looks like you can't really use a traditional loop and instead will have to look at restructuring this to chain things together. 

Here is the updated function, that sets and prints only the last extent in the collection.  Looking at the "Chain it up" example in the "Keeping Promises" article seems like the GridIndexPoint function below should work.  But line 8 runs through all of the GridIndexfeatures.length before lines 12 & 13 are written to the console.

        GridIndexPrint = function () {

            dom.byId("mapBusyIndicatorMessage").innerHTML = "Processing...";

            showMapBusyIndicator();

            console.log("length", GridIndexfeatures.length);

            for (var f = 0, fl = GridIndexfeatures.length; f < fl; f++) {

                console.log("f", f);

                var feature = GridIndexfeatures;

                var tmpExtent = new Extent(feature.geometry.getExtent().xmin, feature.geometry.getExtent().ymin, feature.geometry.getExtent().xmax, feature.geometry.getExtent().ymax, new SpatialReference({ wkid: 102100 }));

                theMap.setExtent(tmpExtent).then(function () {

                    console.log(f, "Extent has been set - ready to print", tmpExtent);

                    console.log("PageNumber: " + feature.attributes.PageNumber, "PageName: " + feature.attributes.PageName);

                    Print("PNG8.5x11Land");

                });

            }

            hideMapBusyIndicator();

        }

0 Kudos
AndrewTimmins
Regular Contributor

I have done similar (in Flex) as you describe in Option B.. 

I believe it was the PrintParamters map object I just needed to modify. I believe i cloned the map object, updated the properties i needed and then passed it as that as the map parameter

This way the visible map was not really used.

Drew

0 Kudos
KellyHutchins
Esri Frequent Contributor

If you don't need to display the map then I think Andrew's approach is the way to go. Here's an example that shows this in action - we create a new map for each extent then use that map with the print task. H

JS Bin - Collaborative JavaScript Debugging

View solution in original post

JoshVan_Kylen
New Contributor III

Ultimately this is a working answer.  But it makes for a cumbersome solution.  The web map that I'm trying to provide functionality similar to data driven pages also includes a UniqueValueRenderer that changes based on a user selection.  Creating a new map with the DefinitionExpression, user selected layers, and recreating the UniqueValueRenderer adds to the complexity.  And ultimately the user would be provided a collection of one page PDFs or images, when what they ultimately want is one multi page PDF.

For this I think I'm going to have to switch gears and look at a geoprocessor based solution.

0 Kudos