Common_WebMappingApp_CSharp\Measure.ascx.cs
// Copyright 2011 ESRI // // All rights reserved under the copyright laws of the United States // and applicable international laws, treaties, and conventions. // // You may freely redistribute and use this sample code, with or // without modification, provided you include the original copyright // notice and use restrictions. // // See the use restrictions. // using System; using System.Data; using System.Configuration; using System.Collections; using System.Collections.Specialized; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; using ESRI.ArcGIS.ADF.Web; using ESRI.ArcGIS.ADF.Web.UI.WebControls; using ESRI.ArcGIS.ADF.Web.DataSources; using ESRI.ArcGIS.ADF.Web.Geometry; namespace WebMapApp { public enum MapUnit { Resource_Default, Degrees, Feet, Meters } public enum MeasureUnit { Feet, Kilometers, Meters, Miles } public enum AreaUnit { Acres, Sq_Feet, Sq_Kilometers, Sq_Meters, Sq_Miles, Hectares } public partial class Measure : System.Web.UI.UserControl, IPostBackEventHandler, ICallbackEventHandler { MapResourceManager _resourceManger; public string CallbackInvocation = ""; private string _mapBuddyId = "Map1"; private string _id; private Map _map; private MapUnit _fallbackMapUnit = MapUnit.Degrees; // fallback value used if resource value is not available private MapUnit _mapUnits; private MapUnit _startMapUnits = MapUnit.Degrees; private MeasureUnit _measureUnits = MeasureUnit.Miles; private AreaUnit _areaUnits = AreaUnit.Sq_Miles; private double _numberDecimals = 4; protected void Page_Load(object sender, EventArgs e) { _id = this.ClientID; Page page = this.Page; // find the map control if (_mapBuddyId == null || _mapBuddyId.Length == 0) _mapBuddyId = "Map1"; _map = page.FindControl(_mapBuddyId) as Map; // find the map resource manager _resourceManger = page.FindControl(_map.MapResourceManager) as MapResourceManager; CallbackInvocation = CallbackFunctionString; } protected internal virtual string CallbackFunctionString { get { if(ScriptManager.GetCurrent(Page) != null) return String.Format("__esriDoPostBack('{0}','{1}', argument, ESRI.ADF.System.ProcessMSAjaxCallbackResult, context)", this.UniqueID, this.ClientID); else return Page.ClientScript.GetCallbackEventReference(this, "argument", "ESRI.ADF.System.processCallbackResult", "context", "postBackError", false); } } protected void Page_PreRender(object sender, EventArgs e) { MeasureDisplayPanel.InnerHtml = WriteInitialContents(); ScriptManager sm = ScriptManager.GetCurrent(this.Page); if(sm != null) sm.RegisterAsyncPostBackControl(this); } #region Action Methods IMapFunctionality _mapFunctionality; private IMapFunctionality MapFunctionality { get { if (_mapFunctionality == null) { // use the primary resouce, if defined string primeResource = _map.PrimaryMapResource; IGISResource resource = null; if (primeResource != null && primeResource.Length > 0) resource = _resourceManger.GetResource(primeResource); if (resource == null) { for (int i = 0; i < _resourceManger.ResourceItems.Count; i++) { resource = _resourceManger.GetResource(i); if (resource != null) break; } } if (resource != null) _mapFunctionality = (IMapFunctionality)resource.CreateFunctionality(typeof(IMapFunctionality), "mapFunctionality"); } return _mapFunctionality; } } private string ProcessMeasureRequest(NameValueCollection queryString) { object o = Session["MeasureMapUnits"]; if (o != null) _mapUnits = (MapUnit)Enum.Parse(typeof(MapUnit), o.ToString()); else if (_startMapUnits == MapUnit.Resource_Default) _mapUnits = GetResourceDefaultMapUnit(); else _mapUnits = _startMapUnits; bool forceRefresh = queryString["refresh"] == "true"; string eventArg = queryString["EventArg"]; string vectorAction = queryString["VectorMode"]; string[] coordPairs, xys; string coordString = queryString["coords"]; if (coordString == null && coordString.Length == 0) coordString = ""; coordPairs = coordString.Split(char.Parse("|")); string mapUnitString = queryString["MapUnits"]; if (mapUnitString != null && mapUnitString.Length > 0) _mapUnits = (MapUnit)Enum.Parse(typeof(MapUnit), mapUnitString); Session["MeasureMapUnits"] = _mapUnits; string measureUnitString = queryString["MeasureUnits"]; if (measureUnitString != null && measureUnitString.Length > 0) _measureUnits = (MeasureUnit)Enum.Parse(typeof(MeasureUnit), measureUnitString); string areaUnitstring = queryString["AreaUnits"]; if (areaUnitstring != null && areaUnitstring.Length > 0) _areaUnits = (AreaUnit)Enum.Parse(typeof(AreaUnit), areaUnitstring); string response = null; PointCollection points = new PointCollection(); PointCollection dPoints = new PointCollection(); ArrayList distances = new ArrayList(); double totalDistance = 0; double segmentDistance = 0; double area = 0; double perimeter = 0; double roundFactor = Math.Pow(10, _numberDecimals); double xD, yD, tempDist, tempDist2, tempArea, x1, x2, y1, y2; if (vectorAction == "measure") { if (coordPairs != null && coordPairs.Length > 1) { for (int cp = 0; cp < coordPairs.Length; cp++) { xys = coordPairs[cp].Split(char.Parse(":")); points.Add(new Point(Convert.ToDouble(xys[0], System.Globalization.CultureInfo.InvariantCulture), Convert.ToDouble(xys[1], System.Globalization.CultureInfo.InvariantCulture))); if (cp > 0) { // check for duplicate points from double click.... Firefox will send coords for both clicks, causing segmentDistance to be zero. if (points[cp - 1].X != points[cp].X || points[cp - 1].Y != points[cp].Y) { if (_mapUnits == MapUnit.Degrees) { // use great circle formula tempDist = DegreeToFeetDistance(points[cp - 1].X, points[cp - 1].Y, points[cp].X, points[cp].Y); y1 = DegreeToFeetDistance(points[cp].X, points[cp].Y, points[cp].X, 0); x1 = DegreeToFeetDistance(points[cp].X, points[cp].Y, 0, points[cp].Y); dPoints.Add(new Point(x1, y1)); segmentDistance = ConvertUnits(tempDist, MapUnit.Feet, _measureUnits); } else { // get third side of triangle for distance xD = Math.Abs(points[cp].X - points[cp - 1].X); yD = Math.Abs(points[cp].Y - points[cp - 1].Y); tempDist = Math.Sqrt(Math.Pow(xD, 2) + Math.Pow(yD, 2)); segmentDistance = ConvertUnits(tempDist, _mapUnits, _measureUnits); } distances.Add(segmentDistance); totalDistance += segmentDistance; segmentDistance = Math.Round(segmentDistance * roundFactor) / roundFactor; totalDistance = Math.Round(totalDistance * roundFactor) / roundFactor; } } else { if (_mapUnits == MapUnit.Degrees) { y1 = DegreeToFeetDistance(points[cp].X, points[cp].Y, points[cp].X, 0); x1 = DegreeToFeetDistance(points[cp].X, points[cp].Y, 0, points[cp].Y); dPoints.Add(new Point(x1, y1)); } } } } if (eventArg == "polygon") { if (points.Count > 2 || forceRefresh) { if (_mapUnits == MapUnit.Degrees) { tempDist = DegreeToFeetDistance(points[points.Count - 1].X, points[points.Count - 1].Y, points[0].X, points[0].Y); tempDist2 = ConvertUnits(tempDist, MapUnit.Feet, _measureUnits); distances.Add(tempDist2); dPoints.Add(dPoints[0]); } else { xD = Math.Abs(points[points.Count - 1].X - points[0].X); yD = Math.Abs(points[points.Count - 1].Y - points[0].Y); tempDist = Math.Sqrt(Math.Pow(xD, 2) + Math.Pow(yD, 2)); tempDist2 = ConvertUnits(tempDist, _mapUnits, _measureUnits); distances.Add(tempDist2); } points.Add(points[0]); perimeter = totalDistance + tempDist2; // add area calculation tempArea = 0; MapUnit mUnits = _mapUnits; double xDiff, yDiff; if (_mapUnits == MapUnit.Degrees) { points = dPoints; mUnits = MapUnit.Feet; } for (int j = 0; j < points.Count - 1; j++) { x1 = Convert.ToDouble(points[j].X, System.Globalization.CultureInfo.InvariantCulture); x2 = Convert.ToDouble(points[j + 1].X, System.Globalization.CultureInfo.InvariantCulture); y1 = Convert.ToDouble(points[j].Y, System.Globalization.CultureInfo.InvariantCulture); y2 = Convert.ToDouble(points[j + 1].Y, System.Globalization.CultureInfo.InvariantCulture); xDiff = x2 - x1; yDiff = y2 - y1; tempArea += x1 * yDiff - y1 * xDiff; } tempArea = Math.Abs(tempArea) / 2; area = ConvertAreaUnits(tempArea, mUnits, _areaUnits); perimeter = Math.Round(perimeter * roundFactor) / roundFactor; area = Math.Round(area * roundFactor) / roundFactor; } else response = String.Format( "<table cellspacing='0' ><tr><td>Perimeter: </td><td align='right' id=\"tdperimiter\"> 0</td><td >{0}</td></tr><tr><td>Area:</td><td align='right' id=\"tdarea\">0 </td><td>{1}</td></tr></table>", WriteMeasureUnitDropDown(), WriteAreaUnitDropDown()); } else if (eventArg == "polyline") { if (points.Count < 3 || forceRefresh) response = String.Format("<table cellspacing='0' ><tr><td>Segment: </td><td align='right' id=\"tdsegment\">{0} </td><td>{1}</td></tr><tr><td>Total Length:</td><td align='right' id=\"tdtotaldistance\">{2} </td><td>{3}</td></tr></table>", String.Format("{0}",segmentDistance), _measureUnits, String.Format("{0}",totalDistance), WriteMeasureUnitDropDown()); } else if (eventArg == "point" && coordPairs.Length > 0) { xys = coordPairs[0].Split(char.Parse(":")); response = String.Format("<table cellspacing='0' ><tr><td>X Coordinate:</td><td align='right' dir='ltr'>{0}</td></tr><tr><td>Y Coordinate:</td><td align='right' dir='ltr'>{1}</td></tr></table>", (Math.Round(Convert.ToDouble(xys[0], System.Globalization.CultureInfo.InvariantCulture) * roundFactor) / roundFactor).ToString(), (Math.Round(Convert.ToDouble(xys[1], System.Globalization.CultureInfo.InvariantCulture) * roundFactor) / roundFactor).ToString()); } } CallbackResultCollection coll = new CallbackResultCollection(); coll.Add(new CallbackResult("", "", "invoke", "measureComplete", new object[] { response, _id, String.Format("{0}", area), String.Format("{0}", perimeter), String.Format("{0}", segmentDistance), String.Format("{0}", totalDistance) })); return coll.ToString(); } private string CheckFormMeasureUnits(string unit) { string response = ""; if (unit == _measureUnits.ToString()) response = "selected=\"selected\""; return response; } private string CheckFormAreaUnits(string unit) { string response = ""; if (unit == _areaUnits.ToString()) response = "selected=\"selected\""; return response; } private string WriteMeasureUnitDropDown() { System.Text.StringBuilder sb = new System.Text.StringBuilder(); sb.Append("<select id=\"MeasureUnits2\" onchange=\"changeMeasureUnits()\" style=\"font: normal 7pt Verdana; width: 100px;\">"); Array mArray = Enum.GetValues(typeof(MeasureUnit)); foreach (MeasureUnit mu in mArray) { sb.AppendFormat("<option value=\"{0}\" {1}>{0}</option>", mu, CheckFormMeasureUnits(mu.ToString())); } sb.Append("</select>"); return sb.ToString(); } private string WriteAreaUnitDropDown() { System.Text.StringBuilder sb = new System.Text.StringBuilder(); sb.Append("<select id=\"AreaUnits2\" onchange=\"changeAreaUnits()\" style=\"font: normal 7pt Verdana; width: 100px;\">"); Array aArray = Enum.GetValues(typeof(AreaUnit)); foreach (AreaUnit au in aArray) { sb.AppendFormat("<option value=\"{0}\" {1}>{0}</option>", au, CheckFormAreaUnits(au.ToString())); } sb.Append("</select>"); return sb.ToString(); } private string WriteInitialContents() { System.Text.StringBuilder sb = new System.Text.StringBuilder(); // set up tool buttons sb.Append("<table cellpadding='0' cellspacing='0' ><tr>\n"); sb.Append("<td id='MeasureToolbarButton_point' style='border: solid White 1px; background-color: White;' onmouseover='this.style.cursor=\"pointer\"; this.style.borderColor=\"Black\";' onmouseout='checkMeasureToolbarBorder(this, \"point\")' onmousedown='setMeasureToolbarTool(\"point\")'><img id='ToolbarImage_point' src='images/measure-point.png' align='middle' alt='Point - Coordinates' title='Point - Coordinates' style='padding: 0px 0px 0px 0px' /></td>\n"); sb.Append("<td id='MeasureToolbarButton_polyline' style='border: solid Black 1px; background-color: #EEEEEE;' onmouseover='this.style.cursor=\"pointer\";this.style.borderColor=\"Black\";' onmouseout='checkMeasureToolbarBorder(this, \"polyline\")' onmousedown='setMeasureToolbarTool(\"polyline\")'><img id='ToolbarImage_polyline' src='images/measure-line.png' align='middle' alt='Line - Distance' title='Line - Distance' style='padding: 0px 0px 0px 0px' /></td>\n"); sb.Append("<td id='MeasureToolbarButton_polygon' style='border: solid White 1px; background-color: White;' onmouseover='this.style.cursor=\"pointer\";this.style.borderColor=\"Black\";' onmouseout='checkMeasureToolbarBorder(this, \"polygon\")' onmousedown='setMeasureToolbarTool(\"polygon\")'><img id='ToolbarImage_polygon' src='images/measure-poly.png' align='middle' alt='Polygon - Area' title='Polygon - Area' style='padding: 0px 0px 0px 0px' /> </td>\n"); sb.Append("</tr></table>\n"); sb.AppendFormat("<input id='MeasureUnits' type='hidden' value='{0}'/>\n", MeasureUnits); sb.AppendFormat(" <input id='AreaUnits' type='hidden' value='{0}'/>\n", AreaUnits); // create display area sb.Append("<table id='MeasureToolbarTable' cellspacing='2' cellpadding='1' style=' width: 100%;font: normal 7pt Verdana; '>\n"); sb.Append("<tr><td style='background-color: #ffffff' id='MeasureDisplay' colspan='2' valign='top'>\n"); sb.Append("Click on the map and draw a line. Double-click to end line.\n"); sb.Append("</td></tr>\n"); sb.Append("</table>\n"); return sb.ToString(); } #endregion #region Conversion Methods private double ConvertUnits(double distance, MapUnit fromUnits, MeasureUnit toUnits) { double mDistance = distance; if (fromUnits == MapUnit.Feet) { if (toUnits == MeasureUnit.Miles) { mDistance = distance / 5280; } else if (toUnits == MeasureUnit.Meters) { mDistance = distance * 0.304800609601; } else if (toUnits == MeasureUnit.Kilometers) { mDistance = distance * 0.0003048; } } else { if (toUnits == MeasureUnit.Miles) { mDistance = distance * 0.0006213700922; } else if (toUnits == MeasureUnit.Feet) { mDistance = distance * 3.280839895; } else if (toUnits == MeasureUnit.Kilometers) { mDistance = distance / 1000; } } return mDistance; } private double ConvertAreaUnits(double area, MapUnit baseUnits, AreaUnit toUnits) { double mArea = area; if (baseUnits == MapUnit.Feet) { if (toUnits == AreaUnit.Acres) mArea = area * 0.000022956; else if (toUnits == AreaUnit.Sq_Meters) mArea = area * 0.09290304; else if (toUnits == AreaUnit.Sq_Miles) mArea = area * 0.00000003587; else if (toUnits == AreaUnit.Sq_Kilometers) mArea = area * 0.09290304 / 1000000; else if (toUnits == AreaUnit.Hectares) mArea = area * 0.09290304 * 0.0001; } else if (baseUnits == MapUnit.Meters) { if (toUnits == AreaUnit.Acres) mArea = area * 0.0002471054; else if (toUnits == AreaUnit.Sq_Miles) mArea = area * 0.0000003861003; else if (toUnits == AreaUnit.Sq_Kilometers) mArea = area * 1.0e-6; else if (toUnits == AreaUnit.Sq_Feet) mArea = area * 10.76391042; else if (toUnits == AreaUnit.Hectares) mArea = area * 0.0001; } return mArea; } private double DegreeToFeetDistance(double x1, double y1, double x2, double y2) { // use great circle formula double Lat1 = DegToRad(y1); double Lat2 = DegToRad(y2); double Lon1 = DegToRad(x1); double Lon2 = DegToRad(x2); double LonDist = Lon1 - Lon2; double LatDist = Lat1 - Lat2; double x = Math.Pow(Math.Sin(LatDist / 2), 2) + Math.Cos(Lat1) * Math.Cos(Lat2) * Math.Pow(Math.Sin(LonDist / 2), 2); x = 2 * Math.Asin(Math.Min(1, Math.Sqrt(x))); x = (3963 - 13 * Math.Sin((Lat1 + Lat2) / 2)) * x; // in miles... convert to feet and use that as base return (x * 5280); } private double DegToRad(double degrees) { return degrees * Math.PI / 180.0; } private MapUnit GetResourceDefaultMapUnit() { MapUnit mUnit = MapUnit.Degrees; try { Units mu = MapFunctionality.Units; if (mu == Units.DecimalDegrees) mUnit = MapUnit.Degrees; else if (mu == Units.Feet) mUnit = MapUnit.Feet; else if (mu == Units.Meters) mUnit = MapUnit.Meters; } catch { // cannot get units from resource... default to fallback value set in declaration mUnit = _fallbackMapUnit; } return mUnit; } /// <summary> /// Whether map has at least one resource that supports returning map units /// </summary> /// <returns></returns> public bool CanGetUnits() { foreach (ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality mapFunc in _map.GetFunctionalities()) { try { Units units = mapFunc.Units; return true; } catch { } } return false; } #endregion #region IPostBackEventHandler Members public void RaisePostBackEvent(string eventArgument) { string strResult = processPostbackEvent(eventArgument); if (!String.IsNullOrEmpty(strResult)) ScriptManager.GetCurrent(this.Page).RegisterDataItem(this, strResult, false); } private string processPostbackEvent(string requestString) { // break out the responseString into a querystring Array keyValuePairs = requestString.Split("&".ToCharArray()); NameValueCollection queryString = new NameValueCollection(); Page page = this.Page; Map map = page.FindControl(this._mapBuddyId) as Map; string[] keyValue; string response = ""; if (keyValuePairs.Length > 0) { for (int i = 0; i < keyValuePairs.Length; i++) { keyValue = keyValuePairs.GetValue(i).ToString().Split("=".ToCharArray()); queryString.Add(keyValue[0], keyValue[1]); } } else { keyValue = requestString.Split("=".ToCharArray()); if (keyValue.Length > 0) queryString.Add(keyValue[0], keyValue[1]); } // isolate control type and mode string controlType = queryString["ControlType"]; string eventArg = queryString["EventArg"]; if (controlType == null) controlType = "Map"; switch (controlType) { case "Map": // request is for the map control string vectorMode = queryString["VectorMode"]; if (vectorMode != null && vectorMode == "measure") { response = ProcessMeasureRequest(queryString); } break; default: // break; } return response; } #endregion #region Properties public string Id { get { return _id; } set { _id = value; } } private MapResourceManager MapResourceManager { get { return _resourceManger; } set { _resourceManger = value; } } /// <summary> /// Id of Buddy MapControl /// </summary> public string MapBuddyId { get { return _mapBuddyId; } set { _mapBuddyId = value; } } /// <summary> /// Unit used resource. Resource_Default will return value from resource, if available. Other values will force calculations to use that unit. /// </summary> public MapUnit MapUnits { get { return _startMapUnits; } set { _startMapUnits = value; } } /// <summary> /// Unit used in display of linear measurements. /// </summary> public MeasureUnit MeasureUnits { get { return _measureUnits; } set { _measureUnits = value; } } /// <summary> /// Area Units - Unit used in display of area measurements. /// </summary> public AreaUnit AreaUnits { get { return _areaUnits; } set { _areaUnits = value; } } // Number of Decimals - Number of decimal digits displayed in measurements. public double NumberDecimals { get { return _numberDecimals; } set { _numberDecimals = value; } } public override bool Visible { get { return base.Visible; } } public override bool EnableTheming { get { return base.EnableTheming; } } public override bool EnableViewState { get { return base.EnableViewState; } } #endregion #region ICallbackEventHandler Members private string callbackArg; public string GetCallbackResult() { return processPostbackEvent(callbackArg); } public void RaiseCallbackEvent(string eventArgument) { callbackArg = eventArgument; } #endregion } }