Select to view content in your preferred language

Closest point on a polyline to an input point

4025
12
05-24-2011 08:42 AM
SamBishop
Deactivated User
Does anyone know the simplest way to return the closest point on a polyline to an input point using the javascript api with/without a geometry service?  Perhaps I need to publish my own geoprocessing service to best achieve this functionality?  Any pointers would be useful...thanks
0 Kudos
12 Replies
HemingZhu
Frequent Contributor
Does anyone know the simplest way to return the closest point on a polyline to an input point using the javascript api with/without a geometry service?  Perhaps I need to publish my own geoprocessing service to best achieve this functionality?  Any pointers would be useful...thanks


I don't think the geometry service provide such functionality. Best approach might be to create a gp service that include a modified version of Spatial Join system tool (Match Option set up as CLOSEST) to serve your puopose.
0 Kudos
JianHuang
Deactivated User
Please take a look at the document of SnappingManager. The method getSnappingPoint() returns a deferred object, which contains the closest point from a input location.
Hope this helps.
0 Kudos
SamBishop
Deactivated User
Please take a look at the document of SnappingManager. The method getSnappingPoint() returns a deferred object, which contains the closest point from a input location.
Hope this helps.


Thanks for this Jian.  I've tried to implement but not had much luck.  In the function below, I am looping through a set of polyline geometries, adding each one in turn to a graphics layer then attempting to calculate the snapping point (shortest diatance I hope) from a point feature (loc).  No joy, I don't see as much as an error message.  Chrome gives me a 'TypeError: Cannot read property 'valid' of undefined' on line 14 of the api which doesn't mean a whole lot to me...any ideas where I am going wrong?  Thanks

function pipeQueryCallback(pipeFeatureset){
  if (pipeFeatureset.features.length > 0) {
    var pipeGeoms = [];
    var snappingManager = new esri.SnappingManager({alwaysSnap:true,
      layerInfos:[{layer:graphicsLayer}],map:map,tolerance:1000});
    dojo.forEach(pipeFeatureset.features,function(pipeFeature){pipeGeoms.push(pipeFeature.geometry)});
      geomServ.intersect(pipeGeoms,bufferGeom,function(intPipeGeoms){
        dojo.forEach(intPipeGeoms,function(intPipeGeom){
          graphicsLayer.clear();
          graphicsLayer.add(new esri.Graphic(intPipeGeom,symPipeDist));
          var deferred = snappingManager.getSnappingPoint(loc);
          deferred.then(function(value){
            if(value !== undefined){
              map.graphics.add(value,new esri.symbol.SimpleMarkerSymbol());
            }
          },function(error){alert(error.message)});
        });
      });
  }
}
0 Kudos
GeorgeNewbury
Frequent Contributor
Depending on your data model you could make 'streets' out of your polylines, create an address locator service, and then do a reverse geocode.
0 Kudos
JianHuang
Deactivated User
Sam,

The method getSnappingPoint() accepts screen point, instead of map point. Please call map.toScreen before passing it to the method.
Thanks.
0 Kudos
GiedriusStanevi_ius
New Contributor
I had the same error with SnappingManager. Could it be that SnappingManager supports only WGS and Web Mercator spatial references? Couldn't find any information about such restriction, but the same code with other spatial references does not work
0 Kudos
SamBishop
Deactivated User
I had the same error with SnappingManager. Could it be that SnappingManager supports only WGS and Web Mercator spatial references? Couldn't find any information about such restriction, but the same code with other spatial references does not work


Using screen coords instead of map coords produces the same error.  Thanks for the tip giedrius_stanevicius, I am currently working in ED1950 GCS - I will test on a reprojected version.
0 Kudos
JianHuang
Deactivated User
SnappingManager works with any coordinate systems. Please try the code below. Hope this helps.

    <script type="text/javascript">
      dojo.require("esri.map");
      dojo.require("esri.layers.FeatureLayer");
      dojo.require("esri.SnappingManager");
      dojo.require("esri.toolbars.draw");

      var map, snap, featureLayer;

      function init() {
        var extent = new esri.geometry.Extent({"xmin":-96.6063,"ymin":38.3106,"xmax":-96.4764,
              "ymax":38.3689,"spatialReference":{"wkid":4269}});
        map = new esri.Map("map", { extent: esri.geometry.geographicToWebMercator(extent)});

        var imagery = new esri.layers.ArcGISTiledMapServiceLayer("http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer");
        map.addLayer(imagery);

        featureLayer = new esri.layers.FeatureLayer("http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Hydrography/Watershed173811/FeatureServer/1",{
          mode: esri.layers.FeatureLayer.MODE_ONDEMAND
        });
        map.addLayer(featureLayer);
        dojo.connect(map, "onLoad", initSnapping);
      }

      function initSnapping(themap) {
        var map = themap;        
        snap = new esri.SnappingManager({layerInfos:[{layer:featureLayer, snapToPoint: true, snapToEdge:false}], map: map, tolerence: 500});
        toolbar = new esri.toolbars.Draw(map);
        dojo.connect(toolbar, "onDrawEnd", addToMap);
      }

      function addToMap(geometry) {
        toolbar.deactivate();
        map.showZoomSlider();
        var sPt = map.toScreen(geometry);
        var defer = snap.getSnappingPoint(sPt);
        defer.addCallback(function(result){
            if(result){
              var snapPoint = result;
              console.log(snapPoint);
            }
        });

        var symbol = new esri.symbol.SimpleMarkerSymbol(esri.symbol.SimpleMarkerSymbol.STYLE_SQUARE, 10, new esri.symbol.SimpleLineSymbol(esri.symbol.SimpleLineSymbol.STYLE_SOLID, new dojo.Color([255, 0, 0]), 1), new dojo.Color([0, 255, 0, 0.25]));
        var graphic = new esri.Graphic(geometry, symbol);
        map.graphics.add(graphic);
      }

      dojo.addOnLoad(init);
    </script>
  </head>
  
  <body class="tundra">
    <button dojoType="dijit.form.Button" onClick="toolbar.activate(esri.toolbars.Draw.POINT);map.hideZoomSlider();">
      Point
    </button>
    </div>
    <div id="map" style="width:900px;height:500px;">
    </div>
  </body>
0 Kudos
SamBishop
Deactivated User
SnappingManager works with any coordinate systems. Please try the code below. Hope this helps.

    <script type="text/javascript">
      dojo.require("esri.map");
      dojo.require("esri.layers.FeatureLayer");
      dojo.require("esri.SnappingManager");
      dojo.require("esri.toolbars.draw");

      var map, snap, featureLayer;

      function init() {
        var extent = new esri.geometry.Extent({"xmin":-96.6063,"ymin":38.3106,"xmax":-96.4764,
              "ymax":38.3689,"spatialReference":{"wkid":4269}});
        map = new esri.Map("map", { extent: esri.geometry.geographicToWebMercator(extent)});

        var imagery = new esri.layers.ArcGISTiledMapServiceLayer("http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer");
        map.addLayer(imagery);

        featureLayer = new esri.layers.FeatureLayer("http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Hydrography/Watershed173811/FeatureServer/1",{
          mode: esri.layers.FeatureLayer.MODE_ONDEMAND
        });
        map.addLayer(featureLayer);
        dojo.connect(map, "onLoad", initSnapping);
      }

      function initSnapping(themap) {
        var map = themap;        
        snap = new esri.SnappingManager({layerInfos:[{layer:featureLayer, snapToPoint: true, snapToEdge:false}], map: map, tolerence: 500});
        toolbar = new esri.toolbars.Draw(map);
        dojo.connect(toolbar, "onDrawEnd", addToMap);
      }

      function addToMap(geometry) {
        toolbar.deactivate();
        map.showZoomSlider();
        var sPt = map.toScreen(geometry);
        var defer = snap.getSnappingPoint(sPt);
        defer.addCallback(function(result){
            if(result){
              var snapPoint = result;
              console.log(snapPoint);
            }
        });

        var symbol = new esri.symbol.SimpleMarkerSymbol(esri.symbol.SimpleMarkerSymbol.STYLE_SQUARE, 10, new esri.symbol.SimpleLineSymbol(esri.symbol.SimpleLineSymbol.STYLE_SOLID, new dojo.Color([255, 0, 0]), 1), new dojo.Color([0, 255, 0, 0.25]));
        var graphic = new esri.Graphic(geometry, symbol);
        map.graphics.add(graphic);
      }

      dojo.addOnLoad(init);
    </script>
  </head>
  
  <body class="tundra">
    <button dojoType="dijit.form.Button" onClick="toolbar.activate(esri.toolbars.Draw.POINT);map.hideZoomSlider();">
      Point
    </button>
    </div>
    <div id="map" style="width:900px;height:500px;">
    </div>
  </body>


Thanks for this Jian.  I don't have time to do any tests right now, but yesterday when I swapped out my WKID 4230 mapservice for an equivalent being served in 102100, the getSnappingPoint() method mysteriously started returning snap points.  I'll look at the code you provided when I get a chance and see if I can see anything in my code that might be causing this.
0 Kudos