Three.js加载中国GeoJSON行政边界,线条混乱问题如何解决?

threejs加载中国行政边界geojson,用Line实现,画线交错混乱
image.png
image.png

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Three.js Scene</title>
    <style>
        body { margin: 0; }
        canvas { width: 100%; height: 100% }
    </style>
</head>
<body>
    <script type="importmap">
        {
            "imports": {
                "three": "./build/three.module.js",
                "three/addons/": "./examples/jsm/"
            }
        }
    </script>

    <script type="module">
        import * as THREE from 'three';
        import {china} from "http://116.196.110.130:5353/chinaBound.js"
        import {OrbitControls} from 'three/addons/controls/OrbitControls.js'
        import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'

        window.THREE = THREE
        function getArcPoints(vec1, vec2, radius) {
            // 计算两个向量之间的夹角
            const angle = vec1.angleTo(vec2);

            // 四等分角度
            const theta1 = angle / 10;

            // 计算四等分点的坐标
            const points = [];
            const axis = new THREE.Vector3().crossVectors(vec1, vec2).normalize();

            for (let i = 1; i <= 9; i++) {
                const newQuaternion = new THREE.Quaternion().setFromAxisAngle(axis, theta1 * i);
                const newVec = vec1.clone().applyQuaternion(newQuaternion);
                newVec.normalize().multiplyScalar(radius);
                points.push(newVec);
            }
            return points;
        }
        function latLongToVector3(lat, lon, radius) {
            const phi = (90 - lat) * (Math.PI / 180);
            const theta = (lon + 180) * (Math.PI / 180);
            const x = -(radius * Math.sin(phi) * Math.cos(theta));
            const y = radius * Math.cos(phi);
            const z = radius * Math.sin(phi) * Math.sin(theta);
            return new THREE.Vector3(x, y, z);
        }
        // 创建场景
        let flowingLineTexture
        const scene = new THREE.Scene();

        
        scene.background = new THREE.TextureLoader().load('./start.jpg')

        // 创建相机
        const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 8000);
        camera.position.set(-31.194504736429177, 125.39432756985642, -141.89889140580726);
        window.camera = camera

        // 创建渲染器
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // 创建蓝色网格
        const gridHelper = new THREE.GridHelper(100, 100, 0x0000ff, 0x0000ff);
        scene.add(gridHelper);

        const light = new THREE.AmbientLight(0xffffff);
        scene.add(light);

        //添加地球和贴图
        const addEarth = () => {
            const geometry = new THREE.SphereGeometry(100, 64, 64);
            const texture = new THREE.TextureLoader().load("./earth.jpg");
            const material = new THREE.MeshLambertMaterial({
                map: texture,
            });
            const earth = new THREE.Mesh(geometry, material);
            scene.add(earth);
        }
        //曲线
        function initFlowingLine() {
            flowingLineTexture = new THREE.TextureLoader().load('./123.jpg', function(tex) {
                tex.needsUpdate = true
                tex.wrapS = tex.wrapT = THREE.RepeatWrapping;
                tex.repeat.set(1, 1)
            });

            //创建纹理贴图材质
            // let material = new THREE.MeshBasicMaterial({
            //     map: flowingLineTexture,
            //     side: THREE.DoubleSide, //显示背面
            //     transparent: true
            // });

            //创建线条路径
            let points = []
            console.log(china);
            china.features[0].geometry.coordinates.forEach(element => {
                element.forEach(item => {
                    item.forEach(site => {
                        debugger
                        points.push(latLongToVector3(site[1],site[0],100.2))
                    });
                });
            });
            // 创建几何体和材质
            const geometry = new THREE.BufferGeometry().setFromPoints(points);
            const material = new THREE.LineBasicMaterial({ color: 0x0000ff });

            // 使用LineSegments来绘制线条
            const lineSegments = new THREE.LineSegments(geometry, material);
            scene.add(lineSegments);
        }


        addEarth()
        initFlowingLine()



        // 渲染场景
        function animate() {
            controls.update();
            renderer.render(scene, camera);
            flowingLineTexture.offset.x -= 0.01;
            flowingLineTexture.offset.y -= 0.01;
            requestAnimationFrame(animate);
        }

        // 鼠标控制相机
        const controls = new OrbitControls(camera, renderer.domElement);
        // controls.zoomSpeed = 0.1
        // controls.rotateSpeed  = 0.1

        animate();
        // 键盘控制相机
        document.addEventListener('keydown', (event) => {
            switch (event.code) {
                case 'ArrowUp':
                    camera.position.z -= 0.1;
                    break;
                case 'ArrowDown':
                    camera.position.z += 0.1;
                    break;
                case 'ArrowLeft':
                    camera.position.x -= 0.1;
                    break;
                case 'ArrowRight':
                    camera.position.x += 0.1;
                    break;
            }
        });

    </script>
</body>
</html>

求告知方案和原因

阅读 1.4k
avatarAI BotBETA
### 解决方案

这个问题通常是由于GeoJSON数据中的坐标顺序或者坐标系不一致导致的。以下是一些可能的解决方案:

1. **确保GeoJSON数据的坐标系正确**:
   - 确认GeoJSON数据使用的是WGS84(EPSG:4326)或者适合你的Three.js场景的其他坐标系(如EPSG:3857用于Web墨卡托投影)。
   - 如果数据使用的是其他坐标系,需要进行转换。

2. **检查坐标顺序**:
   - GeoJSON中的坐标通常按照经度(longitude)、纬度(latitude)的顺序排列。
   - 确认Three.js中加载和处理这些坐标时的顺序是否正确。

3. **使用适当的Three.js几何体**:
   - 使用`THREE.BufferGeometry`和`THREE.LineSegments`而不是`THREE.Line`来绘制线条,因为`THREE.LineSegments`可以更好地处理断开的线段。

4. **调整线段连接**:
   - 如果线段连接出现问题,可能是因为线段之间的连接点没有正确处理。
   - 确保每个线段的首尾点正确连接。

5. **示例代码**:

const loader = new THREE.FileLoader();
loader.load('path/to/china.geojson', function (data) {

   const geojson = JSON.parse(data);
   const coordinates = geojson.features[0].geometry.coordinates;

   // 假设这是一个多边形(Polygon),你需要将每个环(ring)转换为线段
   const points = [];
   coordinates.forEach(ring => {
       ring.forEach(coord => {
           points.push(new THREE.Vector2(coord[0], coord[1])); // 注意坐标顺序
       });
   });

   const geometry = new THREE.BufferGeometry().setFromPoints(points);
   const material = new THREE.LineBasicMaterial({ color: 0xff0000 });
   const lineSegments = new THREE.LineSegments(geometry, material);
   scene.add(lineSegments);

});


### 原因

- **坐标系不匹配**:如果GeoJSON数据的坐标系与Three.js场景中的坐标系不一致,会导致线条位置错误。
- **坐标顺序错误**:在Three.js中处理GeoJSON数据时,如果坐标顺序(经度、纬度)不正确,也会导致线条位置错误。
- **线段连接问题**:如果线段之间的连接点没有正确处理,会导致线条交错混乱。

确保上述方面都得到正确处理,通常可以解决加载中国GeoJSON行政边界时线条混乱的问题。
2 个回答
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Three.js Scene</title>
    <style>
        body { margin: 0; }
        canvas { width: 100%; height: 100% }
    </style>
</head>
<body>
    <script type="importmap">
        {
            "imports": {
                "three": "./build/three.module.js",
                "three/addons/": "./examples/jsm/"
            }
        }
    </script>

    <script type="module">
        import * as THREE from 'three';
        import {china} from "./chinaBound.js"
        import {OrbitControls} from 'three/addons/controls/OrbitControls.js'
        import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'

        window.THREE = THREE;

        function latLongToVector3(lat, lon, radius) {
            const phi = (90 - lat) * (Math.PI / 180);
            const theta = (lon + 180) * (Math.PI / 180);
            const x = -(radius * Math.sin(phi) * Math.cos(theta));
            const y = radius * Math.cos(phi);
            const z = radius * Math.sin(phi) * Math.sin(theta);
            return new THREE.Vector3(x, y, z);
        }

        // 创建场景
        let flowingLineTexture;
        const scene = new THREE.Scene();
        scene.background = new THREE.TextureLoader().load('./start.jpg');

        // 创建相机
        const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 8000);
        camera.position.set(-31.194504736429177, 125.39432756985642, -141.89889140580726);
        window.camera = camera;

        // 创建渲染器
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // 创建蓝色网格
        const gridHelper = new THREE.GridHelper(100, 100, 0x0000ff, 0x0000ff);
        scene.add(gridHelper);

        const light = new THREE.AmbientLight(0xffffff);
        scene.add(light);

        // 添加地球和贴图
        const addEarth = () => {
            const geometry = new THREE.SphereGeometry(100, 64, 64);
            const texture = new THREE.TextureLoader().load("./earth.jpg");
            const material = new THREE.MeshLambertMaterial({
                map: texture,
            });
            const earth = new THREE.Mesh(geometry, material);
            scene.add(earth);
        };

        // 曲线
        function initFlowingLine() {
            flowingLineTexture = new THREE.TextureLoader().load('./123.jpg', function(tex) {
                tex.needsUpdate = true;
                tex.wrapS = tex.wrapT = THREE.RepeatWrapping;
                tex.repeat.set(1, 1);
            });

            // 创建线条路径
            let points = [];
            china.features[0].geometry.coordinates.forEach(element => {
                element.forEach(item => {
                    item.forEach(site => {
                        points.push(latLongToVector3(site[1], site[0], 100.2));
                    });
                });
            });

            // 创建几何体和材质
            const geometry = new THREE.BufferGeometry().setFromPoints(points);
            const material = new THREE.LineBasicMaterial({ color: 0x0000ff });

            // 使用Line来绘制线条
            const line = new THREE.Line(geometry, material);
            scene.add(line);
        }

        addEarth();
        initFlowingLine();

        // 渲染场景
        function animate() {
            controls.update();
            renderer.render(scene, camera);
            if (flowingLineTexture) {
                flowingLineTexture.offset.x -= 0.01;
                flowingLineTexture.offset.y -= 0.01;
            }
            requestAnimationFrame(animate);
        }

        // 鼠标控制相机
        const controls = new OrbitControls(camera, renderer.domElement);

        animate();

        // 键盘控制相机
        document.addEventListener('keydown', (event) => {
            switch (event.code) {
                case 'ArrowUp':
                    camera.position.z -= 0.1;
                    break;
                case 'ArrowDown':
                    camera.position.z += 0.1;
                    break;
                case 'ArrowLeft':
                    camera.position.x -= 0.1;
                    break;
                case 'ArrowRight':
                    camera.position.x += 0.1;
                    break;
            }
        });

    </script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Three.js Scene</title>
    <style>
        body { margin: 0; }
        canvas { width: 100%; height: 100% }
    </style>
</head>
<body>
    <script type="importmap">
        {
            "imports": {
                "three": "./build/three.module.js",
                "three/addons/": "./examples/jsm/"
            }
        }
    </script>

    <script type="module">
        import * as THREE from 'three';
        import {china} from "./chinaBound.js"
        import {OrbitControls} from 'three/addons/controls/OrbitControls.js'
        import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'

        window.THREE = THREE
        function getArcPoints(vec1, vec2, radius) {
            // 计算两个向量之间的夹角
            const angle = vec1.angleTo(vec2);

            // 四等分角度
            const theta1 = angle / 10;

            // 计算四等分点的坐标
            const points = [];
            const axis = new THREE.Vector3().crossVectors(vec1, vec2).normalize();

            for (let i = 1; i <= 9; i++) {
                const newQuaternion = new THREE.Quaternion().setFromAxisAngle(axis, theta1 * i);
                const newVec = vec1.clone().applyQuaternion(newQuaternion);
                newVec.normalize().multiplyScalar(radius);
                points.push(newVec);
            }
            return points;
        }
        function latLongToVector3(lat, lon, radius) {
            const phi = (90 - lat) * (Math.PI / 180);
            const theta = (lon + 180) * (Math.PI / 180);
            const x = -(radius * Math.sin(phi) * Math.cos(theta));
            const y = radius * Math.cos(phi);
            const z = radius * Math.sin(phi) * Math.sin(theta);
            return new THREE.Vector3(x, y, z);
        }
        // 创建场景
        let flowingLineTexture
        const scene = new THREE.Scene();

        
        scene.background = new THREE.TextureLoader().load('./start.jpg')

        // 创建相机
        const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 8000);
        camera.position.set(-31.194504736429177, 125.39432756985642, -141.89889140580726);
        window.camera = camera

        // 创建渲染器
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // 创建蓝色网格
        const gridHelper = new THREE.GridHelper(100, 100, 0x0000ff, 0x0000ff);
        scene.add(gridHelper);

        const light = new THREE.AmbientLight(0xffffff);
        scene.add(light);

        //添加地球和贴图
        const addEarth = () => {
            const geometry = new THREE.SphereGeometry(100, 64, 64);
            const texture = new THREE.TextureLoader().load("./earth.jpg");
            const material = new THREE.MeshLambertMaterial({
                map: texture,
            });
            const earth = new THREE.Mesh(geometry, material);
            scene.add(earth);
        }
        //曲线
        function initFlowingLine() {
            flowingLineTexture = new THREE.TextureLoader().load('./123.jpg', function(tex) {
                tex.needsUpdate = true;
                tex.wrapS = tex.wrapT = THREE.RepeatWrapping;
                tex.repeat.set(1, 1);
            });

            // 创建线条路径
            console.log(china);
            
            china.features[0].geometry.coordinates.forEach(element => {
                element.forEach(item => {
                    
                    let points = [];
                    item.forEach(site => {
                        points.push(latLongToVector3(site[1], site[0], 100.2));
                    });
                    
                    // 创建几何体和材质
                    const geometry = new THREE.BufferGeometry().setFromPoints(points);
                    const material = new THREE.LineBasicMaterial({ color: 0x0000ff });

                    // 使用Line来绘制线条
                    const line = new THREE.Line(geometry, material);
                    scene.add(line);
                });
            });

        }

        addEarth()
        initFlowingLine()



        // 渲染场景
        function animate() {
            controls.update();
            renderer.render(scene, camera);
            flowingLineTexture.offset.x -= 0.01;
            flowingLineTexture.offset.y -= 0.01;
            requestAnimationFrame(animate);
        }

        // 鼠标控制相机
        const controls = new OrbitControls(camera, renderer.domElement);
        // controls.zoomSpeed = 0.1
        // controls.rotateSpeed  = 0.1

        animate();
        // 键盘控制相机
        document.addEventListener('keydown', (event) => {
            switch (event.code) {
                case 'ArrowUp':
                    camera.position.z -= 0.1;
                    break;
                case 'ArrowDown':
                    camera.position.z += 0.1;
                    break;
                case 'ArrowLeft':
                    camera.position.x -= 0.1;
                    break;
                case 'ArrowRight':
                    camera.position.x += 0.1;
                    break;
            }
        });

    </script>
</body>
</html>
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏