I want to display a hurricane (big isosurface object) in Cesium. For this I converted an OBJ file with longitude, latitude, altitude columns for each vertex of the isosurface representing the hurricane, in a new OBJ file reprojected in ECEF (Earth Centered) projection.So the final OBJ file contains now X,Y,Z for each vertex instead of longitude, latitude, altitude. After final reformat by obj2gltf, I try to display the GLTF "hurricane" file in Cesium.JS using the code below:
console.log('loading hurricane.gltf';
var mymodel = viewer.scene.primitives.add(Cesium.Model.fromGltf({
url : 'data/hurricane.gltf',
modelMatrix : Cesium.Matrix4.IDENTITY,
asynchronous: false
}));
I can see my hurricane on the earth, but not at the good position. I suspect a problem of matrix. IDENTITY matrix seems not to be the good one. I could try to make a new matrix but I can't find enough informations about the axes orientation used by Cesium.
I verified the X,Y,Z ECEF coordinates, they are good. Does anyone already meet this problem ?
If your glTF model origin is at the center of the hurricane, you can place it using a Cesium Entity, something like this:
// Longitude degrees, Latitude degrees, height in meters
var position = Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706, height);
var heading = Cesium.Math.toRadians(0);
var pitch = 0;
var roll = 0;
var hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
var orientation = Cesium.Transforms.headingPitchRollQuaternion(position, hpr);
var entity = viewer.entities.add({
name : 'Hurricane',
position : position,
orientation : orientation,
model : {
uri : 'data/hurricane.gltf'
}
});
viewer.trackedEntity = entity;
There are more complete working demos of this on Sandcastle.
But, if your hurricane is visible on the surface of the Earth using the identity matrix, that likely means that the origin of that model is nowhere near the center of the hurricane. You may need to edit the glTF file, to make sure that the model is centered on its own origin, and does not have some fixed Earth location pre-baked into the model's internal transformations.
Related
I am looking to turn 2 lat/lon positions into an x and y distance of the canvas, then apply the distance formula to it.
Right now I have:
const leftPoint = new LatLon(center.lat, center.lon).destinationPoint(semiMajorAxis, 270);
const rightPoint = new LatLon(center.lat, center.lon).destinationPoint(semiMajorAxis, 90);
const leftXY = Cartographic.toCartesian(Cartographic.fromDegrees(leftPoint.lon, leftPoint.lat));
const rightXY = Cartographic.toCartesian(Cartographic.fromDegrees(rightPoint.lon, rightPoint.lat));
const diameter = distanceFormula(leftXY.x, leftXY.y, rightXY.x, rightXY.y);
But the result of diameter is 18,000, even though both points are on my screen!
Cesium's Cartographic.toCartesian function converts a Cartographic (lon/lat/alt) type of coordinate to a full 3D Cartesian position. Imagine X, Y, Z with zero being the center of the Earth itself, with the Earth's surface being approximately 6.3 million meters in any direction.
If you're looking for 2D canvas / screen coordinates, you must follow this call with another function, Cesium.SceneTransforms.wgs84ToWindowCoordinates. That function converts the 3D WGS84 (Cartesian3) Earth position into a 2D (Cartesian2) screen position. There's a demo of wgs84ToWindowCoordinates being used in the Sandcastle Star Burst Example around line 287.
Also it looks like you've rolled your own LatLon class, not specified above, that appears to have similar functions to Cesium's Cartographic class. You might be able to make the code a little cleaner by using Cartographic directly instead of a homebrew class there. Likewise you don't need to roll your own distanceFormula on the last line. Once you have 2D Cartesian2 window coordinates, call Cesium.Cartesian2.distance to get the distance.
I can't understand your saying 'x and y distance of the canvas'.
Generally, for calculate distance between two point on CesiumJS follow below steps.
1.Define two points
//Define x,y coordinate and convert to radian
const longitudeRadian_1 = Cesium.Math.toRadians(longitudeDegree_1)
const latitudeRadian_1 = Cesium.Math.toRadians(latitudeDegree_1)
const longitudeRadian_2 = Cesium.Math.toRadians(longitudeDegree_2)
const latitudeRadian_2 = Cesium.Math.toRadians(latitudeDegree_2)
//Get cartographic from degrees
const Carto_Point_1 = new Cesium.Cartographic(longitudeRadian_1 , latitudeRadian_1 )
const Carto_Point_2 = new Cesium.Cartographic(longitudeRadian_2 , latitudeRadian_2)
//Get cartesian from cartographic
const Cartesian_Point_1 = Cesium.Cartographic.toCartesian(Carto_Point_1)
const Cartesian_Point_2 = Cesium.Cartographic.toCartesian(Carto_Point_2)
2.Calculate distance between two points
const distance = Cesium.Cartesian3.distance(Cartesian_Point_1, Cartesian_Point_2)
console.log(distance)
I hope this would help
I am testing the possibilities of the forge V7 viewer on a web browser with a revit model.
The idea of the test is:
display 2 viewers simultaneously with 1 3D view and a 2D view
click on a point of the 2D view and
display a sphere in the 2D view on this point
display another sphere in the 3D view at the equivalent point
the spheres are added with SceneBuilder (to allow them to be selected later)
I tried to follow the behavior of
https://forge.autodesk.com/ja/node/1765 and
https://github.com/Autodesk-Forge/viewer-navigation.sample
and i have questions
1 - the 2D view must be a sheet, using a 2D view directly (a floor view for example) does not allow the calculation, is that correct?
2 - https://forge.autodesk.com/ja/node/1765 seems to have calculation and/or precision issues (cf forge1.png generated from the test in Heroku and forge2.png generated by my program)
3 - the method only works if you click on an object
how can I retrieve the coordinates if I click in an empty area?
4 - the spheres are identified in the viewer (cf forge3.png), but
a - how to give them a name (and not "object")
b - how to replace the name "model" by the name of the scene?
Can you help me ?
Thanks in advance
Luc
Here is my code
// click listener on 2d viewer
function listenerScene(ev){
var intersection = viewer2d.hitTest(ev.offsetX, ev.offsetY);
if (intersection) {
AddItem(intersection.intersectPoint.x, intersection.intersectPoint.y,
intersection.intersectPoint.z);
AddItem3d(intersection);
}
// add in 2d
async function AddItem(x,y,z){
... add a sphere at (x,y,z)
}
// add in 3d
async function AddItem3d(intersection) {
const worldPos = sheetToWorld(intersection.intersectPoint,
viewer2d.model,viewer.model);
if (worldPos) {
... add a sphere at worldPos
}
}
// compute 3d point
function sheetToWorld(sheetPos, model2d, model3d) {
const viewportExt = viewer2d.getExtension('Autodesk.AEC.ViewportsExtension');
const viewport = viewportExt.findViewportAtPoint(model2d, new THREE.Vector2(sheetPos.x, sheetPos.y));
if (!viewport) {
return null;
}
const sheetUnitScale = model2d.getUnitScale();
const globalOffset = model3d.getData().globalOffset;
const matrix = viewport.get2DTo3DMatrix(sheetUnitScale);
var worldPos = sheetPos.clone().applyMatrix4(matrix);
worldPos = worldPos.sub(globalOffset);
return worldPos;
}
How to draw a gizmo by giving it a position, orientation and eventually a scale in a CesiumJS application?
By gizmo I mean a 3-axes right-handed reference frame using (x,y,z) vectors, ideally depicted as (RGB) values, such as these, for example:
I wish I could depict the orientation of any object (e.g. a glTF) by placing such reference frame, for example, at the position of the object origin (e.g. using its longitude, latitude and elevation) and following its orientation, as defined by its heading, pitch and roll values which must follow the three given angles in their original order (heading first, pitch second and roll third) starting from the LTP-ENU (0,0,0) convention (x=0=east, y=0=north, z=0=upward).
The inspector is not an option.
You can use DebugModelMatrixPrimitive.
Here 's Sandcastle
Sample code
const viewer = new Cesium.Viewer("cesiumContainer");
const position = Cesium.Cartesian3.fromDegrees(-107.0, 40.0, 300000.0);
const redSphere = viewer.entities.add({
name: "Red sphere with black outline",
position: position,
ellipsoid: {
radii: new Cesium.Cartesian3(300000.0, 300000.0, 300000.0),
material: Cesium.Color.RED.withAlpha(0.5),
outline: true,
outlineColor: Cesium.Color.BLACK,
},
});
const heading = Cesium.Math.toRadians(10);
const pitch = Cesium.Math.toRadians(50);
const roll = Cesium.Math.toRadians(0);
const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
const frame = Cesium.Transforms.headingPitchRollToFixedFrame(position, hpr);
viewer.scene.primitives.add(new Cesium.DebugModelMatrixPrimitive({
modelMatrix: frame,
length: 800000,
width: 3.0
}));
viewer.zoomTo(viewer.entities);
I am trying to create a d3 SVG that draws a map of New York State and scale it so that it fits the size of my SVG, the issue I am having is that when I use .fitSize([height, width], mapObject) it only returns a NaN error in the console.
the topoJSON file of NYS I am using
I am able to get the map to display without scaling but of course, it is not optimized and needs to be scaled
I have attempted what is said in this post but I have not figured out the correct solution
var map = d3.json('./ny.json')
Promise.all([map]).then(data => {
var height = 800;
var width = 800;
var mapData = data[0]
// original geoJSON to that works without scaling
// var geoData = topojson.feature(mapData, mapData.objects["cb_2015_new_york_county_20m"]).features
//
var geoData = topojson.feature(mapData, {
type:"GeometryCollection",
geometries: mapData.objects["cb_2015_new_york_county_20m"].geometries,
})
var projection = d3.geoMercator()
.fitSize([width, height], geoData)
var path = d3.geoPath()
.projection(projection)
d3.select('svg')
.attr('height', height)
.attr('width', width)
.selectAll('.county')
.data(geoData)
.enter()
.append('path')
.classed('.county', true)
.attr('d', path)
})
I am pretty sure this is a formatting error on my part, but I am unsure of what data .fitSize() or .fitExtent() is trying to compare against.
right now the way the code site I receive no error outputted to the console but I also have no data append to the SVG
The issue is that fitSize takes a geojson object while selectAll.data() takes an array, you are using one of these two for both in geoData. This leaves two solutions:
Solution 1:
If we use
var geoData = topojson.feature(mapData, mapData.objects["cb_2015_new_york_county_20m"]).features
var projection = d3.geoMercator()
.fitSize([width, height], geoData)
We get NaN errors because the projection is not set properly as we aren't passing a geojson object, just an array of geojson objects. We could solve this by making a feature collection with geoData and passing that to fitSize:
var geoData = topojson.feature(mapData, mapData.objects["cb_2015_new_york_county_20m"]).features
var projection = d3.geoMercator()
.fitSize([width, height], {type:"FeatureCollection", features: geoData})
Now we are passing a geojson feature collection to fitSize, we're all go on the projection, and since geoData is still an array, we can pass that to selectAll.data() unchanged.
Here's a block.
Solution 2:
If we use:
var geoData = topojson.feature(mapData, {
type:"GeometryCollection",
geometries: mapData.objects["cb_2015_new_york_county_20m"].geometries,
})
We get a geojson object, projection.fitSize works, but selectAll().data(geoData) doesn't add any features as it isn't an array - the enter selection is empty. We can substitute in selectAll().data(geoData.features) to solve this and enter one path per feature (alternatively we could use .data([geoData]) to enter one feature for all the paths).
Here's a block.
Both blocks are drawn at the correct scale - the map exceeds the block bounds as I didn't alter your 800x800 dimensions
I am trying to implement a WebGL-based rendering on Google Map (api3) as I want to render a massive amount of dynamic geometries.
Basically, I create a google.maps.OverlayView attached with a WebGL canvas into the map.
However, I encountered some problem with the mapping of the projection. Basically, I extracted the "fromLatLngToPoint" function from the googlemap api as follows:
function fromLatLngToPoint(a){
var c={x:0,y:0},
d=this.j;
c.x=d.x+a.lng*this.B;
var e=oe(m.sin(re(a.lat)),-(1-1E-15),1-1E-15);
c.y=d.y+.5*m.log((1+e)/(1-e))*-this.F;
return c
}
function oe(a,b,c){null!=b&&(a=m.max(a,b));null!=c&&(a=m.min(a,c));return a}
function re(a){return m.PI/180*a}
Then I implemented it in my vertex shader based on the documentation in Google Map Coordinates.
Basically, I have a event listener to send the updated projection constants, the viewport bounds, and the zoom level to my shader.
Then my shader will calculate the new screen coordinates based on these inputs.
highp float e, x, y, offsetY, offsetX;
// projection transformation for target points
e = sin(p.y* PI/180.0);
y = prj_y + 0.5 * log((1.0+e)/(1.0-e))*(-F);
x = prj_x + p.x*B;
// projection transformation for offset (bounds)
e = sin(bound_y*PI/180.0);
offsetY = prj_y + 0.5 * log((1.0+e)/(1.0-e))*(-F);
offsetX = prj_x + bound_x*B;
// calculate actual pixel coord wrt zoom/numTiles
x = (x* numTiles - offsetX* numTiles);
y = (y* numTiles - offsetY* numTiles);
gl_PointSize = 5.0;
gl_Position = projectionMatrix * modelViewMatrix * vec4(x,y,0.0,1.0);
However, as shown in the screenshot below, it seems there are some errors? The rendered geometries are distorted. (I used the google map polygon api to render some of the geometries as comparison)
Screenshot Here
I am totally at a loss, what might be the reason for this distortion?
I am suspecting that the single precision in the shader is giving rise to the error. So I am wondering if there is any workaround?
It is hard to debug this piece of code and diagnose the cause of the issue. I would suggest you using the CanvasLayer library that hides all these concrete details of specifying the coordinates you want to draw the polygon. Rather you would be able to focus on your app code and functionality. The performance will be better in terms of projected image.