List nearest feature nearby in popup

809
12
Jump to solution
07-15-2022 08:56 AM
Labels (2)
DavideDi_Carlo
New Contributor

Hi, I would to show the list of nearest substations in the popup of the areas that I'm drawing. 
I've managed to get it in the console but I cannot return a list; it would be gret if i could display the first 3 from the nearest

var substat= FeatureSetByName($map,"OSM SUBSTATION",["operator"])
var searchDistance = 10;
var inrangesubstat = Intersects( substat, Buffer( $feature, searchDistance, "kilometers") );
var cnt = Count(inrangesubstat)
Console("Total Number of addresses within "+searchDistance+ "km buffer: " + cnt);
var min_dist = infinity;

for (var f in inrangesubstat){
var substDist = Round(Distance(f,$feature,"kilometers"),2);
if (substDist < min_dist) {
inrangesubstat = f.operator;
min_dist = substDist;
}
console(f.operator + ": " + " " + substDist);
}

return f.operator+": at " + " " + substDist+" km"

 

thanks

0 Kudos
2 Solutions

Accepted Solutions
KenBuja
MVP Esteemed Contributor

In that case, push the values into an array of objects ([operator, Name, distance]), sort them by distance (item 2 in the array) and write out the three smallest results.

 

// define Sort comparator returning IsEmpty as least values
function Comp(a,b) { 
    When(
        IsEmpty(a[2]), -1,
        IsEmpty(b[2]), 1,
        IIf(a[2]>b[2],1,-1)
    );
};

var substat= FeatureSetByName($map,"OSM_SUBSTATION",["operator","Name"])
var inrangesubstat = Intersects(substat, Buffer($feature, 10, "kilometers"));

// Cycle through the polygon features and find the nearest one
var geomArea=Geometry($feature)
var arr = Array(0);
var counter = 0;
for (var f in inrangesubstat){
  var geomSub=Geometry(f)
  var substDist = Round(Distance(geomArea,geomSub, "kilometers"),2);
  arr[counter] = [f.operator, f.name, substDist];
  counter++;
}
arr = Reverse(Sort(arr, Comp));
var lbl = '';
for (var k in arr){
  if (k < 3) {
    lbl = Concatenate([lbl, `${arr[k][2]} km: ${arr[k][0]}-${arr[k][1]}`], TextFormatting.NewLine);}
  }
}
return lbl;

 

This is using the sorting method from this post.

View solution in original post

KenBuja
MVP Esteemed Contributor

Start with the Arcade documentation to get a better understanding of all the functions and how to use them.

I would also suggest the Esri Training site. They have several lessons (videos or web courses) that are free or could be covered by a maintenance agreement. They also have curated learning plans (such as this one) that are more guided.

View solution in original post

0 Kudos
12 Replies
KenBuja
MVP Esteemed Contributor

You can use the OrderBy function to sort the inrangesubstat featureset, then use the Top function to get the top three values.

Take a look at this post to see an example

0 Kudos
DavideDi_Carlo
New Contributor

Thanks for the answer, but the Orderby function is ordering the Substation operator name, I would to order it by substdistance, that is a variable calculated in the loop.

var substat= FeatureSetByName($map,"OSM_SUBSTATION",["operator","Name"])
var inrangesubstat = Intersects(substat, Buffer($feature, 10, "kilometers"));

// Cycle through the polygon features and find the nearest one
var min_dist = infinity; 
var geomArea=Geometry($feature)
var nearestSubstat=null;
for (var f in inrangesubstat){
var geomSub=Geometry(f)
var substDist = Round(Distance(geomArea,geomSub, "kilometers"),2);
if (substDist <min_dist) {
min_dist=substDist
nearestSubstat = substDist+ "km: " + " " +f.operator+"-"+f.name
}
console(f.operator + ": " + " " + substDist);
}
return nearestSubstat

 

@KenBuja 
@XanderBakker 

0 Kudos
KenBuja
MVP Esteemed Contributor

In that case, push the values into an array of objects ([operator, Name, distance]), sort them by distance (item 2 in the array) and write out the three smallest results.

 

// define Sort comparator returning IsEmpty as least values
function Comp(a,b) { 
    When(
        IsEmpty(a[2]), -1,
        IsEmpty(b[2]), 1,
        IIf(a[2]>b[2],1,-1)
    );
};

var substat= FeatureSetByName($map,"OSM_SUBSTATION",["operator","Name"])
var inrangesubstat = Intersects(substat, Buffer($feature, 10, "kilometers"));

// Cycle through the polygon features and find the nearest one
var geomArea=Geometry($feature)
var arr = Array(0);
var counter = 0;
for (var f in inrangesubstat){
  var geomSub=Geometry(f)
  var substDist = Round(Distance(geomArea,geomSub, "kilometers"),2);
  arr[counter] = [f.operator, f.name, substDist];
  counter++;
}
arr = Reverse(Sort(arr, Comp));
var lbl = '';
for (var k in arr){
  if (k < 3) {
    lbl = Concatenate([lbl, `${arr[k][2]} km: ${arr[k][0]}-${arr[k][1]}`], TextFormatting.NewLine);}
  }
}
return lbl;

 

This is using the sorting method from this post.

DavideDi_Carlo
New Contributor

Worked great! Thanks a lot

// define Sort comparator returning IsEmpty as least values
function Comp(a,b) {
When(
IsEmpty(a[2]), -1,
IsEmpty(b[2]), 1,
IIf(a[2]<b[2],1,-1)
);
};

var substat= FeatureSetByName($map,"OSM_SUBSTATION",["operator","Name"])
var inrangesubstat = Intersects(substat, Buffer($feature, 10, "kilometers"));

// Cycle through the polygon features and find the nearest one
var geomArea=Geometry($feature)
var arr = Array(0);
var counter = 0;
for (var f in inrangesubstat){
var geomSub=Geometry(f)
var substDist = Round(Distance(geomArea,geomSub, "kilometers"),2);
arr[counter] = [f.operator, f.name, substDist];
counter++;
}
arr = Reverse(Sort(arr, Comp));
var lbl = '';
for (var k in arr){
if (k < 3) {
lbl = Concatenate([lbl,`${arr[k][2]} km: ${arr[k][0]}-${arr[k][1]}`], TextFormatting.NewLine);}
}

return lbl;

 




0 Kudos
SpatialSean
New Contributor III

Ken I have a similar question.  I have 2 feature layers in a map - situations, stores.  

For the Situations popup I want to return any stores that are within a 2 mile radius but I can't seem to get it to work to even simply count the number stores.  

Any thoughts?

0 Kudos
KenBuja
MVP Esteemed Contributor

What have you tried so far?

0 Kudos
SpatialSean
New Contributor III

I started with this blog: https://www.esri.com/arcgis-blog/products/arcgis-online/data-management/your-arcade-questions-answer...

where I used the earthquake map listed there as basis - Nearby Cities - Closest to Furthest:

// Exclude the following calculation if the event
// magnitude is less than 3
if($feature.mag < 3) {
   return ""
}

// Create a 250km buffer around the selected earthquake event
var buff = BufferGeodetic($feature, 250, 'kilometers')

// Return City Centres within 250km of what's selected
var portal = Portal("https://www.arcgis.com")
var cityLocations = FeatureSetByPortalItem(portal,"6996f03a1b364dbab4008d99380370ed", 0, ['CITY_NAME'])
var nearbyCities = Intersects(cityLocations, buff)

var cities = []

for(var f in nearbyCities){
    var distance = DistanceGeodetic(f, $feature, "kilometers")
    var city_distance = Dictionary("CityName",f.CITY_NAME,"Distance",distance)
    cities[Count(cities)] = city_distance 
}

function compareDistance(a,b){
  if(a['Distance']<b['Distance'])
    return -1;
  if(a['Distance']>b['Distance'])
    return 1;
  return 0;
}

// Build the pop-up string
var popup = ''
cities = Sort(cities, compareDistance)
for(var city in cities){
    popup += cities[city].CityName + ": " + ROUND(cities[city].Distance, 0) + "km" +
        TextFormatting.NewLine
}

//return pop-up string
When(Count(cities) == 0, TextFormatting.NewLine + TextFormatting.NewLine +
        "No major City Centres within 250km of event",

    Count(cities) == 1, TextFormatting.NewLine + TextFormatting.NewLine +
        "1 major City Centre within 250km of event" + TextFormatting.NewLine
        + popup,
    
    TextFormatting.NewLine + TextFormatting.NewLine +
        Count(cities) + " major City Centres within 250km of event" + TextFormatting.NewLine
        + popup)

 

From there I tried to swap in my stores dataset and commented out the < 3 magnitude.  Both FeatureSetByName and FeatureSetById but keep ending with null values.

0 Kudos
KenBuja
MVP Esteemed Contributor

Have you used "console" to see what values are in various places? This helps you track down where your code is failing.

For example

arcade.png

Also, have you updated the variables used in the arrays to match the attributes in your services? For example, in the line

var city_distance = Dictionary("CityName",f.CITY_NAME,"Distance",distance)

does your data contain the field "CITY_NAME"?

0 Kudos
SpatialSean
New Contributor III

Did not know about the console, very helpful.  I am able to return the correct # of stores but it seems to be something within city_distance

The field City exists 

// Exclude the following calculation if the event
// magnitude is less than 3
/*if($feature.mag < 3) {
   return ""
}
*/
// Create a 250km buffer around the selected earthquake event
var buff = BufferGeodetic($feature, 2, 'miles')

// Return City Centres within 250km of what's selected
var portal = Portal("https://www.arcgis.com")
var cityLocations = FeatureSetByPortalItem(portal,"42a5957d3d7741399e279eea3cf1506b", 0, ['City'])
var nearbyCities = Intersects(cityLocations, buff)

var cities = []

//console(count(cityLocations))  returns correct
//console(count(cities)) returns 0

for(var f in nearbyCities){
    var distance = DistanceGeodetic(f, $feature, "miles")
    var city_distance = Dictionary("City",f.City,"Distance",distance)
    cities[Count(cities)] = city_distance 
}
//console(count(cities)) returns 0

function compareDistance(a,b){
  if(a['Distance']<b['Distance'])
    return -1;
  if(a['Distance']>b['Distance'])
    return 1;
  return 0;
}

// Build the pop-up string
var popup = ''
cities = Sort(cities, compareDistance)
for(var city in cities){
    popup += cities[city].CityName + ": " + ROUND(cities[city].Distance, 0) + "mi" +
        TextFormatting.NewLine
}

//return pop-up string
When(Count(cities) == 0, TextFormatting.NewLine + TextFormatting.NewLine +
        "No Stores within 2 miles of event",

    Count(cities) == 1, TextFormatting.NewLine + TextFormatting.NewLine +
        "1 Store within 2 miles of event" + TextFormatting.NewLine
        + popup,
    
    TextFormatting.NewLine + TextFormatting.NewLine +
        Count(cities) + " Stores within 2 miles of event" + TextFormatting.NewLine
        + popup)

 

0 Kudos