Select to view content in your preferred language

Include Overview Map in Snapshot Image

2796
36
06-03-2010 09:19 AM
StuartBlumberg
Emerging Contributor
I am using a "snapshot" button in order to export the current extent as a JPEG.  How would I include an overview map in a corner of the JPEG snapshot showing where in the city the snapshot is showing?  I am using the Flex Sample Viewer.  I am using the following code to create the snapshot:

private function clickHandler() : void   
   {       
   var map:Map = SiteContainer.getInstance().controller.map;
   const decoder: JPEGEncoder = new JPEGEncoder();       
   map.logoVisible=false;       
   map.scaleBarVisible=true;
   map.zoomSliderVisible = false;
   SiteContainer.getInstance().mapManager.control.visible = false;
  
               //get the current background color of the map
               var currentMapColor:Object = map.getStyle("backgroundColor");
               //set the background color to white
               map.setStyle("backgroundColor", "#CCCCCC");  
               //validate the changes                        
               map.validateNow();      
   const imageSnapshot:ImageSnapshot =
  ImageSnapshot.captureImage(map);       
   map.logoVisible=true;       
   map.scaleBarVisible=true;       
   map.zoomSliderVisible = true;
   map.setStyle("backgroundColor", currentMapColor);
               //validate the changes            
               map.validateNow();     
   const fileReference:FileReference = new
  FileReference();       
   fileReference.save
  (imageSnapshot.data,"map.jpg");
Tags (2)
0 Kudos
36 Replies
RobertScheitlin__GISP
MVP Emeritus
Stuart,

   OK I think I got it worked out for dynamic and static this time.

<?xml version="1.0" encoding="utf-8"?>
<!--
////////////////////////////////////////////////////////////////////////////////
//
// Copyright © 2008 - 2009 ESRI
//
// All rights reserved under the copyright laws of the United States.
// You may freely redistribute and use this software, with or
// without modification, provided you include the original copyright
// and use restrictions.  See use restrictions in the file:
// <install location>/FlexViewer/License.txt
//
////////////////////////////////////////////////////////////////////////////////
-->
<BaseWidget xmlns    ="com.esri.solutions.flexviewer.*" 
   xmlns:esri   ="http://www.esri.com/2008/ags" 
   xmlns:mx   ="http://www.adobe.com/2006/mxml" 
   xmlns:mxeffects  ="com.adobe.ac.mxeffects.*"
   xmlns:widgets  ="com.esri.solutions.flexviewer.widgets.*"
   x     ="600" 
   y     ="300" 
   widgetConfigLoaded ="init()">
 
 
 <mx:Script>
  <![CDATA[
   import com.esri.ags.events.LayerEvent;
   import mx.collections.ArrayCollection;
   import com.esri.ags.utils.WebMercatorUtil;  
   import com.esri.ags.virtualearth.VETiledLayer;
   import com.esri.ags.events.ExtentEvent;
   import com.esri.ags.geometry.Extent;
   import com.esri.ags.geometry.MapPoint;
   import com.esri.ags.Graphic;
   import com.esri.ags.layers.GraphicsLayer;
   import com.esri.ags.layers.ArcGISDynamicMapServiceLayer;
   import com.esri.ags.layers.ArcGISTiledMapServiceLayer;
   import com.esri.ags.symbol.SimpleFillSymbol;
   import com.esri.ags.symbol.SimpleLineSymbol;
   import com.esri.ags.Map;
   import flash.events.MouseEvent;
   import mx.controls.Alert;
   import mx.graphics.ImageSnapshot;   
   
   private var overviewMode:String;
   
   private var graphicLineSym:SimpleLineSymbol = new SimpleLineSymbol("solid", 0xFF0000, 0.8, 3);
   
   private var graphicPolySym:SimpleFillSymbol = new SimpleFillSymbol(null, 0xFF0000, 0.01, graphicLineSym);
   
   private var graphicsLayer:GraphicsLayer;
   
   private var ovGraphic:Graphic;
   
   private var xOff:Number;
   
      private var yOff:Number;
    
   private function init():void
   {
    if (configXML)
    {
     var type:String = configXML.mapservice.@type;
     var url:String = configXML.mapservice;
     var style:String = configXML.mapservice.@style;
     var label:String = configXML.mapservice.@label;
     overviewMode = configXML.mapservice.@mode;
     
     switch (type.toLowerCase())
     {
      case "tiled":
      {
       var tiledlayer:ArcGISTiledMapServiceLayer = new ArcGISTiledMapServiceLayer(url);
       tiledlayer.alpha = alpha;
       if (overviewMode == "dynamic")
        tiledlayer.addEventListener(LayerEvent.TILES_UPDATED,updateOvExtent);
       ovMap.addLayer(tiledlayer);
       break;
      }
       
      case "dynamic":
      {
       var dynlayer:ArcGISDynamicMapServiceLayer = new ArcGISDynamicMapServiceLayer(url);
       dynlayer.alpha = alpha;
       if (overviewMode == "dynamic")
        dynlayer.addEventListener(Event.COMPLETE,updateOvExtent);
       ovMap.addLayer(dynlayer);
       break;
      }
      
      case "virtualearth":
      {
       var veTiledLayer:VETiledLayer =  new VETiledLayer();
       veTiledLayer.id = label;
        veTiledLayer.tokenURL = url;
        veTiledLayer.environment = "production";
        veTiledLayer.visible = true; 
        veTiledLayer.alpha = alpha;
        veTiledLayer.mapStyle = style;
        veTiledLayer.name = label;
        ovMap.addLayer(veTiledLayer);
        break;
      }
     }
                
     graphicsLayer = new GraphicsLayer();
     graphicsLayer.symbol = graphicPolySym;
     ovMap.addLayer(graphicsLayer);     
     ovGraphic = new Graphic();          
     ovGraphic.geometry = map.extent;
     ovGraphic.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
     ovGraphic.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
     graphicsLayer.add(ovGraphic);
     map.addEventListener(ExtentEvent.EXTENT_CHANGE, setOverviewExtent);
     ovGraphic.addEventListener(Event.CHANGE,updateOvExtent);
     updateOverviewExtent();
     setTimeout(updateOverviewExtent, 4000);
    }
   }   
   
   private function setOverviewExtent(event:ExtentEvent):void
   {
    ovGraphic.geometry = map.extent;
    if (overviewMode == "dynamic"){
     ovMap.extent = map.extent.expand(3);
    }
    try{
     var ac:ArrayCollection = new ArrayCollection([]);
     var bmpDat:BitmapData = ImageSnapshot.captureBitmapData(ovMap);
     ac.addItem(bmpDat);
     addSharedData("ovMap",ac);
    }
    catch(err:Error){
    }
    AddOVMap();
   }   
   
   private function updateOverviewExtent():void
   {
    ovGraphic.geometry = map.extent;
    if (overviewMode == "dynamic"){
     ovMap.extent = map.extent.expand(3); 
    }
    try{
     var ac:ArrayCollection = new ArrayCollection([]);
     var bmpDat:BitmapData = ImageSnapshot.captureBitmapData(ovMap);
     ac.addItem(bmpDat);
     addSharedData("ovMap",ac);
    }
    catch(err:Error){
    }
    AddOVMap();   
   }
   
   private function updateOvExtent(Evt:Event):void
   {
    callLater(AddOVMap,null);
   }
   
   private function AddOVMap():void
   {
    try{
     var ac:ArrayCollection = new ArrayCollection([]);
     var bmpDat:BitmapData = ImageSnapshot.captureBitmapData(ovMap);
     ac.addItem(bmpDat);
     addSharedData("ovMap",ac);
    }
    catch(err:Error){
    }
   }
   
   private function mouseDownHandler(event:MouseEvent):void 
      {
       var ext:Extent = ovGraphic.geometry as Extent;
       var mPt:MapPoint = ovMap.toMapFromStage(event.stageX, event.stageY);
       xOff = ext.center.x - mPt.x;
          yOff = ext.center.y - mPt.y;
          ovGraphic.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
   }           
              
   private function mouseMoveHandler(event:MouseEvent):void 
      {
       var mPt:MapPoint = ovMap.toMapFromStage(event.stageX, event.stageY);
       var tempX:Number = mPt.x + xOff;
    var tempY:Number = mPt.y + yOff; 
    var ext:Extent = ovGraphic.geometry as Extent;
    var newext:Extent = new Extent(tempX - ext.width / 2, tempY - ext.height/ 2, tempX + ext.width / 2, tempY + ext.height / 2);
       ovGraphic.geometry = newext;
       if (!event.buttonDown)
        ovGraphic.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler); 
   }          
          
      private function mouseUpHandler(event:MouseEvent):void 
      {
       map.extent = ovGraphic.geometry as Extent;
       ovGraphic.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);    
      }      
      
      private function widgetOpenedHandler(event:Event):void
   {
    map.addEventListener(ExtentEvent.EXTENT_CHANGE, setOverviewExtent);
    setTimeout(updateOverviewExtent, 1000);
   }   
   
      private function widgetClosedHandler(event:Event):void
   {
    map.removeEventListener(ExtentEvent.EXTENT_CHANGE, setOverviewExtent);
   }   
   
   private function widgetMinimizedHandler(event:Event):void
   {
    map.removeEventListener(ExtentEvent.EXTENT_CHANGE, setOverviewExtent);
   }
    
  ]]>
 </mx:Script>
 

 <WidgetTemplate id="wTemplate" widgetClosed="widgetClosedHandler(event)" widgetOpened="widgetOpenedHandler(event)" widgetMinimized="widgetMinimizedHandler(event)">
  <esri:Map id       ="ovMap" 
     width      ="100%"
     height      ="100%"
     panArrowsVisible   ="false" 
     zoomSliderVisible   ="false" 
     logoVisible     ="false" 
     scaleBarVisible    ="false" 
     panEnabled     ="false"
     clickRecenterEnabled  ="false"
     doubleClickZoomEnabled  ="false"
      keyboardNavigationEnabled ="false"
      rubberbandZoomEnabled  ="false"
     scrollWheelZoomEnabled  ="false">
  </esri:Map>
 </WidgetTemplate>
</BaseWidget>
0 Kudos
StuartBlumberg
Emerging Contributor
Works beautifully now, you are a genius!  City workers here will be pleased.  Thank you!
0 Kudos
KarlWilson
Frequent Contributor
Hello,

I'd like to be able to have this "snapshot" functionality in a widget for SFV 2.1 so that I can save a map as a JPG. Does something like this already exist?

If not, what would be the best way to go about creating this? Some tips for a beginner here would be much appreciated!

Thanks.
0 Kudos
ErwanCaradec
Emerging Contributor
Is it possible to make a snapshot of a specified extent of the map without zooming ?

Erwan
0 Kudos
RobertScheitlin__GISP
MVP Emeritus
0 Kudos
RobertScheitlin__GISP
MVP Emeritus
Erwan,

   It would take you using the exportMapImage method on the map services that supported it and merging them together in flex using bitmap methods (to the best of my knowledge).
0 Kudos
ErwanCaradec
Emerging Contributor
Thanks Robert ! You showed me the right way using the bitmap methods.
But instead of using captureImage method of ImageSnapshot, i directly use captureBitmapData, wich return a BitmapData. Then I can copy only pixels corresponding to the rectangle of my selected extent.

Finally, i could create a SnapshotWidget wich can save selected part of the map (with a preview) or the entire map in formart jpg or png.

http://erwan.caradec.pagesperso-orange.fr/flexViewer/index.html?config=configs/tests/config.xml

Regards

Erwan
0 Kudos
KarlWilson
Frequent Contributor
Thanks Robert!

Erwan, your widget is very neat. Are you planning to add that to the code gallery by any chance?

Cheers.
0 Kudos
philippschnetzer
Frequent Contributor
Robert,

Great ExportMapImage widget!  Is it possible to allow certain widgets to be able to be included in the jpg as well?  Namely your dynamic legend should be included in an exported image of the map.  Would be great to know where to add this in the code...

Thanks!
0 Kudos
RobertScheitlin__GISP
MVP Emeritus
Phillip,

   There is a public function (exportImage) that can be called on the DynamicLegendWidget that will return a BitmapImage. But you will still have to work with flex bitmap objects to place and combine the returned BitmapImage with the one that is returned for the map. Sorry I don't have time to give a code example right now.
0 Kudos