I have a group layer in an ArcGIS JS project. In the group layer there are 21 separate KML layers. Each KML layer has a URL where the date is defined in that URL like such below.
url: "https://.../YYYYMMDD_something.kml"
I need to be able to update the URL based on the day that is in the slider widget also in this ArcGIS project. I have tried two iterations that logically made sense to me. The first attempt is below.
// -- Time slider logic for L1/L2 Overlays & data
slider.watch("timeExtent",updateKMLLayerURLS);
function updateKMLLayerURLS(event) {
console.log("changed")
// Get the date from the time extent of the slider and format it YYYYMMDD.
let dateObj = slider.timeExtent.start;
let formattedDate = formatDate(dateObj).replace(/-/g, '');
l1l2OverlaysAndData.layers.forEach(layer => {
const regex = /[0-9]{8}/g;
let oldURL = layer.url;
if (regex.test(oldURL)) {
let newURL = oldURL.replace(regex,formattedDate);
console.log(newURL)
layer.url = newURL;
layer.load();
}
});
}
This did not actually update the layer in any capacity. I logged the new URL and the logic does change the URL correctly. However, I could tell that it was not loading the correct date by comparing it to the production site we have running. There is a date set in the original call of each layer not just YYYYMMDD, in this case it is 20240228. (see last code snippet) I could tell it was not changing because the layer would not update or shift at all like it should be when comparing it to our production site. I then tried below.
// -- Time slider logic for L1/L2 Overlays & data
slider.watch("timeExtent",updateKMLLayerURLS);
function updateKMLLayerURLS(event) {
console.log("changed")
// Get the date from the time extent of the slider and format it YYYYMMDD.
let dateObj = slider.timeExtent.start;
let formattedDate = formatDate(dateObj).replace(/-/g, '');
const regex = /[0-9]{8}/g;
let array = l1l2OverlaysAndData.layers;
for (let i = 0; i < array.length; i++) {
let layer = array[i];
let oldURL = layer.url;
let title = layer.title;
let visStatus = layer.visible;
console.log(oldURL);
if (regex.test(oldURL)) {
let newURL = oldURL.replace(regex,formattedDate);
console.log(newURL)
layer.destroy();
let layer2 = new KMLLayer({
visible: visStatus,
title: title,
url: newURL
});
l1l2OverlaysAndData.add(layer2);
};
};
}
This attempt was even worse as a everything inside the for loop makes the layers and map disappear from the project when it is loaded. I logged the line (let layer = array[i];)
and it gave me 21 undefined object which now has me at a loss. Below is an example of how one of the layers is defined as well as the group layer. The URL is not complete because it is government related and I cannot disclose.
const jason2AltimeterKMLLayer = new KMLLayer({
visible: false,
title: "Jason-2 Altimeter (Decommissioned)",
url: "https://.../20240228_jason2.kmz"
});
const l1l2OverlaysAndData = new GroupLayer({
title: "L1/L2 Overlays & Data",
visible: false,
visibilityMode: "independent",
layers: [ snppVIIRSKMLLayer, n20VIIRSKMLLayer, n21VIIRSKMLLayer, s3AOLCIKMLLayer, s3BOLCIKMLLayer, s3ASLSTRascKMLLayer,
s3ASLSTRdesKMLLayer, s3BSLSTRascKMLLayer, s3BSLSTRdesKMLLayer, s1ANRCSKMLLayer, s1BNRCSKMLLayer, s1ANRCSCPKMLLayer,
s1BNRCSCPKMLLayer, s2ABMSIKMLLayer, jason3AltimeterKMLLayer, s3AAltimeterKMLLayer, s3BAltimeterKMLLayer,
saralAltimeterKMLLayer, cryosat2AltimeterKMLLayer, s6AAltimeterKMLLayer, jason2AltimeterKMLLayer
],
opacity: 0.75
})
1 Answer 1
If you look at the description of the KML layer .load
method at https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-KMLLayer.html#load, you can read:
The load() method only triggers the loading of the resource the first time it is called. The subsequent calls return the same promise.
This means you can't use it to reload/refresh layer with new data, unlike for example GeoJSON layer, which has .refresh
method for this purpose.
The only way seems to be to create new layer with new data, insert it in the group and then remove old layer.
Here is simple example of doing that. Timeout delay when removing old layer is used to prevent blink effect.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<title>KMLLayer | Sample | ArcGIS Maps SDK for JavaScript 4.29</title>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
#containerDiv1 {
background-color: #eeeeee;
padding: 3px;
text-align: center;
min-width: 250px;
}
#containerDiv2 {
background-color: white;
padding: 3px;
text-align: center;
}
.esri-slider__label {
text-shadow: #fff 0px 0px 2px, #fff 0px 0px 2px, #fff 0px 0px 2px,
#fff 0px 0px 2px, #fff 0px 0px 2px, #fff 0px 0px 2px;
}
</style>
<link rel="stylesheet" href="https://js.arcgis.com/4.29/esri/themes/light/main.css" />
<script src="https://js.arcgis.com/4.29/"></script>
<script>
require([
"esri/Map",
"esri/views/MapView",
"esri/layers/KMLLayer",
"esri/layers/GroupLayer",
"esri/layers/GeoJSONLayer",
"esri/widgets/ScaleBar",
"esri/widgets/Slider"
], (Map, MapView, KMLLayer, GroupLayer, GeoJSONLayer, ScaleBar, Slider) => {
var kmlLayer = new KMLLayer({
url: "https://earthquake.usgs.gov/fdsnws/event/1/query?format=kml&minmagnitude=5"
});
const countriesLayer = new GeoJSONLayer({
title: "Forest area by country",
url: "https://developers.arcgis.com/javascript/latest//sample-code/client-projection/live/percent-forest-area.json",
renderer: {
type: "unique-value",
defaultLabel: "No data",
defaultSymbol: {
type: "simple-fill",
color: [0, 0, 0, 0],
outline: {
color: [255, 255, 255, 1],
width: 0.5
}
}
}
});
const groupLayer = new GroupLayer({
title: "Goup layer",
visible: true,
visibilityMode: "independent",
layers: [countriesLayer, kmlLayer],
opacity: 0.75
})
const map = new Map({
basemap: "dark-gray-vector",
layers: [groupLayer]
});
const view = new MapView({
container: "viewDiv",
map: map
});
const scalebar = new ScaleBar({
view: view
});
view.ui.add(scalebar, "bottom-left");
const slider = new Slider({
container: "sliderDiv",
min: 2,
max: 10,
precison: 2,
labelFormatFunction: function(value, type) {
return (type === "value") ? value.toFixed(1) : value;
},
values: [5],
visibleElements: {
labels: true,
rangeLabels: true
}
});
var startValue;
slider.on('thumb-drag', function(evt) {
if (evt.state == 'start')
startValue = evt.value;
else if ((evt.state == 'stop') && (startValue != evt.value)) {
loadLayer(evt.value.toFixed(1), 1);
}
});
view.ui.add("containerDiv1", "bottom-left");
view.ui.add("containerDiv2", "bottom-left");
function loadLayer(value, position) {
var newLayer = new KMLLayer({
url: "https://earthquake.usgs.gov/fdsnws/event/1/query?format=kml&minmagnitude=" + value
});
newLayer.on('layerview-create', function(evt) {
setTimeout(function() {
var layer = groupLayer.layers.at(position + 1);
groupLayer.remove(layer);
delete layer;
document.getElementById('containerDiv2').style.display = 'none';
}, 200);
});
document.getElementById('containerDiv2').style.display = 'block';
groupLayer.add(newLayer, position);
}
});
</script>
</head>
<body>
<div id="viewDiv"></div>
<div id="containerDiv1" class="esri-widget">
<div id="sliderDiv"></div>
</div>
<div id="containerDiv2" class="esri-widget" style="display: none;">
<div id="loadingDiv">loading ...</div>
</div>
</body>
</html>
Here is working JSFiddle: https://jsfiddle.net/TomazicM/s0q52kpj/