TransWikia.com

How to properly point the Sun in CesiumJS?

Geographic Information Systems Asked on May 7, 2021

I prepared a Cesium sandcastle example based on this answer from @FSimardGIS, but something is going wrong: only one of many rays correctly points to the Sun, the other appears random.

Screenshot
What’s wrong here?
I took the test coordinates from SunCalc library, but you can also insert data manually, using for example this site for calculations:

https://planetcalc.com/320/?day=2020-11-19%2004:31:37&plat=36.13&plon=138.36&gmtdiff=0&UTCoffset=0

This is the source code from the other answer:

function CalcPosFromAltAzDist(startPoint, vectorPointing) {
  var ellipsoid = Cesium.Ellipsoid.WGS84;
  var ENU = new Cesium.Matrix4();
  Cesium.Transforms.eastNorthUpToFixedFrame(startPoint,ellipsoid,ENU);
  var myX = vectorPointing.dist * Math.sin(vectorPointing.az * Math.PI / 180);
  var myY = vectorPointing.dist * Math.cos(vectorPointing.az * Math.PI / 180);
  var myZ = vectorPointing.dist * Math.sin(vectorPointing.alt * Math.PI / 180);
  var offset = new Cesium.Cartesian3(myX,myY,myZ);
  var finalPoint = Cesium.Matrix4.multiplyByPoint(ENU, offset, new Cesium.Cartesian3());
  return finalPoint;    
}

Call with:

vectorPointing= {"alt" : 30, "az" : 0};
startPoint= Cesium.Cartesian3.fromDegrees(290.6, -35.78);

2 Answers

The reason of malfunctioning of the algorith mentioned in the question were two wrong formulas; right function is:

function CalcPosFromAltAzDist(startPoint, vectorPointing) {

  var ellipsoid = Cesium.Ellipsoid.WGS84;
  var ENU = new Cesium.Matrix4();
  Cesium.Transforms.eastNorthUpToFixedFrame(startPoint,ellipsoid,ENU);
  var myX = vectorPointing.dist * Math.cos(vectorPointing.alt * Math.PI / 180) * Math.sin(vectorPointing.az * Math.PI / 180);
  var myY = vectorPointing.dist * Math.cos(vectorPointing.alt * Math.PI / 180) * Math.cos(vectorPointing.az * Math.PI / 180);
  var myZ = vectorPointing.dist * Math.sin(vectorPointing.alt * Math.PI / 180);
  var offset = new Cesium.Cartesian3(myX,myY,myZ);
  var finalPoint = Cesium.Matrix4.multiplyByPoint(ENU, offset, new Cesium.Cartesian3());
  return finalPoint;
  
}

"Math.cos(vectorPointing.alt * Math.PI / 180)" factor was missing in myX and myY calculations.

Now all works fine:

Screenshot of Cesium

Look here for a working sandcastle example

Correct answer by jumpjack on May 7, 2021

I have built a solution based on different algorithm and Cesium functions.

I didn't understand anything about the transformations and methods used... :-) , but it works fine.

Sandcastle example.

Source code:

var viewer = new Cesium.Viewer("cesiumContainer");
viewer.scene.globe.enableLighting = true;

var suntest = [];

suntest[0] = {'time' : '2020-11-20T12:00:00.000Z', 'lat' : 0, 'lon' :-180, 'alt' : -69.936132980914, 'az' :170.491579546217};
suntest[1] = {'time' : '2020-11-20T12:00:00.000Z', 'lat' : 0, 'lon' :-150, 'alt' : -51.732788127111, 'az' :123.115116564788};
suntest[2] = {'time' : '2020-11-20T12:00:00.000Z', 'lat' : 0, 'lon' :-120, 'alt' : -24.870948550859, 'az' :111.897154212120};
suntest[3] = {'time' : '2020-11-20T12:00:00.000Z', 'lat' : 0, 'lon' :-90, 'alt' : 3.248817322124, 'az' :109.809795550210};
suntest[4] = {'time' : '2020-11-20T12:00:00.000Z', 'lat' : 0, 'lon' :-60, 'alt' : 31.247432115441, 'az' :113.313745536185};
suntest[5] = {'time' : '2020-11-20T12:00:00.000Z', 'lat' : 0, 'lon' :-30, 'alt' : 57.331014803665, 'az' :128.816746856710};
suntest[6] = {'time' : '2020-11-20T12:00:00.000Z', 'lat' : 0, 'lon' :0, 'alt' : 69.936132980893, 'az' :189.508420454115};
suntest[7] = {'time' : '2020-11-20T12:00:00.000Z', 'lat' : 0, 'lon' :30, 'alt' : 51.732788127357, 'az' :236.884883435008};
suntest[8] = {'time' : '2020-11-20T12:00:00.000Z', 'lat' : 0, 'lon' :60, 'alt' : 24.870948550745, 'az' :248.102845787901};
suntest[9] = {'time' : '2020-11-20T12:00:00.000Z', 'lat' : 0, 'lon' :90, 'alt' : -3.248817321848, 'az' :250.190204449796};
suntest[10] = {'time' : '2020-11-20T12:00:00.000Z', 'lat' : 0, 'lon' :120, 'alt' : -31.247432115554, 'az' :246.686254463785};
suntest[11] = {'time' : '2020-11-20T12:00:00.000Z', 'lat' : 0, 'lon' :150, 'alt' : -57.331014803436, 'az' :231.183253143577};
suntest[12] = {'time' : '2020-11-20T12:00:00.000Z', 'lat' : 0, 'lon' :180, 'alt' : -69.936132980873, 'az' :170.491579545552};
suntest[13] = {'time' : '2020-11-20T12:00:00.000Z', 'lat' : 10, 'lon' :-180, 'alt' : -79.671227366220, 'az' :161.573903931188};
suntest[14] = {'time' : '2020-11-20T12:00:00.000Z', 'lat' : 10, 'lon' :-150, 'alt' : -56.300357978857, 'az' :110.783422611356};
suntest[15] = {'time' : '2020-11-20T12:00:00.000Z', 'lat' : 10, 'lon' :-120, 'alt' : -28.225368684317, 'az' :107.175147833478};
suntest[16] = {'time' : '2020-11-20T12:00:00.000Z', 'lat' : 10, 'lon' :-90, 'alt' : -0.168643089275, 'az' :110.063187466725};
suntest[17] = {'time' : '2020-11-20T12:00:00.000Z', 'lat' : 10, 'lon' :-60, 'alt' : 26.878473297456, 'az' :118.330636399825};
suntest[18] = {'time' : '2020-11-20T12:00:00.000Z', 'lat' : 10, 'lon' :-30, 'alt' : 50.377210160732, 'az' :138.739128733690};
suntest[19] = {'time' : '2020-11-20T12:00:00.000Z', 'lat' : 10, 'lon' :0, 'alt' : 60.029872737499, 'az' :186.514048228677};
suntest[20] = {'time' : '2020-11-20T12:00:00.000Z', 'lat' : 10, 'lon' :30, 'alt' : 45.598015050067, 'az' :227.849125941148};


function drawLine(x1,y1,z1, x2, y2, z2) {
  var origin = new Cesium.Cartesian3(x1,y1,z1);
  var dest = new Cesium.Cartesian3(x2,y2,z2);
 
  viewer.entities.add({
    polyline: {
        positions: [
          origin, 
          dest
          ],
      arcType: Cesium.ArcType.NONE   , 
      width: 20,
      material: new Cesium.PolylineArrowMaterialProperty(Cesium.Color.WHITE)
    }
  });  
}



function createROIfromRotation(position,  rotation, lengthKM) {
  // position: Cartographic - {latitude, longitude, altitude})
  // rotation: HeadingPitchRoll - {heading, pitch, roll}
  
  // Based on answer found here:
  // https://stackoverflow.com/questions/58021985/create-a-point-in-a-direction-in-cesiumjs
  
    var cartesianPosition = Cesium.Ellipsoid.WGS84.cartographicToCartesian(position);

    rotation.heading = rotation.heading - Cesium.Math.toRadians(90);
    var referenceFrame1 = Cesium.Transforms.headingPitchRollQuaternion(cartesianPosition, rotation);
    var rotationMatrix = Cesium.Matrix3.fromQuaternion(referenceFrame1, new Cesium.Matrix3());
    var rotationScaled = Cesium.Matrix3.multiplyByVector(rotationMatrix, new Cesium.Cartesian3(lengthKM * 1000.0, 0, 0), new Cesium.Cartesian3());
    var roiPos = Cesium.Cartesian3.add(cartesianPosition, rotationScaled, new Cesium.Cartesian3());
    return roiPos;
}


function arrowFromTo(latitude, longitude, height, elev, azimut, lengthKM){
  latitude = Cesium.Math.toRadians(latitude);
  longitude = Cesium.Math.toRadians(longitude);
  var origin = new Cesium.Cartographic(longitude, latitude, height);
  var originC3 = new Cesium.Cartographic.toCartesian(origin);

  // Altitude (aka Elevation) and Azimuth can also be seen as Pitch and Heading, with Roll = 0:
  var heading = Cesium.Math.toRadians(azimut);
  var pitch = Cesium.Math.toRadians(elev);
  var roll = 0.0;
  var direction = new Cesium.HeadingPitchRoll(heading, pitch, roll);
 
  
  ////////////
  var result = createROIfromRotation(origin, direction, lengthKM);
  ////////////
  
  drawLine(originC3.x ,originC3.y, originC3.z, result.x ,result.y, result.z);
}



// Bind button to test function:
document.getElementById("btnCalc").addEventListener("click",function(a,b,c) {
  var obsLat = document.getElementById("obsLat").value * 1.0;
  var obsLon = document.getElementById("obsLon").value * 1.0;
  var obsAlt = document.getElementById("obsAlt").value * 1.0;
  var sunAlt = document.getElementById("sunAlt").value * 1.0;
  var sunAz = document.getElementById("sunAz").value * 1.0;
  var userDate = document.getElementById("userDate").value;
  
  viewer.clock.currentTime = Cesium.JulianDate.fromDate(new Date(userDate));
  arrowFromTo(obsLat,obsLon,obsAlt,sunAlt,sunAz,150000000);
            //latitude, longitude, height, elev, azimut, lengthKM
});


///////////////////////
// Draw test lines:
  viewer.clock.currentTime = Cesium.JulianDate.fromDate(new Date(suntest[0].time));
  
  for (var i = 0; i<suntest.length; i++) {
    arrowFromTo(suntest[i].lat, suntest[i].lon, 0, suntest[i].alt, suntest[i].az,150000000 );
  }

  

Answered by jumpjack on May 7, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP