Skip To Content

Determine Spatial Relationships

<?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.*"
               initialize="application_initializeHandler(event)"
               pageTitle="Determine spatial relationships">
    <!--
    Description:
    This sample demonstrates how to use the Geometry Service to determine spatial
    relationships between two sets of geometries. Click the map to generate
    a 2 minute drive time service area which will be used to query the census service
    for blockgroup points within the network service area.  The geometry service
    will then determine if the blockgroup points are 'within' the network service area.

    Attention:
    To use this sample locally, delete the following line 'proxyURL="https://developers.arcgis.com/sproxy/"'
    from the '<esri:Geoprocessor id="gp"' task in the Declarations section.
    Note: Requires an ArcGIS Online Organizational subscription.

    While the Query task can also calculate spatial relationships, the
    Geometry Service exposes more relationship types and can be used
    with geometries that do not come from ArcGIS Server.

    This sample finds features (block points) within 2 minute drive time
    of a point. The features are not part of a map service. They are just
    graphics that are within the current map extent. The drive time polygon
    is not part of a map service either. It is returned as the result of a
    Geoprocessing service.

    Both the block points and the drive time polygon are passed to the
    GeometryService.relation() method. One of the parameters for this method
    is the relationship type. This sample looks for block points within the
    drive time polygon. In other words, those satisfying the relationship
    type RelationParameters.SPATIAL_REL_WITHIN.

    The symbols for all the features within the drive time are changed to
    depict the result.

    Note: the relation operation is only available with ArcGIS Server 9.3
    Service Pack 1 or above.

    Documentation:
    For more information, see the API documentation.
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/FeatureSet.html
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/Graphic.html
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/Map.html#zoomTo()
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/events/GeometryServiceEvent.html
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/events/GeoprocessorEvent.html
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/events/MapMouseEvent.html
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/events/QueryEvent.html
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/symbols/SimpleFillSymbol.html
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/symbols/SimpleLineSymbol.html
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/symbols/SimpleMarkerSymbol.html
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/tasks/GeometryService.html#relation()
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/tasks/Geoprocessor.html
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/tasks/Geoprocessor.html#submitJob()
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/tasks/QueryTask.html
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/tasks/QueryTask.html#execute()
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/tasks/supportClasses/ParameterValue.html
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/tasks/supportClasses/Query.html
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/tasks/supportClasses/RelationParameters.html
    https://developers.arcgis.com/en/flex/api-reference/com/esri/ags/tasks/supportClasses/RelationParameters.html#SPATIAL_REL_WITHIN

    ArcGIS REST API documentation:
    http://resources.arcgis.com/en/help/rest/apiref/geometryserver.html
    http://resources.arcgis.com/en/help/rest/apiref/relation.html
    http://resources.arcgis.com/en/help/rest/apiref/gpserver.html
    http://resources.arcgis.com/en/help/rest/apiref/gptask.html
    http://resources.arcgis.com/en/help/rest/apiref/gpexecute.html
    http://resources.arcgis.com/en/help/rest/apiref/gpsubmit.html
    http://resources.arcgis.com/en/help/rest/apiref/gpjob.html

    ArcGIS for Server documentation:
    What is a geoprocessing service?
    http://resources.arcgis.com/en/help/main/10.1/#/What_is_a_geoprocessing_service/0154000004v5000000/
    A quick tour of authoring and sharing geoprocessing services
    http://resources.arcgis.com/en/help/main/10.1/#/A_quick_tour_of_authoring_and_sharing_geoprocessing_services/01540000054n000000/
    Essential vocabulary for geoprocessing services
    http://resources.arcgis.com/en/help/main/10.1/#/Essential_vocabulary_for_geoprocessing_services/0154000004v2000000/
    Geoprocessing service settings: Parameters
    http://resources.arcgis.com/en/help/main/10.1/index.html#/Parameters/01540000054v000000/

    ArcGIS for Desktop documentation:
    A quick tour of creating custom tools
    http://resources.arcgis.com/en/help/main/10.1/index.html#//001500000001000000
    A quick tour of creating tools with ModelBuilder
    http://resources.arcgis.com/en/help/main/10.1/index.html#/A_quick_tour_of_creating_tools_with_ModelBuilder/00150000001t000000/
    A quick tour of creating tools with Python
    http://resources.arcgis.com/en/help/main/10.1/index.html#/A_quick_tour_of_creating_tools_with_Python/00150000002q000000/

    ArcGIS Network Analyst:
    Service Area Analysis
    http://resources.arcgis.com/en/help/arcgis-rest-api/#/Service_Area_service_with_asynchronous_execution/02r3000000n0000000/
    http://logistics.arcgis.com/arcgis/rest/directories/arcgisoutput/World/ServiceAreas_GPServer/World_ServiceAreas/GenerateServiceAreas.htm
    -->

    <fx:Script>
        <![CDATA[
            import com.esri.ags.FeatureSet;
            import com.esri.ags.Graphic;
            import com.esri.ags.components.IdentityManager;
            import com.esri.ags.events.GeometryServiceEvent;
            import com.esri.ags.events.GeoprocessorEvent;
            import com.esri.ags.events.MapMouseEvent;
            import com.esri.ags.events.QueryEvent;
            import com.esri.ags.geometry.MapPoint;
            import com.esri.ags.symbols.SimpleFillSymbol;
            import com.esri.ags.symbols.SimpleLineSymbol;
            import com.esri.ags.symbols.SimpleMarkerSymbol;
            import com.esri.ags.tasks.supportClasses.JobInfo;
            import com.esri.ags.tasks.supportClasses.RelationParameters;

            import mx.collections.ArrayCollection;
            import mx.controls.Alert;
            import mx.events.FlexEvent;
            import mx.rpc.events.FaultEvent;

            []public var driveTimeGeometries:Array = [];

            []public var blockPointGeometries:Array = [];

            private var gpPending:Boolean = false;

            protected function application_initializeHandler(event:FlexEvent):void
            {
                IdentityManager.instance.enabled = true;
            }

            private function myMap_mapClickHandler(event:MapMouseEvent):void
            {
                if (gpPending)
                {
                    Alert.show("Please be patient while we determine your first analysis", "Drive Time");
                    return; // a new gp request has been started
                }
                myMap.defaultGraphicsLayer.clear();
                var mapPoint:MapPoint = event.mapPoint;
                var graphic:Graphic = new Graphic(mapPoint);
                graphic.symbol = new SimpleMarkerSymbol("cross", 10, 0, 1, 0, 0, 0, new SimpleLineSymbol("solid", 0, 1, 2));
                myMap.defaultGraphicsLayer.add(graphic);

                // getting the drive time polygon
                getDriveTimePolygon(mapPoint);
            }

            private function getDriveTimePolygon(mapPoint:MapPoint):void
            {
                var graphic:Graphic = new Graphic(mapPoint);
                var featureSet:FeatureSet = new FeatureSet();
                featureSet.features = [ graphic ];
                var params:Object =
                    {
                        "Facilities": featureSet,
                        "Break_Values": "2",
                        "Break_Units": "Minutes"
                    };

                // submit gp task job to get the drive time polygon
                gp.submitJob(params);
                gpPending = true;
                progressMessage.showMessage("Determining 2 minute drive time", true);
            }

            protected function gp_faultHandler(event:FaultEvent):void
            {
                Alert.show("This service request an ArcGIS Organizational subscription\nYou can also try the example on the ArcGIS API for Flex Resource Center"
                           + event.fault.faultString, "Drive Time");
                progressMessage.clearMessage();
            }

            protected function gp_jobCompleteHandler(event:GeoprocessorEvent):void
            {
                if (event.jobInfo.jobStatus == JobInfo.STATUS_SUCCEEDED)
                {
                    var jobID:String = event.jobInfo.jobId;
                    progressMessage.setMessage("Retrieving drive time");
                    gp.getResultData(gp.submitJobLastResult.jobId, "Service_Areas");
                }
                else
                {
                    Alert.show(event.jobInfo.jobStatus, "Analysis Status");
                }
            }

            protected function gp_getResultDataCompleteHandler(event:GeoprocessorEvent):void
            {
                /* The parameterValue property is set when the type is getResultDataComplete,
                 * getInputComplete or getResultImageComplete.
                 */
                if (event.parameterValue)
                {
                    progressMessage.showMessage("Retrieving blockgroup points.", true);
                    var serviceAreas:FeatureSet = event.parameterValue.value as FeatureSet;
                    var drivetimeGraphic:Graphic = serviceAreas.features[0];
                    var symbol:SimpleFillSymbol = new SimpleFillSymbol(SimpleFillSymbol.STYLE_SOLID, 0xFF0000, 0.1);
                    symbol.outline = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, 0xFF0000, 1.0, 2);
                    drivetimeGraphic.symbol = symbol;
                    myMap.defaultGraphicsLayer.add(drivetimeGraphic);

                    driveTimeGeometries = [];
                    // geometry to be used in finding spatial relationship
                    driveTimeGeometries.push(drivetimeGraphic.geometry);

                    //setting the extent of the map to the extent of returned drive time polygon
                    //Note: the zoomTo method was added at version 3.1
                    myMap.zoomTo(drivetimeGraphic.geometry.extent);

                    // query the map extent
                    query.geometry = myMap.extent;
                    queryTask.execute(query);
                }
                else
                {
                    Alert.show("Service did not return any results", "Drive Time");
                    gpPending = false;
                }
            }

            private function queryTask_executeCompleteHandler(event:QueryEvent):void
            {
                var symbol:SimpleMarkerSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_SQUARE, 8, 0xFFFF00, 0.5);
                symbol.outline = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, 0xB3B3B3);

                blockPointGeometries = [];
                var blockpointsFeatureSet:FeatureSet = event.featureSet;
                for (var i:Number = 0; i < blockpointsFeatureSet.features.length; i++)
                {
                    blockpointsFeatureSet.features[i].symbol = symbol;
                    myMap.defaultGraphicsLayer.add(blockpointsFeatureSet.features[i]);
                    // get the features(geometries) within the current map extent
                    blockPointGeometries.push(blockpointsFeatureSet.features[i].geometry);
                }
                progressMessage.showMessage("Calculating the relationship between the drive time and blockgroups.", true);
                calculateSpatialRelationship();
            }

            private function calculateSpatialRelationship():void
            {
                // find which geometries are 'within' the  drive time polygon
                var relationParameters:RelationParameters = new RelationParameters();
                relationParameters.geometries1 = blockPointGeometries;
                relationParameters.geometries2 = driveTimeGeometries;
                relationParameters.spatialRelationship = RelationParameters.SPATIAL_REL_WITHIN;

                // Note: As of version 2.0, relation() takes a RelationParameters object.
                geometryService.relation(relationParameters);
            }

            private function geometryService_relationCompleteHandler(event:GeometryServiceEvent):void
            {
                var result:Array = event.result as Array;
                var graphics:ArrayCollection = ArrayCollection(myMap.defaultGraphicsLayer.graphicProvider);
                // highlight the geometries within the polygon.
                for each (var obj:Object in result)
                {
                    var resultMapPoint:MapPoint = MapPoint(obj.geometry1);
                    for each (var graphic:Graphic in graphics)
                    {
                        if (graphic.geometry is MapPoint)
                        {
                            var graphicMapPoint:MapPoint = MapPoint(graphic.geometry);
                            if (resultMapPoint.x == graphicMapPoint.x && resultMapPoint.y == graphicMapPoint.y)
                            {
                                graphic.symbol = blueSquareSMS;
                                break;
                            }
                        }
                    }
                }
                messages.text = "The total number of features within the drive time area are " + (event.result as Array).length + ".";
                progressMessage.clearMessage();
                gpPending = false;
            }

            protected function geometryService_faultHandler(event:FaultEvent):void
            {
                Alert.show(event.fault.faultString, 'GeometryService error');
                progressMessage.clearMessage();
                gpPending = false;
            }

            protected function queryTask_faultHandler(event:FaultEvent):void
            {
                Alert.show(event.fault.faultString, 'Census query error');
                progressMessage.clearMessage();
                gpPending = false;
            }
        ]]>
    </fx:Script>

    <fx:Declarations>
        <esri:Geoprocessor id="gp"
                           fault="gp_faultHandler(event)"
                           getResultDataComplete="gp_getResultDataCompleteHandler(event)"
                           jobComplete="gp_jobCompleteHandler(event)"
                           outSpatialReference="{myMap.spatialReference}"
                           proxyURL="https://developers.arcgis.com/sproxy/"
                           url="https://logistics.arcgis.com/arcgis/rest/services/World/ServiceAreas/GPServer/GenerateServiceAreas"/>

        <esri:QueryTask id="queryTask"
                        concurrency="last"
                        executeComplete="queryTask_executeCompleteHandler(event)"
                        fault="queryTask_faultHandler(event)"
                        showBusyCursor="true"
                        url="http://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer/0"/>

        <esri:Query id="query"
                    outSpatialReference="{myMap.spatialReference}"
                    returnGeometry="true">
            <esri:outFields>
                <fx:String>POP2000</fx:String>
            </esri:outFields>
        </esri:Query>

        <esri:GeometryService id="geometryService"
                              fault="geometryService_faultHandler(event)"
                              relationComplete="geometryService_relationCompleteHandler(event)"
                              showBusyCursor="true"
                              url="http://sampleserver6.arcgisonline.com/arcgis/rest/services/Utilities/Geometry/GeometryServer"/>

        <esri:SimpleMarkerSymbol id="blueSquareSMS"
                                 alpha="1"
                                 color="0x0000FF"
                                 size="8"
                                 style="square"/>
    </fx:Declarations>

    <s:controlBarLayout>
        <s:VerticalLayout gap="10"
                          paddingBottom="7"
                          paddingLeft="10"
                          paddingRight="10"
                          paddingTop="7"/>
    </s:controlBarLayout>
    <s:controlBarContent>
        <s:RichText width="100%">
            This sample demonstrates how to use the Geometry Service to determine spatial
            relationships between two sets of geometries. Click the map to generate
            a 2 minute drive time service area which will be used to query the census service
            for blockgroup points within the network service area.  The geometry service
            will then determine if the blockgroup points are 'within' the network service area.
        </s:RichText>
        <s:Label id="messages"
                 width="100%"
                 fontSize="12"
                 fontWeight="bold"/>
    </s:controlBarContent>

    <esri:Map id="myMap" mapClick="myMap_mapClickHandler(event)">
        <esri:extent>
            <esri:Extent xmin="-10605000" ymin="4712000" xmax="-10601000" ymax="4718000">
                <esri:SpatialReference wkid="102100"/>
            </esri:Extent>
        </esri:extent>
        <esri:ArcGISTiledMapServiceLayer url="http://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer"/>
    </esri:Map>

    <s:BorderContainer id="driveTime"
                       backgroundAlpha="0.5"
                       backgroundColor="0x353930"
                       borderColor="0x353930"
                       cornerRadius="5"
                       horizontalCenter="0"
                       verticalCenter="0"
                       visible="{progressMessage.visible}">
        <s:layout>
            <s:VerticalLayout horizontalAlign="center"
                              paddingBottom="5"
                              paddingLeft="5"
                              paddingRight="5"
                              paddingTop="5"/>
        </s:layout>
        <components:ProgressMessage id="progressMessage" textColor="0xE6E6E6"/>
    </s:BorderContainer>

    <s:BorderContainer id="Search"
                       width="250"
                       right="20" top="20"
                       backgroundAlpha="0.5"
                       backgroundColor="0x353930"
                       borderColor="0x353930"
                       cornerRadius="5">
        <s:layout>
            <s:HorizontalLayout horizontalAlign="center"
                                paddingBottom="5"
                                paddingLeft="5"
                                paddingRight="5"
                                paddingTop="5"/>
        </s:layout>

        <esri:Geocoder width="100%"
                       map="{myMap}"
                       prompt="Where do you want to go?"/>
    </s:BorderContainer>
</s:Application>