Best practice to reset a map

4037
6
04-09-2013 07:02 AM
JeremieJoalland1
Occasional Contributor II
Reset a map is a basic functionnality in most applications.

In my case, I needed it because end-user will have possibilities to change the map context and content without exiting the application. But removing all layers before adding a new one is not enough as spatial reference can change, or tiling scheme and extent can vary from on layer to another (switch from one Tiled Package to another one).

So I've implemented a "reset map" sample application (disconnected scenario), but I'm not sure it's best practice so I hope to have some comments on it to improve it if necessary (is dispose() correctly used ?)...

and more importantly: I'm not satisfied because I have to create new JMap component each time I reset the map... it's the only way I found to reset the spatial reference and map extent properties (like min/max scale).

So is it possible to properly reset a map, with the same JMap object (without new instanciation) ?

Sample code (tpk used are from arcgis samples):

public class ResetMapApplication {

/**
  * Layer initialize complete listener
  */
public class LayerListener implements LayerInitializeCompleteListener {
  @Override
  public void layerInitializeComplete(LayerInitializeCompleteEvent e) {
   Layer layer = e.getLayer();
   if (layer.getStatus() == LayerStatus.INITIALIZED) {
    System.out.println(String.format("-LayerInitializeCompleteEvent- Layer '%s' is initialized on map '%s' !", layer.getName(), map.getName()));
         } else {
          System.out.println(String.format("-LayerInitializeCompleteEvent- ERROR: Layer '%s' is NOT initialized on map '%s' !", layer.getName(), map.getName()));
          System.out.println("    initialization error: " + e.getLayer().getInitializationError());
         }
  }
}

/**
  * Map event listener
  */
public class MapListener implements MapEventListener {
  @Override
  public void mapDispose(MapEvent event) {
   System.out.println(String.format("-MapEventListener- Map '%s' is disposed !", map.getName()));
  }

  @Override
  public void mapReady(final MapEvent event) {
   SwingUtilities.invokeLater(new Runnable() {
    @Override
    public void run() {
     System.out.println(String.format("-MapEventListener- Map '%s' is ready !", map.getName()));
    }
   });
  }

  @Override
  public void mapExtentChanged(MapEvent event) {
   // do nothing
  }
}

/**
  * Main : application's entry
  * @param args
  */
public static void main(String[] args) {
  SwingUtilities.invokeLater(new Runnable() {
   public void run() {
    try {
     // instance of this application
     ResetMapApplication layerApp = new ResetMapApplication();
    
     // create the UI, including the map, for the application.
     JFrame appWindow = layerApp.createWindow();
     appWindow.add(layerApp.createUI());
     appWindow.setVisible(true);
    } catch (Exception e) {
     // on any error, display the stack trace.
     e.printStackTrace();
    }
   }
  });
}

// content pane
private JLayeredPane contentPane;

// map
private JMap map;

// map count
private int count = 1;

// base layer TILED PACKAGE 1
private static final String TPK_BASEMAP_1 = "C:\\temp\\Topographic.tpk";

// base layer TILED PACKAGE 2
private static final String TPK_BASEMAP_2 = "C:\\temp\\SanFrancisco.tpk";

/**
  * Creates a window.
  * @return a window.
  */
private JFrame createWindow() {
  JFrame window = new JFrame("Test - Reset a Map");
  window.setBounds(100, 100, 1000, 700);
  window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  window.getContentPane().setLayout(new BorderLayout(0, 0));
 
  window.addWindowListener(new WindowAdapter() {
   @Override
   public void windowClosing(WindowEvent windowEvent) {
    super.windowClosing(windowEvent);
    if (map != null) {
     map.dispose();
    }
   }
  });
 
  return window;
}

/**
  * Creates application content with ToolBar and Map's container
  * @return
  */
private JComponent createUI() {
  // application content
  JPanel applicationPanel = new JPanel();
  applicationPanel.setLayout(new BorderLayout(0, 0));
 
  // button toolbar
  final JToolBar toolBar = this.createToolBar();
 
  // panel for the map : map will be created later...
  this.contentPane = this.createContentPane();
 
  applicationPanel.add(toolBar, BorderLayout.NORTH);
  applicationPanel.add(this.contentPane, BorderLayout.CENTER);
 
  return applicationPanel;
}

/**
  * Create a toolbar with buttons
  * @return toolBar - the toolBar
  */
private JToolBar createToolBar() {
  JToolBar toolBar = new JToolBar();
  toolBar.setLayout(new FlowLayout(FlowLayout.CENTER));
 
  // button add TPK 1
  JButton buttonTPK1 = new JButton("Add TPK Topographic");
  buttonTPK1.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) {
    // reset the map
    resetMap();
   
    // add tiled layer
    ArcGISLocalTiledLayer tiledLayer = new ArcGISLocalTiledLayer(TPK_BASEMAP_1);
    tiledLayer.setName(TPK_BASEMAP_1);
    tiledLayer.addLayerInitializeCompleteListener(new LayerListener());
    map.getLayers().add(tiledLayer);
   }
  });
  toolBar.add(buttonTPK1);
 
  // button Add TPK 2
  JButton buttonTPK2 = new JButton("Add TPK SanFrancisco");
  buttonTPK2.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) {
    // reset the map
    resetMap();
   
    // add tiled layer
    ArcGISLocalTiledLayer tiledLayer = new ArcGISLocalTiledLayer(TPK_BASEMAP_2);
    tiledLayer.setName(TPK_BASEMAP_2);
    tiledLayer.addLayerInitializeCompleteListener(new LayerListener());
    map.getLayers().add(tiledLayer);
   }
  });
  toolBar.add(buttonTPK2);
 
  return toolBar;
}

/**
  * Creates a content pane.
  * @return a content pane.
  */
private JLayeredPane createContentPane() {
  JLayeredPane contentPane = new JLayeredPane();
  contentPane.setLayout(new BorderLayout(0, 0));
  contentPane.setVisible(true);
  return contentPane;
}

/**
  * Reset the map :
  *  - remove and dispose resources
  *  - create new map
  *  - remove and add on panel (container)
  */
private void resetMap() {
  System.out.println("-actionPerformed - RESET MAP !");
 
  if (map != null) {
   // remove all layers
   for (int i = (this.map.getLayers().size() - 1) ; i == 0 ; i--) {
    this.map.getLayers().remove(i);    
   }
  
   // dispose data ressources
   this.map.dispose();
   // =>> ISSUE HERE : dispose() seems to be fired on the new map after createMap() below  (see Console prints)
  
   // remove map from content pane
   this.contentPane.remove(map);
  }
 
  // re-create the map
  this.map = createMap();
  // =>> ISSUE HERE : can I avoid a new JMap to re-initialize the map (no spatial ref, no fullextent, etc.) ??
 
  // add new map to the content pane
  this.contentPane.add(map);
 
  // Revalidate the map to perform a hierarchical refresh of all components
  this.map.revalidate();
}

/**
  * Create a map with navigator overlay
  * @return JMap - the map
  */
private JMap createMap() {
  JMap jMap = new JMap();
 
  // give an incremental name
  jMap.setName("map " + count ++);
 
  // Add map event listner
  jMap.addMapEventListener(new MapListener());
 
  // Add NavigatorOverlay
  NavigatorOverlay navigator = new NavigatorOverlay();
  jMap.addMapOverlay(navigator);
 
  return jMap;
}
}


source code also available in zip file
0 Kudos
6 Replies
CarlosColón-Maldonado
Occasional Contributor III
I'm afraid is not as simple as you tried it, and not as simple to discuss here. For starters, any MapOverlay extension does not need to contain a JMap because its super already contains it. Simply call getMap(). It'll probably be null if it has not been added to the JMap on main.

If you want to know how it is done, look at the code! The SDK which comes with the toolkit jar also comes with the source files for the toolkit. You have to associate the source file to the referenced toolkit on your project though eclipse in order to see it. On this post, I mentioned how to do that. I'm enclosing the source file of the NavigatorOverlay class which is rather complicated but can be studied.

P.S. Nice touch on your "onMouseWheelMoved" method. I'll be using that to override mouse wheel actions I need.
0 Kudos
ColinAnderson1
Esri Contributor
If you change your dispose event handler to use the map reference from the event you will get the correct output e.g.

System.out.println(String.format("-MapEventListener- Map '%s' is disposed !", ((JMap)event.getSource()).getName()));

By the time this event has fired you have pointed map at a new instance of JMap with the line this.map = createMap() so the name given is that of the new map not the one being disposed.
0 Kudos
JeremieJoalland1
Occasional Contributor II
If you change your dispose event handler to use the map reference from the event you will get the correct output


Exact. Thanks for noticing it... now I get correct map name for dispose event, when out.printing in my console.

Any inputs for the second question :
- How to perform similar reset, without instanciate a new JMap ? a kind of re-initialize the JMap...
0 Kudos
GayleYoung
Esri Contributor
It's not possible to do this at the moment; there is no 'reset'  method on the JMap class. It's something we are considering for a future release.
0 Kudos
JeremieJoalland1
Occasional Contributor II

Hy Gayle,

What is the status regarding a 'reset'  method on the JMap class ?

0 Kudos
JeremieJoalland1
Occasional Contributor II
It's not possible to do this at the moment; there is no 'reset'  method on the JMap class. It's something we are considering for a future release.


Thanks for the confirmation... it will definitly be something useful for us in future release !
0 Kudos