1
\$\begingroup\$

Background

I am working on a WebGIS application using JavaScript and ArcGIS JS API. I am new with js, so please bear with me.

While learning and developing with js, I am trying to follow some rules, such as Asynchronous Module Definition (which ArcGIS JS API encourages), Revealing Module Pattern, and the Standard Style. Certainly, I still fail at following them correctly.

This code is built on top of this responsive template: github.com/Esri/bootstrap-map-js.

Objective

This app consumes some ArcGIS REST services (MapServer). Assume that I don't have any control over them (I have access to server, but that is another thing that I know nothing about).

One of the functionalities is to filter a certain layer (Feature Layer with Polyline geometry), according to a selection that the user does using check boxes. When a check box is checked, only some parts of the layer should be visible. If multiple checks are done, the visible parts of the layer must have different colors.

Code

I set up an example of this application using one of the example ArcGIS services. It is available in this repo: https://github.com/iled/fiddle
(sorry, I failed at making a jsfiddle out of it)

Below I make a brief description of the relevant parts of the code (file app/js/map.js)

Add the layer

When the map is added, all layers on the service are automatically loaded and placed in a tree with checkboxes.

restUrl = "http://sampleserver5.arcgisonline.com/arcgis/rest/services/Water_Network/MapServer"
var mapServiceLayer = new ArcGISDynamicMapServiceLayer(restUrl, opt)
map.addLayer(mapServiceLayer)

Check boxes

These check boxes are built with all possible values, of a given attribute (facilityid), within one of the layers (MidScale/Water Mains - Block View -- it is the last one inside layer1), so they should filter that layer.

Here is how those values are fetched.

var queryOperators = function (mapServiceLayer) {
  var queryTask = new QueryTask("http://sampleserver5.arcgisonline.com/arcgis/rest/services/Water_Network/MapServer/23")
  var query = new Query()
  var mps = mapServiceLayer
  query.returnGeometry = false
  query.outFields = ["facilityid"]
    // toy query to fetch all records
  query.where = "1 = 1"
  query.returnDistinctValue = true
  query.orderByFields = ["facilityid ASC"]
  queryTask.execute(query, addOperatorsList)
}

The list of check boxes is then built by the callback method.

var addOperatorsList = function (operatorsFeatureSet) {
  var operatorsCount = operatorsFeatureSet.features.length
  var operatorsNames = []
  var operatorsList = []
  var operatorsDiv = dom.byId("operatorList")
  var line = "<input  id='operatorCheckBox_'"
  for (var i = 0; i < operatorsCount; i++) {
    operatorsNames.push(operatorsFeatureSet.features[i].attributes["facilityid"])

    domConstruct.create("p", {
      id: "operatorCheckBox_" + i,
      style: "font-size:10px"
    }, operatorsDiv)

    domConstruct.create("label", {
      for: "opbox_" + i,
      innerHTML: operatorsNames[i]
    }, "operatorCheckBox_" + i)

    var cbox = new CheckBox({
      id: "opbox_" + i,
      value: operatorsFeatureSet.features[i].attributes["facilityid"],
      checked: false,
      onChange: function (evt) {
        console.log('value:', this.value)
        querySegments(this.value)
      }
    })
    cbox.placeAt("operatorCheckBox_" + i, "first")
  }
}

Filter the layer

Here is where one of the problems is located, the method setLayerDefinitions fails with the message Uncaught TypeError: this.setLayerDefinitions is not a function. I know that this is not supposed to work, here, this is just a placeholder for whatever I've got to put there.

var querySegments = function (operator_id) {
    // build the query
    var queryStatement = "facilityid = " + operator_id
    var layerDefinitions = [queryStatement]
    console.log(layerDefinitions)
    this.setLayerDefinitions(layerDefinitions)
}

This function is called when an event is fired up, which connection was defined on a callback function. How to reach the target layer from here is kind of obscure to me...

Example

If a user checks the box with the number 128608, the result should look like this (taken from ArcGIS webmap viewer).

Example of the polyline with <code>facilityid = 128608</code>.

Problems/Questions

  1. After getting the data from the toggled check box, I can't reach the layer in order to call the method that would filter it.

  2. How to hide/remove layer0 and layer1 (labels and check boxes) from the layers list? More generally, how to format this list?

  3. After selection and filtering, how to give different colors to each filtered polyline?

Please, let me know if I can improve the question in any way.

I thought about posting this on other StackExchange sites (SO, gis), but I decided to post it here because it involves a larger code review; I'll also appreciate if someone explains that I should do otherwise.

\$\endgroup\$

closed as off-topic by 200_success Feb 26 '18 at 17:45

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "Code not implemented or not working as intended: Code Review is a community where programmers peer-review your working code to address issues such as security, maintainability, performance, and scalability. We require that the code be working correctly, to the best of the author's knowledge, before proceeding with a review." – 200_success
If this question can be reworded to fit the rules in the help center, please edit the question.

1
\$\begingroup\$
  1. From the code lines you posted above, you could replace this.setLayerDefinitions() to mapServiceLayer.setLayerDefinitions(). Othewise, you'll have to pass the layer you queried, from your function queryOperators() to querySegments() in this way :
var queryOperators = function(mapServiceLayer) {
  ...
  queryTask.execute(query, function(res) {
    addOperatorsList(res, mps);
  });
};
var cbox = new CheckBox({
  ...
  onChange: function(evt) {
    querySegments(this.value, layer);
  }
});
var querySegments = function(operator_id, layer) {
  ...
  layer.setLayerDefinitions(layerDefinitions);
};
  1. I'm not sure what you want to do, but I guess the first example from this dojo example will suit your need https://dojotoolkit.org/reference-guide/1.10/dojo/store/Observable.html#dojo-store-observable

  2. apply a new Symbol to your selected polyline, then add that Graphic to the map.

var cbox = new CheckBox({
    ...
    onChange: function(evt) {
      ...
      var polyline = operatorsFeatureSet.features[i];
      showPolyline(polyline);
    }
  });

And outside addOperatorsList function :

var showPolyline = function(polyline) {
  function randomColor() {
    return Math.floor((Math.random() * 255) + 0);
  }
  var symbol = new SimpleLineSymbol();
  symbol.setColor(new Color([randomColor(), randomColor(), randomColor(), 1]));
  var graphic = new Graphic(polyline, symbol);
  map.graphics.add(graphic);
};
\$\endgroup\$
  • \$\begingroup\$ Thank you for your input! This was some time ago and I'm no longer working on that project. At the time, I guess I finally got it working in a similar fashion to what you described, so I'm accepting your answer (I'm sorry I forgot to come here post my own findings). \$\endgroup\$ – iled Feb 26 '18 at 17:10
  • \$\begingroup\$ hey ! thanks for your feedback, much appreciated :) glad you finally managed to get it working in a way or another. cheers \$\endgroup\$ – LMokrane Feb 26 '18 at 17:26

Not the answer you're looking for? Browse other questions tagged or ask your own question.