pagrangeInterpolation for curve fitting - 使用拉格朗日插值平滑曲线

使用拉格朗日插值平滑曲线

1 平滑前后效果对比:

平滑前

平滑后

2 拉格朗日插值代码:(参考cesium源码)

结合拉格朗日原理:https://zhuanlan.zhihu.com/p/135229305

源码如下:https://github.com/CesiumGS/cesium/blob/b30/Source/Core/LagrangePolynomialApproximation.js#L39

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
* Interpolates values using Lagrange Polynomial Approximation.
*
* @param {Number} x The independent variable for which the dependent variables will be interpolated.
* @param {Number[]} xTable The array of independent variables to use to interpolate. The values
* in this array must be in increasing order and the same value must not occur twice in the array.
* @param {Number[]} yTable The array of dependent variables to use to interpolate. For a set of three
* dependent values (p,q,w) at time 1 and time 2 this should be as follows: {p1, q1, w1, p2, q2, w2}.
* @param {Number} yStride The number of dependent variable values in yTable corresponding to
* each independent variable value in xTable.
* @param {Number[]} [result] An existing array into which to store the result.
* @returns {Number[]} The array of interpolated values, or the result parameter if one was provided.
*/
LagrangePolynomialApproximation.interpolateOrderZero = function(x, xTable, yTable, yStride, result) {
if (!defined(result)) {
result = new Array(yStride);
}

var i;
var j;
var length = xTable.length;

for (i = 0; i < yStride; i++) {
result[i] = 0;
}

for (i = 0; i < length; i++) {
var coefficient = 1;

for (j = 0; j < length; j++) {
if (j !== i) {
var diffX = xTable[i] - xTable[j];
coefficient *= (x - xTable[j]) / diffX;
}
}

for (j = 0; j < yStride; j++) {
result[j] += coefficient * yTable[i * yStride + j];
}
}

return result;
};

3 cesium中实现代码

放入https://sandcastle.cesium.com/index.html?src=Callback%20Property.html运行即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// This example illustrates a Callback Property, a property whose
// value is lazily evaluated by a callback function.
// Use a CallbackProperty when your data can't be pre-computed
// or needs to be derived from other properties at runtime.
var viewer = new Cesium.Viewer("cesiumContainer");
viewer.clock.shouldAnimate = true;

var startLatitude = 35;
var startLongitude = -120;
var endLongitude;
var startTime = Cesium.JulianDate.now();
var stTime = startTime;


// Add a polyline to the scene. Positions are dynamic.
var isConstant = false;
var redLine = viewer.entities.add({
polyline: {
// This callback updates positions each frame.
positions: new Cesium.CallbackProperty(function (time, result) {
endLongitude =
startLongitude +
0.001 * Cesium.JulianDate.secondsDifference(time, startTime);

// 平滑前效果
// return computeCirclularFlight(-112.110693, 36.0994841, 0.03, time);
// 平滑后效果
return computeCirclularFlightSmoothly(-112.110693, 36.0994841, 0.03, 500, stTime, time);
/*
return Cesium.Cartesian3.fromDegreesArray(
[startLongitude, startLatitude, endLongitude, startLatitude],
Cesium.Ellipsoid.WGS84,
result
);
*/
}, isConstant),
width: 5,
material: Cesium.Color.RED,
},
});


//Generate a random circular pattern with varying heights.
function computeCirclularFlight(lon, lat, radius, curTime) {

// let a = JulianDate.secondsDifference(JulianDate.now(), stTime);
let positions = [];
// Generate a random circular pattern with letying heights.
let property = new Cesium.SampledPositionProperty();
for (let i = 0; i <= 360; i += 45) {
let radians = Cesium.Math.toRadians(i);
let time = Cesium.JulianDate.addSeconds(
stTime,
i,
new Cesium.JulianDate()
);

let position = Cesium.Cartesian3.fromDegrees(
lon + radius * 1.5 * Math.cos(radians),
lat + radius * Math.sin(radians),
Cesium.Math.nextRandomNumber() * 500 +
Math.random() * 20 * Cesium.JulianDate.secondsDifference(curTime, stTime)
);

positions.push(position);
property.addSample(time, position);
}

return positions;
}

// Generate a random circular pattern with varying heights.
function computeCirclularFlightSmoothly(lon, lat, radius, levelHeight, stTime, curTime) {

// let a = JulianDate.secondsDifference(JulianDate.now(), stTime);
let positions = [];
let llhPositions = [];
// let property = new PositionProperty();
// Generate a random circular pattern with letying heights.
for (let i = 0; i <= 360; i += 45) {
let radians = Cesium.Math.toRadians(i);
let time = Cesium.JulianDate.addSeconds(
stTime,
i,
new Cesium.JulianDate()
);

let tmpPoint = new Cesium.Cartographic(
lon + radius * 1.5 * Math.cos(radians),
lat + radius * Math.sin(radians),
Cesium.Math.nextRandomNumber() * 0.1 * levelHeight + levelHeight +
Cesium.Math.random() * 20 * Cesium.JulianDate.secondsDifference(curTime, stTime)
);
llhPositions.push(tmpPoint);
positions.push(
Cesium.Cartesian3.fromDegrees(
tmpPoint.longitude,
tmpPoint.latitude,
tmpPoint.height
));
}

// let iptPositions = Array<Cartographic>();
let iptPositions = [];
let xRes = [];
let xTable = []; // interpolate at xTable[i]
for(let ix = 0; ix < positions.length; ++ix) {
xTable.push(ix * 100);
}
let yTable = []; // used to interpolate, in {la1, lon1, h1, l2, lon2, h2}
for(let iy = 0; iy < positions.length; ++iy) {
yTable.push(llhPositions[iy].longitude);
yTable.push(llhPositions[iy].latitude);
yTable.push(llhPositions[iy].height);
}
let yStride = 3; // 3 dependent vaule in yTable is viewed as one item

for (let ix = 0; ix < xTable[positions.length - 1]; ++ix) {
let iptRes = [];
Cesium.LagrangePolynomialApproximation.interpolateOrderZero(
ix,
xTable,
yTable,
yStride,
iptRes
);

iptPositions.push(
Cesium.Cartesian3.fromDegrees(
iptRes[0],
iptRes[1],
iptRes[2]
));
}

return iptPositions;
}

// Keep the view centered.
viewer.trackedEntity = redLine;