Skip To Content

SOE GetElevations

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
               xmlns:s="library://ns.adobe.com/flex/spark"
               xmlns:mx="library://ns.adobe.com/flex/mx"
               xmlns:esri="http://www.esri.com/2008/ags"
               xmlns:components="com.esri.ags.samples.components.*"
               xmlns:tasks="com.esri.ags.samples.tasks.*"
               currentState="selectionState"
               pageTitle="Example - SOE GetElevations">
    <!--
    Description:
    This sample demonstrates the programming patterns to create your own
    custom Task which extends BaseTask.  The sample calculates the elevations
    along a user defined path (polyline), and plots that data in an interactive
    chart window.  To use this sample: Select a mountain from the list of buttons,
    next activate the draw tool to sketch a line over the mountain surface, the
    chart represents the profile or terrain over the line (mouse over the profile
    in the chart window to see the various chart locations on the map).

    The charting component and utility functions were taken directly from the Esri APL FlexViewer widgets sample.

    Documentation:
    For more information, see the API documentation.
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/events/DrawEvent.html
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/events/DrawEvent.html#graphic
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/events/LocatorEvent.html
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/tasks/BaseTask.html
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/tasks/Locator.html
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/tasks/supportClasses/AddressCandidate.html
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/tasks/supportClasses/AddressToLocationsParameters.html
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/tools/DrawTool.html
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/tools/DrawTool.html#graphicsLayer
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/tools/DrawTool.html#map
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/tools/DrawTool.html#activate()
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/tools/DrawTool.html#deactivate()
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/tools/DrawTool.html#event:drawStart
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/tools/DrawTool.html#event:drawEnd
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/utils/WebMercatorUtil.html

    http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/spark/formatters/NumberFormatter.html

    http://sampleserver4.arcgisonline.com/ArcGIS/rest/services/Elevation/ESRI_Elevation_World/MapServer/exts/ElevationsSOE "Live service"
    http://blogs.esri.com/Dev/blogs/apl/archive/2010/10/07/Elevation-Server-Object-Extension.aspx "Blog article on SOE"
    http://resources.arcgis.com/gallery/file/arcobjects-net-api/details?entryID=87BEC705-1422-2418-34B5-308930DE323A "Source code for SOE"
    http://www.arcgis.com/home/item.html?id=b9d247c297f144459854751740f59f68 "FlexViewer SOE widgets"

    For more details on the World Geocoding Service follow the link below.
    http://geocode.arcgis.com/arcgis/index.html

    ArcGIS REST API documentation:
    http://sampleserver4.arcgisonline.com/ArcGIS/rest/services/Elevation/ESRI_Elevation_World/MapServer/exts/ElevationsSOE/ElevationLayers/1/GetElevations

    ArcGIS for Server documentation:
    http://resources.arcgis.com/en/help/main/10.1/index.html#/What_is_a_server_object_extension/0154000004s5000000/
    http://resources.arcgis.com/en/help/main/10.1/index.html#/Steps_for_developing_and_deploying_a_server_object_extension/0154000004s7000000/
    http://resources.arcgis.com/en/help/main/10.1/index.html#/Alternatives_to_server_object_extensions/015400000528000000/

    This sample also uses the following files:
    com/esri/ags/samples/tasks/ElevationsSOETask.as
    com/esri/ags/samples/tasks/supportClasses/ElevationsSOEParameters.as
    com/esri/ags/samples/tasks/supportClasses/ElevationsSOEResult.as
    com/esri/ags/samples/tasks/supportClasses/InterpolatedRasterProperties.as
    com/esri/ags/samples/events/ElevationsSOEEvent.as
    com/esri/ags/samples/components/ChartWindow.mxml
    com/esri/ags/samples/components/DataTipRenderer.mxml
    -->

    <!-- STATES -->
    <s:states>
        <s:State name="selectionState"/>
        <s:State name="selectionProcessingState"/>
        <s:State name="drawingState"/>
        <s:State name="drawingProcessingState"/>
        <s:State name="resultsState"/>
    </s:states>

    <fx:Style>
        @namespace s "library://ns.adobe.com/flex/spark";
        @namespace mx "library://ns.adobe.com/flex/mx";
        s|Panel
        {
            backgroundColor: #C0C2B7;
            chromeColor: #9D7037;
        }
        s|ButtonBar
        {
            chromeColor: #AF8E56;
        }
        mx|ToolTip
        {
            font-size: 14;
            backgroundColor: #EEEEEE;
        }
    </fx:Style>

    <fx:Script>
        <![CDATA[
            import com.esri.ags.Graphic;
            import com.esri.ags.events.DrawEvent;
            import com.esri.ags.events.LocatorEvent;
            import com.esri.ags.geometry.MapPoint;
            import com.esri.ags.geometry.Polyline;
            import com.esri.ags.samples.events.ElevationsSOEEvent;
            import com.esri.ags.samples.tasks.supportClasses.ElevationsSOEParameters;
            import com.esri.ags.samples.tasks.supportClasses.ElevationsSOEResult;
            import com.esri.ags.tasks.supportClasses.AddressCandidate;
            import com.esri.ags.tasks.supportClasses.AddressToLocationsParameters;
            import com.esri.ags.utils.WebMercatorUtil;

            import mx.controls.Alert;
            import mx.core.IFlexDisplayObject;
            import mx.managers.PopUpManager;
            import mx.managers.PopUpManagerChildList;
            import mx.rpc.events.FaultEvent;

            import spark.events.IndexChangeEvent;

            []private var placeElevation:Number = 0.0;
            []private var placeResultDescription:String = "";

            protected function placeSelectedChangeHandler(event:IndexChangeEvent):void
            {
                var selectedItem:Object = ButtonBar(event.currentTarget).selectedItem as Object;
                if (selectedItem)
                {
                    var params:AddressToLocationsParameters = new AddressToLocationsParameters();
                    var place:Object = { SingleLine: selectedItem.data };
                    params.address = place;
                    params.outFields = [ 'Match_Addr' ];
                    currentState = 'selectionProcessingState';
                    setProgress("Locating " + selectedItem.data + "...", true);
                    placesLocator.addressToLocations(params);
                }
            }

            protected function placesLocator_addressToLocationsCompleteHandler(event:LocatorEvent):void
            {
                placesButtonBar.selectedIndex = -1;
                var placesResults:Array = event.addressCandidates as Array;
                if (placesResults[0])
                {
                    var placeCandidate:AddressCandidate = placesResults[0] as AddressCandidate;
                    placeResultDescription = placeCandidate.attributes["Match_Addr"];
                    var mapPoint:MapPoint = placeCandidate.location as MapPoint;
                    var webMercatorGeom:MapPoint = WebMercatorUtil.geographicToWebMercator(mapPoint) as MapPoint;
                    addPlace(webMercatorGeom);
                    map.level = 11;
                    map.centerAt(webMercatorGeom);
                    currentState = 'drawingState';
                }
            }

            protected function addPlace(value:MapPoint):void
            {
                map.defaultGraphicsLayer.clear();
                var gra:Graphic = new Graphic(value, elevationAtSymbol);
                map.defaultGraphicsLayer.add(gra);
            }

            protected function setProgress(value:String, visible:Boolean):void
            {
                progressBar.visible = visible;
                progressBar.label = value;
            }

            protected function drawTypeSelectedChangeHandler(event:IndexChangeEvent):void
            {
                drawGraphicsLayer.clear();
                var selectedItem:Object = ButtonBar(event.currentTarget).selectedItem as Object;
                if (selectedItem)
                {
                    drawTool.activate(selectedItem.data);
                }
            }

            protected function drawTool_drawEndHandler(event:DrawEvent):void
            {
                var elevationPathPolyline:Polyline = event.graphic.geometry as Polyline;
                drawTool.deactivate();
                drawingButtonBar.selectedIndex = -1;
                var inputGeometries:Array = [];
                inputGeometries.push(elevationPathPolyline);
                var params:ElevationsSOEParameters = new ElevationsSOEParameters();
                params.geometries = inputGeometries;
                currentState = 'drawingProcessingState';
                setProgress("Calculating elevation values...", true);
                soeTask.executeGetElevations(params);
            }

            protected function soeTask_executeCompleteHandler(event:ElevationsSOEEvent):void
            {
                var elevationResult:ElevationsSOEResult = event.elevationsSOEResult;
                try
                {
                    var aryDistances:Array = distancesAlongProfile(elevationResult.chartDistances);
                    var aryData:Array = consolidateDataForCharts(elevationResult.chartResultData, aryDistances);

                    chtWindow.chartData = aryData;

                    if (!systemManager.popUpChildren.contains(chtWindow))
                    {
                        PopUpManager.addPopUp(chtWindow as IFlexDisplayObject, map, false, PopUpManagerChildList.POPUP);
                        PopUpManager.centerPopUp(chtWindow);
                    }
                }
                catch (error:Error)
                {
                    Alert.show("A problem occurred while parsing and charting the returned elevation data:\n" + error.message.toString());
                }
                currentState = 'resultsState';
            }

            protected function esriService_faultHandler(event:FaultEvent):void
            {
                placesButtonBar.selectedIndex = -1;
                Alert.show("Error: " + event.fault.faultString, "Error code: " + event.fault.faultCode);
                currentState = 'selectionState';
            }

            // Utility functions
            /**
             * Given an array of web mercator points, produce a new array of the same length
             * indicating how many kilometers along the profile path each data point lies.
             */
            private function distancesAlongProfile(aryLocs:Array):Array
            {
                var aryDists:Array = [];
                if (aryLocs.length > 0)
                {
                    aryDists.push(0);

                    for (var iPt:int = 1; iPt < aryLocs.length; iPt++)
                    {
                        var nDistFromLast:Number = Math.sqrt(
                            Math.pow((aryLocs[iPt][0] - aryLocs[iPt - 1][0]), 2) +
                            Math.pow((aryLocs[iPt][1] - aryLocs[iPt - 1][1]), 2)
                            ) / 1000; // Web Mercator is in meters, so divide by 1000 to get kilometers
                        aryDists.push(aryDists[iPt - 1] + nDistFromLast);
                    }
                }
                return aryDists;
            }

            /**
             * Merge various arrays of data into a single array of objects for use
             * as a dataProvider for charting components.
             */
            private function consolidateDataForCharts(oResult:Object, aryDistances:Array):Array
            {
                // Get the global minimum elevation value so the chart area is correctly filled in for
                // negative values. If min value > 0, lower it to zero to fill in for highly positive values.
                var nElevationMin:Number = Math.min(
                    Math.min.apply(null, oResult.elevations[0]),
                    0
                    );

                var aryData:Array = [];
                for (var iItem:int = 0; iItem < aryDistances.length; iItem++)
                {
                    aryData.push({
                                     "elevation": oResult.elevations[0][iItem],
                                     "geometry": oResult.geometries[0].paths[0][iItem],
                                     "distance": aryDistances[iItem],
                                     "elevationMin": nElevationMin
                                 });
                }
                return aryData;
            }

            /**
             * Close chart window and remove user-drawn line graphic
             */
            private function closeChartWindow():void
            {
                drawGraphicsLayer.clear();
                PopUpManager.removePopUp(chtWindow);
                currentState = 'resultsState';
            }

            protected function drawAnother_clickHandler(event:MouseEvent):void
            {
                closeChartWindow();
                drawGraphicsLayer.clear();
                currentState = 'drawingState';
            }

            protected function startOver_clickHandler(event:MouseEvent):void
            {
                closeChartWindow();
                map.defaultGraphicsLayer.clear();
                drawGraphicsLayer.clear();
                currentState = 'selectionState';
            }
        ]]>
    </fx:Script>

    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->
        <esri:SimpleLineSymbol id="drawLineColor"
                               width="3"
                               color="0x1B009F"/>
        <esri:DrawTool id="drawTool"
                       drawEnd="drawTool_drawEndHandler(event)"
                       graphicsLayer="{drawGraphicsLayer}"
                       lineSymbol="{drawLineColor}"
                       map="{map}"/>
        <esri:SimpleMarkerSymbol id="elevationAtSymbol"
                                 color="0xB5804E"
                                 outline="{smsOutline}"
                                 size="30"
                                 style="triangle"/>
        <esri:SimpleLineSymbol id="smsOutline"
                               width="1"
                               color="0x578DB8"
                               style="solid"/>
        <tasks:ElevationsSOETask id="soeTask"
                                 executeComplete="soeTask_executeCompleteHandler(event)"
                                 fault="esriService_faultHandler(event)"
                                 url="http://sampleserver4.arcgisonline.com/ArcGIS/rest/services/Elevation/ESRI_Elevation_World/MapServer/exts/ElevationsSOE/ElevationLayers/1"/>
        <esri:Locator id="placesLocator"
                      addressToLocationsComplete="placesLocator_addressToLocationsCompleteHandler(event)"
                      fault="esriService_faultHandler(event)"
                      url="http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer"/>
        <s:NumberFormatter id="numberFormatter" fractionalDigits="0"/>
        <!-- Chart pop-up window -->
        <components:ChartWindow id="chtWindow"
                                close="closeChartWindow()"
                                glProfileLine="{drawGraphicsLayer}"
                                title="Elevation Profile"/>
    </fx:Declarations>

    <s:controlBarContent>
        <s:RichText width="100%">
            This sample demonstrates the programming patterns to create your own
            custom Task which extends BaseTask.  The sample calculates the elevations
            along a user defined path (polyline), and plots that data in an interactive
            chart window.  To use this sample: Select a mountain from the list of buttons,
            next activate the draw tool to sketch a line over the mountain surface, the
            chart represents the profile or terrain over the line (mouse over the profile
            in the chart window to see the various chart locations on the map).
        </s:RichText>
    </s:controlBarContent>

    <esri:Map id="map" wrapAround180="true">
        <esri:ArcGISTiledMapServiceLayer url="http://services.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer"/>
        <esri:GraphicsLayer id="drawGraphicsLayer"/>
    </esri:Map>
    <s:Panel width="440" height="130"
             right="20" top="20"
             title="Generate an interactive client-side elevation profile">
        <s:layout>
            <s:VerticalLayout gap="10"
                              paddingBottom="7"
                              paddingLeft="10"
                              paddingRight="10"
                              paddingTop="7"/>
        </s:layout>
        <s:Group id="selectionState" includeIn="selectionState">
            <s:ButtonBar id="placesButtonBar" change="placeSelectedChangeHandler(event)">
                <s:ArrayList>
                    <fx:Object data="Mount St. Helens" label="Mount St. Helens"/>
                    <fx:Object data="Gunung Merapi" label="Mount Merapi"/>
                    <fx:Object data="Mount Pinatubo" label="Mount Pinatubo"/>
                    <fx:Object data="Kilauea Volcano" label="Kilauea Volcano"/>
                </s:ArrayList>
            </s:ButtonBar>
        </s:Group>
        <s:Group id="drawingState" includeIn="drawingState">
            <s:layout>
                <s:VerticalLayout gap="10"
                                  paddingBottom="7"
                                  paddingLeft="10"
                                  paddingRight="10"
                                  paddingTop="7"/>
            </s:layout>
            <s:Label id="placeResult"
                     width="100%"
                     fontSize="14"
                     text="{placeResultDescription}"
                     visible="{placeResultDescription != ''}"/>
            <s:ButtonBar id="drawingButtonBar" change="drawTypeSelectedChangeHandler(event)">
                <s:ArrayList>
                    <fx:Object data="{DrawTool.FREEHAND_POLYLINE}" label="Line (Freehand Draw)"/>
                    <fx:Object data="{DrawTool.LINE}" label="Line (Click,Drag,Release)"/>
                </s:ArrayList>
            </s:ButtonBar>
        </s:Group>
        <s:Group id="results" includeIn="resultsState">
            <s:layout>
                <s:VerticalLayout gap="10"
                                  paddingBottom="7"
                                  paddingLeft="10"
                                  paddingRight="10"
                                  paddingTop="7"/>
            </s:layout>
            <s:Button click="drawAnother_clickHandler(event)" label="Click to draw another line over the mountain."/>
            <s:Button click="startOver_clickHandler(event)" label="Click to Start Over"/>
        </s:Group>
        <mx:ProgressBar id="progressBar"
                        width="80%"
                        includeIn="selectionProcessingState,drawingProcessingState"
                        indeterminate="true"
                        visible="false"/>
    </s:Panel>
</s:Application>