3

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);
1
  • 1
    Oops, the cos(alt) factor in the myX and myY calculation is missing. I updated my answer to correct the issue.
    – FSimardGIS
    Commented Nov 27, 2020 at 2:36

2 Answers 2

1

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 );
  }

  
0

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.