esri jsapi calculate distance

623
3
Jump to solution
01-07-2019 03:50 AM
MarkBalman
Occasional Contributor

Hi All

Are there any examples showing how to return the distance of objects (points, lines, polygons) that fall within a buffer of a user defined location? I cannot seem to find anything to help me apart from this DistanceParameters | API Reference | ArcGIS API for JavaScript 3.27

Any help or pointers much appreciated.

Thanks in advance.

Mark

0 Kudos
1 Solution

Accepted Solutions
RobertScheitlin__GISP
MVP Esteemed Contributor

Mark,

   Here is a sample I made that has the user click on the map and it buffers the click point by 1/4 mile and finds the Census block points in that buffer and displays the distance from the click point to all Census block points in that buffer and flashes the closest block point.

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <!--The viewport meta tag is used to improve the presentation and behavior of the samples
      on iOS devices-->
    <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
    <title>Select with feature layer</title>
    <link rel="stylesheet" href="https://js.arcgis.com/3.26/dijit/themes/tundra/tundra.css">
    <link rel="stylesheet" href="https://js.arcgis.com/3.26/esri/css/esri.css">
    <style>
      html, body, #mapDiv {
        padding: 0;
        margin: 0;
        height: 100%;
      }
      #messages{
        background-color: #fff;
        box-shadow: 0 0 5px #888;
        font-size: 1.1em;
        max-width: 15em;
        padding: 0.5em;
        position: absolute;
        right: 20px;
        top: 20px;
        z-index: 40;
      }
    </style>
    <script src="http://js.arcgis.com/3.26/"></script>
    <script>
      var map, userMP;
      require([
        "esri/map", "esri/layers/FeatureLayer", "dojo/_base/lang",
        "esri/tasks/query", "esri/geometry/Circle", "esri/layers/GraphicsLayer",
        "esri/graphic", "esri/InfoTemplate", "esri/symbols/SimpleMarkerSymbol",
        "esri/symbols/SimpleLineSymbol", "esri/symbols/SimpleFillSymbol", "esri/renderers/SimpleRenderer",
        "esri/config", "esri/Color", "dojo/dom", "esri/tasks/DistanceParameters", "esri/tasks/GeometryService",
        "dojo/promise/all", "dojox/gfx/fx", "dojo/domReady!"
      ], function(
        Map, FeatureLayer, lang,
        Query, Circle, GraphicsLayer,
        Graphic, InfoTemplate, SimpleMarkerSymbol,
        SimpleLineSymbol, SimpleFillSymbol, SimpleRenderer,
        esriConfig, Color, dom, DistanceParameters, GeometryService, all, fx
      ) {
        // use a proxy page if a URL generated by this page is greater than 2000 characters
        //
        // this should not be needed as nearly all query & select functions are performed on the client
        esriConfig.defaults.io.proxyUrl = "/proxy";

        map = new Map("mapDiv", {
          basemap: "streets",
          center: [-95.249, 38.954],
          zoom: 14,
          slider: false
        });

        //add the census block points in on demand mode. Note that an info template has been defined so when
        //selected features are clicked a popup window will appear displaying the content defined in the info template.
        var featureLayer = new FeatureLayer("http://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer/0",{
          infoTemplate: new InfoTemplate("Block: ${BLOCK}", "${*}"),
          outFields: ["POP2000","HOUSEHOLDS","HSE_UNITS", "TRACT", "BLOCK"]
        });

        // selection symbol used to draw the selected census block points within the buffer polygon
        var symbol = new SimpleMarkerSymbol(
          SimpleMarkerSymbol.STYLE_CIRCLE,
          12,
          new SimpleLineSymbol(
            SimpleLineSymbol.STYLE_NULL,
            new Color([247, 34, 101, 0.9]),
            1
          ),
          new Color([207, 34, 171, 0.5])
        );
        var symbol2 = new SimpleMarkerSymbol(
          SimpleMarkerSymbol.STYLE_DIAMOND,
          15,
          new SimpleLineSymbol(
            SimpleLineSymbol.STYLE_NULL,
            new Color([0, 0, 0, 0.9]),
            1
          ),
          new Color([255, 0, 0, 1])
        );

        var graphic;

        featureLayer.setSelectionSymbol(symbol);

        //make unselected features invisible
        var nullSymbol = new SimpleMarkerSymbol().setSize(0);
        featureLayer.setRenderer(new SimpleRenderer(nullSymbol));

        map.addLayer(featureLayer);

        var circleSymb = new SimpleFillSymbol(
          SimpleFillSymbol.STYLE_NULL,
          new SimpleLineSymbol(
            SimpleLineSymbol.STYLE_SHORTDASHDOTDOT,
            new Color([105, 105, 105]),
            2
          ), new Color([255, 255, 0, 0.25])
        );
        var circle;

        //when the map is clicked create a buffer around the click point of the specified distance.
        map.on("click", function(evt){
          userMP = evt.mapPoint;
          circle = new Circle({
            center: evt.mapPoint,
            geodesic: true,
            radius: 0.25,
            radiusUnit: "esriMiles"
          });
          map.graphics.clear();
          map.infoWindow.hide();
          var graphic = new Graphic(circle, circleSymb);
          map.graphics.add(graphic);

          var query = new Query();
          query.geometry = circle.getExtent();
          //use a fast bounding box query. will only go to the server if bounding box is outside of the visible map
          featureLayer.queryFeatures(query, selectInBuffer);
        });

        function selectInBuffer(response){
          var feature;
          var features = response.features;
          var inBuffer = [];
          //filter out features that are not actually in buffer, since we got all points in the buffer's bounding box
          for (var i = 0; i < features.length; i++) {
            feature = features[i];
            if(circle.contains(feature.geometry)){
              inBuffer.push(feature.attributes[featureLayer.objectIdField]);
            }
          }
          var query = new Query();
          query.objectIds = inBuffer;
          //use a fast objectIds selection query (should not need to go to the server)
          featureLayer.selectFeatures(query, FeatureLayer.SELECTION_NEW, function(results){
            findClosest(results);
          });
        }

        function findClosest(features) {
          var geometryService = new GeometryService("http://utility.arcgisonline.com/arcgis/rest/services/Geometry/GeometryServer");
          var promises;
          var dlist = [];
          var list = [];
          var rstr = "";
          var graphicFlash;
          var BLOCK, ptloc, distp;
          for (var x = 0; x < features.length; x++) {
            var distParams = new DistanceParameters();
            distParams.distanceUnit = GeometryService.UNIT_FOOT;
            distParams.geometry1 = userMP;
            distParams.geodesic = true;
            BLOCK = features[x].attributes["BLOCK"];
            ptloc = features[x].geometry;
            distParams.geometry2 = features[x].geometry;
            dlist.push({name:BLOCK, loc:ptloc});

            distp = geometryService.distance(distParams);
            list.push(distp)
          }
          all(list).then(lang.hitch(this,function(results){
            var shortestDist = Number.POSITIVE_INFINITY;
            var closestBlock = "";
            for (var dv = 0; dv < dlist.length; dv++){
              rstr += "Block: " + dlist[dv].name + " is " + results[dv].toFixed(2) + "ft<br>";
              if(results[dv] < shortestDist){
                shortestDist = results[dv];
                closestBlock = dlist[dv].name;
                graphicFlash = new esri.Graphic(dlist[dv].loc, symbol2)
              }
            }
            rstr += "<br><strong>The closest Block is " + closestBlock + " at " + shortestDist.toFixed(2) + "ft</strong>"
            dom.byId("messages").innerHTML = rstr;
            map.graphics.add(graphicFlash);

            var shape = graphicFlash.getDojoShape();
            var animStroke = fx.animateStroke({
                shape: shape,
                duration: 500,
                color: { end: new dojo.Color([0, 0, 0, 0]) }
            });
            var animFill = fx.animateFill({
                shape: shape,
                duration: 500,
                color: { end: new dojo.Color([0, 0, 0, 0]) }
            });
            var anim = dojo.fx.combine([animStroke, animFill]).play();
            var animConnect = dojo.connect(anim, "onEnd", function () {
                map.graphics.remove(graphicFlash);
            });

          }));
        }
      });
    </script>
  </head>

  <body>
    <span id="messages">Click on the map to select census block points within 1/4 mile and calculate the distance from map click to each block.</span>
    <div id="mapDiv"></div>
  </body>
</html>

View solution in original post

3 Replies
RobertScheitlin__GISP
MVP Esteemed Contributor

Mark,

   Here is a sample I made that has the user click on the map and it buffers the click point by 1/4 mile and finds the Census block points in that buffer and displays the distance from the click point to all Census block points in that buffer and flashes the closest block point.

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <!--The viewport meta tag is used to improve the presentation and behavior of the samples
      on iOS devices-->
    <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
    <title>Select with feature layer</title>
    <link rel="stylesheet" href="https://js.arcgis.com/3.26/dijit/themes/tundra/tundra.css">
    <link rel="stylesheet" href="https://js.arcgis.com/3.26/esri/css/esri.css">
    <style>
      html, body, #mapDiv {
        padding: 0;
        margin: 0;
        height: 100%;
      }
      #messages{
        background-color: #fff;
        box-shadow: 0 0 5px #888;
        font-size: 1.1em;
        max-width: 15em;
        padding: 0.5em;
        position: absolute;
        right: 20px;
        top: 20px;
        z-index: 40;
      }
    </style>
    <script src="http://js.arcgis.com/3.26/"></script>
    <script>
      var map, userMP;
      require([
        "esri/map", "esri/layers/FeatureLayer", "dojo/_base/lang",
        "esri/tasks/query", "esri/geometry/Circle", "esri/layers/GraphicsLayer",
        "esri/graphic", "esri/InfoTemplate", "esri/symbols/SimpleMarkerSymbol",
        "esri/symbols/SimpleLineSymbol", "esri/symbols/SimpleFillSymbol", "esri/renderers/SimpleRenderer",
        "esri/config", "esri/Color", "dojo/dom", "esri/tasks/DistanceParameters", "esri/tasks/GeometryService",
        "dojo/promise/all", "dojox/gfx/fx", "dojo/domReady!"
      ], function(
        Map, FeatureLayer, lang,
        Query, Circle, GraphicsLayer,
        Graphic, InfoTemplate, SimpleMarkerSymbol,
        SimpleLineSymbol, SimpleFillSymbol, SimpleRenderer,
        esriConfig, Color, dom, DistanceParameters, GeometryService, all, fx
      ) {
        // use a proxy page if a URL generated by this page is greater than 2000 characters
        //
        // this should not be needed as nearly all query & select functions are performed on the client
        esriConfig.defaults.io.proxyUrl = "/proxy";

        map = new Map("mapDiv", {
          basemap: "streets",
          center: [-95.249, 38.954],
          zoom: 14,
          slider: false
        });

        //add the census block points in on demand mode. Note that an info template has been defined so when
        //selected features are clicked a popup window will appear displaying the content defined in the info template.
        var featureLayer = new FeatureLayer("http://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer/0",{
          infoTemplate: new InfoTemplate("Block: ${BLOCK}", "${*}"),
          outFields: ["POP2000","HOUSEHOLDS","HSE_UNITS", "TRACT", "BLOCK"]
        });

        // selection symbol used to draw the selected census block points within the buffer polygon
        var symbol = new SimpleMarkerSymbol(
          SimpleMarkerSymbol.STYLE_CIRCLE,
          12,
          new SimpleLineSymbol(
            SimpleLineSymbol.STYLE_NULL,
            new Color([247, 34, 101, 0.9]),
            1
          ),
          new Color([207, 34, 171, 0.5])
        );
        var symbol2 = new SimpleMarkerSymbol(
          SimpleMarkerSymbol.STYLE_DIAMOND,
          15,
          new SimpleLineSymbol(
            SimpleLineSymbol.STYLE_NULL,
            new Color([0, 0, 0, 0.9]),
            1
          ),
          new Color([255, 0, 0, 1])
        );

        var graphic;

        featureLayer.setSelectionSymbol(symbol);

        //make unselected features invisible
        var nullSymbol = new SimpleMarkerSymbol().setSize(0);
        featureLayer.setRenderer(new SimpleRenderer(nullSymbol));

        map.addLayer(featureLayer);

        var circleSymb = new SimpleFillSymbol(
          SimpleFillSymbol.STYLE_NULL,
          new SimpleLineSymbol(
            SimpleLineSymbol.STYLE_SHORTDASHDOTDOT,
            new Color([105, 105, 105]),
            2
          ), new Color([255, 255, 0, 0.25])
        );
        var circle;

        //when the map is clicked create a buffer around the click point of the specified distance.
        map.on("click", function(evt){
          userMP = evt.mapPoint;
          circle = new Circle({
            center: evt.mapPoint,
            geodesic: true,
            radius: 0.25,
            radiusUnit: "esriMiles"
          });
          map.graphics.clear();
          map.infoWindow.hide();
          var graphic = new Graphic(circle, circleSymb);
          map.graphics.add(graphic);

          var query = new Query();
          query.geometry = circle.getExtent();
          //use a fast bounding box query. will only go to the server if bounding box is outside of the visible map
          featureLayer.queryFeatures(query, selectInBuffer);
        });

        function selectInBuffer(response){
          var feature;
          var features = response.features;
          var inBuffer = [];
          //filter out features that are not actually in buffer, since we got all points in the buffer's bounding box
          for (var i = 0; i < features.length; i++) {
            feature = features[i];
            if(circle.contains(feature.geometry)){
              inBuffer.push(feature.attributes[featureLayer.objectIdField]);
            }
          }
          var query = new Query();
          query.objectIds = inBuffer;
          //use a fast objectIds selection query (should not need to go to the server)
          featureLayer.selectFeatures(query, FeatureLayer.SELECTION_NEW, function(results){
            findClosest(results);
          });
        }

        function findClosest(features) {
          var geometryService = new GeometryService("http://utility.arcgisonline.com/arcgis/rest/services/Geometry/GeometryServer");
          var promises;
          var dlist = [];
          var list = [];
          var rstr = "";
          var graphicFlash;
          var BLOCK, ptloc, distp;
          for (var x = 0; x < features.length; x++) {
            var distParams = new DistanceParameters();
            distParams.distanceUnit = GeometryService.UNIT_FOOT;
            distParams.geometry1 = userMP;
            distParams.geodesic = true;
            BLOCK = features[x].attributes["BLOCK"];
            ptloc = features[x].geometry;
            distParams.geometry2 = features[x].geometry;
            dlist.push({name:BLOCK, loc:ptloc});

            distp = geometryService.distance(distParams);
            list.push(distp)
          }
          all(list).then(lang.hitch(this,function(results){
            var shortestDist = Number.POSITIVE_INFINITY;
            var closestBlock = "";
            for (var dv = 0; dv < dlist.length; dv++){
              rstr += "Block: " + dlist[dv].name + " is " + results[dv].toFixed(2) + "ft<br>";
              if(results[dv] < shortestDist){
                shortestDist = results[dv];
                closestBlock = dlist[dv].name;
                graphicFlash = new esri.Graphic(dlist[dv].loc, symbol2)
              }
            }
            rstr += "<br><strong>The closest Block is " + closestBlock + " at " + shortestDist.toFixed(2) + "ft</strong>"
            dom.byId("messages").innerHTML = rstr;
            map.graphics.add(graphicFlash);

            var shape = graphicFlash.getDojoShape();
            var animStroke = fx.animateStroke({
                shape: shape,
                duration: 500,
                color: { end: new dojo.Color([0, 0, 0, 0]) }
            });
            var animFill = fx.animateFill({
                shape: shape,
                duration: 500,
                color: { end: new dojo.Color([0, 0, 0, 0]) }
            });
            var anim = dojo.fx.combine([animStroke, animFill]).play();
            var animConnect = dojo.connect(anim, "onEnd", function () {
                map.graphics.remove(graphicFlash);
            });

          }));
        }
      });
    </script>
  </head>

  <body>
    <span id="messages">Click on the map to select census block points within 1/4 mile and calculate the distance from map click to each block.</span>
    <div id="mapDiv"></div>
  </body>
</html>
MarkBalman
Occasional Contributor

Hi Robert

Many thanks for this, I will have a look and see if I can decipher this and get the functions to work within my tool.

Very best regards,

Mark

0 Kudos
RobertScheitlin__GISP
MVP Esteemed Contributor

Mark,

   When you do, don't forget to come back and mark this question as answered.

0 Kudos