原文链接及内容

效果如下所示:

示例代码如下:

关于对峙(zhí)点的概念请阅读:https://zh.wikipedia.org/zh-cn/%E5%B0%8D%E8%B9%A0%E9%BB%9E

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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
const viewer = new Cesium.Viewer("cesiumContainer");
const toDegrees = Cesium.Math.toDegrees;

/**
* 绘制纬线
* @param {number} latitude 纬度
* @param {Cesium.Color} color 颜色/材质
* @param {Cesium.Property | undefined} granularity 每个纬度和经度之间的角度距离
* @return {Cesium.Entity} 纬线实体
*/
function parallel(latitude, color, granularity) {
return viewer.entities.add({
name: `纬线 ${latitude}`,
polyline: {
positions: Cesium.Cartesian3.fromDegreesArray([
-180,
latitude,
-90,
latitude,
0,
latitude,
90,
latitude,
180,
latitude,
]),
width: 2,
/**
* arcType属性默认值为:ArcType.GEODESIC
* - NONE: 一条不贴合椭球表面的直线。
* - GEODESIC: 测地线(大圆)路径
* - RHUMB: 沿着经线或罗盘(北向)路径。
*/
arcType: Cesium.ArcType.RHUMB,
material: color,
/**
* 获取或设置一个数值属性,指定当 arcType 不是 ArcType.NONE 且 clampToGround 为 false 时,
* 每个纬度和经度之间的角度距离。
*/
granularity,
},
});
}

/**
* 绘制经线
* @param {number} longitude 经度
* @param {Cesium.Color} color 颜色/材质
* @param {Cesium.Property | undefined} granularity 每个纬度和经度之间的角度距离
* @return {Cesium.Entity} 经线实体
*/
function meridian(longitude, color, granularity) {
return viewer.entities.add({
name: `经线 ${longitude}`,
polyline: {
positions: Cesium.Cartesian3.fromDegreesArray([
longitude,
90,
longitude,
0,
longitude,
-90,
]),
width: 2,
arcType: Cesium.ArcType.RHUMB,
material: color,
granularity,
},
});
}

/**
* 为给定的地理坐标(单位为弧度)添加一个标签
* @param {Cesium.Cartographic} cartographic 弧度经纬度
* @return {Cesium.Entity} 标签实体
*/
function labelCoordinates(cartographic) {
const position = Cesium.Cartographic.toCartesian(cartographic);
const latitude = toDegrees(cartographic.latitude).toFixed(4);
const longitude = toDegrees(cartographic.longitude).toFixed(4);
const label = `lat: ${latitude}°\nlon: ${longitude}°`;

return viewer.entities.add({
position: position,
label: {
text: label,
showBackground: true,
font: "14px monospace",
},
});
}

/**
* 以给定的经纬网格划分数量和颜色值来绘制地球的经纬线
* @param {number} numberOfDivisions 经纬线网格的划分数量
* @param {esium.Color} color 颜色/材质
* @param {boolean} show 纬线的可见性
* @return {Array} 所有经纬线的实体组成的数组
*/
function makeGrid(numberOfDivisions, color, show) {
const parallels = makeParallelsRecursive(-90, 90, numberOfDivisions, color);
const meridians = makeMeridiansRecursive(-180, 180, numberOfDivisions, color);
meridians.push(meridian(180, color));

/**
* concat方法用于多个数组的合并。
* 它将新数组的成员,添加到原数组成员的后部,然后返回一个新数组,原数组不变。
*/
const allLines = parallels.concat(meridians);
allLines.forEach(function (line) {
line.show = show;
});

return allLines;
}

/**
* 绘制纬线的递归方法
* @param {number} minLatitude 纬度范围的最小值
* @param {number} maxLatitude 纬度范围的最大值
* @param {number} depth 递归深度,控制划分的次数
* @param {Cesium.Color} color 纬线的颜色/材质
* @return {Array} 按一定范围绘制的所有纬线
*/
function makeParallelsRecursive(minLatitude, maxLatitude, depth, color) {
let result = [];
const midpoint = (minLatitude + maxLatitude) / 2;
result.push(parallel(midpoint, color));

if (depth > 0) {
//对南半球部分(minLatitude 到 midpoint)调用自身,生成更细分的纬线。
const southernLines = makeParallelsRecursive(
minLatitude,
midpoint,
depth - 1,
color
);
//对北半部分(midpoint 到 maxLatitude)调用自身,生成更细分的纬线。
const northernLines = makeParallelsRecursive(
midpoint,
maxLatitude,
depth - 1,
color
);
result = southernLines.concat(result, northernLines);
}

return result;
}

/**
* 绘制经线的递归方法
* @param {number} minLongitude 经度范围的最小值
* @param {number} maxLongitude 经度范围的最大值
* @param {number} depth 递归深度,控制经线的划分次数
* @param {Cesium.Color} color 经线颜色/材质
* @return {Array} 按一定范围绘制的所有经线
*/
function makeMeridiansRecursive(minLongitude, maxLongitude, depth, color) {
let result = [];
const midpoint = (minLongitude + maxLongitude) / 2;
result.push(meridian(midpoint, color));

if (depth > 0) {
//对西半球部分(minLongitude 到 midpoint)调用自身,生成更细分的经线。
const westernLines = makeMeridiansRecursive(
minLongitude,
midpoint,
depth - 1,
color
);
//对东半球部分(midpoint 到 maxLongitude)调用自身,生成更细分的经线。
const easternLines = makeMeridiansRecursive(
midpoint,
maxLongitude,
depth - 1,
color
);
result = westernLines.concat(result, easternLines);
}

return result;
}

/**
* Antipodal point:对跖(zhí)点,具体参考:https://zh.wikipedia.org/zh-cn/%E5%B0%8D%E8%B9%A0%E9%BB%9E
*/
let showAntipodalPoint = false;
const primitives = {
equator: parallel(0, Cesium.Color.BLUE),//赤道,零度纬线圈
primeMeridian: meridian(0, Cesium.Color.BLUE),//本初子午线
//鼠标拾取的点位
selectedPoint: {
meridian: undefined,
parallel: undefined,
label: undefined,
},
//对跖点
antipodalPoint: {
meridian: undefined,
parallel: undefined,
label: undefined,
},
//低分辨率网格(经纬网)
lowResolutionGrid: makeGrid(2, Cesium.Color.PALEGREEN, false),//淡绿色
//高分辨率网格(经纬网)
higherResolutionGrid: makeGrid(5, Cesium.Color.DARKORANGE, false),//深橙色
};

/**
* 更新十字准线(准星)
* @param {Cesium.Cartographic} cartographic 弧度经纬度
*/
function updateCrosshairs(cartographic) {
const selectedPoint = primitives.selectedPoint;
const antipodalPoint = primitives.antipodalPoint;
if (Cesium.defined(selectedPoint.parallel)) {
viewer.entities.remove(selectedPoint.parallel);
viewer.entities.remove(selectedPoint.meridian);
viewer.entities.remove(selectedPoint.label);
viewer.entities.remove(antipodalPoint.parallel);
viewer.entities.remove(antipodalPoint.meridian);
viewer.entities.remove(antipodalPoint.label);
}

const pointLatitude = toDegrees(cartographic.latitude);
const antipodeLatitude = -pointLatitude;

const pointLongitude = toDegrees(cartographic.longitude);
const antipodeLongitude = (pointLongitude + 180) % 360;

// 提高放大时的粒度以增强准确性
const finerGranularity = 0.001;
const red = Cesium.Color.RED;
selectedPoint.parallel = parallel(
toDegrees(cartographic.latitude),
red,
finerGranularity
);
selectedPoint.meridian = meridian(
toDegrees(cartographic.longitude),
red,
finerGranularity
);
selectedPoint.label = labelCoordinates(cartographic);

const cyan = Cesium.Color.CYAN;
const antipode = new Cesium.Cartographic.fromDegrees(
antipodeLongitude,
antipodeLatitude,
0
);
antipodalPoint.parallel = parallel(antipodeLatitude, cyan, finerGranularity);
antipodalPoint.meridian = meridian(antipodeLongitude, cyan, finerGranularity);
antipodalPoint.label = labelCoordinates(antipode);

antipodalPoint.parallel.show = showAntipodalPoint;
antipodalPoint.meridian.show = showAntipodalPoint;
antipodalPoint.label.show = showAntipodalPoint;
}

// 点击移动十字准线(准星)
viewer.screenSpaceEventHandler.setInputAction(function (mouse) {
viewer.scene.pick(mouse.position);
const ray = viewer.camera.getPickRay(mouse.position);
const globe = viewer.scene.globe;
const cartesian = globe.pick(ray, viewer.scene);

if (!Cesium.defined(cartesian)) {
return;
}

const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
updateCrosshairs(cartographic);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

Sandcastle.addToggleButton("显示赤道", true, function (checked) {
primitives.equator.show = checked;
});

Sandcastle.addToggleButton("显示本初子午线", true, function (checked) {
primitives.primeMeridian.show = checked;
});

Sandcastle.addToggleButton(
"显示低分辨率经纬网(格)",
false,
function (checked) {
primitives.lowResolutionGrid.forEach(function (line) {
line.show = checked;
});
}
);

Sandcastle.addToggleButton(
"显示低分辨率经纬网(格)",
false,
function (checked) {
primitives.higherResolutionGrid.forEach(function (line) {
line.show = checked;
});
}
);

Sandcastle.addToggleButton("显示对跖点", false, function (checked) {
showAntipodalPoint = checked;
const antipodalPoint = primitives.antipodalPoint;

if (Cesium.defined(antipodalPoint.parallel)) {
antipodalPoint.parallel.show = showAntipodalPoint;
antipodalPoint.meridian.show = showAntipodalPoint;
antipodalPoint.label.show = showAntipodalPoint;
}
});