I tried this sample which demonstrates how to use TimeSlider widget with minimal configuration. The TimeSlider widget simplifies the process of visualizing temporal data in a JavaScript application.
It uses an instance of FeatureLayer to visualize incremental precipitation forecast for next 72 hours across the contiguous United States with 6-hour interval.
The map displays the Quantitative Precipitation Forecast (QPF) for the next 72 hours across the contiguous United States. Data are updated hourly from the National Digital Forecast Database produced by the National Weather Service. Visit the portal item description page for more information.
There are four different mode options when initializing the TimeSlider widget: instant, time-window, cumulative-from-start, and cumulative-from-end. The default mode for the time slider is time-window. The sample uses this default mode, meaning that slider will show precipitation data that falls within a given time range, which is 6 hours in this case.
We are setting the view property on the time slider widget. The TimeSlider widget will update the view's timeExtent whenever the time slider's timeExtent changes. Setting the view property will affect any time-aware layer in the view.
<script>
require([
"esri/Map",
"esri/views/MapView",
"esri/layers/FeatureLayer",
"esri/widgets/TimeSlider",
"esri/widgets/Expand",
"esri/widgets/Legend"
], (Map, MapView, FeatureLayer, TimeSlider, Expand, Legend) => {
const layer = new FeatureLayer({
url: "https://services9.arcgis.com/RHVPKKiFTONKtxq3/arcgis/rest/services/NDFD_Precipitation_v1/FeatureServer/0"
});
const map = new Map({
basemap: "hybrid",
layers: [layer]
});
const view = new MapView({
map: map,
container: "viewDiv",
zoom: 4,
center: [-100, 30]
});
// time slider widget initialization
const timeSlider = new TimeSlider({
container: "timeSlider",
view: view,
timeVisible: true, // show the time stamps on the timeslider
loop: true
});
// add the UI for a title
view.ui.add("titleDiv", "top-right");
view.whenLayerView(layer).then((lv) => {
// around up the full time extent to full hour
timeSlider.fullTimeExtent =
layer.timeInfo.fullTimeExtent.expandTo("hours");
timeSlider.stops = {
interval: layer.timeInfo.interval
};
});
const legend = new Legend({
view: view
});
const legendExpand = new Expand({
expandIconClass: "esri-icon-legend",
expandTooltip: "Legend",
view: view,
content: legend,
expanded: false
});
view.ui.add(legendExpand, "top-left");
});
</script>
CodePudding user response:
For time offset on the slider bar, this is another approach to do the time slider.
let timeSlider, firesChart;
const map = new Map({
basemap: {
portalItem: {
id: "4f2e99ba65e34bb8af49733d9778fb8e"
}
}
});
const view = new MapView({
container: "viewDiv",
map: map,
zoom: 5,
center: [-118, 37.49] // longitude, latitude
});
// create five new instances of feature layers
// based on the following definitions
const url =
"https://services.arcgis.com/V6ZHFr6zdgNZuVG0/arcgis/rest/services/California_fires_since_2014/FeatureServer/";
const definitions = [
{ id: 0, year: 2014, offset: 4 },
{ id: 1, year: 2015, offset: 3 },
{ id: 2, year: 2016, offset: 2 },
{ id: 3, year: 2017, offset: 1 },
{ id: 4, year: 2018, offset: 0 }
];
const layers = definitions.map((definition) => {
return createLayer(definition);
});
// add the california fire layers
map.addMany(layers);
// get layerviews of each fire layers once the layers are loaded
const layerViewsEachAlways = () => {
return promiseUtils.eachAlways(
layers.map((layer) => {
return view.whenLayerView(layer);
})
);
};
view.when(() => {
timeSlider = new TimeSlider({
container: "timeSliderDiv",
view: view,
fullTimeExtent: {
start: new Date(2018, 0, 1),
end: new Date(2018, 11, 1)
},
playRate: 2000,
stops: {
interval: {
value: 1,
unit: "months"
}
},
// create actions that will allow users to
// jump to months that had most burns
actions: [
{
id: "july",
icon: "clock",
title: "Month with most burns"
},
{
id: "august",
icon: "clock",
title: "Month with second most burns"
},
{
id: "september",
icon: "clock",
title: "Month with third most burns"
}
],
// set custom labels for the timeslider's min, max dates
// then hide the extent label
labelFormatFunction: (value, type, element, layout) => {
const normal = new Intl.DateTimeFormat("en-us");
switch (type) {
case "min":
element.setAttribute("style", "color: #ff642e;");
element.innerText = "Jan";
break;
case "max":
element.setAttribute("style", "color: #ff642e;");
element.innerText = "Dec";
break;
case "extent":
element.parentNode.setAttribute("style", "width:0px");
break;
}
}
});
// the "trigger-action" event occurs when an item in the action button is selected
// define custom actions to occur for each action.id
timeSlider.on("trigger-action", function (event) {
var id = event.action.id;
if (id === "july") {
timeSlider.timeExtent = {
start: new Date(2018, 6, 1),
end: new Date(2018, 7, 1)
};
} else if (id === "august") {
timeSlider.timeExtent = {
start: new Date(2018, 7, 1),
end: new Date(2018, 8, 1)
};
} else if (id === "september") {
timeSlider.timeExtent = {
start: new Date(2018, 8, 1),
end: new Date(2018, 9, 1)
};
}
});
// update the fire stats between 2014 - 2018 once timeExtent of
// the timeSlider changes
timeSlider.watch("timeExtent", () => {
updateFiresStats();
});
createFiresChart();
});
view.whenLayerView(layers[0]).then((layerView) => {
watchUtils.whenFalseOnce(layerView, "updating", () => {
updateFiresStats();
});
});
// fire stats for each year between 2014 - 2018
const sumAcres = {
onStatisticField: "GIS_ACRES",
outStatisticFieldName: "acres_sum",
statisticType: "sum"
};
const fireCounts = {
onStatisticField: "OBJECTID",
outStatisticFieldName: "fire_counts",
statisticType: "count"
};
const year = {
onStatisticField: "ALARM_DATE",
outStatisticFieldName: "year",
statisticType: "max"
};
// stats query
const statQuery = {
outStatistics: [sumAcres, fireCounts, year]
};
// query five layerviews representing fires between 2014-2018
// this will be called from the UudateFiresStats function
const queryFeaturesForStats = (layerViewsResults) => {
return promiseUtils.eachAlways(
layerViewsResults.map((result) => {
// If a Promise was rejected, you can check for the rejected error
if (result.error) {
return promiseUtils.resolve(result.error);
}
// The results of the Promise are returned in the value property
else {
const layerView = result.value;
// set the timeExtent for the stats query object. But
// we need to offset the timeExtent for each layer by
// number of years specified in the layer.timeOffset
let start = new Date(timeSlider.timeExtent.start);
let end = new Date(timeSlider.timeExtent.end);
start.setFullYear(
start.getFullYear() - layerView.layer.timeOffset.value
);
end.setFullYear(
end.getFullYear() - layerView.layer.timeOffset.value
);
// now we have the right timeExtent for each layer
// set the timeExtent for the stats query
statQuery.timeExtent = {
start: start,
end: end
};
// query the layerviews for the stats
return layerView.queryFeatures(statQuery).then(
(response) => {
return response.features[0].attributes;
},
(e) => {
return promiseUtils.resolve(e);
}
);
}
})
);
};
// This function is called when the timeSlider's timeExtent changes
// It queries each layer view for fire stats and updates the chart
function updateFiresStats() {
layerViewsEachAlways().then((layerViewsResults) => {
// query each and every fire layerviews for stats and process the results
queryFeaturesForStats(layerViewsResults).then(
(fireStatsQueryResults) => {
monthDiv.innerHTML = "";
let month;
let acresBurntList = [];
let chartLabels = [];
// fire stats query results are back. Loop through them and process.
fireStatsQueryResults.map((result) => {
if (result.error) {
return promiseUtils.resolve(result.error);
}
// The results of the Promise are returned in the value property
else {
// if the stats query returned a year for the given layerview
// then process and update the chart
if (result.value.year !== null) {
// extract the year and month from the date
let date = new Date(result.value.year);
let year = date.getFullYear();
// array of burnt acres sum returned in the query results
// for each layerview representing fires between 2014-2018
acresBurntList.push(result.value.acres_sum.toFixed(2));
//chart labels will show the year and count of fires for that year
const label = year ", " result.value.fire_counts;
chartLabels.push(label);
}
}
});
// query results have been processed. Update the fires chart to display
// sum of acres burnt for the given month and year
firesChart.data.datasets[0].data = acresBurntList;
firesChart.data.labels = chartLabels;
firesChart.update();
startMonth = timeSlider.timeExtent.start.toLocaleString(
"default",
{ month: "long" }
);
endMonth = timeSlider.timeExtent.end.toLocaleString("default", {
month: "long"
});
monthDiv.innerHTML =
"<b> Month: <span>"
startMonth
" - "
endMonth
"</span></b>";
}
);
});
}
// Creates five instances of feature layers each representing
// fires for the given year (between 2014 - 2018)
function createLayer(definition) {
const year = definition.year;
return new FeatureLayer({
url: url definition.id,
timeOffset: {
value: definition.offset,
unit: "years"
},
outFields: ["*"],
popupTemplate: {
title: "{ALARM_DATE}",
content: "{YEAR_}, {ALARM_DATE}, {GIS_ACRES}"
}
});
}
// create the legend
const layerInfos = layers.map((layer, i) => {
return {
title: "",
layer: layer
};
});
const legend = new Legend({
view: view,
layerInfos: layerInfos
});
view.ui.add(legend, "top-left");
// Fires chart section
const monthDiv = document.getElementById("monthDiv");
const infoDiv = document.getElementById("infoDiv");
const chartCanvas = document.getElementById("fireChart");
const infoDivExpand = new Expand({
collapsedIconClass: "esri-icon-collapse",
expandIconClass: "esri-icon-expand",
expandTooltip: "Expand earthquakes info",
view: view,
content: infoDiv,
expanded: true
});
view.ui.add(infoDivExpand, "top-right");
// create a bar chart to display sum of acres
// burnt for the given month and year.
function createFiresChart() {
Chart.defaults.global.defaultFontColor = "#d1d1d1";
firesChart = new Chart(chartCanvas.getContext("2d"), {
type: "bar",
data: {
labels: [2014, 2015, 2016, 2017, 2018],
datasets: [
{
label: "Acres burnt, year, fire counts",
backgroundColor: "#ff642e",
data: [0, 0, 0, 0, 0]
}
]
},
options: {
responsive: false,
legend: {
position: "bottom"
},
title: {
display: true,
text: "Acres burnt by month",
fontFamily:
"'Avenir Next W00','Helvetica Neue', Helvetica, Arial, sans-serif"
},
scales: {
yAxes: [
{
ticks: {
beginAtZero: true,
callback: (value) => {
if (value % 1 === 0) {
return value;
}
}
},
gridLines: {
zeroLineColor: "#d1d1d1",
color: "#696969"
}
}
],
xAxes: [
{
gridLines: {
zeroLineColor: "#d1d1d1",
color: "#696969"
}
}
]
},
tooltips: {
callbacks: {
title: (tooltipItem, data) => {
return "Fire";
},
label: (tooltipItem, data) => {
const yearCount = tooltipItem.xLabel.split(",");
let customTooltip = [];
customTooltip.push("Year: " yearCount[0]);
customTooltip.push("Count:" yearCount[1]);
customTooltip.push("Acres:" tooltipItem.yLabel);
return customTooltip;
}
},
displayColors: false
}
}
});
CodePudding user response:
For a more formatted and visualized approach, use the below code:
labelFormatFunction: (value, type, element, layout) => {
const normal = new Intl.DateTimeFormat('en-us');
switch (type) {
case "min":
case "max":
element.setAttribute("style", "font-family: 'Orbitron', sans-serif; font-size: 16px; color: magenta;");
element.innerText = normal.format(value);
break;
case "extent":
const start = timeSlider.fullTimeExtent.start;
const days = (value[0].getTime() - start.getTime()) / 1000 / 3600 / 24;
element.setAttribute(`style`, `font-family: 'Orbitron', sans-serif; font-size: 22px; color: red;`);
element.innerText = `Day ${days}`;
break;
}
CodePudding user response:
As compared to previous versions, this Javascript API is the most simplified version. But, as in my case, i was using older version, but than to change on newer version requires a lot of effort to rewrite the whole code again.
CodePudding user response:
This is my code, hope this will help you out
function setupHoverTooltip(layerview) {
var promise;
var highlight;
var tooltip = createTooltip();
view.on("pointer-move", function (event) {
if (promise) {
promise.cancel();
}
promise = view.hitTest(event.x, event.y)
.then(function (hit) {
promise = null;
// remove current highlighted feature
if (highlight) {
highlight.remove();
highlight = null;
}
var results = hit.results.filter(function (result) {
return result.graphic.layer === bldtmlinelayer;
});
if (results.length) {
var graphic = results[0].graphic;
var screenPoint = hit.screenPoint;
highlight = layerview.highlight(graphic);
tooltip.show(screenPoint, "Built in " graphic.getAttribute(attrib.BUILDINGS.YEARBUILT));
} else {
tooltip.hide();
}
});
});
}
function startAnimation() {
stopAnimation();
animation = animate(parseFloat(slider.value));
playButton.classList.add("toggled");
}
function stopAnimation() {
if (!animation) {
return;
}
animation.remove();
animation = null;
playButton.classList.remove("toggled");
}
function animate(startValue) { var animating = true; var value = startValue;
setYear(value);
// Update at 30fps
setTimeout(function () {
requestAnimationFrame(frame);
}, 1000 / 3);
};
frame();
return {
remove: function () {
animating = false;
}
};
}
function createTooltip() {
var tooltip = document.createElement("div");
var style = tooltip.style;
tooltip.setAttribute("role", "tooltip");
tooltip.classList.add("tooltip");
var textElement = document.createElement("div");
textElement.classList.add("esri-widget");
tooltip.appendChild(textElement);
view.container.appendChild(tooltip);
var x = 0;
var y = 0;
var targetX = 0;
var targetY = 0;
var visible = false;
// move the tooltip progressively
function move() {
x = (targetX - x) * 0.1;
y = (targetY - y) * 0.1;
if (Math.abs(targetX - x) < 1 && Math.abs(targetY - y) < 1) {
x = targetX;
y = targetY;
} else {
requestAnimationFrame(move);
}
style.transform = "translate3d(" Math.round(x) "px," Math.round(y) "px, 0)";
}
return {
show: function (point, text) { if (!visible) { x = point.x; y = point.y;} targetX = point.x; targetY = point.y; style.opacity = 1; visible = true; textElement.innerHTML = text; move();},
hide: function () { style.opacity = 0; visible = false; }
};
}
scene.add(bldtmlinelayer);