import {azimuth} from "./sphere-calculator.js"; const toXYZ = (longitude, latitude) => { const lon = (longitude * Math.PI) / 180; const lat = (latitude * Math.PI) / 180; const x = RADIUS * Math.cos(lat) * Math.cos(lon); const y = RADIUS * Math.cos(lat) * Math.sin(lon); const z = RADIUS * Math.sin(lat); return [x, y, z]; }; const toHomogeneousXYZ = (longitude, latitude) => { const xyz = toXYZ(longitude, latitude); xyz.unshift(0); return xyz; } const toLonLat = (x, y, z) => { const R = Math.hypot(x, y, z); const longitude = (Math.atan2(y, x) * 180) / Math.PI; // [-180, 180] const latitude = (Math.asin(Math.max(-1, Math.min(1, z / R))) * 180) / Math.PI; // [-90, 90] return [longitude, latitude]; }; const cross = (a, b) => [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0], ]; const normalVector = ([lon1, lat1], [lon2, lat2]) => { const n = cross(toXYZ(lon1, lat1), toXYZ(lon2, lat2)); n.unshift(0); return n; } const toGeographicLabel = (lon, lat) => { return `${ lon.toFixed(1) }°|${ lat.toFixed(1)}°`; } const solveProjectionPoint = (startX, startY, startZ, targetX, targetY, targetZ) => { const t = targetX*targetX + targetY*targetY + targetZ*targetZ; const k = -(startX*targetX + startY*targetY + startZ*targetZ - t) / t; return [targetX + k * startX, targetY + k*startY, targetZ + k*startZ] } // board commands const RADIUS = 6; const BOARD_SIZE = 14; const board = JXG.JSXGraph.initBoard( 'box', { boundingbox: [-BOARD_SIZE, BOARD_SIZE, BOARD_SIZE, -BOARD_SIZE], //axis: true }); const bound = [-10, 10]; const startLongitude = board.create("slider", [[-2, 10], [9, 10], [-180, -30, 180]], {name: "Start LON.", precision: 2} ); const startLatitude = board.create("slider", [[-2, 9], [9, 9], [-90, 60, 90]], {name: "Start LAT.", precision: 2} ); const targetLongitude = board.create("slider", [[-2, -10], [9, -10], [-180, 120, 180]], {name: "Ziel LON.", precision: 2} ); const targetLatitude = board.create("slider", [[-2, -11], [9, -11], [-90, 0, 90]], {name: "Ziel LAT.", precision: 2} ); const displayAzimuth = () => { const a = azimuth(startLongitude.Value(), startLatitude.Value(), targetLongitude.Value(), targetLatitude.Value() ); return `Kurswinkel: ${a.toFixed(2)}°`; } const azimuthText = board.create('text', [8,6, displayAzimuth], {}); const view = board.create('view3d', [[-10, -10], [19, 19], [bound, bound, bound]], { axesPosition: 'none', xPlaneRear: {visible: false}, yPlaneRear: {visible: false}, zPlaneRear: {visible: false}, // az: { slider: { visible: true } }, el: { slider: { visible: true } }, bank: { slider: { visible: true } }, } ); const origin = view.create('point3d', [0,0,0], {name:"O", fixed: true, highlight: false, showInfobox: false,} ); const earth = view.create('sphere3d', [origin, RADIUS]); const NORTH_POLE = [0, 90]; const north_pole = toHomogeneousXYZ(...NORTH_POLE); const equator = view.create('circle3d', [origin, north_pole, RADIUS], { strokeColor:"red", fixed: true }); const northPoint = view.create('point3d', [toXYZ(...NORTH_POLE)], { name:'N', fixed: true, highlight: true, showInfobox: false, }); const prime_meridian_normal = toHomogeneousXYZ(90, 0) const prime_meridian = view.create('circle3d', [origin, prime_meridian_normal, RADIUS], { strokeColor: "blue", fixed: true }); const startPoint = view.create('point3d', () => [...toXYZ(startLongitude.Value(), startLatitude.Value())], {id:'S', fixed: true, highlight: true, showInfobox: false,} ); startPoint.element2D.label.setText( () => "Start: " + toGeographicLabel( ...toLonLat(startPoint.X(),startPoint.Y(), startPoint.Z() ) ) ); const tangentPlan = view.create('circle3d', [startPoint, () => [0, startPoint.X(), startPoint.Y(), startPoint.Z()], RADIUS], {fillColor:"red", fillOpacity:0.25, fixed: true} ); const targetPoint = view.create('point3d', () => [...toXYZ(targetLongitude.Value(), targetLatitude.Value())], {id: 'T', fixed: true, highlight: true, showInfobox: false,} ); targetPoint.element2D.label.setText( () => "Ziel: " + toGeographicLabel( ...toLonLat(targetPoint.X(), targetPoint.Y(), targetPoint.Z() ) ) ); const projectionPoint = view.create('point3d', () => solveProjectionPoint( startPoint.X(), startPoint.Y(), startPoint.Z(), targetPoint.X(), targetPoint.Y(), targetPoint.Z() ), {id: 'P', name:"Prj. Ziel", fixed: true, highlight: true, showInfobox: false, strokeColor:"black", fillColor: "gray"} ); const routeNormalVector = () => { return normalVector( [startLongitude.Value(), startLatitude.Value()], [targetLongitude.Value(), targetLatitude.Value()] ); }; const route = view.create('circle3d', [origin, routeNormalVector, RADIUS] ); const meridianNormalVector = () => normalVector([startLongitude.Value(), startLatitude.Value()], NORTH_POLE); const meridianPlan = view.create('circle3d', [origin, meridianNormalVector, RADIUS]); const ot = view.create('line3d', [origin, startPoint]) const projectionDirectionVector = () => [startPoint.X(), startPoint.Y(), startPoint.Z()]; const projectionLine = view.create('line3d', [targetPoint, projectionDirectionVector, [0, Infinity]], {strokeColor: "gray", dash: 2, strokeWidth: 1} );