Questions about Length and Area returned by Geometry Service

1422
3
Jump to solution
08-14-2013 12:30 PM
ZachLiu1
Occasional Contributor II
I used a simple app to test the lengths and Areas returned by Geometry Service. I found the geometryService.lengths(geometry) can return accurate lengths of polylines, but it looks like the lengths and areas returned by geometryService.areasAndLengths are exaggerated.

e.g. The perimeter of the state of Colorado is around 1300 miles. When I draw a polyline around it, it will return similar number. But if I draw a polygon around it, it will return 1600+ miles and the area is exaggerated accordingly.

Does anyone has a clue? I included the complete code in case someone wants to test it.

<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=7, IE=9, IE=10"> <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no"> <title>Draw and Measure</title> <link rel="stylesheet" href="http://js.arcgis.com/3.6/js/dojo/dijit/themes/nihilo/nihilo.css"> <link rel="stylesheet" href="http://js.arcgis.com/3.6/js/esri/css/esri.css"> <link rel="stylesheet" type="text/css" href="http://code.jquery.com/ui/1.10.3/themes/start/jquery-ui.css" />  <style>     html, body{         font-family: sans-serif;         height: 100%;         width: 100%;     }     html, body {         margin: 0;         padding: 0;     }     h1,h2,h3 {         margin: 0;         padding: 0;     }     header {         position: absolute;         top: 0;         left: 0;         right: 0;         height: 5em;         z-index: 100;     }     .title_bar {         text-align: center;         height: 2.4em;         background-color: #222;         color: #fff;     }     .title_bar h2 {         font-size: 1.3em;         padding-top: 5px;         letter-spacing: 1px;     }     .draw_toolbar {         height: 2.6em;         padding-left: 15px;         padding-right: 15px;         background-color: #eee;         -webkit-box-shadow: 0px 0px 15px -2px black;         -moz-box-shadow: 0px 0px 15px -2px black;         box-shadow: 0px 0px 15px -2px black;     }     .draw_tool, .graphic_clear{         display: inline-block;         height: 78%;         margin-top: .3em;         font-size: .9em;         font-weight: bold;     }     #map {         position: absolute;         top: 5em;         bottom: 0;         left: 0;         right: 0;     }     .float_right {         float: right;     }      .results {         display: none;         position: absolute;         top: 30px;         left: 80px;         padding: 10px;         width: 200px;         height: 60px;         background: #fff;         z-index: 100;          -moz-border-radius: 1px;         -webkit-border-radius: 1px;         border-radius: 1px;          -webkit-box-shadow: 0 0 0 10px rgba(34,34,34,.6);         -moz-box-shadow: 0 0 0 10px rgba(34,34,34,.6);         box-shadow: 0 0 0 10px rgba(34,34,34,.6);         *box-shadow: 0 0 0 10px rgba(154,179,199,.8);     }   </style> <script src="http://js.arcgis.com/3.6/"></script> <script src='http://code.jquery.com/jquery.min.js'></script> <script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>  <script> var map, toolbar, geometryService, returnedGraphics;  require([     "dojo/_base/connect",     "dojo/parser",     "dijit/registry",     "dojo/ready",      "esri/config",     "esri/map",     "esri/toolbars/draw",     "esri/graphic",     "esri/geometry/Geometry",     "esri/geometry/webMercatorUtils",     "esri/tasks/GeometryService",     "esri/tasks/AreasAndLengthsParameters",     "esri/tasks/LengthsParameters",       "esri/symbols/SimpleMarkerSymbol",     "esri/symbols/SimpleLineSymbol",     "esri/symbols/SimpleFillSymbol",       "dijit/layout/BorderContainer",     "dijit/layout/ContentPane",      "dojo/domReady!"  ], function (         connect,         parser,         registry,         ready,          esriConfig,         Map,         Draw,         Graphic,         Geometry,         webMercatorUtils,         GeometryService,         AreasAndlengthsParameters,         LengthsParameters,         SimpleMarkerSymbol,         SimpleLineSymbol,         SimpleFillSymbol) {      ready(function () {                      //beginning of ready function         parser.parse();         esriConfig.defaults.io.proxyUrl = "proxy.php";         esriConfig.defaults.io.alwaysUseProxy = false;          map = new Map("map", {             basemap: "topo",             center: [-105.2404, 39.3396],             zoom: 8         });          console.log("Map Created!");          map.on("load", function(){             $(document).ready(jQueryReady);         });     }); //end of ready function      function jQueryReady(){         map.graphics.clear();          //create toolbar         toolbar = new Draw(map);         toolbar.on("draw-end", addToMap);          //add Geometry Service         geometryService = new GeometryService("http://tasks.arcgisonline.com/ArcGIS/rest/services/Geometry/GeometryServer");         geometryService.on("areas-and-lengths-complete", outputAreaAndLength);         geometryService.on("lengths-complete", outputLength);           $('.draw_tool').button().click(function(){             var tool = $(this).text().toUpperCase().replace(/ /g, "_");             toolbar.activate(Draw[tool]);             map.hideZoomSlider();         });          $('.graphic_clear').button().click(function(event){             map.graphics.clear();             $('.results').html('').hide();              map.showZoomSlider();             toolbar.deactivate();         });     }      function addToMap(event) {         var symbol;         map.showZoomSlider();         var geom = event.geometry;         switch (geom.type) {             case "point":                 symbol = new SimpleMarkerSymbol();                 var pointGeographic = webMercatorUtils.xyToLngLat(geom.x, geom.y, true);                 $('.results').html('Latitude = ' + pointGeographic[0].toFixed(2) + '&#176;' + '</br>' + 'Longitude = ' + pointGeographic[1].toFixed(2) + '&#176;').show();                 break;              case "polyline":                 symbol = new SimpleLineSymbol();                 //get length                 var lengthParams = new LengthsParameters();                 lengthParams.polylines = [geom];                 lengthParams.lengthUnit = GeometryService.UNIT_STATUTE_MILE;                 lengthParams.geodesic = true;                 geometryService.lengths(lengthParams);                 break;              default:                 symbol = new SimpleFillSymbol();                 //get length and area                 var areasAndLengthParams = new AreasAndlengthsParameters();                 areasAndLengthParams.lengthUnit = GeometryService.UNIT_STATUTE_MILE;                 areasAndLengthParams.areaUnit = GeometryService.UNIT_SQUARE_MILES;                 geometryService.simplify([geom], function(simplifiedGeometries) {                     areasAndLengthParams.polygons = simplifiedGeometries;                     geometryService.areasAndLengths(areasAndLengthParams);                 });                 break;         }         var graphic = new Graphic(geom, symbol);         map.graphics.add(graphic);     }     function outputAreaAndLength(event) {         var result = event.result;         console.log(JSON.stringify(result));         $('.results').html("Length = " + result.lengths[0].toFixed(2) + " Miles<br/>" + "Area = " + result.areas[0].toFixed(2) + " Sq. Miles").show();     }     function outputLength(event) {         var result = event.result;         console.log(JSON.stringify(result));         $('.results').html("Length = " + result.lengths[0].toFixed(2) + " Miles").show();     }  }); //end of require    </script>  </head> <body> <header>     <div class='title_bar'>         <h2>Draw and Measure</h2>     </div>     <div class='draw_toolbar'>         <button class='draw_tool'>Point</button>         <button class='draw_tool'>Polyline</button>         <button class='draw_tool'>Polygon</button>         <button class='draw_tool'>Freehand Polyline</button>         <button class='draw_tool'>Freehand Polygon</button>         <button class='draw_tool'>Rectangle</button>         <button class='draw_tool'>Triangle</button>         <button class='draw_tool'>Circle</button>          <button class='graphic_clear float_right'>Clear</button>     </div> </header> <div id='map'>     <div class="results"></div> </div> </body> </html>
0 Kudos
1 Solution

Accepted Solutions
BenFousek
Occasional Contributor III
I assume the geometry you are sending to geometry service is in web mercator.  Web mercator does not preserve areas very well.  It is after all a projection of the entire planet.  Large area projections either preserve shape, area or some mix thereof depending on the area of interest.

There are 2 fixes to your problem:
1) Re-project the geometry into a local spatial reference like state plane and then send that geometry to the geometry service.

2) Use the geodesic utilities of the esri.geometry namespace https://developers.arcgis.com/en/javascript/jsapi/namespace_geometry-amd.html.  This is what the api measure widget uses for mercator and geographic inputs.  Here's an example where geom is a polyline in web mercator.

var length = esri.geometry.geodesicLengths([esri.geometry.webMercatorToGeographic(geom)], esri.Units.FEET); results.set('content', '<b>Length</b><br />' + (Math.round(length[0] * 100) / 100) + ' feet<br />' + (Math.round((length[0] / 5280) * 100) / 100) + ' miles')

View solution in original post

0 Kudos
3 Replies
ZachLiu1
Occasional Contributor II
http://developers.arcgis.com/en/javascript/samples/util_measurepoly/

I tested this example and it also seems to over measure lengths and areas.

Then I tried to use another Geometry service, the result is the same.
0 Kudos
BenFousek
Occasional Contributor III
I assume the geometry you are sending to geometry service is in web mercator.  Web mercator does not preserve areas very well.  It is after all a projection of the entire planet.  Large area projections either preserve shape, area or some mix thereof depending on the area of interest.

There are 2 fixes to your problem:
1) Re-project the geometry into a local spatial reference like state plane and then send that geometry to the geometry service.

2) Use the geodesic utilities of the esri.geometry namespace https://developers.arcgis.com/en/javascript/jsapi/namespace_geometry-amd.html.  This is what the api measure widget uses for mercator and geographic inputs.  Here's an example where geom is a polyline in web mercator.

var length = esri.geometry.geodesicLengths([esri.geometry.webMercatorToGeographic(geom)], esri.Units.FEET); results.set('content', '<b>Length</b><br />' + (Math.round(length[0] * 100) / 100) + ' feet<br />' + (Math.round((length[0] / 5280) * 100) / 100) + ' miles')
0 Kudos
ZachLiu1
Occasional Contributor II
This really answers my question, thanks very much. I missed that part!

I add this to my code and it works as expected.

areasAndLengthParams.calculationType = 'geodesic';
0 Kudos