Data expression to calculate area of two overlapping weblayers in a dashboard element

895
8
Jump to solution
12-06-2022 08:21 PM
JasmineSpring
New Contributor III

Hello 

I am trying to add an indicator to my dashboard which shows the hectares of where two polygons weblayers intersect. ie the hectares of sugarcane in treatment areas. I am new to arcade so a little unsure whats the best way to go about it.  The final feature set I would like to have the TA name, TA hectares, (from the TA weblayer) and the hectares in sugarcane (intersection calculation).

// Access data layers from portal


var port = Portal( 'http://www.argis.com')
var TA = FeatureSetByPortalItem(Port, 'itemid', 0)
var sugar = FeatureSetByPortalItem(Port,'itemid', 0)

//Create empty dictionary  
var sugarDict = {
Fields:[
{name: “TA Name”, type:esriFieldString”},
{name: “TA Ha”, type:esriFieldDouble”},
{name: “TA Sugar”, type:esriFieldDouble”},
],
Geometry type: '',
Features: [],
};


// calculate area of sugarcane in Treatment area - below works fine in pop up


var intlayer = Intersects(TA, sugar);
var intcount = Count(intlayer);
var sugarinTA = 0;
if(intcount > 0){
for(var sugararea in intlayer){
sugarinTA += Area(Intersection(TA, sugararea),"hectares")
}
}


// Loop through and store attributes in dictionary - not sure how to do this


for (var t in TA){
var feat = {
attributes: {
TA Name: t["name"],
TA Ha: t["Area_ha"],
for (var s in sugarinTA){
var feat = {
attributes: {
TA Sugar: s["sugarinTA"],
}
}
} Push(features,feat);

Thank you so much for your help 

0 Kudos
3 Solutions

Accepted Solutions
JohannesLindner
MVP Frequent Contributor
// Access data layers from portal
var port = Portal( 'http://www.argis.com')
var fsTa = FeatureSetByPortalItem(port, 'itemid', 0)
var fsSugar = FeatureSetByPortalItem(port,'itemid', 0)

//Create empty dictionary  
var sugarDict = {
    fields:[
        {name: "TA_Name", type: "esriFieldTypeString"},
        {name: "TA_Ha", type: "esriFieldTypeDouble"},
        {name: "TA_Sugar", type: "esriFieldTypeDouble"},
    ],
    geometryType: "",
    features: [],
}

// loop over the treatment area features
for(var ta in fsTa) {
    // get the intersecting sugar areas
    var intSugar = Intersects(fsSugar, ta)
    // loop over those features and get the sum of the intersections
    var sugarArea = 0
    for(var s in intSugar) {
        sugarArea += Area(Intersection(s, ta), "hectares")
    }
    // append to the output dict
    var f = {attributes: {TA_Name: ta.Name, TA_Ha: Area(ta, "hectares"), TA_Sugar: sugarArea}}
    Push(sugarDict.features, f)
}

// convert to Featureset and return
return Featureset(Text(sugarDict))

Have a great day!
Johannes

View solution in original post

JohannesLindner
MVP Frequent Contributor

To format code:

JohannesLindner_0-1670479234964.png

JohannesLindner_1-1670479257402.png

 

I have added another layer area intersect, it appears as a field but it isn't calculating the intersect area

That's because you put the intersection with the WHA features into a separate loop. They get calculated and then overwritten without being stored in the output dict.

 

I would also like to group the phases

Fortunately, that is quite simple in this case. I used When() to get a default value if the phase is in neither of those groups (eg when it's null).

 

 

// blablabla

// loop over the treatment area features
for(var ta in fsTa) {
    // get the intersecting sugar area
    var intSugar = Intersects(fsSugar, ta)
    var sugarArea = 0
    for(var s in intSugar) {
        sugarArea += AreaGeodetic(Intersection(s, ta), "hectares")
    }
    // get the intersecting WHA area
    var intWHA = Intersects(fsWHA, ta)
    var WHAArea = 0
    for(var w in intWHA) {
        WHAArea += AreaGeodetic(Intersection(w, ta), "hectares")
    }
    // calculate the status value
    var status = When(Includes([1, 2, 3, 4], ta.Phase), "Status 1", ta.Phase == 5, "Status 2", "Default")
    // append to the output dict
    var f = {attributes: {TA_Status: status, TA_Ha: AreaGeodetic(ta, "hectares"), TA_Sugar: sugarArea, TA_WHA: WHAArea}}
    Push(combinedDict.features, f)
}

// convert to Featureset and return
return Featureset(Text(combinedDict))

 

 


Have a great day!
Johannes

View solution in original post

JohannesLindner
MVP Frequent Contributor

Loop through Rounds - would grouping it like above allow me to loop through the rounds and buffer and union them??

No, it's a little more complicated. Buffer(), Union(), and Area() don't work on Featuresets, only on singular Features/Geometries (or arrays in case of Union), so you need two loops:

  • outer loop over the rounds
  • inner loop over the features of a round

Something like this should work:

// we don't need GroupBy, we just need the distinct values of the group field
var treatmentRounds = Distinct(fsTreatment, "Round_Num")

// outer loop over the rounds
for(var treatRound in treatmentRounds) {
    // filter the Featureset
    var roundNum = treatRound.Round_Num
    var query = "Round_Num " + Iif(roundNum == null, "IS NULL", "= @roundNum")
    var filtered = Filter(fsTreatment, query)
    // inner loop over the filtered features
    var buffer_geometries = []
    for(var f in filtered) {
        // buffer the geometry and append it to the array
        Push(buffer_geometries, Buffer(f, 5, "meters"))
    }
    // union all those buffers into one geometry
    var union_geometry = Union(buffer_geometries)
    // create the new output feature
    var f = {attributes: {Round_Treat: roundNum, Area_Treat: AreaGeodetic(union_geometry, "hectares")}}
    Push(TreatmentDict.features, f)
}
return Featureset(Text(TreatmentDict))

Have a great day!
Johannes

View solution in original post

8 Replies
JohannesLindner
MVP Frequent Contributor
// Access data layers from portal
var port = Portal( 'http://www.argis.com')
var fsTa = FeatureSetByPortalItem(port, 'itemid', 0)
var fsSugar = FeatureSetByPortalItem(port,'itemid', 0)

//Create empty dictionary  
var sugarDict = {
    fields:[
        {name: "TA_Name", type: "esriFieldTypeString"},
        {name: "TA_Ha", type: "esriFieldTypeDouble"},
        {name: "TA_Sugar", type: "esriFieldTypeDouble"},
    ],
    geometryType: "",
    features: [],
}

// loop over the treatment area features
for(var ta in fsTa) {
    // get the intersecting sugar areas
    var intSugar = Intersects(fsSugar, ta)
    // loop over those features and get the sum of the intersections
    var sugarArea = 0
    for(var s in intSugar) {
        sugarArea += Area(Intersection(s, ta), "hectares")
    }
    // append to the output dict
    var f = {attributes: {TA_Name: ta.Name, TA_Ha: Area(ta, "hectares"), TA_Sugar: sugarArea}}
    Push(sugarDict.features, f)
}

// convert to Featureset and return
return Featureset(Text(sugarDict))

Have a great day!
Johannes
JasmineSpring
New Contributor III

 

Legendary ! That worked perfectly.

I have added another layer area intersect, it appears as a field but it isn't calculating the intersect area. I have added it below.

I would also like to group the phases, they are 1,2,3,4,5. and I would like to group them into two categories (5) and (1,2,3,4)

Thank you so much for your help

 

var port = Portal( 'https://qgsp.maps.arcgis.com/')
var fsTa = FeatureSetByPortalItem(port, 'item', 0)
var fsWHA = FeatureSetByPortalItem(port,'item', 0)
var fsSugar = FeatureSetByPortalItem(port,'item', 0)

//Create empty dictionary
var CombinedDict = {
fields:[
{name: "TA_Status", type: "esriFieldTypeString"},
{name: "TA_Ha", type: "esriFieldTypeDouble"},
{name: "TA_WHA", type: "esriFieldTypeDouble"},
{name: "TA_Sugar", type: "esriFieldTypeDouble"},
],
geometryType: "",
features: [],
}

// loop over the treatment area features
for(var t in fsTa) {
// get the intersecting sugar areas
var intWHA = Intersects(fsWHA, t)
// loop over those features and get the sum of the intersections
var WHAArea = 0
for(var w in intWHA) {
WHAArea += AreaGeodetic(Intersection(w, t), "hectares")
}


// loop over the treatment area features
for(var ta in fsTa) {
// get the intersecting sugar areas
var intSugar = Intersects(fsSugar, ta)
// loop over those features and get the sum of the intersections
var sugarArea = 0
for(var s in intSugar) {
sugarArea += Area(Intersection(s, ta), "hectares")
}


// append to the output dict
var f = {attributes: {TA_Status:ta.Phase, TA_Ha: AreaGeodetic(ta, "hectares"), TA_WHA: WHAArea,TA_Sugar: sugarArea}}
Push(CombinedDict.features, f)
}

// convert to Featureset and return
return Featureset(Text(CombinedDict))}

0 Kudos
JohannesLindner
MVP Frequent Contributor

To format code:

JohannesLindner_0-1670479234964.png

JohannesLindner_1-1670479257402.png

 

I have added another layer area intersect, it appears as a field but it isn't calculating the intersect area

That's because you put the intersection with the WHA features into a separate loop. They get calculated and then overwritten without being stored in the output dict.

 

I would also like to group the phases

Fortunately, that is quite simple in this case. I used When() to get a default value if the phase is in neither of those groups (eg when it's null).

 

 

// blablabla

// loop over the treatment area features
for(var ta in fsTa) {
    // get the intersecting sugar area
    var intSugar = Intersects(fsSugar, ta)
    var sugarArea = 0
    for(var s in intSugar) {
        sugarArea += AreaGeodetic(Intersection(s, ta), "hectares")
    }
    // get the intersecting WHA area
    var intWHA = Intersects(fsWHA, ta)
    var WHAArea = 0
    for(var w in intWHA) {
        WHAArea += AreaGeodetic(Intersection(w, ta), "hectares")
    }
    // calculate the status value
    var status = When(Includes([1, 2, 3, 4], ta.Phase), "Status 1", ta.Phase == 5, "Status 2", "Default")
    // append to the output dict
    var f = {attributes: {TA_Status: status, TA_Ha: AreaGeodetic(ta, "hectares"), TA_Sugar: sugarArea, TA_WHA: WHAArea}}
    Push(combinedDict.features, f)
}

// convert to Featureset and return
return Featureset(Text(combinedDict))

 

 


Have a great day!
Johannes
JasmineSpring
New Contributor III

Thank you very much 🙂 Your help is greatly appreciated!  thanks for showing me how to add code also.

I couldnt see a dissolve function or option in the buffer function. Is it possible to buffer and dissolve overlapping buffers? I would like to Group a weblayer by Rounds, buffer each round and calculate the area in my dashboard element.

Thank you 🙂

 

 

 

0 Kudos
JohannesLindner
MVP Frequent Contributor

There is Union(). With that, you can union/dissolve multiple geometries.

 


Have a great day!
Johannes
0 Kudos
JasmineSpring
New Contributor III

Thank you!

Ok Im trying to give it a go.  Would you use a group by to separate the web layer by rounds? (there is a round field) then loop through with a buffer then a union then area? The final featureset would just have area in hectares for each round

 

// Access data layers from portal
var port = Portal( 'https://arcgis.com/')
var fsTreatment = FeatureSetByPortalItem(port, 'itemID', 0)

//Create empty dictionary  
var TreatmentDict = {
    fields:[
        {name: "Round_Treat", type: "esriFieldTypeString"},
        {name: "Area_Treat", type: "esriFieldTypeDouble"},
            ],
    geometryType: "",
    features: [],
}

// Group feature set by round  

Var treatmentround = GroupBy(fstreatment,'Round_Num',COUNT)

//Loop through Rounds - would grouping it like above allow me to loop through the rounds and buffer and union them??

// buffer group 5m
var treatbuff = BufferGeodetic(treatmentround, 5,'meters')

// union to dissolve
var treatunion = Union(treatbuff)

// calculate area treated
var AreaTreated = AreaGeodetic(treatunion, "hectares")

// append to the output dict
var f = {attributes: {Round_Treat: .Round_num , Area_Treat: AreaTreated }}
Push(TreatmentDict.features, f)
}

// convert to Featureset and return
return Featureset(Text(TreatmentDict))

 

0 Kudos
JohannesLindner
MVP Frequent Contributor

Loop through Rounds - would grouping it like above allow me to loop through the rounds and buffer and union them??

No, it's a little more complicated. Buffer(), Union(), and Area() don't work on Featuresets, only on singular Features/Geometries (or arrays in case of Union), so you need two loops:

  • outer loop over the rounds
  • inner loop over the features of a round

Something like this should work:

// we don't need GroupBy, we just need the distinct values of the group field
var treatmentRounds = Distinct(fsTreatment, "Round_Num")

// outer loop over the rounds
for(var treatRound in treatmentRounds) {
    // filter the Featureset
    var roundNum = treatRound.Round_Num
    var query = "Round_Num " + Iif(roundNum == null, "IS NULL", "= @roundNum")
    var filtered = Filter(fsTreatment, query)
    // inner loop over the filtered features
    var buffer_geometries = []
    for(var f in filtered) {
        // buffer the geometry and append it to the array
        Push(buffer_geometries, Buffer(f, 5, "meters"))
    }
    // union all those buffers into one geometry
    var union_geometry = Union(buffer_geometries)
    // create the new output feature
    var f = {attributes: {Round_Treat: roundNum, Area_Treat: AreaGeodetic(union_geometry, "hectares")}}
    Push(TreatmentDict.features, f)
}
return Featureset(Text(TreatmentDict))

Have a great day!
Johannes
KristaSchultze123
New Contributor

Hi @JohannesLindner

I am trying to do something similar by intersecting two polygon layers to get the area of the resulting intersections.

So far I have: 

var p = Portal('https://csparks.maps.arcgis.com')
var itemid = '617b93e023cc463d862f5e6836d9a8d1'
var layerid = 0
var fs1 = (FeatureSetByPortalItem(p, itemid, layerid, ['*'], true))
var itemid2 = '91196d5c9aaf4144be3a0be62de8d524'
var layerid2 = 0
var fs2 = (FeatureSetByPortalItem(p, itemid2, layerid2, ['*'], true))

var Dict = {
fields:[
   {name:"park", type: "esriFieldTypeString"},
   {name: "space", type: "esriFieldTypeDouble"},
  ],
  geometryType:"",
  features:[],
  }

var i = 0
for (var block in fs2){
  var intersect = Intersects(fs1, block)
  var cnt = Count(intersect)
  var riskArea = 0
  for (var r in intersect){
    riskArea += Area(Intersection(r, block), "hectares")
}
  var feat = {
            attributes: {
                park: block ['UNITNAME'],
                space: Round(riskArea, 2)
            }}
  Push(Dict.features, feat)
}
i++;
return FeatureSet (Text(Dict))

 

I am able to successfully get the count of each intersect if I do:

 var feat = {
            attributes: {
                park: block ['UNITNAME'],
                space: Text(cnt)
            }}

 

but then when I add lines 22, 23, and 24 I end up getting "Test execution error: Unknown Error. Verify test data."

Thank you!

0 Kudos