Highlight Line Segment in Feature Layer

1888
9
Jump to solution
12-07-2017 12:12 PM
AndrewSchwartz
New Contributor II

I have made a simple map that will be inserted into a larger application in the future, which will enable the user to call up a record from a database based on a feature they have selected in he map.

The line segments are a Feature Layer hosted on arcgis.com

I want the color of the line segment to change when it is clicked on, and if another is clicked on for the first line segment to return to the default color.

I feel like this should be a simple process, but everything I have been able to find has been specific to markers or points. I am trying to change the color of a line segment.

I have attached the code I have so far. 

Tags (1)
0 Kudos
1 Solution

Accepted Solutions
ThomasSolow
Occasional Contributor III

There's a few ways you could do this.  I notice you're using hitTest to see if the user clicked a line segment.  This is fine, but you might run into issues where the user clicks close enough to a segment to open a popup but too far to trigger the hitTest.

To avoid this, you could do something like this:

view.popup.watch("selectedFeature", function(event) {
  if (event && event.attributes && event.attributes.CIRCUIT_ID) {
    var segId = event.attributes.CIRCUIT_ID;
    featureLayer.renderer = generateRenderer(segId);
    history.pushState("", document.title, window.location.pathname + window.location.search);
    window.location.hash += segId;
  }
});

function generateRenderer(id) {
  return {
    type: "unique-value",
    field: "CIRCUIT_ID",
    defaultSymbol: featureLayer.renderer.symbol || featureLayer.renderer.defaultSymbol,
    uniqueValueInfos: [{
      value: id,
      symbol: {
        type: "simple-line",
        color: "orange",
        width: 5,
        cap: "round"
      }
    }]
  }
}

View solution in original post

9 Replies
ThomasSolow
Occasional Contributor III

There's a few ways you could do this.  I notice you're using hitTest to see if the user clicked a line segment.  This is fine, but you might run into issues where the user clicks close enough to a segment to open a popup but too far to trigger the hitTest.

To avoid this, you could do something like this:

view.popup.watch("selectedFeature", function(event) {
  if (event && event.attributes && event.attributes.CIRCUIT_ID) {
    var segId = event.attributes.CIRCUIT_ID;
    featureLayer.renderer = generateRenderer(segId);
    history.pushState("", document.title, window.location.pathname + window.location.search);
    window.location.hash += segId;
  }
});

function generateRenderer(id) {
  return {
    type: "unique-value",
    field: "CIRCUIT_ID",
    defaultSymbol: featureLayer.renderer.symbol || featureLayer.renderer.defaultSymbol,
    uniqueValueInfos: [{
      value: id,
      symbol: {
        type: "simple-line",
        color: "orange",
        width: 5,
        cap: "round"
      }
    }]
  }
}
AndrewSchwartz
New Contributor II

Hi Thomas,

This works perfectly! You're right - hitTest was causing that issue. I was planning to tackle that next, but you beat me to it!

Thanks so much! 

0 Kudos
LA
by
New Contributor

Hi Thomas,

I would like to do something very similar, the user clicks on a start and end segment, and I'd like for all segments in between (including start and end) to change line style. The difference is that I am using a Query to select the segments that should change color since I am not explicitly using a FeatureLayer. In the code that I am attaching (and is partially not working since I get an "Uncaught (in promise) TypeError: c.getSymbol is not a function" error), I know I need to change the line "value: 5" to set "value" equal to the segments that should change linestyle, but I haven't been able to do this. There may also be an error in "lyr.renderer" but I am assuming that lyr is a FeaturLayer so it should have a renderer method. The code runs fine until console.info(results); so it looks like the query is working.

Thanks,

0 Kudos
ThomasSolow
Occasional Contributor III

Hi,

Looks to me like the issue here is the version of the API you're using.  4.4 doesn't support autocasting renderers and symbols (that is, creating a renderer without using the constructor) - that functionality was added in 4.5.  So, if you're using 4.4, you'll need to pull in UniqueValueRenderer and SimpeLineSymbol and use them as classes.

I made a few changes and here's the result: JS Bin - Collaborative JavaScript Debugging 

One thing I changed was how you determine the layer to search. I just hardcoded your layer's URL.

Another change I made is using a ClassBreaksRenderer instead of a UniqueValueRenderer.  Since you're interested in highlighting all features in a range, that seems like the right choice.

A final thing to note is that you may not actually have to do a query here since the min and max segment ids are available pre-query.  You could just construct the renderer with that information.  Here's another version that does the same thing without the query: JS Bin - Collaborative JavaScript Debugging 

0 Kudos
LA
by
New Contributor

Hi Thomas,

This was really helpful, thank you!

0 Kudos
LA
by
New Contributor

Thomas, 

I have a follow-up question, if the answer is not too long. We have added another layer to the FeatureServer (originally we only had one layer with ID=0), and after reading the documentation I thought that I could search for it by using something like

var segmentsLayer = webmap.layers.find(function(lyr) {
 return lyr.url === "https://giswebnew.dotd.la.gov/arcgis/rest/services/DevTemp_Data/FreeValWorkzone_Pilot/FeatureServer/..."
 });

and then change the linestyle using the layer's renderer. But if I use the code shown above, the ClassBreaksRenderer won't work, so I assume it didn't find the layer with ID=1. In fact, the original code will only work if I leave the layer ID part of the url blank as shown below

"https://giswebnew.dotd.la.gov/arcgis/rest/services/DevTemp_Data/FreeValWorkzone_Pilot/FeatureServer"

How can I access each layer individually?

Thanks.

0 Kudos
ThomasSolow
Occasional Contributor III

According to the feature layer docs, it's possible for the url property to point to the service root and for the layerId property to specify the index of the service, or for the url to include the index.  So you could use the layerId property to distinguish between your two layers here.

You could access these layers in a few ways.  If you know that this app will always have a webmap with two layers, you could do:

// webmap.layers is a esri/core/collection of layers excluding the basemap layers
var layer1 = webmap.layers.getItemAt(0);
var layer2 = webmap.layers.getItemAt(1);‍‍‍‍‍‍

But if the webmap changes, your app could break.

Alternatively, you could use the url and layerId to find the layers you want:

var featureServerURL = "‍‍‍‍‍‍https://giswebnew.dotd.la.gov/arcgis/rest/services/DevTemp_Data/FreeValWorkzone_Pilot/FeatureServer"

var layer1 = webmap.layers.find(function(layer) {
  return layer.url === featureServerURL && layer.layerId === 0;
});

var layer2 = webmap.layers.find(function(layer) {
  return layer.url === featureServerURL && layer.layerId === 1;
});‍‍‍‍‍‍‍‍‍‍‍‍

// but maybe it would be better to write a function and cover both cases
function findLayer(url, layerId, map) {
  return map.layers.find(function(layer) {
    return (layer.url === url && layer.layerId === layerId) ||
      (layer.url === url + "/" + layerId);
  });
}

var layer1 = findLayer(featureServerURL, 0, webmap);
var layer2 = findLayer(featureServerURL, 1, webmap);‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

The docs also specify that, in the case where the url points to the service root and no layerId is given, the layer will pull in the first layer in the service.  This may not be relevant for your case, but just something to keep in mind.

LA
by
New Contributor

Thanks again, Thomas. This is what I needed.

Just a short comment in case someone else uses the code, there seems to be a little typo in the var layer1 and var layer2 statements, the layerId condition seems that it should be == instead of =.

Thanks.

0 Kudos
LA
by
New Contributor

Hi Thomas,

With your help I developed a working application using the 4.4 API, however, I now need to add labels to the webmap and I found out the 4.4 version has limited labeling support.

So I started to code the application using the 3.24 API using Map instead of WebMap and MapView and found the 3.24 API more intuitive when working with layers and events, so I switched over to 3.24. I am having trouble with the ClassBreaksRenderer not 'highlighting' the selected features on a layer. Could you take a look at the attached code and point me in the right direction? As far as I can tell, the ClassBreaksRenderer section starts around line 89. The idea is to change the line-style of the 'dummy' layer (gray line) to a 'highlighter' line-style to mimic a transparency on top of the alternating blue line.

Thank you,

0 Kudos