Common_WebMappingApp_VBNet\Measure.ascx.vb
' Copyright 2010 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. ' Imports System Imports System.Data Imports System.Configuration Imports System.Collections Imports System.Collections.Specialized Imports System.Web Imports System.Web.Security Imports System.Web.UI Imports System.Web.UI.WebControls Imports System.Web.UI.WebControls.WebParts Imports System.Web.UI.HtmlControls Imports ESRI.ArcGIS.ADF.Web Imports ESRI.ArcGIS.ADF.Web.UI.WebControls Imports ESRI.ArcGIS.ADF.Web.DataSources Imports ESRI.ArcGIS.ADF.Web.Geometry Namespace WebMapApp Public Enum MapUnit Resource_Default Degrees Feet Meters End Enum 'MapUnit Public Enum MeasureUnit Feet Kilometers Meters Miles End Enum 'MeasureUnit Public Enum AreaUnit Acres Sq_Feet Sq_Kilometers Sq_Meters Sq_Miles Hectares End Enum 'AreaUnit Partial Class Measure Inherits System.Web.UI.UserControl Implements IPostBackEventHandler Implements ICallbackEventHandler Private _resourceManger As MapResourceManager Private _mapFunctionality As IMapFunctionality Public CallbackInvocation As String = "" Private _mapBuddyId As String = "Map1" Private _id As String Private _map As Map Private _mapUnits As MapUnit = MapUnit.Degrees Private _startMapUnits As MapUnit = MapUnit.Degrees Private _fallbackMapUnits As MapUnit = MapUnit.Degrees Private _measureUnits As MeasureUnit = MeasureUnit.Miles Private _areaUnits As AreaUnit = AreaUnit.Sq_Miles Private _numberDecimals As Double = 4 Private callbackArg As String Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) _id = Me.ClientID Dim page As Page = Me.Page ' find the map control If (_mapBuddyId Is Nothing Or _mapBuddyId.Length = 0) Then _mapBuddyId = "Map1" End If _map = page.FindControl(_mapBuddyId) ' find the map resource manager _resourceManger = page.FindControl(_map.MapResourceManager) CallbackInvocation = CallbackFunctionString End Sub Protected ReadOnly Property CallbackFunctionString() As String Get If ScriptManager.GetCurrent(Page) Is Nothing Then Return Page.ClientScript.GetCallbackEventReference(Me, "argument", "ESRI.ADF.System.processCallbackResult", "context", "postBackError", False) Else Return String.Format("__esriDoPostBack('{0}','{1}', argument, ESRI.ADF.System.ProcessMSAjaxCallbackResult, context)", Me.UniqueID, Me.ClientID) End If End Get End Property Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As EventArgs) MeasureDisplayPanel.InnerHtml = WriteInitialContents() If Not ScriptManager.GetCurrent(Page) Is Nothing Then ScriptManager.GetCurrent(Me.Page).RegisterAsyncPostBackControl(Me) End If End Sub Private ReadOnly Property MapFunctionality() As IMapFunctionality Get If _mapFunctionality Is Nothing Then ' use the primary resouce, if defined Dim primeResource As String = _map.PrimaryMapResource Dim resource As IGISResource = Nothing If primeResource <> Nothing And primeResource.Length > 0 Then resource = _resourceManger.GetResource(primeResource) End If If resource Is Nothing Then Dim i As Integer For i = 0 To _resourceManger.ResourceItems.Count - 1 Step i + 1 resource = _resourceManger.GetResource(i) If Not resource Is Nothing Then Exit For End If Next End If If Not resource Is Nothing Then _mapFunctionality = CType(resource.CreateFunctionality(GetType(IMapFunctionality), "mapFunctionality"), IMapFunctionality) End If End If Return _mapFunctionality End Get End Property Private Function ProcessMeasureRequest(ByVal queryString As NameValueCollection) As String Dim o As Object = Session("MeasureMapUnits") If Not (o Is Nothing) Then _mapUnits = CType([Enum].Parse(GetType(MapUnit), o.ToString()), MapUnit) ElseIf _startMapUnits = MapUnit.Resource_Default Then _mapUnits = GetResourceDefaultMapUnit() Else _mapUnits = _startMapUnits End If Dim eventArg As String = queryString("EventArg") Dim vectorAction As String = queryString("VectorMode") Dim coordPairs(), xys() As String Dim coordString As String = queryString("coords") If coordString Is Nothing And coordString.Length = 0 Then coordString = "" End If coordPairs = coordString.Split(Char.Parse("|")) Dim mapUnitString As String = queryString("MapUnits") Dim forceRefresh As Boolean = (queryString("refresh") = "true") If mapUnitString Is Nothing Then mapUnitString = "" If mapUnitString.Length > 0 Then _mapUnits = CType([Enum].Parse(GetType(MapUnit), mapUnitString), MapUnit) End If Session("MeasureMapUnits") = _mapUnits Dim measureUnitString As String = queryString("MeasureUnits") If measureUnitString Is Nothing Then measureUnitString = "" If measureUnitString.Length > 0 Then _measureUnits = CType([Enum].Parse(GetType(MeasureUnit), measureUnitString), MeasureUnit) Dim areaUnitstring As String = queryString("AreaUnits") If areaUnitstring Is Nothing Then areaUnitstring = "" If areaUnitstring.Length > 0 Then _areaUnits = CType([Enum].Parse(GetType(AreaUnit), areaUnitstring), AreaUnit) Dim response As String = Nothing Dim points As New PointCollection() Dim dPoints As New PointCollection() Dim distances As New ArrayList() Dim totalDistance As Double = 0 Dim segmentDistance As Double = 0 Dim area As Double = 0 Dim perimeter As Double = 0 Dim roundFactor As Double = Math.Pow(10, _numberDecimals) Dim xD, yD, tempDist, tempDist2, tempArea, x1, x2, y1, y2 As Double Dim transformationParameters As TransformationParams = _map.GetTransformationParams(TransformationDirection.ToMap) If (vectorAction = "measure") Then If Not coordPairs Is Nothing And coordPairs.Length > 1 Then For cp As Integer = 0 To coordPairs.Length - 1 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 Then ' check for duplicate points from double click.... Firefox will send coords for both clicks, causing segmentDistance to be zero. If (Not points(cp - 1).X = points(cp).X OrElse Not points(cp - 1).Y = points(cp).Y) Then If _mapUnits = MapUnit.Degrees Then ' 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) End If distances.Add(segmentDistance) totalDistance += segmentDistance segmentDistance = Math.Round(segmentDistance * roundFactor) / roundFactor totalDistance = Math.Round(totalDistance * roundFactor) / roundFactor End If Else If (_mapUnits = MapUnit.Degrees) Then 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)) End If End If Next End If If (eventArg = "polygon") Then If (points.Count > 2 OrElse forceRefresh) Then If (_mapUnits = MapUnit.Degrees) Then 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) End If points.Add(points(0)) perimeter = totalDistance + tempDist2 ' add area calculation tempArea = 0 Dim mUnits As MapUnit = _mapUnits Dim xDiff As Double = 0 Dim yDiff As Double = 0 If (_mapUnits = MapUnit.Degrees) Then points = dPoints mUnits = MapUnit.Feet End If For j As Integer = 0 To points.Count - 2 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 Next 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()) End If ElseIf (eventArg = "polyline") Then If points.Count < 3 OrElse forceRefresh Then 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>", segmentDistance, _measureUnits, totalDistance, WriteMeasureUnitDropDown()) End If ElseIf (eventArg = "point" And coordPairs.Length > 0) Then 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), (Math.Round(Convert.ToDouble(xys(1), System.Globalization.CultureInfo.InvariantCulture) * roundFactor) / roundFactor)) End If End If Dim coll As CallbackResultCollection = 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() End Function Private Function CheckFormMeasureUnits(ByVal unit As String) As String Dim response As String = "" If unit = _measureUnits.ToString() Then response = "selected=""selected""" End If Return response End Function 'CheckFormMeasureUnits Private Function CheckFormAreaUnits(ByVal unit As String) As String Dim response As String = "" If unit = _areaUnits.ToString() Then response = "selected=""selected""" End If Return response End Function 'CheckFormAreaUnits Private Function WriteMeasureUnitDropDown() As String Dim sb As New System.Text.StringBuilder() sb.Append("<select id=""MeasureUnits2"" onchange=""changeMeasureUnits()"" style=""font: normal 7pt Verdana; width: 100px;"">") Dim mArray As Array = [Enum].GetValues(GetType(MeasureUnit)) Dim mu As MeasureUnit For Each mu In mArray sb.AppendFormat("<option value=""{0}"" {1}>{0}</option>", mu, CheckFormMeasureUnits(mu.ToString())) Next mu sb.Append("</select>") Return sb.ToString() End Function 'WriteMeasureUnitDropdown Private Function WriteAreaUnitDropDown() As String Dim sb As New System.Text.StringBuilder() sb.Append("<select id=""AreaUnits2"" onchange=""changeAreaUnits()"" style=""font: normal 7pt Verdana; width: 100px;"">") Dim aArray As Array = [Enum].GetValues(GetType(AreaUnit)) Dim au As AreaUnit For Each au In aArray sb.AppendFormat("<option value=""{0}"" {1}>{0}</option>", au, CheckFormAreaUnits(au.ToString())) Next au sb.Append("</select>") Return sb.ToString() End Function 'WriteAreaUnitDropDown Private Function ConvertUnits(ByVal distance As Double, ByVal fromUnits As MapUnit, ByVal toUnits As MeasureUnit) As Double Dim mDistance As Double = distance If fromUnits = MapUnit.Feet Then If toUnits = MeasureUnit.Miles Then mDistance = distance / 5280 Else If toUnits = MeasureUnit.Meters Then mDistance = distance * 0.304800609601 Else If toUnits = MeasureUnit.Kilometers Then mDistance = distance * 0.0003048 End If End If End If Else If toUnits = MeasureUnit.Miles Then mDistance = distance * 0.0006213700922 Else If toUnits = MeasureUnit.Feet Then mDistance = distance * 3.280839895 Else If toUnits = MeasureUnit.Kilometers Then mDistance = distance / 1000 End If End If End If End If Return mDistance End Function 'ConvertUnits Private Function ConvertAreaUnits(ByVal area As Double, ByVal baseUnits As MapUnit, ByVal toUnits As AreaUnit) As Double Dim mArea As Double = area If baseUnits = MapUnit.Feet Then If toUnits = AreaUnit.Acres Then mArea = area * 0.000022956 Else If toUnits = AreaUnit.Sq_Meters Then mArea = area * 0.09290304 Else If toUnits = AreaUnit.Sq_Miles Then mArea = area * 0.00000003587 Else If toUnits = AreaUnit.Sq_Kilometers Then mArea = area * 0.09290304 / 1000000 Else If toUnits = AreaUnit.Hectares Then mArea = area * 0.09290304 * 0.0001 End If End If End If End If End If Else If baseUnits = MapUnit.Meters Then If toUnits = AreaUnit.Acres Then mArea = area * 0.0002471054 Else If toUnits = AreaUnit.Sq_Miles Then mArea = area * 0.0000003861003 Else If toUnits = AreaUnit.Sq_Kilometers Then mArea = area * 0.000001 Else If toUnits = AreaUnit.Sq_Feet Then mArea = area * 10.76391042 Else If toUnits = AreaUnit.Hectares Then mArea = area * 0.0001 End If End If End If End If End If End If End If Return mArea End Function 'ConvertAreaUnits Private Function DegreeToFeetDistance(ByVal x1 As Double, ByVal y1 As Double, ByVal x2 As Double, ByVal y2 As Double) As Double ' use great circle formula Dim Lat1 As Double = DegToRad(y1) Dim Lat2 As Double = DegToRad(y2) Dim Lon1 As Double = DegToRad(x1) Dim Lon2 As Double = DegToRad(x2) Dim LonDist As Double = Lon1 - Lon2 Dim LatDist As Double = Lat1 - Lat2 Dim x As Double = 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 End Function 'DegreeToFeetDistance Private Function DegToRad(ByVal degrees As Double) As Double Return Convert.ToDouble((degrees * Math.PI / 180)) End Function 'DegToRad Private Function GetResourceDefaultMapUnit() As MapUnit Dim mUnit As MapUnit = MapUnit.Degrees Try Dim mu As Units = MapFunctionality.Units If mu = Units.DecimalDegrees Then mUnit = MapUnit.Degrees Else If mu = Units.Feet Then mUnit = MapUnit.Feet Else If mu = Units.Meters Then mUnit = MapUnit.Meters End If End If End If Catch ' cannot get units from resource... default to fallback value set in declaration mUnit = _fallbackMapUnits End Try Return mUnit End Function 'GetResourceDefaultMapUnit Public Function CanGetUnits() As Boolean For Each mapFunc As ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality In _map.GetFunctionalities Try Dim units As Units = mapFunc.Units Return True Catch ex As Exception ' Getting units failed End Try Next Return False End Function Private Function WriteInitialContents() As String Dim sb As New System.Text.StringBuilder() ' set up tool buttons sb.AppendFormat("<table cellpadding='0' cellspacing='0' ><tr>{0}", Chr(10)) sb.AppendFormat("<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>{0}", Chr(10)) sb.AppendFormat("<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>{0}", Chr(10)) sb.AppendFormat("<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' />{1}</td>{0}", Chr(10), Chr(9)) sb.AppendFormat("</tr></table>{0}", Chr(10)) sb.AppendFormat("<input id='MeasureUnits' type='hidden' value='{0}'/>{1}", MeasureUnits, Chr(10)) sb.AppendFormat(" <input id='AreaUnits' type='hidden' value='{0}'/>{1}", AreaUnits, Chr(10)) ' create display area sb.AppendFormat("<table id='MeasureToolbarTable' cellspacing='2' cellpadding='1' style=' width: 100%;font: normal 7pt Verdana; '>{0}", Chr(10)) sb.AppendFormat("<tr><td style='background-color: #ffffff' id='MeasureDisplay' colspan='2' valign='top'>{0}", Chr(10)) sb.AppendFormat("Click on the map and draw a line. Double-click to end line.{0}", Chr(10)) sb.AppendFormat("</td></tr>{0}", Chr(10)) sb.AppendFormat("</table>{0}", Chr(10)) Return sb.ToString() End Function Private Function processPostbackEvent(ByVal requestString As String) As String ' break out the responseString into a querystring Dim keyValuePairs As Array = requestString.Split("&".ToCharArray()) Dim m_queryString As New NameValueCollection() Dim map As Map = Me.Page.FindControl(Me._mapBuddyId) Dim keyValue() As String Dim response As String = "" If keyValuePairs.Length > 0 Then Dim i As Integer For i = 0 To keyValuePairs.Length - 1 keyValue = keyValuePairs.GetValue(i).ToString().Split("=".ToCharArray()) m_queryString.Add(keyValue(0), keyValue(1)) Next i Else keyValue = requestString.Split("=".ToCharArray()) If keyValue.Length > 0 Then m_queryString.Add(keyValue(0), keyValue(1)) End If End If ' isolate control type and mode ' isolate control type and mode Dim controlType As String = m_queryString("ControlType") Dim eventArg As String = m_queryString("EventArg") If controlType Is Nothing Then controlType = "Map" End If Select Case controlType Case "Map" ' request is for the map control Dim vectorMode As String = m_queryString("VectorMode") If Not (vectorMode Is Nothing) And vectorMode = "measure" Then response = ProcessMeasureRequest(m_queryString) End If Case Else End Select ' Return response End Function Public Sub RaisePostBackEvent(ByVal eventArgument As String) Implements IPostBackEventHandler.RaisePostBackEvent Dim strResult As String = processPostbackEvent(eventArgument) If Not [String].IsNullOrEmpty(strResult) Then ScriptManager.GetCurrent(Me.Page).RegisterDataItem(Me, strResult, False) End If End Sub Public Overrides Property Id() As String ' Get _id = MyBase.ID Return MyBase.ID End Get Set(ByVal value As String) _id = value MyBase.ID = value End Set End Property Public Property MapBuddyId() As String Get Return _mapBuddyId End Get Set(ByVal value As String) _mapBuddyId = value End Set End Property Public Property MapUnits() As MapUnit Get Return _startMapUnits End Get Set(ByVal value As MapUnit) _startMapUnits = value End Set End Property Public Property MeasureUnits() As MeasureUnit Get Return _measureUnits End Get Set(ByVal value As MeasureUnit) _measureUnits = value End Set End Property Public Property AreaUnits() As AreaUnit Get Return _areaUnits End Get Set(ByVal value As AreaUnit) _areaUnits = value End Set End Property Public Property NumberDecimals() As Double Get Return _numberDecimals End Get Set(ByVal value As Double) _numberDecimals = value End Set End Property Public Function GetCallbackResult() As String Implements ICallbackEventHandler.GetCallbackResult Return processPostbackEvent(callbackArg) End Function Public Sub RaiseCallbackEvent(ByVal eventArgument As String) Implements ICallbackEventHandler.RaiseCallbackEvent callbackArg = eventArgument End Sub End Class End Namespace