I want to create a webmap that is based off of this data-set, for example: Will County EEC Mobile Site
I'm not familiar enough with querying to be able to recreate the querying. My data-set has multiple categories and multiple keywords. I geocoded the spreadsheet, so now I have a point for each field. Like the example, I would like to have a orderly drop down for the category field and the keyword field.
Currently, I can query features by the category field, but as you can see it is very impractical being that there are too many choices. How can I create my dropdown to look like the above?
My current code (taken from sample code😞
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport"
content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<title>Recycling Map</title>
<link rel="stylesheet"
href="https://js.arcgis.com/4.15/esri/themes/light/main.css" />
<script src="https://js.arcgis.com/4.15/"></script>
<style>
html,
body,
#viewDiv {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
#infoDiv {
background-color: white;
color: black;
padding: 6px;
width: 400px;
}
#results {
font-weight: bolder;
padding-top: 10px;
}
.slider {
width: 100%;
height: 60px;
}
#drop-downs {
padding-bottom: 15px;
}
</style>
<script>require([
"esri/Map",
"esri/views/MapView",
"esri/layers/FeatureLayer",
"esri/layers/GraphicsLayer",
"esri/geometry/geometryEngine",
"esri/Graphic",
], function(
Map,
MapView,
FeatureLayer,
GraphicsLayer,
geometryEngine,
Graphic,
) {
var wellTypeSelect = document.getElementById("category");
// oil and gas wells
var wellsLayer = new FeatureLayer({
portalItem: {
// autocasts as new PortalItem()
id: "f76b85e6c5314a0c96539d704fe92452"
},
outFields: ["*"],
visible: false
});
// GraphicsLayer for displaying results
var resultsLayer = new GraphicsLayer();
var map = new Map({
basemap: "dark-gray",
layers: [wellsLayer, resultsLayer]
});
var view = new MapView({
container: "viewDiv",
map: map,
center: [-87.95, 41.47],
zoom: 10
});
view.ui.add("infoDiv", "top-left");
// query all features from the wells layer
view
.when(function() {
return wellsLayer.when(function() {
var query = wellsLayer.createQuery();
return wellsLayer.queryFeatures(query);
});
})
.then(getValues)
.then(getUniqueValues)
.then(addToSelect)
// return an array of all the values in the
// STATUS2 field of the wells layer
function getValues(response) {
var features = response.features;
var values = features.map(function(feature) {
return feature.attributes.USER_Categ;
});
return values;
}
// return an array of unique values in
// the STATUS2 field of the wells layer
function getUniqueValues(values) {
var uniqueValues = [];
values.forEach(function(item, i) {
if (
(uniqueValues.length < 1 || uniqueValues.indexOf(item) === -1) &&
item !== ""
) {
uniqueValues.push(item);
}
});
return uniqueValues;
}
// Add the unique values to the wells type
// select element. This will allow the user
// to filter wells by type.
function addToSelect(values) {
values.sort();
values.forEach(function(value) {
var option = document.createElement("option");
option.text = value;
wellTypeSelect.add(option);
});
return setWellsDefinitionExpression(wellTypeSelect.value);
}
// set the definition expression on the wells
// layer to reflect the selection of the user
function setWellsDefinitionExpression(newValue) {
wellsLayer.definitionExpression = "USER_Categ = '" + newValue + "'";
if (!wellsLayer.visible) {
wellsLayer.visible = true;
}
return queryForWellGeometries();
}
// Get all the geometries of the wells layer
// the createQuery() method creates a query
// object that respects the definitionExpression
// of the layer
function queryForWellGeometries() {
var wellsQuery = wellsLayer.createQuery();
return wellsLayer.queryFeatures(wellsQuery).then(function(response) {
wellsGeometries = response.features.map(function(feature) {
return feature.geometry;
});
return wellsGeometries;
});
}
// set a new definitionExpression on the wells layer
wellTypeSelect.addEventListener("change", function() {
var type = event.target.value;
setWellsDefinitionExpression(type);
});
});</script>
</head>
<body>
<div id="viewDiv"></div>
<div id="infoDiv" class="esri-widget">
<div id="drop-downs">
Select Category:
<select id="category" class="esri-widget"></select>
</div>
<div id="results" class="esri-widget"></div>
</div>
</body>
</html>
Solved! Go to Solution.
Jared,
No that would be overkill/bad workflow. You would want to re-code the functions to handle both fields at the same time. Here is my re-work of the sample based on what you are wanting. Each function works with both values and now there is a button to filter the features instead.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<title>Recycling Map</title>
<link rel="stylesheet" href="https://js.arcgis.com/4.15/esri/themes/light/main.css" />
<script src="https://js.arcgis.com/4.15/"></script>
<style>
html,
body,
#viewDiv {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
#infoDiv {
background-color: white;
color: black;
padding: 6px;
width: 440px;
}
#results {
font-weight: bolder;
padding-top: 10px;
}
.slider {
width: 100%;
height: 60px;
}
#category,
#keyword {
margin-top: 8px;
margin-bottom: 8px;
}
</style>
<script>
require([
"esri/Map",
"esri/views/MapView",
"esri/layers/FeatureLayer",
"esri/layers/GraphicsLayer",
"esri/geometry/geometryEngine",
"esri/Graphic",
], function (
Map,
MapView,
FeatureLayer,
GraphicsLayer,
geometryEngine,
Graphic,
) {
var catTypeSelect = document.getElementById("category");
var keyTypeSelect = document.getElementById("keyword");
var filterButton = document.getElementById("filterBtn");
// oil and gas wells
var recycleLayer = new FeatureLayer({
portalItem: {
// autocasts as new PortalItem()
id: "227061be60a14cc89946a978b440d227"
},
outFields: ["*"],
visible: false
});
// GraphicsLayer for displaying results
var resultsLayer = new GraphicsLayer();
var map = new Map({
basemap: "dark-gray",
layers: [recycleLayer, resultsLayer]
});
var view = new MapView({
container: "viewDiv",
map: map,
center: [-87.95, 41.47],
zoom: 10
});
view.ui.add("infoDiv", "top-left");
// query all features from the wells layer
view
.when(function () {
return recycleLayer.when(function () {
var query = recycleLayer.createQuery();
return recycleLayer.queryFeatures(query);
});
})
.then(getValues)
.then(getUniqueValues)
.then(addToSelect)
// return an array of all the values in the
// STATUS2 field of the wells layer
function getValues(response) {
var features = response.features;
var values = features.map(function (feature) {
return {
USER_Categ: feature.attributes.USER_Categ,
USER_Keywo: feature.attributes.USER_Keywo
}
});
return values;
}
// return an array of unique values in
// the STATUS2 field of the wells layer
function getUniqueValues(values) {
var uniqueKeyValues = [];
var uniqueCatValues = [];
values.forEach(function (item, i) {
var keyVal = item.USER_Keywo.split(";");
var catVal = item.USER_Categ.split(";");
catVal.map(function (val1) {
if (
(uniqueCatValues.length < 1 || uniqueCatValues.indexOf(val1) === -1) &&
val1 !== ""
) {
uniqueCatValues.push(val1);
}
});
keyVal.map(function (val2) {
if (
(uniqueKeyValues.length < 1 || uniqueKeyValues.indexOf(val2) === -1) &&
val2 !== ""
) {
uniqueKeyValues.push(val2);
}
});
});
return {
uKeyVals: uniqueKeyValues,
uCatVals: uniqueCatValues
};
}
// Add the unique values to the wells type
// select element. This will allow the user
// to filter wells by type.
function addToSelect(values) {
values.uCatVals.sort();
values.uCatVals.forEach(function (value) {
var option = document.createElement("option");
option.text = value;
catTypeSelect.add(option);
});
values.uKeyVals.sort();
values.uKeyVals.forEach(function (value) {
var option = document.createElement("option");
option.text = value;
keyTypeSelect.add(option);
});
return setDefinitionExpression();
}
// set the definition expression on the recycle
// layer to reflect the selection of the user
function setDefinitionExpression() {
var sqlExp = "";
if (catTypeSelect.selectedIndex > 0) {
sqlExp += "USER_Categ LIKE '%" + catTypeSelect.options[catTypeSelect.selectedIndex].value + "%'";
}
if(keyTypeSelect.selectedIndex > 0) {
if(sqlExp === ""){
sqlExp += "USER_Keywo LIKE '%" + keyTypeSelect.options[keyTypeSelect.selectedIndex].value + "%'";
}else{
sqlExp += " AND USER_Keywo LIKE '%" + keyTypeSelect.options[keyTypeSelect.selectedIndex].value + "%'";
}
}
recycleLayer.definitionExpression = sqlExp;
if (!recycleLayer.visible) {
recycleLayer.visible = true;
}
return queryForGeometries();
}
// Get all the geometries of the recycle layer
// the createQuery() method creates a query
// object that respects the definitionExpression
// of the layer
function queryForGeometries() {
var rQuery = recycleLayer.createQuery();
return recycleLayer.queryFeatures(rQuery).then(function (response) {
rGeometries = response.features.map(function (feature) {
return feature.geometry;
});
return rGeometries;
});
}
filterButton.addEventListener("click", function() {
setDefinitionExpression();
});
// set a new definitionExpression on the recycle layer
// catTypeSelect.addEventListener("change", function () {
// var type = event.target.value;
// setDefinitionExpression(type, "cat");
// });
// set a new definitionExpression on the recycle layer
// keyTypeSelect.addEventListener("change", function () {
// var type = event.target.value;
// setDefinitionExpression(type, "key");
// });
});
</script>
</head>
<body>
<div id="viewDiv"></div>
<div id="infoDiv" class="esri-widget">
<div id="drop-downs">
Select Category:
<select id="category" class="esri-widget"></select>
Select Keyword:
<select id="keyword" class="esri-widget"></select>
<button id="filterBtn" class="esri-button esri-button--secondary">Filter</button>
</div>
<div id="results" class="esri-widget"></div>
</div>
</body>
</html>
Jared,
It is a simple matter of splitting the results string by the ";" delimiter inside your getUniqueValues function.
// return an array of unique values in
// the STATUS2 field of the wells layer
function getUniqueValues(values) {
var uniqueValues = [];
values.forEach(function(item, i) {
var uVal = item.split(";");
uVal.map(function(val){
if (
(uniqueValues.length < 1 || uniqueValues.indexOf(val) === -1) &&
item !== ""
) {
uniqueValues.push(val);
}
});
});
return uniqueValues;
}
Oh and update your query expression:
// set the definition expression on the wells
// layer to reflect the selection of the user
function setWellsDefinitionExpression(newValue) {
wellsLayer.definitionExpression = "USER_Categ LIKE '%" + newValue + "%'";
if (!wellsLayer.visible) {
wellsLayer.visible = true;
}
return queryForWellGeometries();
}
Hi Robert,
Thanks, that did a nice job of making the selections more readable:
But, I was also wondering how I can get two drop-downs to display. There is this one, Category, and then I would also like to be able to select by Keyword (USER_Keywo field).
Here's the feature layer view for reference: https://willcountygis.maps.arcgis.com/home/item.html?id=227061be60a14cc89946a978b440d227&view=table#...
Would I have to duplicate all the query blocks of the script for the USER_Keywo field to do this?
Jared,
No that would be overkill/bad workflow. You would want to re-code the functions to handle both fields at the same time. Here is my re-work of the sample based on what you are wanting. Each function works with both values and now there is a button to filter the features instead.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<title>Recycling Map</title>
<link rel="stylesheet" href="https://js.arcgis.com/4.15/esri/themes/light/main.css" />
<script src="https://js.arcgis.com/4.15/"></script>
<style>
html,
body,
#viewDiv {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
#infoDiv {
background-color: white;
color: black;
padding: 6px;
width: 440px;
}
#results {
font-weight: bolder;
padding-top: 10px;
}
.slider {
width: 100%;
height: 60px;
}
#category,
#keyword {
margin-top: 8px;
margin-bottom: 8px;
}
</style>
<script>
require([
"esri/Map",
"esri/views/MapView",
"esri/layers/FeatureLayer",
"esri/layers/GraphicsLayer",
"esri/geometry/geometryEngine",
"esri/Graphic",
], function (
Map,
MapView,
FeatureLayer,
GraphicsLayer,
geometryEngine,
Graphic,
) {
var catTypeSelect = document.getElementById("category");
var keyTypeSelect = document.getElementById("keyword");
var filterButton = document.getElementById("filterBtn");
// oil and gas wells
var recycleLayer = new FeatureLayer({
portalItem: {
// autocasts as new PortalItem()
id: "227061be60a14cc89946a978b440d227"
},
outFields: ["*"],
visible: false
});
// GraphicsLayer for displaying results
var resultsLayer = new GraphicsLayer();
var map = new Map({
basemap: "dark-gray",
layers: [recycleLayer, resultsLayer]
});
var view = new MapView({
container: "viewDiv",
map: map,
center: [-87.95, 41.47],
zoom: 10
});
view.ui.add("infoDiv", "top-left");
// query all features from the wells layer
view
.when(function () {
return recycleLayer.when(function () {
var query = recycleLayer.createQuery();
return recycleLayer.queryFeatures(query);
});
})
.then(getValues)
.then(getUniqueValues)
.then(addToSelect)
// return an array of all the values in the
// STATUS2 field of the wells layer
function getValues(response) {
var features = response.features;
var values = features.map(function (feature) {
return {
USER_Categ: feature.attributes.USER_Categ,
USER_Keywo: feature.attributes.USER_Keywo
}
});
return values;
}
// return an array of unique values in
// the STATUS2 field of the wells layer
function getUniqueValues(values) {
var uniqueKeyValues = [];
var uniqueCatValues = [];
values.forEach(function (item, i) {
var keyVal = item.USER_Keywo.split(";");
var catVal = item.USER_Categ.split(";");
catVal.map(function (val1) {
if (
(uniqueCatValues.length < 1 || uniqueCatValues.indexOf(val1) === -1) &&
val1 !== ""
) {
uniqueCatValues.push(val1);
}
});
keyVal.map(function (val2) {
if (
(uniqueKeyValues.length < 1 || uniqueKeyValues.indexOf(val2) === -1) &&
val2 !== ""
) {
uniqueKeyValues.push(val2);
}
});
});
return {
uKeyVals: uniqueKeyValues,
uCatVals: uniqueCatValues
};
}
// Add the unique values to the wells type
// select element. This will allow the user
// to filter wells by type.
function addToSelect(values) {
values.uCatVals.sort();
values.uCatVals.forEach(function (value) {
var option = document.createElement("option");
option.text = value;
catTypeSelect.add(option);
});
values.uKeyVals.sort();
values.uKeyVals.forEach(function (value) {
var option = document.createElement("option");
option.text = value;
keyTypeSelect.add(option);
});
return setDefinitionExpression();
}
// set the definition expression on the recycle
// layer to reflect the selection of the user
function setDefinitionExpression() {
var sqlExp = "";
if (catTypeSelect.selectedIndex > 0) {
sqlExp += "USER_Categ LIKE '%" + catTypeSelect.options[catTypeSelect.selectedIndex].value + "%'";
}
if(keyTypeSelect.selectedIndex > 0) {
if(sqlExp === ""){
sqlExp += "USER_Keywo LIKE '%" + keyTypeSelect.options[keyTypeSelect.selectedIndex].value + "%'";
}else{
sqlExp += " AND USER_Keywo LIKE '%" + keyTypeSelect.options[keyTypeSelect.selectedIndex].value + "%'";
}
}
recycleLayer.definitionExpression = sqlExp;
if (!recycleLayer.visible) {
recycleLayer.visible = true;
}
return queryForGeometries();
}
// Get all the geometries of the recycle layer
// the createQuery() method creates a query
// object that respects the definitionExpression
// of the layer
function queryForGeometries() {
var rQuery = recycleLayer.createQuery();
return recycleLayer.queryFeatures(rQuery).then(function (response) {
rGeometries = response.features.map(function (feature) {
return feature.geometry;
});
return rGeometries;
});
}
filterButton.addEventListener("click", function() {
setDefinitionExpression();
});
// set a new definitionExpression on the recycle layer
// catTypeSelect.addEventListener("change", function () {
// var type = event.target.value;
// setDefinitionExpression(type, "cat");
// });
// set a new definitionExpression on the recycle layer
// keyTypeSelect.addEventListener("change", function () {
// var type = event.target.value;
// setDefinitionExpression(type, "key");
// });
});
</script>
</head>
<body>
<div id="viewDiv"></div>
<div id="infoDiv" class="esri-widget">
<div id="drop-downs">
Select Category:
<select id="category" class="esri-widget"></select>
Select Keyword:
<select id="keyword" class="esri-widget"></select>
<button id="filterBtn" class="esri-button esri-button--secondary">Filter</button>
</div>
<div id="results" class="esri-widget"></div>
</div>
</body>
</html>