Greetings!
I am trying to create a custom widget that combines the attribute inspector in one tab, and a live video feed in another tab. I'm currently stuck on populating the contents of each tab. I'm trying to accomplish this via functions, but my results keep showing up as 'undefined' when i run it in the sandbox: ArcGIS API for JavaScript Sandbox
Any help would be greatly appreciated!!
Thank you
-Laura
Solved! Go to Solution.
Laura,
Here is you code updated to work:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
<link rel="stylesheet" href="https://js.arcgis.com/3.24/dijit/themes/soria/soria.css">
<link rel="stylesheet" href="https://js.arcgis.com/3.24/dojox/layout/resources/ExpandoPane.css">
<link rel="stylesheet" href="https://js.arcgis.com/3.24/esri/css/esri.css">
<style>
html,
body,
#mapDiv {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
body {
background-color: #FFF;
overflow: hidden;
font-family: "Trebuchet MS";
}
</style>
<script src="https://js.arcgis.com/3.24/"></script>
<script>
var atInspContent, videoContent;
var map, updateFeature, cameras;
require([
"esri/map",
"esri/InfoTemplate",
"esri/layers/FeatureLayer",
"esri/tasks/query",
"esri/dijit/AttributeInspector",
"esri/dijit/InfoWindow",
"esri/config",
"dojo/dom",
"dojo/dom-construct",
"dojo/number",
"dojo/on",
"dojo/parser",
"dijit/layout/TabContainer",
"dijit/layout/BorderContainer",
"dijit/layout/ContentPane",
"dijit/form/Button",
"dojo/domReady!"
],
function(
Map, InfoTemplate, FeatureLayer, Query, AttributeInspector, InfoWindow,
esriConfig,
dom, domConstruct, number, on, parser,
TabContainer, BorderContainer, ContentPane, Button
) {
parser.parse();
//create the infoWindow
var infoWindow = new InfoWindow({}, domConstruct.create("div"));
infoWindow.startup();
//create the map
var map = new Map("mapDiv", {
basemap: "topo",
center: [-96.755, 33.176],
zoom: 18,
infoWindow: infoWindow
});
//set the infoTemplate for the new infoWindow
var infoTemplate = new InfoTemplate();
infoTemplate.setTitle("${CAMERA_NAME}");
infoTemplate.setContent(setWindowContent());
//define the camera layer that will be added to map
cameras = new FeatureLayer("your url", {
mode: FeatureLayer.MODE_SNAPSHOT,
infoTemplate: infoTemplate,
outFields: ["*"]
});
map.on("layers-add-result", initSelectToolbar);
//add the camera layer to the map
map.addLayers([cameras]);
//resize the infoWindow
map.infoWindow.resize(300, 300);
//function that formats and populates the infoWindow content's 2 tabs
function setWindowContent(evt) {
//make a container for tabs
var tc = new TabContainer({
style: "height: 100%; width: 100%;"
}, dojo.create("div"));
//tab 1
videoContent = new ContentPane({
title: "Video"
});
tc.addChild(videoContent);
//tab 2
atInspContent = new ContentPane({
title: "Edit"
});
tc.addChild(atInspContent);
return tc.domNode;
};
//function that gets the camera url to open and display in tab1
function getVideo(evt) {
selectedCamera = evt.attributes.URL;
return selectedCamera;
};
//function that creates the attribute inspector tab2
function initSelectToolbar(evt) {
var selectQuery = new Query();
map.on("click", function(evt) {
selectQuery.geometry = evt.mapPoint;
selectQuery.distance = 40;
selectQuery.units = "feet"
selectQuery.returnGeometry = true;
selectQuery.outSpatialReference = map.spatialReference;
cameras.selectFeatures(selectQuery, FeatureLayer.SELECTION_NEW, function(features) {
if (features.length > 0) {
//store the current feature
updateFeature = features[0];
map.infoWindow.setTitle(features[0].getLayer().name);
map.infoWindow.show(evt.screenPoint, map.getInfoWindowAnchor(evt.screenPoint));
videoContent.setContent(getVideo(features[0]));
} else {
map.infoWindow.hide();
}
});
});
map.infoWindow.on("hide", function() {
cameras.clearSelection();
});
var layerInfos = [{
'featureLayer': cameras,
'showAttachments': false,
'isEditable': true,
'fieldInfos': [{
'fieldName': 'CAMERA_NAME',
'label': 'Name',
'isEditable': false
},
{
'fieldName': 'VideoStream',
'label': 'Video Stream?',
'isEditable': true
},
{
'fieldName': 'CorrectView',
'label': 'Correct View?',
'isEditable': true
},
{
'fieldName': 'Comments',
'label': 'Comments',
'isEditable': true
}
]
}];
//Initialize Attribute Inspector
var attInspector = new AttributeInspector({
layerInfos: layerInfos
}, domConstruct.create("div"));
//add a save button next to the delete button
var saveButton = new Button({
label: "Save",
"class": "saveButton"
}, domConstruct.create("div"));
domConstruct.place(saveButton.domNode, attInspector.deleteBtn.domNode, "after");
saveButton.on("click", function() {
updateFeature.getLayer().applyEdits(null, [updateFeature], null);
});
//adds a close button next to the save button
var closeButton = new Button({
label: "Close",
"class": "closeButton"
}, domConstruct.create("div"));
domConstruct.place(closeButton.domNode, saveButton.domNode, "after");
//by clicking the close button the following action will happen
closeButton.on("click", function() {
map.infoWindow.hide();
});
attInspector.on("attribute-change", function(evt) {
//store the updates to apply when the save button is clicked
updateFeature.attributes[evt.fieldName] = evt.fieldValue;
});
attInspector.on("next", function(evt) {
updateFeature = evt.feature;
console.log("Next " + updateFeature.attributes.OBJECTID);
});
attInspector.on("delete", function(evt) {
evt.feature.getLayer().applyEdits(null, null, [evt.feature]);
map.infoWindow.hide();
});
atInspContent.setContent(attInspector.domNode);
}
});
</script>
</head>
<body class="soria">
<div data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="design:'headline', gutters:true" style="width: 100%; height: 100%; margin: 0;">
<div id="mapDiv" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'center'"></div>
</div>
</body>
</html>
Laura,
Here is you code updated to work:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
<link rel="stylesheet" href="https://js.arcgis.com/3.24/dijit/themes/soria/soria.css">
<link rel="stylesheet" href="https://js.arcgis.com/3.24/dojox/layout/resources/ExpandoPane.css">
<link rel="stylesheet" href="https://js.arcgis.com/3.24/esri/css/esri.css">
<style>
html,
body,
#mapDiv {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
body {
background-color: #FFF;
overflow: hidden;
font-family: "Trebuchet MS";
}
</style>
<script src="https://js.arcgis.com/3.24/"></script>
<script>
var atInspContent, videoContent;
var map, updateFeature, cameras;
require([
"esri/map",
"esri/InfoTemplate",
"esri/layers/FeatureLayer",
"esri/tasks/query",
"esri/dijit/AttributeInspector",
"esri/dijit/InfoWindow",
"esri/config",
"dojo/dom",
"dojo/dom-construct",
"dojo/number",
"dojo/on",
"dojo/parser",
"dijit/layout/TabContainer",
"dijit/layout/BorderContainer",
"dijit/layout/ContentPane",
"dijit/form/Button",
"dojo/domReady!"
],
function(
Map, InfoTemplate, FeatureLayer, Query, AttributeInspector, InfoWindow,
esriConfig,
dom, domConstruct, number, on, parser,
TabContainer, BorderContainer, ContentPane, Button
) {
parser.parse();
//create the infoWindow
var infoWindow = new InfoWindow({}, domConstruct.create("div"));
infoWindow.startup();
//create the map
var map = new Map("mapDiv", {
basemap: "topo",
center: [-96.755, 33.176],
zoom: 18,
infoWindow: infoWindow
});
//set the infoTemplate for the new infoWindow
var infoTemplate = new InfoTemplate();
infoTemplate.setTitle("${CAMERA_NAME}");
infoTemplate.setContent(setWindowContent());
//define the camera layer that will be added to map
cameras = new FeatureLayer("your url", {
mode: FeatureLayer.MODE_SNAPSHOT,
infoTemplate: infoTemplate,
outFields: ["*"]
});
map.on("layers-add-result", initSelectToolbar);
//add the camera layer to the map
map.addLayers([cameras]);
//resize the infoWindow
map.infoWindow.resize(300, 300);
//function that formats and populates the infoWindow content's 2 tabs
function setWindowContent(evt) {
//make a container for tabs
var tc = new TabContainer({
style: "height: 100%; width: 100%;"
}, dojo.create("div"));
//tab 1
videoContent = new ContentPane({
title: "Video"
});
tc.addChild(videoContent);
//tab 2
atInspContent = new ContentPane({
title: "Edit"
});
tc.addChild(atInspContent);
return tc.domNode;
};
//function that gets the camera url to open and display in tab1
function getVideo(evt) {
selectedCamera = evt.attributes.URL;
return selectedCamera;
};
//function that creates the attribute inspector tab2
function initSelectToolbar(evt) {
var selectQuery = new Query();
map.on("click", function(evt) {
selectQuery.geometry = evt.mapPoint;
selectQuery.distance = 40;
selectQuery.units = "feet"
selectQuery.returnGeometry = true;
selectQuery.outSpatialReference = map.spatialReference;
cameras.selectFeatures(selectQuery, FeatureLayer.SELECTION_NEW, function(features) {
if (features.length > 0) {
//store the current feature
updateFeature = features[0];
map.infoWindow.setTitle(features[0].getLayer().name);
map.infoWindow.show(evt.screenPoint, map.getInfoWindowAnchor(evt.screenPoint));
videoContent.setContent(getVideo(features[0]));
} else {
map.infoWindow.hide();
}
});
});
map.infoWindow.on("hide", function() {
cameras.clearSelection();
});
var layerInfos = [{
'featureLayer': cameras,
'showAttachments': false,
'isEditable': true,
'fieldInfos': [{
'fieldName': 'CAMERA_NAME',
'label': 'Name',
'isEditable': false
},
{
'fieldName': 'VideoStream',
'label': 'Video Stream?',
'isEditable': true
},
{
'fieldName': 'CorrectView',
'label': 'Correct View?',
'isEditable': true
},
{
'fieldName': 'Comments',
'label': 'Comments',
'isEditable': true
}
]
}];
//Initialize Attribute Inspector
var attInspector = new AttributeInspector({
layerInfos: layerInfos
}, domConstruct.create("div"));
//add a save button next to the delete button
var saveButton = new Button({
label: "Save",
"class": "saveButton"
}, domConstruct.create("div"));
domConstruct.place(saveButton.domNode, attInspector.deleteBtn.domNode, "after");
saveButton.on("click", function() {
updateFeature.getLayer().applyEdits(null, [updateFeature], null);
});
//adds a close button next to the save button
var closeButton = new Button({
label: "Close",
"class": "closeButton"
}, domConstruct.create("div"));
domConstruct.place(closeButton.domNode, saveButton.domNode, "after");
//by clicking the close button the following action will happen
closeButton.on("click", function() {
map.infoWindow.hide();
});
attInspector.on("attribute-change", function(evt) {
//store the updates to apply when the save button is clicked
updateFeature.attributes[evt.fieldName] = evt.fieldValue;
});
attInspector.on("next", function(evt) {
updateFeature = evt.feature;
console.log("Next " + updateFeature.attributes.OBJECTID);
});
attInspector.on("delete", function(evt) {
evt.feature.getLayer().applyEdits(null, null, [evt.feature]);
map.infoWindow.hide();
});
atInspContent.setContent(attInspector.domNode);
}
});
</script>
</head>
<body class="soria">
<div data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="design:'headline', gutters:true" style="width: 100%; height: 100%; margin: 0;">
<div id="mapDiv" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'center'"></div>
</div>
</body>
</html>
Robert thank you so much! The editor part of the widget is working great! My only issue left is to get the video feed to display in the first tab. Any further insight would be much appreciated. And thank you again! -Laura
Laura,
If your url attribute is just the url to a video then you need some js component that can play that video added to that tab. Like the HTML video tag.
Don't forget to mark this question as answered by clicking on the "Mark Correct" link on the reply that answered your question.
Rob,
I am unable to view the video stream. The video box appears but the video never loads. This is what I tried using last:
function getVideo(evt) {
videoWindow = '<video controls="true" height="260" width="330"><source src=' + evt.attributes.HYPERLINK + ' ></source></video>';
return videoWindow;
};
I've also tried this below, with no luck either.
function getVideo(evt) {
url=evt.attributes.HYPERLINK;
videoWindow = '<video controls="true" height="260" width="330"><source src=' + url + ' ></source></video>';
return videoWindow;
};
Any help would be greatly appreciated.
Thank you
-Laura
Laura,
What is the video link url look like? Can you share an example?
Unfortunately they are secured feeds and I can't share them. However they are similar in format to this url: https://ourOrg.org/viewer.html?camera=1
UPDATE 7/10/2018
In case anyone was curious, I managed to get the video feed working by using the code below. The only issue I noticed is that every time it updates the feed using the setInterval function, the picture flashes as its being updated. A little annoying and not sure how to get around that.
Thank you! -Laura
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
<link rel="stylesheet" href="https://js.arcgis.com/3.24/dijit/themes/soria/soria.css">
<link rel="stylesheet" href="https://js.arcgis.com/3.24/esri/css/esri.css">
<style>
html,
body,
#mapDiv {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
body {
background-color: #FFF;
overflow: hidden;
font-family: "Trebuchet MS";
}
</style>
<script src="https://js.arcgis.com/3.24/"></script>
<script>
var atInspContent, videoContent;
var map, updateFeature, cameras;
require([
"dojo/dom-construct",
"esri/map",
"esri/dijit/InfoWindow",
"esri/layers/FeatureLayer",
"esri/InfoTemplate",
"esri/tasks/query",
"esri/dijit/AttributeInspector",
"esri/config",
"dojo/number",
"dojo/on",
"dijit/layout/TabContainer",
"dijit/layout/BorderContainer",
"dijit/layout/ContentPane",
"dijit/form/Button",
"dojo/domReady!"
],
function (
domConstruct, Map, InfoWindow, FeatureLayer, InfoTemplate, Query, AttributeInspector,
esriConfig,
number, on,
TabContainer, BorderContainer, ContentPane, Button
) {
//infoWindow: Creation
var infoWindow = new InfoWindow({}, domConstruct.create("div"));
infoWindow.startup();
//Map: Creation
var map = new Map("mapDiv", {
basemap: "topo",
center: [-96.755, 33.176],
zoom: 20,
infoWindow: infoWindow
});
//infoTemplate: Creation
var infoTemplate = new InfoTemplate();
infoTemplate.setTitle("${CAMERA_NAME}");
infoTemplate.setContent(setWindowContent());
//Layer: Creation
var cameras = new FeatureLayer("MY_CAMERA_URL", {
mode: FeatureLayer.MODE_SNAPSHOT,
infoTemplate: infoTemplate,
outFields: ["*"]
});
//Map & infoWindow: Actions
//when the camera layer is added to the map, the Attribute Inspector tool (TAB 2) is initialized
map.on("layers-add-result", initSelectToolbar);
//adds camera layer to map
map.addLayers([cameras]);
//resizes the infoWindow
map.infoWindow.resize(450, 410);
//infoWindow Container: Creation
function setWindowContent(evt) {
//makes a container for tabs
var tc = new TabContainer({
style: "height: 100%; width: 100%;"
}, dojo.create("div"));
//creates tab 1
videoContent = new ContentPane({
title: "Video"
});
tc.addChild(videoContent);
//creates tab 2
atInspContent = new ContentPane({
title: "Edit"
});
tc.addChild(atInspContent);
return tc.domNode;
};
//infoWindow TAB 1: Gets the camera video feed--------------------------------------------------------//
function getVideo(evt) {
cameraURL = evt.attributes.URL;
setInterval(function () {
//assigns the selected camera url to the src attribute of the html iframe
document.getElementById('video').src = cameraURL;
//sets the cadence for refreshing the image - in this case it's every 1 second
}, 1000);
return document.getElementById('video');
};
//infoWindow TAB 2: Gets the Attribute Inspector tool--------------------------------------------------//
function initSelectToolbar(evt) {
var selectQuery = new Query();
map.on("click", function (evt) {
selectQuery.geometry = evt.mapPoint;
selectQuery.distance = 4;
selectQuery.units = "feet"
selectQuery.returnGeometry = true;
selectQuery.outSpatialReference = map.spatialReference;
cameras.selectFeatures(selectQuery, FeatureLayer.SELECTION_NEW, function (features) {
if (features.length > 0) {
updateFeature = features[0];
map.infoWindow.show(evt.screenPoint, map.getInfoWindowAnchor(evt.screenPoint));
videoContent.setContent(getVideo(features[0]));
} else {
map.infoWindow.hide();
}
});
});
//infoWindow: Action
map.infoWindow.on("hide", function () {
cameras.clearSelection();
});
//Editable Fields: Configuration
var layerInfos = [{
'featureLayer': cameras,
'showAttachments': false,
'isEditable': true,
'fieldInfos': [{
'fieldName': 'CAMERA_NAME',
'label': 'Name',
'isEditable': false
},
{
'fieldName': 'VideoStream',
'label': 'Video Stream?',
'isEditable': true
},
{
'fieldName': 'CorrectView',
'label': 'Correct View?',
'isEditable': true
},
{
'fieldName': 'Comments',
'label': 'Comments',
'isEditable': true
}
]
}];
//Attribute Inspector: Initialize
var attInspector = new AttributeInspector({
layerInfos: layerInfos
}, domConstruct.create("div"));
//Attribute Inspector: Adds save button next to delete button
var saveButton = new Button({
label: "Save",
"class": "saveButton"
}, domConstruct.create("div"));
domConstruct.place(saveButton.domNode, attInspector.deleteBtn.domNode, "after");
//Save Button: Action
saveButton.on("click", function () {
updateFeature.getLayer().applyEdits(null, [updateFeature], null);
});
//Attribute Inspector: Adds close button next to save button
var closeButton = new Button({
label: "Close",
"class": "closeButton"
}, domConstruct.create("div"));
domConstruct.place(closeButton.domNode, saveButton.domNode, "after");
//Close Button: Action
closeButton.on("click", function () {
map.infoWindow.hide();
});
//Attribute Inspector: Action
attInspector.on("attribute-change", function (evt) {
//stores the updates to apply when the save button is clicked
updateFeature.attributes[evt.fieldName] = evt.fieldValue;
});
//Attribute Inspector: Action
attInspector.on("next", function (evt) {
updateFeature = evt.feature;
});
atInspContent.setContent(attInspector.domNode);
};
});
</script>
</head>
<body class="soria">
<div data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="design:'headline', gutters:true" style="width: 100%; height: 100%; margin: 0;">
<div id="mapDiv" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'censater'">
</div>
</div>
<iframe id="video" width="400" height="330" frameborder="0" src="">
</iframe>
</body>
</html>