Orbiting same in both Forge Viewer while having two viewer - autodesk-forge

I am having two viewers in my application and want both of them to orbit the same with the same positions and camera angles and also follow the same while zooming in and out.
Is there any way?
I've applied the below solution but it's delayed by one second.
viewer2.addEventListener(Autodesk.Viewing.CAMERA_CHANGE_EVENT, function()
{
if(!viewer1CameraChangeMutex) {
clearTimeout(viewer2CameraChangeMutex);
viewer.restoreState(viewer2.getState());
viewer2CameraChangeMutex=setTimeout(function(){viewer2CameraChangeMutex=undefined},1000)
}
});
viewer.addEventListener(Autodesk.Viewing.CAMERA_CHANGE_EVENT, function()
{
if(!viewer2CameraChangeMutex) {
clearTimeout(viewer1CameraChangeMutex);
viewer2.restoreState(viewer.getState());
viewer1CameraChangeMutex=setTimeout(function(){viewer1CameraChangeMutex=undefined},1000)
}
});

You can stop unnecessary event ping-pong b/w viewers, e.g. change view on viewer1 --> change event on viewer 1 then set view state to viewer2 --> change event on viewer 2 then change viewer 1 state --> change event on viewer 1 ....., by comparing viewport status and set view status to other viewer only when viewport status is not in same status.
Below is example code snippet for above explanation.
function compare(stateA, stateB)
{
var viewportA = stateA["viewport"] || {};
var viewportB = stateB["viewport"] || {};
//compare all members of viewportA and viewportB, return true when all members are same.
if( viewportA["name"] !== viewportB["name"] ||
viewportA["projection"] !== viewportB["projection"] ||
viewportA["isOrthographic"] !== viewportB["isOrthographic"] ||
.......//compare all other members of viewport.
//Please note you need to afforded small numeric error for numeric value comparison.
)
{
return false;
}
return true;
}
var sfilter = {
viewport: true
};
viewer3d1.addEventListener(Autodesk.Viewing.CAMERA_CHANGE_EVENT, function () {
var v1state = viewer3d1.getState(sfilter);
var v2state = viewer3d2.getState(sfilter);
if (!compare(v1state, v2state)) {
viewer3d2.restoreState(viewer3d1.getState(sfilter), sfilter, true);
}
});
viewer3d2.addEventListener(Autodesk.Viewing.CAMERA_CHANGE_EVENT, function () {
var v1state = viewer3d1.getState(sfilter);
var v2state = viewer3d2.getState(sfilter);
if (!compare(v1state, v2state)) {
viewer3d1.restoreState(viewer3d2.getState(sfilter), sfilter, true);
}
});

Related

Make own calibration dialog with comma delimiter decimal

Is there an event similar as "FINISHED_CALIBRATION" when the style for the "calibration panel" is set to display: block? I want to create a custom calibration dialog with comma separator. I want to open own calibration dialog instead of your dialog. I check two point for calibration with this code:
const calibrationEndpoints: HTMLCollection = document.getElementsByClassName('calibration-endpoint');
const countPoint = Array.from(calibrationEndpoints).filter(e => e.classList.contains('editable')).length;
if (countPoint === 2) {
this.openCalibrationDialog();
}
Please review this sample
this.showPanel = function() {
var self = this;
const _window = this.getWindow();
if (_calibrationPanel) {
_window.setTimeout(function () { _calibrationPanel.requestedSizeTextbox.focus();}, 0);
_calibrationPanel.setVisible(true);
_calibrationPanel.updatePanelPosition(_measurement.indicator.labelPosition, _measurement.indicator.p1, _measurement.indicator.p2, _measurement.indicator.calibrationLabel.clientHeight);
self.addWindowEventListener("keyup", function onKeyUp(e){
var key = e.key || String.fromCharCode(e.keyCode);
if (key == "Escape" && self.isActive()) {
self.hidePanel();
self.clearSize();
self.showAddCalibrationLabel();
self.removeWindowEventListener("keyup", onKeyUp);
}
});
}
else {
_viewer.dispatchEvent({ type: MeasureCommon.Events.OPEN_CALIBRATION_PANEL_EVENT, data: {size: _selectedSize, units: _selectedUnits } });
}
};

Autodesk-XLSExtension, undefined viewer

Im trying to implement the XLS Extension. In the ModelData class, i cannot get objects leaf nodes because the viewer is undefined.
Here is the problematic method:
getAllLeafComponents(callback) {
// from https://learnforge.autodesk.io/#/viewer/extensions/panel?id=enumerate-leaf-nodes
viewer.getObjectTree(function (tree) {
let leaves = [];
tree.enumNodeChildren(tree.getRootId(), function (dbId) {
if (tree.getChildCount(dbId) === 0) {
leaves.push(dbId);
}
}, true);
callback(leaves);
});
}
Im getting Cannot read properties of undefined (reading 'getObjectTree') , meaning viewer is undefined.
However, viewer is working and displaying documents.
I tried to call it by window.viewer and this.viewer to no avail.
Thanks in advance for any help
It looks like it missed two lines. Could you try the revised one below?
// Model data in format for charts
class ModelData {
constructor(viewer) {
this._modelData = {};
this._viewer = viewer;
}
init(callback) {
var _this = this;
var viewer = _this._viewer;
_this.getAllLeafComponents(function (dbIds) {
var count = dbIds.length;
dbIds.forEach(function (dbId) {
viewer.getProperties(dbId, function (props) {
props.properties.forEach(function (prop) {
if (!isNaN(prop.displayValue)) return; // let's not categorize properties that store numbers
// some adjustments for revit:
prop.displayValue = prop.displayValue.replace('Revit ', ''); // remove this Revit prefix
if (prop.displayValue.indexOf('<') == 0) return; // skip categories that start with <
// ok, now let's organize the data into this hash table
if (_this._modelData[prop.displayName] == null) _this._modelData[prop.displayName] = {};
if (_this._modelData[prop.displayName][prop.displayValue] == null) _this._modelData[prop.displayName][prop.displayValue] = [];
_this._modelData[prop.displayName][prop.displayValue].push(dbId);
})
if ((--count) == 0) callback();
});
})
})
}
getAllLeafComponents(callback) {
var _this = this;
var viewer = _this._viewer;
// from https://learnforge.autodesk.io/#/viewer/extensions/panel?id=enumerate-leaf-nodes
viewer.getObjectTree(function (tree) {
var leaves = [];
tree.enumNodeChildren(tree.getRootId(), function (dbId) {
if (tree.getChildCount(dbId) === 0) {
leaves.push(dbId);
}
}, true);
callback(leaves);
});
}
hasProperty(propertyName){
return (this._modelData[propertyName] !== undefined);
}
getLabels(propertyName) {
return Object.keys(this._modelData[propertyName]);
}
getCountInstances(propertyName) {
return Object.keys(this._modelData[propertyName]).map(key => this._modelData[propertyName][key].length);
}
getIds(propertyName, propertyValue) {
return this._modelData[propertyName][propertyValue];
}
}

CesiumJS not displaying globe with image

I'm trying to display a heatmap over the globe in Cesium but the globe isn't even showing up on the screen, only the background is. I looked in the network part of google chrome and it shows the actual image I need being loaded from the server.
<script>
var count=0;
var viewer = new Cesium.CesiumWidget('cesiumContainer');
var layers = viewer.scene.imageryLayers;
var imageArray = <?php echo json_encode($images) ?>// PARSING PHP ARRAY INTO JAVASCIPT
alert(imageArray[0]);
var date;var name='HeatMap-2006-01-16.png'; //FOR INITAL PAGE LOAD
loadCesium();
function loadCesium()
{
//Cesium Active Window
layers.addImageryProvider(new Cesium.SingleTileImageryProvider({
url : 'images/'.concat(name),
rectangle : Cesium.Rectangle.fromDegrees(-180.0, -90.0, 180.0, 90.0)
}));
}
function overlayChange()
{
name = imageArray[count];
for (i = 0; i < name.length; i++)
{
if(name.charAt(i)=="-")
{
date = name.substring(i);
break;
}
}
loadCesium();
count = count + 1;
}
function overlayChangeBack()
{
if(count == 0)
{
count = 39;
name = 'HeatMap';
name = name.concat(count.toString());
loadCesium();
}
else
{
name = 'HeatMap';
count=count-1;
name = name.concat(count.toString());
loadCesium();
}
}
</script>
Right now I'm just trying to display the name variable('HeatMap-2006-01-16.png') for the initial image but it's not displaying. No image I try to put instead displays either so it's definitely an issue with cesium.
I'm not sure why this fixed it because I've had it working without this statement but when declaring the cesiumContainer in the variable viewer, you have to set it as this:
var viewer = new Cesium.CesiumWidget('cesiumContainer', {
imageryProvider : new Cesium.ArcGisMapServerImageryProvider({
url : 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer'
}),
baseLayerPicker : false
});

mapserver with WMS call with openlayers

I scenario is below :
map is shown under from TiledWMS layer from mapserver. It has 2 layers.
TiledWMS layer for OSM world map.
TiledWMS layer for layers defined in kml file placed in mapserver through .map file. This map file contains many layers.
Now , when user click on map : it got 2 layers as above.
But since 2nd layer is made up of different layers as given in .map file , i am not able to uniquely identify these layers. I want that since 2 nd layer is made up of different layers in kml file i should be able to uniquely identify them on mouse click or hower.
Thanks
Satpal
I am able to get it : below is samaple code for others.
var coord = evt.coordinate;
var pixel = $scope.map.getPixelFromCoordinate(coord);
var viewProjection = $scope.map.getView().getProjection();
var viewResolution = $scope.map.getView().getResolution();
var numberOfLayersOnMap = $scope.map.getLayers();
var feature = $scope.map.forEachFeatureAtPixel(pixel, function(feature, layer){return feature;}, null, function(layer) {return true;});
if(feature === undefined)
{
$scope.map.forEachLayerAtPixel(pixel, function (layer)
{
if(!layer)
{
return ;
}
var urlWMSGetFeatureInfo = layer.getSource().getGetFeatureInfoUrl(coord, viewResolution, viewProjection, {
'INFO_FORMAT': 'application/vnd.ogc.gml'
});
if(urlWMSGetFeatureInfo.indexOf("osm-google.map")<0)
{
$http({
method: 'GET',
url: urlWMSGetFeatureInfo,
}).success(function(data){
var parser = new ol.format.WMSGetFeatureInfo();
var features = parser.readFeatures(data);
if(features.length>0)
{
var featureName = features[0].n.Name;
topOverlayElement.innerHTML = featureName;
$scope.highlightOverlay.setFeatures(new ol.Collection());
if($scope.flagLinkage == true)
{
var xmlObj = utility.StringToXML(data);
var xmlDocumnet = xmlObj.childNodes[0];
var layerNode = xmlDocumnet.children[0];
var gmlLayerNode = layerNode.children[0];
var layerName = gmlLayerNode.textContent;
var layerInfoObject = {};
layerInfoObject.layerName = layerName;
//layerInfoObject.placemarkName = featureName;
$scope.placemarksSelectedObject.push(layerInfoObject);
$scope.placemarksSelectedFeatureObject.push(features[0]);
}
else
{
$scope.placemarksSelectedFeatureObject.length = 0;
$scope.placemarksSelectedFeatureObject.push(features[0]);
}
$scope.highlightOverlay.setFeatures(new ol.Collection($scope.placemarksSelectedFeatureObject));
var featureDescription = features[0].n.description;
middleOverlayElement.innerHTML = (featureDescription === undefined) ? '' : featureDescription;
$scope.showOverlay(coord);
}
}).error(function (data) {
console.log("Not able to get capabilty data.");
});
}
else
{
$scope.closeOverlay(evt);
}
});

Force localStorage event to fire in a different window

When the user clicks on a link it opens a page, adds some content to the local storage then the page that was just opened is supposed to listen for localStorage events. It doesn't seem to fire as I understand both windows must be opened at the same time for the storage event to fire. So I tried to invoke it manually like this:
//page containing the link to other window
$('.view-btn').click(function () {
var selectionName = $(this).parent().parent().find('td:first').text();
var key = "sel-view-1";
addToStorage(key, selectionName);
});
var addToStorage = function (key, value) {
defineKey(key, value);
var e = $.Event("storage");
e.originalEvent = {
key: key,
newValue: value
};
var windowRef = window.open("Create_a_selection", "_self");
$(windowRef).trigger(e);
};
var defineKey = function (key, value) {
if (localStorage.getItem(key) === null) {
localStorage.setItem(key, value);
} else {
var nr = parseInt(key.substring(key.lastIndexOf('-') + 1)) + 1;
key = 'sel-view-' + nr;
return defineKey(key, value);
}
}
//page that was just opened
$(window).bind('storage', onStorageEvent);
function onStorageEvent(storageEvent) {
console.log('yay');
if (storageEvent.originalEvent) {
var key = storageEvent.originalEvent.key;
var value = storageEvent.originalEvent.newValue;
if (key.indexOf("sel-view") > -1) {
$('#selection-name-label').html(value);
}
}
};
But this still doesn't catch the event... It does however catch it if the page is already opened so I'm wondering if this could have something to do with the page not being loaded yet perhaps?
Any help is appreciated.