I wanted to move the model dynamically using keyboard shortcuts. I could not find relevant article on that.
So for now, I'm trying to move the model on click. When click on the model. The model has to move in one direction (increment the value 1 on tick). Find below the sandcastle code for that.
var selectedMesh; var i=0;
var viewer = new Cesium.Viewer('cesiumContainer', {
infoBox: false,
selectionIndicator: false
});
var handle = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
function createModel(url, height) {
viewer.entities.removeAll();
var position = Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706, height);
var heading = Cesium.Math.toRadians(135);
var pitch = 0;
var roll = 0;
var orientation = Cesium.Transforms.headingPitchRollQuaternion(position, heading, pitch, roll);
var entity = viewer.entities.add({
name: url,
position: position,
orientation: orientation,
model: {
uri: url,
minimumPixelSize: 128
}
});
viewer.trackedEntity = entity;
viewer.clock.onTick.addEventListener(function () {
if (selectedMesh) {
console.log("Before 0 : " + selectedMesh.primitive.modelMatrix[12]);
selectedMesh.primitive.modelMatrix[12] = selectedMesh.primitive.modelMatrix[12] + 1;
console.log("After 0 : " + selectedMesh.primitive.modelMatrix[12]);
}
});
}
handle.setInputAction(function (movement) {
console.log("LEFT CLICK");
var pick = viewer.scene.pick(movement.position);
if (Cesium.defined(pick) && Cesium.defined(pick.node) && Cesium.defined(pick.mesh)) {
if (!selectedMesh) {
selectedMesh = pick;
}
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
var options = [{
text: 'Aircraft',
onselect: function () {
createModel('../../SampleData/models/CesiumAir/Cesium_Air.bgltf', 5000.0);
}
}, {
text: 'Ground vehicle',
onselect: function () {
createModel('../../SampleData/models/CesiumGround/Cesium_Ground.bgltf', 0);
}
}, {
text: 'Milk truck',
onselect: function () {
createModel('../../SampleData/models/CesiumMilkTruck/CesiumMilkTruck.bgltf', 0);
}
}, {
text: 'Skinned character',
onselect: function () {
createModel('../../SampleData/models/CesiumMan/Cesium_Man.bgltf', 0);
}
}];
Sandcastle.addToolbarMenu(options);
When I click, the model is moving for the first time. After that, It stays on the same place. I've printed the value in the console. It seems the value is not changing. I'm not sure about the problem here. or I'm implementing the transformation wrongly.
If you keep track of the current lat and lon of the entity, and adjust that lat and lon based on user input, all you need to do is update the orientation of the entity.
var lon = // the updated lon
var lat = // updated lat
var position = Cesium.Cartesian3.fromDegrees(lon, lat, height);
var heading = Cesium.Math.toRadians(135);
var pitch = 0;
var roll = 0;
// create an orientation based on the new position
var orientation = Cesium.Transforms.headingPitchRollQuaternion(position, heading, pitch, roll);
Then you just need to update the orientation of the entity.
entity.orientation = orientation;
By changing the value, the models orientation, and therefore position will get updated.
Related
var container = document.getElementById('popup');
var content = document.getElementById('popup-content');
var closer = document.getElementById('popup-closer');
var overlay = new Overlay({
element: container,
autoPan: true,
autoPanAnimation: {
duration: 250,
},
});
closer.onclick = function () {
overlay.setPosition(undefined);
closer.blur();
return false;
};
this.draw.on('drawend', (e: any) => {
const coordinate = e.coordinate;
const hdms = toStringHDMS(toLonLat(coordinate));
content.innerHTML =
'<p> Current Position are :</p><code>' + hdms + '</code>';
overlay.setPosition(coordinate);
});
this.map.addOverlay(overlay);
}
pop up is not coming after the draw end. but the pop up is working on map single click events .i could not find anything related on internet
I made an info card and this card will disappear if the viewer is rotated until the model is not visible. I use isNodevisible but it always returns true.
updateInfoCard() {
if (this.infoCard && this.posModel) {
const pos = this.viewer.worldToClient(this.posModel);
console.log(pos);
this.infoCard.style.left = `${Math.floor(
50 + pos.x - this.infoCard.offsetWidth / 2
)}px`;
this.infoCard.style.top = `${Math.floor(
50 + pos.y - this.infoCard.offsetWidth / 2
)}px`;
const id = this.infoCard.dataset.id;
console.log(this.viewer.isNodeVisible(id));
this.infoCard.style.display = this.viewer.isNodeVisible(id)
? "block"
: "none";
}
}
If I understand your question correctly, you'll probably want to do an intersection test between the camera's frustum and the models's bounding box. That can be done like so:
viewer.addEventListener(Autodesk.Viewing.CAMERA_CHANGE_EVENT, function () {
if (!viewer.model) {
return;
}
const camera = viewer.getCamera();
const matrix = new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
const frustum = new THREE.Frustum().setFromMatrix(matrix);
const bbox = viewer.model.getBoundingBox();
console.log('Model in the view?', frustum.intersectsBox(bbox));
});
And if you only want to check the visibility of a specific element (based on its dbID) of your model, you can compute its bounding box like so:
function objectBounds(model, dbId) {
const tree = model.getInstanceTree();
const frags = model.getFragmentList();
const objectBounds = new THREE.Box3();
tree.enumNodeFragments(dbId, function (fragId) {
const fragBounds = new THREE.Box3();
frags.getWorldBounds(fragId, fragBounds);
objectBounds.union(fragBounds);
}, true);
return objectBounds;
}
The function isNodeVisible returns the visibility status of your node in the scene. If you do something like this.viewer.hide(id, model) your function will return false.
If I well understood what you want to achieve, you want to hide an info card when the associated object is occluded by others objects, so we can't see it from our point of view ?
So I think what you need is to check for occlusion. You can take a look at the checkOcclusion function of this point cloud markup extension made by Philippe Leefsma.
To check for node occlusion, you basically need to raycast from your point of view to the node that you want to check. If you hit something and it's your node, there is no occlusion. If it's not the same node, it's mean that something occlude your node.
checkOcclusion (markup) {
const clientPoint = this.viewer.worldToClient(
markup.point)
const offset = $(this.viewer.container).offset()
const rayCaster = this.pointToRaycaster(
this.viewer.impl.canvas,
this.viewer.impl.camera, {
x: clientPoint.x + offset.left,
y: clientPoint.y + offset.top
})
const hitTest = this.viewer.model.rayIntersect(
rayCaster, true, this.dbIds)
if (hitTest) {
if (hitTest.fragId === markup.fragId) {
const offset = {
x: hitTest.point.x - markup.point.x,
y: hitTest.point.y - markup.point.y,
z: hitTest.point.z - markup.point.z
}
const dist = Math.sqrt(
offset.x * offset.x +
offset.y * offset.y +
offset.z * offset.z)
if (this.options.logOcclusionDist) {
console.log(dist)
}
if (dist < this.options.occlusionDist) {
return false
}
}
return true
}
}
Updating camera and target based on dbid of a selected node. The code starts with MobileVR function. I am updating camera and target according to frag mesh retrieved with dbid and then moving to VR mode. Currently I have an event listeners for GEOMETRY_LOADED_EVENT, OBJECT_TREE_CREATED_EVENT and EXTENSION_LOADED_EVENT. Currently it works with using a timeout setTimeout(() => { onSpaceObjectTreeCreated(); }, 3000); see image 1, but not without the the timeout image 2. Is there some other event that I should wait before running the code or updating the camera?
function onSpaceObjectTreeCreated() {
const nav = viewer.navigation;
const cam = nav.getCamera();
const it = viewer.model.getData().instanceTree;
let xPos, yPos, zPos;
it.enumNodeFragments(nodeId, (frag) => {
const mesh = viewer.impl.getRenderProxy(viewer.model, frag);
xPos = mesh.matrixWorld.elements[12];
yPos = mesh.matrixWorld.elements[13];
zPos = mesh.matrixWorld.elements[14];
console.log('x: ' + xPos + ' y: ' + yPos + ' z: ' + zPos);
}, false);
zPos = -41000;
cam.position.set(xPos, yPos, zPos);
cam.target.set(xPos, yPos + 10000, zPos);
}
function onViewerGeometryLoaded() {
const nav = viewer.navigation;
const cam = nav.getCamera();
if (nodeId == -1) {
viewer.setGroundShadow(false);
let xValue = viewer.getCamera().position.x;
let yValue = viewer.getCamera().position.y;
let zValue = viewer.getCamera().position.z;
let bbz = viewer.model.getData().bbox.min.z;
let zValue2 = zValue - bbz;
zValue = zValue * 0.3;
yValue = (zValue2 * 0.7071) * -1;
let nav = viewer.navigation;
let cam = viewer.getCamera();
cam.position.set(xValue, yValue, zValue);
} else {
setTimeout(() => {
onSpaceObjectTreeCreated();
}, 3000);
}
viewer.impl.sceneUpdated();
viewer.navigation.updateCamera();
document.getElementById("toolbar-vrTool").click();
};
function afterViewerEvents() {
var events = [
Autodesk.Viewing.GEOMETRY_LOADED_EVENT,
Autodesk.Viewing.OBJECT_TREE_CREATED_EVENT,
Autodesk.Viewing.EXTENSION_LOADED_EVENT
];
async.each(events,
function (event, callback) {
var handler = function (ev) {
viewer.removeEventListener(
event, handler);
console.log('Event: ' + event);
console.log('Ev: ' + ev.extensionId);
callback();
};
viewer.addEventListener(
event, handler);
},
function (err) {
onViewerGeometryLoaded();
});
}
function mobileVR(arkUrn: string, lviUrn: string, zOffset: number, spaceId: number) {
let element = document.getElementById("mobileViewer");
viewer = new Autodesk.Viewing.Private.GuiViewer3D(element);
let options = {
'env': 'AutodeskProduction',
'getAccessToken': getToken,
'refreshToken': getToken
};
av.Initializer(
options,
() => {
viewer.initialize();
loadDocument(arkUrn, zOffset);
if (lviUrn != "") {
loadDocument(lviUrn, zOffset);
}
viewer.loadExtension('Autodesk.Viewing.WebVR');
}
);
nodeId = spaceId;
afterViewerEvents();
}
Try to hookup the events after initializing the viewer and before loading the document:
viewer.initialize();
afterViewerEvents();
loadDocument(arkUrn, zOffset);
Also I don't get why you are using Autodesk.Viewing.EXTENSION_LOADED_EVENT, several extensions are being loaded automatically by the viewer upon startup or model loading, this event will be fired multiple times. If you are looking for a specific extension being loaded you need to check the extensionId and remove the handler only if this is the extension you are waiting for...
Hope that helps
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);
}
});
I have a panto marker function which requires the longitude and latitude variables. I have the ability to send the contact name as a variable.
The contact name is the tag for the marker i want to pan to. Is it possible for me to get the longitude and latitude of the marker by tag?
Here is my panto function
function pantoUser(lati,longi,i)
{
jQuery("#dispatcher").gmap3({
action: 'panTo',
args:[new google.maps.LatLng(lati,longi)],
zoom: 7
});
currentPoint = i;
jQuery("#dispatcher").css({
cursor: 'pointer'
});
jQuery('#markerTitle' + i + '').fadeIn({
duration: 200,
queue: false
}).animate({
bottom: "32px"
}, {
duration: 200,
queue: false
});
jQuery("#target").stop(true, true).fadeIn(1200).delay(500).fadeOut(1200);
jQuery("#dispatcher").css({
cursor: 'default'
});
jQuery('#markerTitle' + i + '').stop(true, true).fadeOut(2000, function() {
jQuery("#dispatcher").css({
bottom: "0"
})
jQuery("#target").stop(true, true).fadeIn(1200).delay(500).fadeOut(1200);
});
}
I was thinking of something like this?
function locateLastSpeaker(name) {
var lati = SOMEHOW GET IT
var longi = SOMEHOW GET IT
pantoUser(lati,longi,1)
}
EDIT after trying duncans solution!!
var stuMarkers = {};
function addMarker(i, lati, longi, id, name, state, datestring) {
var placename = name;
stuMarkers[placename].lat = lati;
stuMarkers[placename].lng = longi;
$('#dispatcher').gmap3(
{ action: 'addMarker', ....etc
Instead of an array as barry suggests, what about an object, keyed on the name. That way you won't even have to loop.
var stuMarkers = {}; // global variable outside of any function
// loop creating your markers
function addMarker(i, lati, longi, id, name, state, datestring) {
var placename = name;
stuMarkers[placename] = {};
stuMarkers[placename].lat = lati;
stuMarkers[placename].lng = longi;
stuMarkers[placename].i = i;
...
}
function locateLastSpeaker(name) {
pantoUser(stuMarkers[name].lat,stuMarkers[name].lng,stuMarkers[name].i);
}
Keep a reference to each marker - probably in an array.
When you want to find a particular marker, loop though that array, and find the particular marker.
Then do what you want with it.