Select to view content in your preferred language

How to measure area on a graphic after editing it?

905
5
Jump to solution
09-06-2012 06:20 AM
ionarawilson1
Deactivated User
Hi guys,

I am working on an application that the user enters an address and then the location is displayed, then he uses a polygon draw tool to draw a polygon that display the area of the measurement. Then if he wants he can edit the graphic that was created for the polygon. However I am not sure how to recalculate the area on the graphic after he edits it. I am using a geometry service to return the measured area.

Any ideas or example that could help? Thank you for any help!


This is the code for the drawing :

  protected function drawTool_drawEndHandler(event:DrawEvent):void    {     // reset after finished drawing a feature     myDrawTool.deactivate();     drawbutton.selected= false;               var polygon:Polygon = event.graphic.geometry as Polygon;     if (GeometryUtil.polygonSelfIntersecting(polygon))     {      // simplify the drawn polygon      // Note: As of version 2.0, GeometryService input is geometries (instead of graphics).      geometryService.simplify([ polygon ]);     }     else     {      //addPolygonToMap(polygon);      projectPolygon(polygon);      myDrawTool.deactivate();     }              }      private function geometryService_simplifyCompleteHandler(event:GeometryServiceEvent):void    {     // Note: GeometryService returns geometries instead of graphics as of Flex API 2.0     if (event.result)     {      var polygon:Polygon = (event.result as Array)[0]; // we only draw one area at a time      projectPolygon(polygon);     }    }        private function projectPolygon(polygon:Polygon):void    {          // project to 54034 (World_Cylindrical_Equal_Area)     const projectParameters:ProjectParameters = new ProjectParameters;     projectParameters.geometries = [ polygon ];     projectParameters.outSpatialReference = new SpatialReference(54034);     geometryService.project(projectParameters, new AsyncResponder(project_resultHandler, project_faultHandler, polygon));    }       private function project_resultHandler(result:Object, token:Object = null):void    {     if (result)     {           var polygon:Geometry = (result as Array)[0];            var areasAndLengthsParameters:AreasAndLengthsParameters = new AreasAndLengthsParameters();      areasAndLengthsParameters.areaUnit = GeometryService.UNIT_ACRES;      areasAndLengthsParameters.polygons = [ polygon ];            geometryService.areasAndLengths(areasAndLengthsParameters, new AsyncResponder(areasAndLengths_resultHandler, areasAndLengths_faultHandler, token));      // project to 54034 (World_Cylindrical_Equal_Area)              }         }        private function project_faultHandler(fault:Fault, token:Object = null):void    {     Alert.show(fault.faultString + "\n\n" + fault.faultDetail, "project Fault " + fault.faultCode);    }        private function areasAndLengths_resultHandler(result:AreasAndLengthsResult, token:Object = null):void    {     const area:String = myNumberFormatter.format(result.areas[0]);     geometryService.labelPoints([ token ], new AsyncResponder(labelPoints_resultHandler, labelPoints_faultHandler, area + " acres"));    }        private function areasAndLengths_faultHandler(fault:Fault, token:Object = null):void    {     Alert.show(fault.faultString + "\n\n" + fault.faultDetail, "areasAndLengths Fault " + fault.faultCode);    }        private function labelPoints_resultHandler(result:Object, token:Object = null):void    {     for each (var geom:Geometry in result)     {      var g:Graphic = new Graphic();      g.geometry = geom;      var tf:TextFormat = new TextFormat(null, 16, 0x41423A);      g.symbol = new TextSymbol(String(token), null, 0xFFFFFF, 1, true, 0x41423A, true,       0xD8DACC, "middle", 0, 0, 0, tf);      myGraphicsLayer.add(g);     }    }        private function labelPoints_faultHandler(fault:Fault, token:Object = null):void    {     Alert.show(fault.faultString + "\n\n" + fault.faultDetail, "labelPoints Fault " + fault.faultCode);    } 


and this is for the editing:

  private function myMap_mapMouseDownHandler(event:MapMouseEvent):void    {     event.currentTarget.addEventListener(MouseEvent.MOUSE_MOVE, map_mouseMoveHandler);     event.currentTarget.addEventListener(MouseEvent.MOUSE_UP, map_mouseUpHandler);       }        private function map_mouseMoveHandler(event:MouseEvent):void    {     event.currentTarget.removeEventListener(MouseEvent.MOUSE_MOVE, map_mouseMoveHandler);     event.currentTarget.removeEventListener(MouseEvent.MOUSE_UP, map_mouseUpHandler);    }        private var graphic:Graphic;    private var lastEditGraphic:Graphic;    private var lastActiveEditTypes:String;            private function map_mouseUpHandler(event:MouseEvent):void    {     event.currentTarget.removeEventListener(MouseEvent.MOUSE_MOVE, map_mouseMoveHandler);     event.currentTarget.removeEventListener(MouseEvent.MOUSE_UP, map_mouseUpHandler);          if (event.target is Graphic)     {                  graphic = Graphic(event.target);           if (lastEditGraphic !== graphic)      {       lastEditGraphic = graphic;       lastActiveEditTypes = "moveRotateScale"; // make sure move and edit vertices is the 1st mode           }      if (graphic.geometry is Polyline || graphic.geometry is Polygon)      {                          if (lastActiveEditTypes == "moveEditVertices")       {                lastActiveEditTypes = "moveRotateScale";        myEditTool.activate(EditTool.MOVE | EditTool.SCALE | EditTool.ROTATE, [ graphic ]);                        }       else       {               lastActiveEditTypes = "moveEditVertices";        myEditTool.activate(EditTool.MOVE | EditTool.EDIT_VERTICES, [ graphic ]);            }      }      else if (graphic.geometry is Extent)      {              myEditTool.activate(EditTool.MOVE | EditTool.SCALE, [ graphic ]);             }      else if (graphic.graphicsLayer == myGraphicsLayer)      {              myEditTool.activate(EditTool.MOVE | EditTool.EDIT_VERTICES, [ graphic ]);             }         }     else     {           myEditTool.deactivate();      lastActiveEditTypes = "moveRotateScale";  // make sure move and edit vertices is the 1st mode     }    } 
Tags (2)
0 Kudos
1 Solution

Accepted Solutions
TrishRempel
Occasional Contributor
Hello Ionara,

It looks like we've used a similar code base. You probably have this section in your code (except my variable name is editTool while yours is myEditTool):

   /**     * Adds event listeners to the current edit tool     */     private function addEditToolEventListeners():void    {     editTool.addEventListener(EditEvent.GHOST_VERTEX_MOUSE_DOWN, handleHideMeasureLabel);     editTool.addEventListener(EditEvent.CONTEXT_MENU_SELECT, handleHideMeasureLabel);     editTool.addEventListener(EditEvent.VERTEX_MOVE_START, handleHideMeasureLabel);          editTool.addEventListener(EditEvent.GRAPHICS_MOVE_START, handleHideMeasureLabel);     editTool.addEventListener(EditEvent.GRAPHIC_ROTATE_START, handleHideMeasureLabel);     editTool.addEventListener(EditEvent.GRAPHIC_SCALE_START, handleHideMeasureLabel);          editTool.addEventListener(EditEvent.VERTEX_ADD, editTool_vertexAddDeleteHandler);     editTool.addEventListener(EditEvent.VERTEX_DELETE, editTool_vertexAddDeleteHandler);     editTool.addEventListener(EditEvent.VERTEX_MOVE_STOP, editTool_vertexMoveStopHandler);          editTool.addEventListener(EditEvent.GRAPHICS_MOVE_STOP, editTool_graphicsMoveStopHandler);     editTool.addEventListener(EditEvent.GRAPHIC_ROTATE_STOP, editTool_graphicRotateStopHandler);     editTool.addEventListener(EditEvent.GRAPHIC_SCALE_STOP, editTool_graphicScaleStopHandler);    }


Many of the handlers have code similar to this:

   /**     * Handle graphic move complete     */     private function editTool_graphicsMoveStopHandler(event:EditEvent):void    {     handleEditEvent(event);    }


Your code looks different enough from mine that it won't be a simple copy and paste. This is my handleEditEvent method:

   /**     * Handle graphic editing     */     private function handleEditEvent(event:EditEvent):void    {     var graphic:Graphic = event.graphic;     if (!graphic && event.graphics)     {      graphic = event.graphics[0];     }          if (map.wrapAround180)     {      normalizeGraphicGeometry(graphic);     }     else if (measurementLabelExists)     {      recalculate(graphic);     }    }


The recalculate() method is where I am calling the geometry service and updating the graphic label. I hope this helps somewhat!

View solution in original post

0 Kudos
5 Replies
TrishRempel
Occasional Contributor
Hello Ionara,

It looks like we've used a similar code base. You probably have this section in your code (except my variable name is editTool while yours is myEditTool):

   /**     * Adds event listeners to the current edit tool     */     private function addEditToolEventListeners():void    {     editTool.addEventListener(EditEvent.GHOST_VERTEX_MOUSE_DOWN, handleHideMeasureLabel);     editTool.addEventListener(EditEvent.CONTEXT_MENU_SELECT, handleHideMeasureLabel);     editTool.addEventListener(EditEvent.VERTEX_MOVE_START, handleHideMeasureLabel);          editTool.addEventListener(EditEvent.GRAPHICS_MOVE_START, handleHideMeasureLabel);     editTool.addEventListener(EditEvent.GRAPHIC_ROTATE_START, handleHideMeasureLabel);     editTool.addEventListener(EditEvent.GRAPHIC_SCALE_START, handleHideMeasureLabel);          editTool.addEventListener(EditEvent.VERTEX_ADD, editTool_vertexAddDeleteHandler);     editTool.addEventListener(EditEvent.VERTEX_DELETE, editTool_vertexAddDeleteHandler);     editTool.addEventListener(EditEvent.VERTEX_MOVE_STOP, editTool_vertexMoveStopHandler);          editTool.addEventListener(EditEvent.GRAPHICS_MOVE_STOP, editTool_graphicsMoveStopHandler);     editTool.addEventListener(EditEvent.GRAPHIC_ROTATE_STOP, editTool_graphicRotateStopHandler);     editTool.addEventListener(EditEvent.GRAPHIC_SCALE_STOP, editTool_graphicScaleStopHandler);    }


Many of the handlers have code similar to this:

   /**     * Handle graphic move complete     */     private function editTool_graphicsMoveStopHandler(event:EditEvent):void    {     handleEditEvent(event);    }


Your code looks different enough from mine that it won't be a simple copy and paste. This is my handleEditEvent method:

   /**     * Handle graphic editing     */     private function handleEditEvent(event:EditEvent):void    {     var graphic:Graphic = event.graphic;     if (!graphic && event.graphics)     {      graphic = event.graphics[0];     }          if (map.wrapAround180)     {      normalizeGraphicGeometry(graphic);     }     else if (measurementLabelExists)     {      recalculate(graphic);     }    }


The recalculate() method is where I am calling the geometry service and updating the graphic label. I hope this helps somewhat!
0 Kudos
ionarawilson1
Deactivated User
Hi Trish,

Thank you so much for this. I think it is going to help! Do you mind sharing the code for the recalculate function with me?  Thanks again!!!!
0 Kudos
TrishRempel
Occasional Contributor
Hi Ionara,

Below is my code for the recalculate() method. There are going to be some changes, because I have adjusted some of the original code for my own purposes (one of the changes I can see is that when creating the measurement label, I'm placing it on its own layer called labelsLayer instead of the original graphicsLayer). So once again, it will probably need adjusting for your code base.

My code is a combination of the ESRI Draw and Measure widget and the DrawAdvanced widget: https://github.com/coomsie/esri-flexviewer-3.0-widget-DrawAdvanced

    /**
    * Update the graphic's measurement label and positioning
    */
   private function recalculate(graphic:Graphic):void
   {
    var geom:Geometry = graphic.geometry;
    measureGeometry(geom, graphic);
   }
   
   /**
    * Depending on the geometry's spatial reference, calculate measurement or 
    * create a new geometry service project call
    */
   private function measureGeometry(geom:Geometry, graphic:Graphic):void
   {
    var wkid:Number = geom.spatialReference.wkid;
    if ((wkid == EPSG_GEOGRAPHIC) || (isWebMercator(wkid)))
    {
     calculateMeasurements(geom, graphic);
    }
    else
    {
     var geographicSpatialReference:SpatialReference = new SpatialReference(EPSG_GEOGRAPHIC);
     const projectParameters:ProjectParameters = new ProjectParameters;
     projectParameters.geometries = [ geom ];
     projectParameters.outSpatialReference = geographicSpatialReference;
     geometryService.project(projectParameters, new AsyncResponder(project_resultHandler, project_faultHandler, geom));
    }
   }
   
   /**
    * Determine if specified spatial reference is web mercator
    */
   private function isWebMercator(wkid:Number):Boolean
   {
    return wkid == 102100 || wkid == 3857 || wkid == 102113;
   }
   
   /**
    * Calculate geometry measurements depending on geometry type
    */
   private function calculateMeasurements(geom:Geometry, graphic:Graphic):void
   {
    (graphic.attributes as GraphicPropertiesVO).measurementGeometry = geom;
    switch (geom.type)
    {
     case Geometry.POLYLINE:
     {
      var polyline:Polyline = Polyline(geom);
      calculatePolylineLengths(polyline, graphic);
      break;
     }
     case Geometry.POLYGON:
     {
      var polygon:Polygon = Polygon(geom);
      calculatePolygonAreasAndLengths(polygon, graphic);
      break;
     }
     case Geometry.EXTENT:
     {
      var extent:Extent = Extent(geom);
      calculatePolygonAreasAndLengths(extent.toPolygon(), graphic); //convert it to polygon for measurement
      break;
     }
     case Geometry.MAPPOINT:
     {
      var mapPoint:MapPoint = MapPoint(geom);
      calculateMapPointCoordinates(mapPoint, graphic);
     }
    }
   }
   
   /**
    * Calculate polyline lengths
    */
   private function calculatePolylineLengths(polyline:Polyline, graphic:Graphic):void
   {
    var polylineToMeasure:Polyline
    
    var wkid:Number = polyline.spatialReference.wkid;
    if (wkid == EPSG_GEOGRAPHIC)
    {
     polylineToMeasure = polyline;
    }
    else if (isWebMercator(wkid))
    {
     polylineToMeasure = WebMercatorUtil.webMercatorToGeographic(polyline) as Polyline;
    }
    
    var lengths:Array = GeometryUtil.geodesicLengths([ polylineToMeasure ], Units.METERS);
    
    var length:Number = lengths[0] * distanceConversion;
    
    var label:String = createLengthsLabel(length, distanceAbbr);
    addDrawLabel(label, graphic);
   }
   
   /**
    * Create lengths label
    */
   private function createLengthsLabel(length:Number, lengthAbbrev:String):String
   {
    return lengthLabel + " " + numFormatter.format(length) + " " + lengthAbbrev;
   }
   
   /**
    * Calculate map point coordinates
    */
   private function calculateMapPointCoordinates(point:MapPoint, graphic:Graphic):void
   {
    var label:String = createPointMeasurementLabel(point);
    addDrawLabel(label, graphic);
   }
   
   /**
    * Formats the length measurement into a string to displayed on the map.
    * <p>
    * <b>Parameters</b><br/>
    * <ul>
    * <li><i>point [MapPoint]: </i>The map point that represents the location whose coordinates should be labelled.</li>
    * </ul>
    * </p>
    */
   private function createPointMeasurementLabel(point:MapPoint):String
   {
    var wkid:Number = point.spatialReference.wkid;
    if (wkid == EPSG_GEOGRAPHIC)
    {
     numFormatter.precision = 6;
     return "Latitude: " + numFormatter.format(point.y) +
      "\n" + "Longitude: " + numFormatter.format(point.x);
    }
    else
    {
     numFormatter.precision = 0;
     return "X: " + numFormatter.format(point.x) +
      "\n" + "Y: " + numFormatter.format(point.y);
    }
   }
   
   /**
    * Calculate Polygon areas and lengths
    */
   private function calculatePolygonAreasAndLengths(polygon:Polygon, graphic:Graphic):void
   {
    var polygonToMeasure:Polygon;
    
    var wkid:Number = polygon.spatialReference.wkid;
    if (wkid == EPSG_GEOGRAPHIC)
    {
     polygonToMeasure = polygon;
    }
    else if (isWebMercator(wkid))
    {
     polygonToMeasure = WebMercatorUtil.webMercatorToGeographic(polygon) as Polygon;
    }
    
    var lengths:Array = GeometryUtil.geodesicLengths([ new Polyline(polygonToMeasure.rings)], Units.METERS);
    var areas:Array = GeometryUtil.geodesicAreas([ polygonToMeasure ], Units.SQUARE_METERS);
    
    var area:Number = areas[0] * areaConversion;
    var length:Number = lengths[0] * distanceConversion;
    
    var label:String = createAreasAndLengthsLabel(area, areaAbbr, length, distanceAbbr);
    addDrawLabel(label, graphic);
   }
   
   /**
    * Create area and lengths label
    */
   private function createAreasAndLengthsLabel(area:Number, areaAbbrev:String, length:Number, lengthAbbrev:String):String
   {
    return areaLabel + " " + numFormatter.format(area) + " " + areaAbbrev +
     "\n" + perimeterLabel + " " + numFormatter.format(length) + " " + lengthAbbrev;
   }
   
   /**
    * Determine the ideal positioning of the measurement label
    */
   private function getMeasureLabelPosition(geom:Geometry):MapPoint
   {
    var measurePt:MapPoint = null;
    switch (geom.type)
    {
     case Geometry.POLYLINE:
     {
      var polyline:Polyline = geom as Polyline;
      var polylineExtent:Extent;
      if (polyline.paths.length == 1)
      {
       polylineExtent = polyline.extent;
      }
      else
      {
       // Multiple paths, hence show the measurement label at the center of first path
       var tempPolyline:Polyline = new Polyline;
       tempPolyline.paths = [ polyline.paths[0]];
       polylineExtent = tempPolyline.extent;
      }
      measurePt = polylineExtent.center;
      break;
     }
     case Geometry.POLYGON:
     {
      var polygon:Polygon = geom as Polygon;
      var polygonExtent:Extent;
      if (polygon.rings.length == 1)
      {
       polygonExtent = polygon.extent;
      }
      else
      {
       // Multiple rings, hence show the measurement label at the center of first ring
       var tempPolygon:Polygon = new Polygon;
       tempPolygon.rings = [ polygon.rings[0]];
       polygonExtent = tempPolygon.extent;
      }
      measurePt = polygonExtent.center;
      break;
     }
     case Geometry.EXTENT:
     {
      measurePt = geom.extent.center;
      break;
     }
     case Geometry.MAPPOINT:
     {
      var screenPoint:Point = map.toScreen(geom as MapPoint);
      screenPoint.x = screenPoint.x + 10;
      screenPoint.y = screenPoint.y - 20;
      measurePt = map.toMap(screenPoint);
     }
    }
    return measurePt;
   }

   /**
    * Create and place the measurement label
    */
   private function addDrawLabel(label:String, graphic:Graphic):void
   {
    // create a text symbol
    var txtSym:TextSymbol = new TextSymbol(label);
    txtSym.yoffset = 8;
    var txtFormat:TextFormat = new TextFormat("Arial", 12, 0x000000, true); // black label
    txtSym.textFormat = txtFormat;
    
    // remove the current graphic's measurement label
    labelsLayer.remove(getMeasurementGraphic(graphic));
    
    // create the new measurement graphic label
    var gra:Graphic = new Graphic();
    gra.name = graphic.id;
    gra.geometry = getMeasureLabelPosition(graphic.geometry);
    gra.symbol = txtSym;
    gra.mouseEnabled = false;
    gra.mouseChildren = false;
    gra.filters = [ measurementBorderFilter ];
    labelsLayer.add(gra);
    
    // associate the measurement label text to the editing or previous graphic's attributes
    (graphic.attributes as GraphicPropertiesVO).measurementLabel = label;
    // update the graphic context menu with the ability to hide or show the measurement label
    graphic_updateContextMenu(graphic);
   }
0 Kudos
ionarawilson1
Deactivated User
Hi Trish,

You helped me so much today! I had no idea how I was going to recalculate the area of a graphic! I am so happy. My code is a little different than yours, but it works perfectly! I am attaching  the code if anybody is interested in having a draw tool with an option of editing and calculating the area! Thank you so much!!!
0 Kudos
TrishRempel
Occasional Contributor
I'm glad to hear that I could help! 🙂
0 Kudos