//地球光圈
function createSprite(R) {
var textureLoader = new THREE.TextureLoader();
var texture = textureLoader.load(spriteImg);//加载纹理贴图
// 创建精灵材质对象SpriteMaterial
var spriteMaterial = new THREE.SpriteMaterial({
map: texture, //设置贴图
transparent: true,//开启透明
// opacity: 0.5,//通过透明度整体调节光圈
});
var sprite = new THREE.Sprite(spriteMaterial);
sprite.scale.set(R * 3.0, R * 3.0, 1);//缩放精灵
return sprite
}
function createEarth(R) {
var earthGroup = new THREE.Group();//地球组对象
earthGroup.add(createSphereMesh(R));//球体Mesh插入earthGroup中
earthGroup.add(createSprite(R));//地球光圈
});
return earthGroup;
}
function lon2xyz(R, longitude, latitude) { //longitude:经度角度值,latitude:纬度角度值
var lon = longitude * Math.PI / 180;//转弧度值
var lat = latitude * Math.PI / 180;
lon = -lon;// three.js坐标系z坐标轴对应经度-90度,而不是90度
// 经纬度坐标转球面坐标计算公式
var x = R * Math.cos(lat) * Math.cos(lon);
var y = R * Math.sin(lat);
var z = R * Math.cos(lat) * Math.sin(lon);
// 返回球面坐标
return {
x: x,
y: y,
z: z,
};
}
通过BufferGeometry构建一个几何体并传入顶点数据,通过LineLoop模型渲染几何体,连点成线,但是设置线模型Line对应线材质LineBasicMaterial的线宽属性.lineWidth,是无效的。可使用拓展库Line2解决此问题。
// pointArr:边界坐标
function line(pointArr) {
var geometry = new THREE.BufferGeometry(); //创建一个Buffer类型几何体对象
var vertices = new Float32Array(pointArr);//创建顶点数据
//BufferAttribute这个类用于存储与BufferGeometry相关联的 attribute(例如顶点位置向量,面片索引,法向量,颜色值,UV坐标以及任何自定义 attribute )。 利用 BufferAttribute,可以更高效的向GPU传递数据。
var attribue = new THREE.BufferAttribute(vertices, 3); //表示一个顶点的xyz坐标
geometry.attributes.position = attribue; // 设置几何体attributes属性的位置属性
var material = new THREE.LineBasicMaterial({
color: 0x00aaaa
});
var line = new THREE.LineLoop(geometry, material);//线条模型对象,首尾顶点连线
return line;
}
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"name": "...",//国家名称
"childNum": 1
}
"geometry": {
"type": "Polygon",//国家轮廓由一个多边形轮廓构成
"coordinates": [//国家边界经纬度坐标
[
[74.54140625000002, 37.02216796875],
[74.00185546875002, 36.823095703125]
// ...等经纬度坐标数据
]
]
}
}, {
"type": "Feature",
"properties": {
"name": "...",//国家名称
"childNum": 1
}
"geometry": {
"type": "MultiPolygon",// MultiPolygon:表示一个国家有多个轮廓构成
"coordinates": [
[
//国家的轮廓1
[
[17.57958984375, -8.099023437500009],
[17.643359375000017, -8.090722656250009]
// ...等经纬度坐标数据
]
],
[
//国家的轮廓2
[
[12.255273437500023, -5.746484374999994]
//...等经纬度坐标数据
]
]
]
}
}
]
}
‘world.json’文件包含每个国家的边界线经纬度数据,所有国家边界坐标数据存放于worldjson.features中,每个国家下对应的"Polygon"代表该国家有一个封闭轮廓,"MultiPolygon代表该国家有多个封闭轮廓。
// 创建国家边界线
function countryLine(R, polygonArr) {
var group = new THREE.Group();// 组对象mapGroup是所有国家边界父对象
polygonArr.forEach(polygon => {
var pointArr = [];//边界线顶点坐标
polygon[0].forEach(elem => {
// 经纬度转球面坐标
var coord = lon2xyz(R, elem[0], elem[1])
pointArr.push(coord.x, coord.y, coord.z);
});
group.add(line(pointArr));
});
return group;
}
function createEarth(R) {
var earthGroup = new THREE.Group();//地球组对象
earthGroup.add(createSphereMesh(R));//球体Mesh插入earthGroup中
earthGroup.add(createSprite(R));//地球光圈
worldjson.features.forEach(function (country) {
if (country.geometry.type === "Polygon") {
// 把"Polygon"和"MultiPolygon"的geometry.coordinates数据结构处理为一致
country.geometry.coordinates = [country.geometry.coordinates];
}
var line = countryLine(R * 1.002, country.geometry.coordinates);//国家边界
earthGroup.add(line);
});
return earthGroup;
}
// 经纬度坐标进行排序
function minMax(arr) {
arr.sort(compareNum);
// 通过向两侧取整,把经纬度的方位稍微扩大
return [Math.floor(arr[0]), Math.ceil(arr[arr.length - 1])]
}
// 排序规则
function compareNum(num1, num2) {
if (num1 < num2) {
return -1;
} else if (num1 > num2) {
return 1;
} else {
return 0;
}
}
2)、在矩形范围内批量生成等间距的网格顶点数据
var lonArr = [];//polygon的所有经度坐标
var latArr = [];//polygon的所有纬度坐标
polygon.forEach(elem => { //polygon表示多边形轮廓的数组
lonArr.push(elem[0])
latArr.push(elem[1])
});
//计算polygon所有经纬度返回的极大值、极小值
var [lonMin, lonMax] = minMax(lonArr);
var [latMin, latMax] = minMax(latArr);
// 经纬度极小值和极大值构成一个矩形范围,在矩形范围内生成等间距顶点
var interval = 3; //各点之间的距离
var row = Math.ceil((lonMax - lonMin) / interval);
var col = Math.ceil((latMax - latMin) / interval)
var rectPointsArr = [];//生成均匀间隔的矩形网格数据
for (var i = 0; i < row + 1; i++) {
for (var j = 0; j < col + 1; j++) {
rectPointsArr.push([lonMin + i * interval, latMin + j * interval])
}
}
var polygonPointsArr = [];//轮廓内的网格顶点数据
rectPointsArr.forEach(function (coord) {
if (pointInPolygon(coord, polygon)) {//判断点是否位于多边形中
polygonPointsArr.push(coord)
}
})
// 返回多边形polygon边界坐标和polygon内等间距顶点坐标
return [...polygon, ...polygonPointsArr];
完整方法:
function gridPoint(polygon) {
var lonArr = [];
var latArr = [];
polygon.forEach(elem => {
lonArr.push(elem[0])
latArr.push(elem[1])
});
var [lonMin, lonMax] = minMax(lonArr);
var [latMin, latMax] = minMax(latArr);
var interval = 3;
var row = Math.ceil((lonMax - lonMin) / interval);
var col = Math.ceil((latMax - latMin) / interval)
var rectPointsArr = [];
for (var i = 0; i < row + 1; i++) {
for (var j = 0; j < col + 1; j++) {
rectPointsArr.push([lonMin + i * interval, latMin + j * interval])
}
}
var polygonPointsArr = [];
rectPointsArr.forEach(function (coord) {
if (pointInPolygon(coord, polygon)) {
polygonPointsArr.push(coord)
}
})
return [...polygon, ...rectPointsArr];
}
function countryMesh(R, polygonArr) {//polygonArr:国家轮廓经纬度
var geometryArr = [];
polygonArr.forEach(obj => {
var polygon = obj[0];//获取多边形轮廓数据
var pointsArr = gridPoint(polygon);
var spherePointsArr = [];
pointsArr.forEach((item, i) => {
// 经纬度坐标转球面坐标
var pos = lon2xyz(R, item[0], item[1])
spherePointsArr.push(pos.x, pos.y, pos.z)
});
var geometry = new THREE.BufferGeometry();
// 设置几何体顶点位置坐标
geometry.attributes.position = new THREE.BufferAttribute(new Float32Array(spherePointsArr), 3)
geometryArr.push(geometry);//geometryArr:一个国家多个轮廓,每个轮廓对应的所有几何体
});
var newGeometry = null;
if (geometryArr.length == 1) {
newGeometry = geometryArr[0];//如果一个国家只有一个多边形轮廓,不用进行几何体合并操作
} else {
newGeometry = mergeBufferGeometries(geometryArr);
}
var pointsMaterial = new THREE.PointsMaterial({
color: 0x1cfb05,
size: 1,
});
var points = new THREE.Points(newGeometry, pointsMaterial);
return points
}
function createEarth(R) {
var earthGroup = new THREE.Group();//地球组对象
earthGroup.add(createSphereMesh(R));
earthGroup.add(createSprite(R));//地球光圈
worldjson.features.forEach(function (country) {
if (country.geometry.type === "Polygon") {
// 把"Polygon"和"MultiPolygon"的geometry.coordinates数据结构处理为一致
country.geometry.coordinates = [country.geometry.coordinates];
}
var line = countryLine(R * 1.002, country.geometry.coordinates);//国家边界
var points= countryMesh(R * 1.001, country.geometry.coordinates);//国家轮廓点阵
earthGroup.add(line);//国家边界集合插入earthGroup中
earthGroup.add(points);//国家轮廓点阵集合插入earthGroup中
});
return earthGroup;
}