Radius of a circle drawn as a polygon

7998
15
08-05-2013 11:10 AM
KunalMehta
New Contributor II
All,

I found this solution to draw a circle as a polygon of 360 points:

-(AGSPolygon*) circleWithCenter:(AGSPoint*)point raduis:(double)radius
{
    AGSMutablePolygon * p = [AGSMutablePolygon new];
    [p addRingToPolygon];
    int pointsCount = 360;
    double slice = 2 * M_PI / pointsCount;
    for(int i = 0; i<pointsCount;i++){
        double rad = slice * i;
        double px = point.x + radius * cos(rad);
        double py = point.y + radius * sin(rad);
        [p addPointToRing:[AGSPoint pointWithX:px y:py spatialReference:[AGSSpatialReference spatialReferenceWithWKID:4326]]];
    }
   
    return p;
}


The question I have is what is the units of the radius passed in? For example, if I want to draw a circle of raduis 10 miles, what value do I pass in? If I pass in 10, it just draws a really huge circle with radius several multiples of 10!

I tried to look into the sdk api docs and could not find much info, so any help will be appreciated.
0 Kudos
15 Replies
DiveshGoyal
Esri Regular Contributor
Since you're using the WGS84 spatial reference, the value 10 is assumed to be in decimal degrees.
At the equator, 1 degree = 69 miles (approx).
So if you want 10 miles, you should use 10/69 as radius
0 Kudos
Nicholas-Furness
Esri Regular Contributor
The units will be decimal degrees.

What you might find useful is to buffer around your center-point.

Try something like this:

-(AGSPolygon*) circleWithCenter:(AGSPoint*)point radius:(double)radiusInMiles
{
    AGSGeometryEngine *ge = [AGSGeometryEngine defaultGeometryEngine];

    // Get a point in a spatial reference whose unit is Meters.
    AGSPoint *pointToBufferAround = (AGSPoint *)[ge projectGeometry:point toSpatialReference:[AGSSpatialReference webMercatorSpatialReference]];

    // Get a "circle" around it, using the unit Meters (1 mile ~= 1609.344 meters).
    AGSPolygon *circle = [ge bufferGeometry:pointToBufferAround byDistance:radiusInMiles*1609.344];

    // Project the output back to the projection of the input point (which is presumably what you're after).
    circle = (AGSPolygon *)[ge projectGeometry:circle toSpatialReference:point.spatialReference];

    return circle;
}


That will use Meters (WebMercator's units). Note that your output may not be what you expect as the input or output approaches the poles. Also, there's no guarantee how many points the output polygon will have in its boundary.
0 Kudos
KunalMehta
New Contributor II
Thanks nfurness and Technobrat for your solutions.

Both work, however, per nfurness's solution using buffer around the center point and webMercatorSpatialReference, I am getting an oval shape rather than a circle. Is this expected?
0 Kudos
Nicholas-Furness
Esri Regular Contributor
Could be. Depends on what the projection is of your map. I'm guessing it's not Web Mercator 😄

My solution will generate you a polygon that is pretty close to circular on the ground (i.e the edge is R miles north and R miles east of the center). If you are using a Web Mercator projection, it will appear circular on the map. If you are using WGS84 for example, it will indeed appear less tall as you move north or south from the equator, by virtue of the projection.

If you want something that looks circular on a WGS84 map, and you know that your center point is WGS84, you can either use your & Technobrat's solution or something like this:

-(AGSPolygon*) circleWithCenter:(AGSPoint*)point radius:(double)radiusInMiles
{
    AGSGeometryEngine *ge = [AGSGeometryEngine defaultGeometryEngine];

    // Get a "circle" around the point, using the 1 decimal degree ~= 69 miles.
    AGSPolygon *circle = [ge bufferGeometry:point byDistance:radiusInMiles/69];

    return circle;
}


But remember that this is approximating the conversion between decimal degrees and meters, which is not constant for longitude as latitude varies.

So, it really depends on what you are generating the 10 mile radius geometry for and (in short) how far north or south of the equator. Your solution may be good enough for what you want, but bear in mind that a degree of longitude will be very much less than 69 miles as you approach the poles. On the other hand, your application users may keep asking you why the "circle" is elliptical, and that might be a bigger headache than considering some things that might be more than 10 miles away.
0 Kudos
SjorsProvoost
New Contributor II
How do I go about adding this to a graphics layer?
0 Kudos
AkhilChoran_Balan
New Contributor

You can add this to a graphics layer by converting it into a AGSGraphic object.

Something like below code will help for that:

[self.graphicsLayer addGraphic:[AGSGraphic graphicWithGeometry: [self circleWithCenter:point radius:10.0f] symbol:[AGSSimpleLineSymbol simpleLineSymbol] attributes:nil]];

0 Kudos
FancyAugustin
New Contributor II

Can i get the solution for arcgis javascript api for same?

0 Kudos
Nicholas-Furness
Esri Regular Contributor

You can do the same thing.

Use the JavaScript GeometryEngine. See geometryEngine GeodesicBuffer which will give you a circular polygon back around the center point, and you can then use Graphic and GraphicsLayer to add these to your MapView.

0 Kudos
FancyAugustin
New Contributor II

Sorry i didnt get you.The code below is used for drawing circle and polygon.From this how can we get the radius of the circle which we draw.

require([
"esri/map", "esri/toolbars/draw",
"esri/symbols/SimpleMarkerSymbol", "esri/symbols/SimpleLineSymbol",
"esri/symbols/SimpleFillSymbol", "esri/geometry/webMercatorUtils", "esri/symbols/CartographicLineSymbol",
"esri/graphic",
"esri/Color", "dojo/dom", "dojo/on", "dojo/domReady!"
], function(
Map, Draw,
SimpleMarkerSymbol, SimpleLineSymbol,
SimpleFillSymbol, WebMercatorUtils, CartographicLineSymbol,
Graphic,
Color, dom, on
) {
var circleArray = [];
map = new Map("mapDiv", {
basemap: "streets",
center: [-148.6234374999831, 62.10143229517362],
zoom: 3
});
map.on("load", initToolbar);


// fill symbol used for extent, polygon and freehand polygon, use a picture fill symbol
// the images folder contains additional fill images, other options: sand.png, swamp.png or stiple.png
var fillSymbol = new SimpleFillSymbol(SimpleFillSymbol.STYLE_SOLID,
new SimpleLineSymbol(SimpleLineSymbol.STYLE_DASH,
new Color([255, 0, 0]), 2), new Color([255, 255, 0, 0.25])
);

function initToolbar() {
tb = new Draw(map);
tb.on("draw-end", addGraphic);

// event delegation so a click handler is not
// needed for each individual button
on(dom.byId("info"), "click", function(evt) {
if (evt.target.id === "info") {
return;
}
var tool = evt.target.id.toLowerCase();
map.disableMapNavigation();
tb.activate(tool);
});
}

function postGraphic(geometry) {
$("#vertices").text("");

if (geometry.type === 'polygon') {
//Our polygons should have only one ring

var polygon = geometry.rings[0];
var vertices = "";

var latLong = [];
for (var i = 0; i < polygon.length; i++) {
if (geometry.spatialReference.isWebMercator()) {
//Convert to traditional decimal degrees
longLat = WebMercatorUtils.xyToLngLat(polygon[0], polygon[1]);
} else
longLat = [polygon[0], polygon[1]];

x = longLat[0];
y = longLat[1];
var objLatLong = {};
objLatLong.long = longLat[0];
objLatLong.lat = longLat[1];
latLong.push(objLatLong)

//Convert the vertices to the format x,y;x,y;
vertices = vertices + x + ',' + y + ';';
}

$("#vertices").text(vertices);
}
}
var circleFlag = false;
var polygonFlag = false;
$('#Circle').on("click", function() {
circleFlag = true;
polygonFlag = false;
})
;
$('#Polygon').on("click", function() {
circleFlag = false;
polygonFlag = true;
})
;
function addGraphic(evt) {
//deactivate the toolbar and clear existing graphics
tb.deactivate();
map.enableMapNavigation();

// figure out which symbol to use
var symbol;

symbol = fillSymbol;
var graphic = new Graphic(evt.geometry, symbol);
map.graphics.add(graphic);
postGraphic(evt.geometry);
}
});

0 Kudos