Hi,
I created a web app with the JavaScript Api 4.18 to show csv files on a web map with a time slider and a dgrid table. The table has a dependency to the map because of the view. The slider also has a dependency to the map because of the view. If I query the points with the time slider, the table shows all points that are in the shown extend. How can I achieve, that the table also shows the reduced points?
Thanks for your help,
Karsten
<!DOCTYPE html>
<html>
<head>
<meta name="description" content="CSVViewer">
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
<title>CSVViewer</title>
<style>
html, body{
padding: 0;
margin: 0;
height: 100%;
width: 100%;
overflow: hidden;
font-family: "Helvetica Neue";
}
#viewDiv{
flex: 2;
width: 100%;
}
.header {
grid-area: header;
background: linear-gradient(to right, #007cb0, #001a4c);
}
#info,
#gridDisplay {
position: absolute;
bottom: 0;
left: 0;
height: 35%;
background-color: white;
border-color: grey;
width: 100%;
font-family: "Avenir Next W00", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
}
#info {
z-index: 90;
font-size: 16px;
padding-left: 20px;
}
#info * {
padding-right: 20px;
}
#gridDisplay {
z-index: 80;
}
.info {
line-height: 20px;
padding-left: 5px ! important;
}
.dgrid-header,
.dgrid-header-row {
background-color: #eee;
color: #57585A;
}
.dgrid-row-even {
background-color: #F7F8F8;
}
.dgrid-row-odd {
background-color: #EFEFEF;
}
.dgrid-selected {
background: #B4DAF5;
}
.dgrid-row {
border: none
}
.dgrid .dgrid-scroller {
position: relative;
max-height: 200px;
overflow: auto;
}
.map {
grid-area: map;
}
.wrapper {
display: grid;
grid-template-rows: 25% 100% auto;
grid-template-areas:
"header"
"map";
width: 100vw;
height: 100%;
}
.box {
color: #fff;
padding: 10px;
font-size: 20px;
}
.container {
display: flex;
flex: 1;
width: 100%;
}
.containerT {
height: 30%;
width: 100%;
}
.hidden {
display: none;
}
@media only screen and (min-width: 544px) {
.wrapper {
grid-template-columns: 100% auto;
grid-template-rows: 40px auto;
grid-template-areas:
"header header"
" map";
}
}
</style>
<link rel="stylesheet" href="https://js.arcgis.com/4.18/esri/css/main.css">
<link rel="stylesheet" href="https://js.arcgis.com/4.18/dgrid/css/dgrid.css">
<script src="https://js.arcgis.com/4.18/"></script>
<script>
require([
"esri/Map",
"esri/views/MapView",
"esri/layers/CSVLayer",
"esri/Graphic",
"esri/widgets/Legend",
"esri/widgets/TimeSlider",
"esri/widgets/Expand",
"esri/widgets/BasemapGallery",
"esri/widgets/FeatureTable",
"esri/widgets/LayerList",
"dgrid/OnDemandGrid",
"dgrid/extensions/ColumnHider",
"dojo/store/Memory",
"dstore/legacy/StoreAdapter",
"dgrid/Selection"
], function(Map, MapView, CSVLayer, Graphic, Legend, TimeSlider,
Expand, BasemapGallery, FeatureTable, LayerList, OnDemandGrid,
ColumnHider, Memory, StoreAdapter, Selection) {
let map, view, csvLayer, csvLayerView, grid, node_table;
const gridDiv = document.getElementById("grid");
const infoDiv = document.getElementById("info");
// create new map, view and csvlayer
setupTheView();
const gridFields = ["__OBJECTID", "time", "depth", "mag",
"magType", "nst"];
//time,latitude,longitude,depth,mag,magType,nst,gap,dmin,rms,net,id,updated,place,type,horizontalError,depthError,magError,magNst,status,locationSource,magSource
// create a new datastore for the on demandgrid
// will be used to display attributes of selected features
const dataStore = new StoreAdapter({
objectStore: new Memory({
idProperty: "__OBJECTID"
})
});
// create a grid with given columns once the csvlayer is loaded
csvLayer.when(function() {
// create a grid with columns specified in gridFields variable
createGrid(csvLayer.fields);
// get a reference the csvlayerview when it is ready. It will used to do
// client side queries when user draws polygon to select features
view.whenLayerView(csvLayer).then(function(layerView) {
csvLayerView = layerView;
console.log("timeout start");
console.log(csvLayer.fullExtent);
//wait for the layerview to be done updating
csvLayerView.watch("updating", function(bool){
if(!bool){
popGrid();
console.log(csvLayer.timeInfo.fullTimeExtent.start);
console.log(csvLayer.timeInfo.fullTimeExtent.end);
}})
});
})
.catch(errorCallback);
/****************************************************
* Selects features from the csv layer that intersect
* a polygon that user drew using sketch view model
****************************************************/
function popGrid() {
view.graphics.removeAll();
if (csvLayerView) {
const query = {
where: "1=1",
outFields: ["*"]
};
console.log(csvLayerView);
// query graphics from the csv layer view. Geometry set for the query
// can be polygon for point features and only intersecting geometries are returned
csvLayerView.queryFeatures(query).then(function(results) {
const graphics = results.features;
console.log(graphics.length);
// if the grid div is displayed while query results does not
// return graphics then hide the grid div and show the instructions div
if (graphics.length > 0) {
gridDiv.style.zIndex = 90;
infoDiv.style.zIndex = 80;
document.getElementById("featureCount").innerHTML =
"<b>Showing attributes for " +
graphics.length.toString() + " features </b>"
} else {
gridDiv.style.zIndex = 80;
infoDiv.style.zIndex = 90;
}
// get the attributes to display in the grid
const data = graphics.map(function(feature, i) {
return Object.keys(feature.attributes)
.filter(function(key) {
// get fields that exist in the grid
return (gridFields.indexOf(key) !== -1);
})
// need to create key value pairs from the feature
// attributes so that info can be displayed in the grid
.reduce(function(obj, key) {
obj[key] = feature.attributes[key];
return obj;
}, {});
});
console.log(data);
// set the datastore for the grid using the
// attributes we got for the query results
dataStore.objectStore.data = data;
grid.set("collection", dataStore);
})
.catch(errorCallback);
}
}
/************************************************
* fires when user clicks a row in the grid
* get the corresponding graphic and select it
*************************************************/
function selectFeatureFromGrid(event) {
// close view popup if it is open
view.popup.close();
// get the ObjectID value from the clicked row
const row = event.rows[0]
const id = row.data.__OBJECTID;
// setup a query by specifying objectIds
const query = {
objectIds: [parseInt(id)],
outFields: ["*"],
returnGeometry: true,
outSpatialReference: view.SpatialReference
};
// query the csvLayerView using the query set above
csvLayerView.queryFeatures(query).then(function(results) {
const graphics = results.features;
console.log(graphics);
// remove all graphics to make sure no selected graphics
view.graphics.removeAll();
view.goTo(graphics[0].geometry);
// create a new selected graphic
const selectedGraphic = new Graphic({
geometry: graphics[0].geometry,
symbol: {
type: "simple-marker",
style: "circle",
color: "orange",
size: "12px", // pixels
outline: { // autocasts as new SimpleLineSymbol()
color: [255, 255, 0],
width: 2 // points
}
}
});
// add the selected graphic to the view
// this graphic corresponds to the row that was clicked
view.graphics.add(selectedGraphic);
})
.catch(errorCallback);
}
/************************************************
* Creates a new grid. Loops through poverty
* csvLayer's fields and creates grid columns
* Grid with selection and columnhider extensions
*************************************************/
function createGrid(fields) {
var columns = fields.filter(function(field, i) {
if (gridFields.indexOf(field.name) !== -1) {
return field;
}
}).map(function(field) {
if (field.name === "__OBJECTID") {
return {
field: field.name,
label: field.name,
sortable: true,
hidden: true
};
} else {
return {
field: field.name,
label: field.alias,
sortable: true
};
}
});
// create a new onDemandGrid with its selection and columnhider
// extensions. Set the columns of the grid to display attributes
// the hurricanes cvslayer
grid = new(OnDemandGrid.createSubclass([Selection, ColumnHider]))({
columns: columns
}, "grid");
// add a row-click listener on the grid. This will be used
// to highlight the corresponding feature on the view
grid.on("dgrid-select", selectFeatureFromGrid);
}
function setupTheView() {
map = new Map({ basemap: "gray"});
//-----------------------------------------
// Layer
//-----------------------------------------
const url = "https://developers.arcgis.com/javascript/latest/sample-code/layers-csv/live/earthquakes.csv";
const template = {
title: "Earthquake Info",
content: "Magnitude {mag} {type} hit {place} on {time}."
};
csvLayer = new CSVLayer({
url: url,
copyright: "USGS Earthquakes",
popupTemplate: template,
timeInfo: {
startField: "time"
},
outFields: "*"
});
view = new MapView({
container: "viewDiv",
map: map,
zoom: 3,
extent: csvLayer.fullExtent,
constraints: {
geometry: csvLayer.fullExten
}
});
map.add(csvLayer);
console.log("finished!");
//-----------------------------------------
// Widgets
//-----------------------------------------
// BaseMapGallery
var bgExpand = new Expand({
view: view,
content: new BasemapGallery({
view: view,
container: document.createElement("div")
})
});
// LayerList mit Legende
const llExpand = new Expand({
view: view,
content: new LayerList({
view: view,
listItemCreatedFunction: function (event) {
const item = event.item;
if (item.layer.type != "group") {
// don't show legend twice
item.panel = {
content: "legend",
open: true
};
}
}
})
});
// TimeSlider
// time slider widget initialization
const timeSlider = new TimeSlider({
container: document.createElement("div"),
view: view,
timeVisible: true, // show the time stamps on the timeslider
loop: true
});
view.whenLayerView(csvLayer).then(function (lv) {
// around up the full time extent to full hour
timeSlider.fullTimeExtent = csvLayer.timeInfo.fullTimeExtent.expandTo(
"days"
);
});
let timeSliderDropExpand = new Expand({
collapsedIconClass: "esri-icon-collapse",
expandIconClass: "esri-icon-time-clock",
expandTooltip: "Zeitschieberegler",
view: view,
content: timeSlider.container,
expanded: false
});
// TableList
//Button Tabelle
node_table = document.getElementById("Table");
// Create a <button> element
node_table.innerHTML ="<div class='esri-widget--button esri-widget esri-interactive' role='button';><span aria-hidden='true' role='presentation' class='esri-icon esri-icon-table'></span></div>";
node_table.id = "TableChange" ;
node_table.title="Tabelle ausblenden";
const TableChange = document.getElementById("TableChange");
TableChange.addEventListener("click", tabelleFunktion);
// Add widget to the top right corner of the view
view.ui.add([bgExpand, llExpand, timeSliderDropExpand, node_table], {
position: "top-right"
});
}
function errorCallback(error) {
console.log("error:", error)
}
function tabelleFunktion() {
if (tableContainer.style.display !== 'none') {
tableContainer.style.display = 'none';
node_table.title ="Tabelle einblenden"
}
else{
tableContainer.style.display = 'block';
node_table.title ="Tabelle ausblenden"}
}
});
</script>
</head>
<body>
<div id="Table"></div>
<div class="wrapper">
<div class="box header" id="titleDiv">Vorlage CSVViewer</div>
<div class="map" id="viewDiv"></div>
<div id="tableContainer" class="containerT">
<div id = "tableDiv">
<div id="info">
<span class="info">
<b>Tabelle wird geladen...</b>
</span>
<br />
</div>
<div id="gridDisplay">
<span class="info" id="featureCount"></span>
<div id="grid" class="dgrid dgrid-grid ui-widget"></div>
</div>
</div>
</div>
</div>
</body>
</html>