English

How To: Create dynamic layers in MapObjects-Java

Summary

When using MapObjects-Java Standard Edition (MOJ), a developer may choose to dynamically create and display graphical features, including text, with data layers in a Map component. The geometry of the graphic components in a dynamic layer is created at runtime, either in the application code or using a data file, such as a text file with x and y coordinates. Different methods can be used to generate a dynamic layer, depending on the extent to which the developer needs to interact with the data in the layer. Three primary techniques are available:

  • override the paintComponent method in AcetateLayer
  • create a GraphicsLayer
  • create a FeatureLayer.
Each method is discussed and the sample code is provided. The sample code demonstrates the creation of the same triangle polygon using a different method for creating a dynamic layer. The triangle coordinates are specified in decimal degrees, and its extent is located within the continental United States.

Procedure

All techniques listed below use virtual memory allocated to the Java Virtual machine upon application runtime - no caching mechanism has been implemented. As a result, memory usage will increase as additional graphic features are added to an acetate layer. Depending on the number and complexity of graphic features in a dynamic layer, you will need to adjust the application code or memory parameters appropriately.

  • Override the paintComponent method in an AcetateLayer

    Subclassing an AcetateLayer and overriding the paintComponent() method is the simplest technique for creating a dynamic layer.

    An AcetateLayer is essentially a transparent JComponent that contains a com.esri.mo.map.dpy.Layer and is added to the JLayeredPane sub-component that is internal to a Map. Changes to a Map extent cause the AcetateLayer to be repainted. Note that all features inherent to an AcetateLayer are asynchronously rendered in the paintComponent() to an off-screen buffer, separate from the off-screen buffer used by the Map component. Sample code:
    public class MyAcetateLayer extends com.esri.mo.ui.bean.AcetateLayer{
    
    	com.esri.mo.ui.bean.Map m_map;
            com.esri.mo.map.draw.SimpleFillSymbol sps;
            com.esri.mo.map.draw.SimpleLineSymbol sls;
    
    	public MyAcetateLayer(com.esri.mo.ui.bean.Map map){
    
    	      m_map = map;
                 sps = new com.esri.mo.map.draw.SimpleFillSymbol();
                 sps.setType(sps.FILLTYPE_CROSS);
                 sps.setSymbolColor(java.awt.Color.green);
      
                 java.awt.BasicStroke bs = new java.awt.BasicStroke(3.0f);
                 sls = new com.esri.mo.map.draw.SimpleLineSymbol();
                 sls.setStroke(bs);
                 sls.setLineColor(java.awt.Color.yellow);       
     
    	}
    
    	public void paintComponent(java.awt.Graphics g){
    
    		// Call the superclass paint component to clear the off-screen buffer
    		super.paintComponent(g);
    		
    		// Retrieve the current graphics component
    		java.awt.Graphics2D g2d = (java.awt.Graphics2D) g;
    		
    		/* Use the Map component to create a Transform - used to convert world coordinates
    		  to pixel cooridinates in the Map display
    		 */
    		com.esri.mo.cs.geom.Transform t = m_map.getWorldToPixelTransform();
    		
    		// Create the features to be displayed in the AcetateLayer
    		double[] xx = {-90.0, -110.0, -100.0, -90};
    		double[] yy = {30.0, 40.0, 50.0, 30.0};
    		com.esri.mo.cs.geom.BasePointsArray bpa = new com.esri.mo.cs.geom.BasePointsArray(xx,yy);
    		com.esri.mo.cs.geom.BasePolyline bpl = new com.esri.mo.cs.geom.BasePolyline(new com.esri.mo.cs.geom.BasePath(bpa));
    		com.esri.mo.cs.geom.BasePolygon bp = new com.esri.mo.cs.geom.BasePolygon(new com.esri.mo.cs.geom.BaseRing(bpa));
    		g2d.transform(t.toAffine());
                
                    sps.draw(bp,g2d,"test");
                    sls.draw(bpl,g2d,"test");     
    	
    	}
    }
     
    The following characteristics of this technique must be considered:
    • Graphic features must be created using the java.awt.geom.* package.
    • Often graphic geometry must be transformed to display in the proper location on the Map component. To transform from world to pixel coordinates, the extent of the Map component must be set with any valid value but 'null'.
    • The new AcetateLayer object can be instantiated and added as an AcetateLayer to a Map component. By default, AcetateLayer's are visible in the Map and not added to the Map's Legend. Here is some sample code to add an AcetateLayer to a Map:
MyAcetateLayer alayer = new MyAcetateLayer(aMap);
aMap.add(alayer);
Use an AcetateLayer when creating simple graphics that do not contain attribute information. Since it renders to a separate offscreen buffer than the Map component, the redraw() method of the Map component does not have to be called to refresh the contents of the AcetateLayer's paintComponent() method. The graphics can be refreshed without redrawing the data layers in the Map component, saving valuable processing and performance time in the MapObjects-Java application on the client. Common uses for AcetateLayers are displaying temporarily highlighted features or text labels.
  • Create a GraphicsLayer

    The GraphicsLayer interface is implemented by two creatable subclasses: BaseGraphicsLayer, BaseFeatureGraphicsLayer. Features are added to a GraphicsLayer as Elements, or more specifically, classes extending from com.esri.mo.map.dpy.BaseElement. Feature elements consist of one or more fields that contain the elements' feature geometry and attributes. Feature geometry consists of components from the com.esri.mo.cs.geom.* package. A single GraphicsLayer can contain different element types, such as polygons, points and images. In addition, GraphicsLayer feature elements can be searched and selected using spatial or attribute queries. The sample code demonstrates the creation of a BaseGraphicsLayer:
    import com.esri.mo.cs.geom.*;
    import com.esri.mo.map.draw.*;
    import com.esri.mo.data.feat.*;
    import com.esri.mo.map.dpy.*;
    import java.awt.*;
    
    public class MyGraphicsLayer extends BaseGraphicsLayer {
    
    	public MyGraphicsLayer(){
    
    		// Create fields to hold the feature geometry and attributes
    		
    		BaseField fld1 = new BaseField("shape", Field.ESRI_SHAPE, 0,0);
    		BaseField fld2 = new BaseField("string", java.sql.Types.VARCHAR, 10,0);
    		BaseFields bfs = new BaseFields();
    		bfs.addField(fld1);
    		bfs.addField(fld2);
    
    		// Create the feature geometry  
    		double[] xx = {-90.0, -110.0, -100.0};
    		double[] yy = {30.0, 40.0, 50.0};
    		BasePointsArray bpa = new BasePointsArray(xx,yy );
    		com.esri.mo.cs.geom.BasePolygon bp = new com.esri.mo.cs.geom.BasePolygon(new BaseRing(bpa));
    
    		// Create a and add the geomtery and attributes
    		BaseFeature bf = new BaseFeature();
    		bf.setFields(bfs);
    		bf.setValue(0, bp);
    		bf.setValue(1, "Area 1");
    
    		// Create an Element to add to the GraphicsLayer
    		BaseFeatureElement bfe1 = new BaseFeatureElement((BaseFeature) bf.clone());
    
    		// Set the renderer for the Element
    		BaseSimpleRenderer rd = new BaseSimpleRenderer();
    		SimplePolygonSymbol symbol = new SimplePolygonSymbol();
    		symbol.setPaint(new Color(0,20,250));
    		rd.setSymbol(symbol);
    		bfe1.setRenderer(rd);
    		
    		// Add Element to the GraphicsLayer
    		this.addElement(bfe1);
    
    	}
    }
     
    The following characteristics of this technique must be considered:
    • Renderer components are set on individual Elements. The Symbol for the elements in the GraphicsLayer can be set using the GraphicsLayer's setDefaultSymbol() and setDefaultSelectSymbol() methods.
    • Associate the GraphicsLayer with an AcetateLayer before adding to a Map component. No Legend will be added to a Map TOC if a GraphicsLayer is added to a Map as an AcetateLayer. Adding the GraphicsLayer directly to a Map's Layerset will create an unusable legend. The TOC will not be able to recognize the GraphicsLayer component and an exception will be thrown when an attempt to set the visibility of the GraphicsLayer.
    • The GraphicsLayer will use the off-screen buffer used by the Map component even though it has been added as an AcetateLayer. Here is some sample code to add a GraphicsLayer as an AcetateLayer to a Map:
MyGraphicsLayer glayer = new MyGraphicsLayer();
AcetateLayer aglayer = new AcetateLayer(glayer);
aMap.add(aglayer);
Use a GraphicsLayer when the graphic features being added to a Map also have underlying attribute data, but do not need to be managed as a layer in the Map's TOC. If you need to add map tips, data listeners, feature labeling or complex rendering, use a FeatureLayer. On the other hand, if you want to add image data as an acetate layer, use a GraphicsLayer.
  • Create a FeatureLayer

    The FeatureLayer interface is implemented by one creatable subclass: BaseFeatureLayer. A FeatureLayer is only capable of storing vector features of the same type. Creating a new FeatureLayer involves creating a feature class (MemoryFeatureClass), adding features and attributes, and setting the layer capabilities. A dynamic FeatureLayer will have the same properties as FeatureLayers using data located on disk, such as shapefile or ArcSDE layer. The sample code demonstrates the creation of a BaseFeatureLayer:
    public class MyFeatureLayer extends BaseFeatureLayer {
    
      BaseFields fields;
      private Vector featureStorage;
    
      public MyFeatureLayer() {
    
          createFeaturesAndFields();
          
          /* Retrieve and set the feature class for this FeatureLayer
           * "MyData" can be replaced with any String value, it will be used
           * as the layer name in the legend entry
           */
          BaseFeatureClass bfc = getFeatureClass("MyData");
          setFeatureClass(bfc);
          
          // Construct and set the renderer for this FeatureLayer
          BaseSimpleRenderer rd = new BaseSimpleRenderer();
          SimplePolygonSymbol symbol = new SimplePolygonSymbol();
          symbol.setPaint(new Color(250,0,20));
          rd.setSymbol(symbol);
          setRenderer(rd);
          
          /* Call local method to set layer capabilities.  Since the capabilities of a 
           * FeatureLayer must be set before it will display, and the setCapabilities() 
           * method has protected access, a dynamic FeatureLayer must subclass BaseFeatureLayer 
           */
          MyLayerCapabilities lc = new MyLayerCapabilities();
          setCapabilities(lc);
        
        }
    
        private void createFeaturesAndFields(){
    
          featureStorage = new Vector();
          fields = new BaseFields();
          
          createDbfFields();
    
          /* Create a new BaseFeature and set the fields template.  Assign a unique DataID for
           * each feature.
           */
          BaseFeature feature = new BaseFeature();
          feature.setFields(fields);
          feature.setDataID(new BaseDataID("myData",0));
    
          double[] xx = {-90.0, -110.0, -100.0};
          double[] yy = {30.0, 40.0, 50.0};
          BasePointsArray bpa = new BasePointsArray(xx,yy );
          com.esri.mo.cs.geom.BasePolygon bp = new com.esri.mo.cs.geom.BasePolygon(new BaseRing(bpa));
          feature.setValue(0, bp);
          feature.setValue(1, new Integer(2));
          
    
          featureStorage.addElement(feature);
        }
    
        private void createDbfFields()
        {
          fields.addField(new BaseField("#SHAPE#", Field.ESRI_SHAPE, 0, 0));
          fields.addField(new BaseField("ID", java.sql.Types.INTEGER, 9, 0));
        }
    
        public BaseFeatureClass getFeatureClass(String Name)
        {
    
          com.esri.mo.map.mem.MemoryFeatureClass featClass = null;
          try{
    
            // Set the feature type to the appropriate value (POLYGON, LINE, POINT)
            featClass = new com.esri.mo.map.mem.MemoryFeatureClass(MapDataset.POLYGON, fields);
    
            }
          catch (IllegalArgumentException ie)
          {
           System.err.println("error creating the base class ="+ie.toString());
          }
    
          featClass.setName(Name);
          featClass.addFeature((Feature) featureStorage.elementAt(0));
    
          return featClass;
        }
    
        private final class MyLayerCapabilities extends com.esri.mo.map.dpy.LayerCapabilities {
            MyLayerCapabilities() {
              for (int i = 0; i < this.size(); i++) {
                    setAvailable(this.getCapabilityName(i),true);
                    setEnablingAllowed(this.getCapabilityName(i),true);
                    getCapability(i).setEnabled(true);
              }
            }
          }
    }
     
    The following characteristics of this technique must be considered:
    • All FeatureLayers must set the layer capabilities, otherwise no features will be visible.
    • A dynamic FeatureLayer can be rendered using any applicable label or feature renderer in Map Objects Java. The renderer can be applied in the class itself or where the BaseFeatureLayer is instantiated.
    • By default, when a dynamic FeatureLayer is added to a Map component, a Legend is added to the TOC. The renderer defined for the FeatureLayer is used by the Legend. Here is some sample code to add a FeatureLayer to a Map:
MyFeatureLayer flayer = new MyFeatureLayer();
flayer.setVisible(true);
aMap.getLayerset().addLayer(flayer);
Use a FeatureLayer when the graphic features being added to dynamic layer consist of points, lines or polygons and require properties available only to FeatureLayers, such as complex rendering or labeling, map tips, and data listeners. In addition, the Map and TOC components will manage the feature and legend visibility of the FeatureLayer. This method provides the most functional solution for maintaining graphic features and their attributes in an acetate layer.

Related Information