get layer from webmap

5634
17
06-06-2020 03:12 PM
TroyBingaman
New Contributor

Hello,

I'm trying to learn the JS API.  I've been following through the tutorials and am currently on buffer and intersect geometry.

As I follow along sometimes I try to adjust things to further learn.  In this case, instead of using the example which uses a featureLayer, I want to do the same thing but on a layer found within a webMap.

So I am replacing this line:

var featureLayer = new FeatureLayer({ url: "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trails_Styled/FeatureServer/0" }); map.add(featureLayer);

with this:

var webmap = new WebMap({
portalItem: {
id: "***mywebmapid***"
}
});

var view = new MapView({
container: "viewDiv",
//*** UPDATE ***//
map: webmap
//center: [-118.80500,34.02700],
//zoom: 13
});

then instead of using featureLayer in the function, I am trying to use a layer withing the webMap.  I tried this:

var parksLayer = webmap.findLayerById("958f6534812a4407925a2a18a640b841");

and changed the one line to read:

return (result.graphic.layer === parksLayer);

I'm getting an error that says:  Uncaught (in promise) TypeError: Cannot read property 'graphic' of undefined
at  (pointing to this line   })[0].graphic; )

I'm not sure what I'm doing wrong here.  Can anyone point me in the right direction?

Thanks!!

0 Kudos
17 Replies
EricGardecki1
New Contributor III

Maybe this will help.  The full code I am using currently:

https://codepen.io/egardecki/pen/OJMNyxz?editors=1000 

0 Kudos
GeorgeAbraham
New Contributor III

Hi Eric Gardecki,

Thanks for the codepen.

I have fixed the code for you - atleast till the part of getting the Park Layer and the graphic selection, however, it is throwing error for geometryEngine not being declared - didn't dig further on this, but I believe you can take this further.

Highlighted in BOLD are the changes I made:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
<title>ArcGIS JavaScript Tutorials: Create a JavaScript starter app</title>
<style>
html, body {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
#viewDiv {
padding: 0;
height: 100%;
width: 100%;
}

#legendWidget {
position: absolute;
right:0;
}


</style>
<link rel="stylesheet" href="https://js.arcgis.com/4.15/esri/themes/light/main.css">
<script src="https://js.arcgis.com/4.15/"></script>

<script>
require([
"esri/WebMap",
"esri/views/MapView",
"esri/widgets/BasemapToggle",
"esri/widgets/Legend",
"esri/widgets/ScaleBar",
"esri/widgets/Expand",
"esri/layers/GraphicsLayer",
"esri/widgets/Locate",
"esri/widgets/Track",
"esri/Graphic",
"esri/widgets/Compass"
], function(WebMap, MapView, BasemapToggle, Legend, ScaleBar, Expand, GraphicsLayer, Locate, Track, Graphic, Compass) {

var webmap = new WebMap({
portalItem: {
id: "af2ccc6638624fa089548b1a0a8fdb45"
}
});

var view = new MapView({
container: "viewDiv",
//*** UPDATE ***//
map: webmap
//center: [-118.80500,34.02700],
//zoom: 13
});

var basemapToggle = new BasemapToggle({
view: view,
nextBasemap: "satellite"
});


view.ui.add(basemapToggle, "bottom-right");


var legend = new Legend({
container: document.createElement("div"),
view: view
});

// view.ui.add(legend, "top-right");

var scalebar = new ScaleBar({
view: view
});

view.ui.add(scalebar, "bottom-left");


legendExpand = new Expand({
expandIconClass: "esri-icon-layer-list", // see https://developers.arcgis.com/javascript/latest/guide/esri-icon-font/
expandTooltip: "Legend", // optional, defaults to "Expand" for English locale
view: view,
content: legend.domNode
});

view.ui.add(legendExpand, "top-right");

var coordsWidget = document.createElement("div");
coordsWidget.id = "coordsWidget";
coordsWidget.className = "esri-widget esri-component";
coordsWidget.style.padding = "7px 15px 5px";

view.ui.add(coordsWidget, "bottom-right");

function showCoordinates(pt) {
var coords = "Lat/Lon " + pt.latitude.toFixed(3) + " " + pt.longitude.toFixed(3) +
" | Scale 1:" + Math.round(view.scale) +
" | Zoom " + view.zoom;
coordsWidget.innerHTML = coords;
}

view.watch("stationary", function(isStationary) {
showCoordinates(view.center);
});

view.on("pointer-move", function(evt) {
showCoordinates(view.toMap({ x: evt.x, y: evt.y }));
});

// var locate = new Locate({
// view: view,
// useHeadingEnabled: false,
// goToOverride: function(view, options) {
// options.target.scale = 1500; // Override the default map scale
// return view.goTo(options.target);
// }
// });
//
// view.ui.add(locate, "top-left");

var track = new Track({
view: view,
graphic: new Graphic({
symbol: {
type: "simple-marker",
size: "12px",
color: "green",
outline: {
color: "#efefef",
width: "1.5px"
}
}
}),
useHeadingEnabled: false // Don't change orientation of the map
});

view.ui.add(track, "top-left");

var compass = new Compass({
view: view
});

// adds the compass to the top left corner of the MapView
view.ui.add(compass, "top-left");

var activeGraphic;

function findNearestGraphic(event) {
return view.hitTest(event).then(function (response) {
var graphic;
// Get the Trail graphics only
if (response.results.length) {
graphic = response.results.filter(function (result) {
return result.graphic.layer === parksLayer;
})[0].graphic;
}
// Only return new graphics are found
if (graphic) {
if (!activeGraphic || (activeGraphic.attributes.OBJECTID !== graphic.attributes.OBJECTID)) {
return graphic;
} else {
return null;
}
} else {
return null;
}
});
}

var bufferGraphic;

function drawBuffer(bufferGeometry) {
view.graphics.remove(bufferGraphic);
bufferGraphic = new Graphic({
geometry: bufferGeometry,
symbol: {
type: "simple-fill",
color: "rgba(0,0,0,0)",
outline: {
color: "rgba(0,0,0,.5)",
width: 1
}
}
});
view.graphics.add(bufferGraphic);
}
var parksLayer;
//var parksLayer = webmap.findLayerById("958f6534812a4407925a2a18a640b841");
view.when(function() {
parksLayer = webmap.allLayers.find(function(layer) {
return layer.title === "Parks";
});
console.log(parksLayer);

view.on("pointer-move", function(event){
findNearestGraphic(event).then(function(graphic){
if (graphic) {
activeGraphic = graphic;
var buffer = geometryEngine.geodesicBuffer(activeGraphic.geometry, .25, "miles");
drawBuffer(buffer);
}
});
});
});
});
</script>

</head>
<body>
<div id="legendWidget"></div>
<div id="viewDiv"></div>
</body>
</html>

Issue 1: When you where trying to get the parksLayer = webmap.allLayers.find, the layers are not yet loaded into the webmap and hence its null, so need to encapsulate in view.when(function(){})

Issue 2: The way you where returning graphic via the Array.filter() function had a small flaw. When we say after filter()[0].graphic what it means is that Array.filter() returns an array of objects and from it we need just the 1st element, ideally here we will have only one element which is equal to the parksLayer. If you put a bracket, then nothing is being returned and hence you are trying to force select an index 0 of an empty array/non-initialized array.

graphic = response.results.filter(function (result) {
 return (result.graphic.layer === parksLayer);
})[0].graphic;

Do let me know if this works.

GeorgeAbraham
New Contributor III

Just to add - you can read more on the Array.filter() at MDN

0 Kudos
EricGardecki1
New Contributor III

Hi George

Sorry it took so long to reply.

I tried your code and am getting the same result...same error.  No change.

Note that I do see the layer in the console.log....so it does seem to be retrieving it.

I made an adjustment to what I wrapped in the view.when function and that cleared up the errors but the buffer event didn't happen and didn't error.   Any other thoughts?  I will keep adjusting things to see what I can figure out.

I also again tried the code straight from the tutorial and it works fine so the issue must lie in the difference between a featureLayer and webMap.

Thanks!

EDIT:

I started over from scratch and seemed to get somewhere so I must have had something messy.  So what happens now though is I get 1 buffer returned and then get that same graphic undefined error again after.

The above codepen had been updated with latest code.

0 Kudos
KenBuja
MVP Esteemed Contributor

You're getting an error because the hitTest is just returning the Township layer when you move the mouse over it. Since filter result is empty, trying to get the first element in its array gives you the error.  Check the length of the filter result before getting its graphic

var graphic;
// Get the Trail graphics only
if (response.results.length) {
  var test = response.results.filter(function (result) {
    return result.graphic.layer === parksLayer;
  });
  if (test.length > 0) {
    graphic = test[0].graphic;
  }
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Your buffer problem is in line 195, trying to look for the OBJECTID attribute. This feature only has the FID attribute, so the line should be

if (!activeGraphic || (activeGraphic.attributes.FID !== graphic.attributes.FID)) {‍‍‍
EricGardecki1
New Contributor III

That was it!  Thanks so much.

I'm having trouble understanding what those changes accomplished though.

For the FID, I get that that was an issue, didn't realize it didn't have the GlobalID.  However, I don't know what the purpose of that line is. 

For the other part, before making that change everything was working, despite that error still occurring.  What do you mean "Township layer"?  

So this error probably occurs in the original tutorial version as well?  It comes from the moments when the cursor is not near a feature to obtain graphics from?

Thanks again!  Glad I'm getting somewhere.  Working through issues like this help me learn it better.

0 Kudos
KenBuja
MVP Esteemed Contributor

The hitTest returns an array of features from each of the layers of the webmap that the mouse is on. When you move the mouse from the edge of the screen, you first move over the feature in the Township layer. That's the only layer returned by the hitTest, so when you filter out only the layer that is named "Parks", the filtered array is empty. It's only when the mouse moves over a park that the hitTest will also include the "Parks" layer.

To see what layers are included in the hitTest, add line 5 to your code

var graphic;
// Get the Trail graphics only
if (response.results.length) {
  var test = response.results.filter(function (result) {
    console.log(result.graphic.layer.title);
    return result.graphic.layer === parksLayer;
  });
  if (test.length > 0) {
    graphic = test[0].graphic;
  }
}
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

In line 195, the if statement is asking whether activeGraphic is undefined or is the FID of activeGraphic different than the FID of the graphic. activeGraphic is the last park feature that the mouse is on. The first time you move the mouse over a park, activeGraphic is empty. So the first part of the if statement (is activeGraphic undefined?) is true. The code continues and activeGraphic is set to the park feature on line 174 before the park buffer is created.

When the mouse moves again, the first part of the if statement is now false (since activeGraphic is defined), but since you haven't moved to another park, the second part of the if statement is also false. When you move the mouse into a new park, the second part is now true, so the activeGraphic is changed again and the buffer is created for the new park.

A note about FID versus OBJECTID. You have to examine that attributes of your data to see which one you have to use. You can look at the feature's service URL (https://services9.arcgis.com/6qDS6gU30E3aEaoi/arcgis/rest/services/Parks_public_view/FeatureServer/0) or by using your browser's console.

EricGardecki1
New Contributor III

Thank you for the explanation!

0 Kudos