• 基于WebGL的三维地形渲染


    1.生成WebMap页面

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    import subprocess
    
    from jinja2 import Environment, FileSystemLoader
    
    
    # Create our DEM
    
    #将DEM数据转化为ENVI的bin格式(一个头文件xml与一个数据文件bin)
    # use gdal_translate command to create an image to store elevation values
    # -scale from 0 meters to 2625 meters
    #     stretch all values to full 16bit  0 to 65535
    # -ot is output type = UInt16 unsigned 16bit
    # -outsize is 200 x 200 px
    # -of is output format ENVI raster image .bin file type
    # then our input .tif with elevation
    # followed by output file name .bin
    subprocess.call("gdal_translate -scale 0 2625 0 65535 "
                    "-ot UInt16 -outsize 200 200 -of ENVI "
                    "../../ch07/geodata/dem_3857.tif "
                    "../geodata/whistler2.bin")
    
    #Jinja2是基于python的模板引擎
    # create our Jinja2 HTML
    # create a standard Jinja2 Environment and load all files
    # located in the folder templates
    env = Environment(loader=FileSystemLoader(["../www/templates"]))
    
    # define which template we want to render
    template = env.get_template("base-3d-map.html")
    
    # path and name of input 16bit raster image with our elevation values
    dem_3d = "../../geodata/whistler2.bin"
    
    # name and location of the output html file we will generate
    out_html = "../www/html/ch10-03_dem3d_map.html"
    
    #生成页面
    # dem_file is the variable name we use in our Jinja2 html template file
    result = template.render(title="Threejs DEM Viewer", dem_file=dem_3d)
    
    # write out our template to the html file on disk
    with open(out_html,mode="w") as f:
        f.write(result)

    2.将数据与页面复制Web服务器的相关目录下

    image

    3.访问WebMap页面

    image

     

    4.相关代码

    1)three.min.js

    2)TerrainLoader.js

    /**
     * @author Bjorn Sandvik / http://thematicmapping.org/
     */
    
    THREE.TerrainLoader = function ( manager ) {
    
        this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
    
    };
    
    
    THREE.TerrainLoader.prototype = {
    
        constructor: THREE.TerrainLoader,
    
        load: function ( url, onLoad, onProgress, onError ) {
    
            var scope = this;
            var request = new XMLHttpRequest();
    
            if ( onLoad !== undefined ) {
    
                request.addEventListener( 'load', function ( event ) {
    
                    onLoad( new Uint16Array( event.target.response ) );
                    scope.manager.itemEnd( url );
    
                }, false );
    
            }
    
            if ( onProgress !== undefined ) {
    
                request.addEventListener( 'progress', function ( event ) {
    
                    onProgress( event );
    
                }, false );
    
            }
    
            if ( onError !== undefined ) {
    
                request.addEventListener( 'error', function ( event ) {
    
                    onError( event );
    
                }, false );
    
            }
    
            if ( this.crossOrigin !== undefined ) request.crossOrigin = this.crossOrigin;
    
            request.open( 'GET', url, true );
    
            request.responseType = 'arraybuffer';
    
            request.send( null );
    
            scope.manager.itemStart( url );
    
        },
    
        setCrossOrigin: function ( value ) {
    
            this.crossOrigin = value;
    
        }
    
    };

    3)TrackballControls.js

    /**
     * @author Eberhard Graether / http://egraether.com/
     */
    
    THREE.TrackballControls = function ( object, domElement ) {
    
        var _this = this;
        var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM: 4, TOUCH_PAN: 5 };
    
        this.object = object;
        this.domElement = ( domElement !== undefined ) ? domElement : document;
    
        // API
    
        this.enabled = true;
    
        this.screen = {  0, height: 0, offsetLeft: 0, offsetTop: 0 };
        this.radius = ( this.screen.width + this.screen.height ) / 4;
    
        this.rotateSpeed = 1.0;
        this.zoomSpeed = 1.2;
        this.panSpeed = 0.3;
    
        this.noRotate = false;
        this.noZoom = false;
        this.noPan = false;
    
        this.staticMoving = false;
        this.dynamicDampingFactor = 0.2;
    
        this.minDistance = 0;
        this.maxDistance = Infinity;
    
        this.keys = [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ];
    
        // internals
    
        this.target = new THREE.Vector3();
    
        var lastPosition = new THREE.Vector3();
    
        var _state = STATE.NONE,
        _prevState = STATE.NONE,
    
        _eye = new THREE.Vector3(),
    
        _rotateStart = new THREE.Vector3(),
        _rotateEnd = new THREE.Vector3(),
    
        _zoomStart = new THREE.Vector2(),
        _zoomEnd = new THREE.Vector2(),
    
        _touchZoomDistanceStart = 0,
        _touchZoomDistanceEnd = 0,
    
        _panStart = new THREE.Vector2(),
        _panEnd = new THREE.Vector2();
    
        // for reset
    
        this.target0 = this.target.clone();
        this.position0 = this.object.position.clone();
        this.up0 = this.object.up.clone();
    
        // events
    
        var changeEvent = { type: 'change' };
    
    
        // methods
    
        this.handleResize = function () {
    
            this.screen.width = window.innerWidth;
            this.screen.height = window.innerHeight;
    
            this.screen.offsetLeft = 0;
            this.screen.offsetTop = 0;
    
            this.radius = ( this.screen.width + this.screen.height ) / 4;
    
        };
    
        this.handleEvent = function ( event ) {
    
            if ( typeof this[ event.type ] == 'function' ) {
    
                this[ event.type ]( event );
    
            }
    
        };
    
        this.getMouseOnScreen = function ( clientX, clientY ) {
    
            return new THREE.Vector2(
                ( clientX - _this.screen.offsetLeft ) / _this.radius * 0.5,
                ( clientY - _this.screen.offsetTop ) / _this.radius * 0.5
            );
    
        };
    
        this.getMouseProjectionOnBall = function ( clientX, clientY ) {
    
            var mouseOnBall = new THREE.Vector3(
                ( clientX - _this.screen.width * 0.5 - _this.screen.offsetLeft ) / _this.radius,
                ( _this.screen.height * 0.5 + _this.screen.offsetTop - clientY ) / _this.radius,
                0.0
            );
    
            var length = mouseOnBall.length();
    
            if ( length > 1.0 ) {
    
                mouseOnBall.normalize();
    
            } else {
    
                mouseOnBall.z = Math.sqrt( 1.0 - length * length );
    
            }
    
            _eye.copy( _this.object.position ).sub( _this.target );
    
            var projection = _this.object.up.clone().setLength( mouseOnBall.y );
            projection.add( _this.object.up.clone().cross( _eye ).setLength( mouseOnBall.x ) );
            projection.add( _eye.setLength( mouseOnBall.z ) );
    
            return projection;
    
        };
    
        this.rotateCamera = function () {
    
            var angle = Math.acos( _rotateStart.dot( _rotateEnd ) / _rotateStart.length() / _rotateEnd.length() );
    
            if ( angle ) {
    
                var axis = ( new THREE.Vector3() ).crossVectors( _rotateStart, _rotateEnd ).normalize();
                    quaternion = new THREE.Quaternion();
    
                angle *= _this.rotateSpeed;
    
                quaternion.setFromAxisAngle( axis, -angle );
    
                _eye.applyQuaternion( quaternion );
                _this.object.up.applyQuaternion( quaternion );
    
                _rotateEnd.applyQuaternion( quaternion );
    
                if ( _this.staticMoving ) {
    
                    _rotateStart.copy( _rotateEnd );
    
                } else {
    
                    quaternion.setFromAxisAngle( axis, angle * ( _this.dynamicDampingFactor - 1.0 ) );
                    _rotateStart.applyQuaternion( quaternion );
    
                }
    
            }
    
        };
    
        this.zoomCamera = function () {
    
            if ( _state === STATE.TOUCH_ZOOM ) {
    
                var factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
                _touchZoomDistanceStart = _touchZoomDistanceEnd;
                _eye.multiplyScalar( factor );
    
            } else {
    
                var factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed;
    
                if ( factor !== 1.0 && factor > 0.0 ) {
    
                    _eye.multiplyScalar( factor );
    
                    if ( _this.staticMoving ) {
    
                        _zoomStart.copy( _zoomEnd );
    
                    } else {
    
                        _zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
    
                    }
    
                }
    
            }
    
        };
    
        this.panCamera = function () {
    
            var mouseChange = _panEnd.clone().sub( _panStart );
    
            if ( mouseChange.lengthSq() ) {
    
                mouseChange.multiplyScalar( _eye.length() * _this.panSpeed );
    
                var pan = _eye.clone().cross( _this.object.up ).setLength( mouseChange.x );
                pan.add( _this.object.up.clone().setLength( mouseChange.y ) );
    
                _this.object.position.add( pan );
                _this.target.add( pan );
    
                if ( _this.staticMoving ) {
    
                    _panStart = _panEnd;
    
                } else {
    
                    _panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) );
    
                }
    
            }
    
        };
    
        this.checkDistances = function () {
    
            if ( !_this.noZoom || !_this.noPan ) {
    
                if ( _this.object.position.lengthSq() > _this.maxDistance * _this.maxDistance ) {
    
                    _this.object.position.setLength( _this.maxDistance );
    
                }
    
                if ( _eye.lengthSq() < _this.minDistance * _this.minDistance ) {
    
                    _this.object.position.addVectors( _this.target, _eye.setLength( _this.minDistance ) );
    
                }
    
            }
    
        };
    
        this.update = function () {
    
            _eye.subVectors( _this.object.position, _this.target );
    
            if ( !_this.noRotate ) {
    
                _this.rotateCamera();
    
            }
    
            if ( !_this.noZoom ) {
    
                _this.zoomCamera();
    
            }
    
            if ( !_this.noPan ) {
    
                _this.panCamera();
    
            }
    
            _this.object.position.addVectors( _this.target, _eye );
    
            _this.checkDistances();
    
            _this.object.lookAt( _this.target );
    
            if ( lastPosition.distanceToSquared( _this.object.position ) > 0 ) {
    
                _this.dispatchEvent( changeEvent );
    
                lastPosition.copy( _this.object.position );
    
            }
    
        };
    
        this.reset = function () {
    
            _state = STATE.NONE;
            _prevState = STATE.NONE;
    
            _this.target.copy( _this.target0 );
            _this.object.position.copy( _this.position0 );
            _this.object.up.copy( _this.up0 );
    
            _eye.subVectors( _this.object.position, _this.target );
    
            _this.object.lookAt( _this.target );
    
            _this.dispatchEvent( changeEvent );
    
            lastPosition.copy( _this.object.position );
    
        };
    
        // listeners
    
        function keydown( event ) {
    
            if ( _this.enabled === false ) return;
    
            window.removeEventListener( 'keydown', keydown );
    
            _prevState = _state;
    
            if ( _state !== STATE.NONE ) {
    
                return;
    
            } else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && !_this.noRotate ) {
    
                _state = STATE.ROTATE;
    
            } else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && !_this.noZoom ) {
    
                _state = STATE.ZOOM;
    
            } else if ( event.keyCode === _this.keys[ STATE.PAN ] && !_this.noPan ) {
    
                _state = STATE.PAN;
    
            }
    
        }
    
        function keyup( event ) {
    
            if ( _this.enabled === false ) return;
    
            _state = _prevState;
    
            window.addEventListener( 'keydown', keydown, false );
    
        }
    
        function mousedown( event ) {
    
            if ( _this.enabled === false ) return;
    
            event.preventDefault();
            event.stopPropagation();
    
            if ( _state === STATE.NONE ) {
    
                _state = event.button;
    
            }
    
            if ( _state === STATE.ROTATE && !_this.noRotate ) {
    
                _rotateStart = _rotateEnd = _this.getMouseProjectionOnBall( event.clientX, event.clientY );
    
            } else if ( _state === STATE.ZOOM && !_this.noZoom ) {
    
                _zoomStart = _zoomEnd = _this.getMouseOnScreen( event.clientX, event.clientY );
    
            } else if ( _state === STATE.PAN && !_this.noPan ) {
    
                _panStart = _panEnd = _this.getMouseOnScreen( event.clientX, event.clientY );
    
            }
    
            document.addEventListener( 'mousemove', mousemove, false );
            document.addEventListener( 'mouseup', mouseup, false );
    
        }
    
        function mousemove( event ) {
    
            if ( _this.enabled === false ) return;
    
            event.preventDefault();
            event.stopPropagation();
    
            if ( _state === STATE.ROTATE && !_this.noRotate ) {
    
                _rotateEnd = _this.getMouseProjectionOnBall( event.clientX, event.clientY );
    
            } else if ( _state === STATE.ZOOM && !_this.noZoom ) {
    
                _zoomEnd = _this.getMouseOnScreen( event.clientX, event.clientY );
    
            } else if ( _state === STATE.PAN && !_this.noPan ) {
    
                _panEnd = _this.getMouseOnScreen( event.clientX, event.clientY );
    
            }
    
        }
    
        function mouseup( event ) {
    
            if ( _this.enabled === false ) return;
    
            event.preventDefault();
            event.stopPropagation();
    
            _state = STATE.NONE;
    
            document.removeEventListener( 'mousemove', mousemove );
            document.removeEventListener( 'mouseup', mouseup );
    
        }
    
        function mousewheel( event ) {
    
            if ( _this.enabled === false ) return;
    
            event.preventDefault();
            event.stopPropagation();
    
            var delta = 0;
    
            if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9
    
                delta = event.wheelDelta / 40;
    
            } else if ( event.detail ) { // Firefox
    
                delta = - event.detail / 3;
    
            }
    
            _zoomStart.y += delta * 0.01;
    
        }
    
        function touchstart( event ) {
    
            if ( _this.enabled === false ) return;
    
            switch ( event.touches.length ) {
    
                case 1:
                    _state = STATE.TOUCH_ROTATE;
                    _rotateStart = _rotateEnd = _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
                    break;
    
                case 2:
                    _state = STATE.TOUCH_ZOOM;
                    var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
                    var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
                    _touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
                    break;
    
                case 3:
                    _state = STATE.TOUCH_PAN;
                    _panStart = _panEnd = _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
                    break;
    
                default:
                    _state = STATE.NONE;
    
            }
    
        }
    
        function touchmove( event ) {
    
            if ( _this.enabled === false ) return;
    
            event.preventDefault();
            event.stopPropagation();
    
            switch ( event.touches.length ) {
    
                case 1:
                    _rotateEnd = _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
                    break;
    
                case 2:
                    var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
                    var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
                    _touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy )
                    break;
    
                case 3:
                    _panEnd = _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
                    break;
    
                default:
                    _state = STATE.NONE;
    
            }
    
        }
    
        function touchend( event ) {
    
            if ( _this.enabled === false ) return;
    
            switch ( event.touches.length ) {
    
                case 1:
                    _rotateStart = _rotateEnd = _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
                    break;
    
                case 2:
                    _touchZoomDistanceStart = _touchZoomDistanceEnd = 0;
                    break;
    
                case 3:
                    _panStart = _panEnd = _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
                    break;
    
            }
    
            _state = STATE.NONE;
    
        }
    
        this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
    
        this.domElement.addEventListener( 'mousedown', mousedown, false );
    
        this.domElement.addEventListener( 'mousewheel', mousewheel, false );
        this.domElement.addEventListener( 'DOMMouseScroll', mousewheel, false ); // firefox
    
        this.domElement.addEventListener( 'touchstart', touchstart, false );
        this.domElement.addEventListener( 'touchend', touchend, false );
        this.domElement.addEventListener( 'touchmove', touchmove, false );
    
        window.addEventListener( 'keydown', keydown, false );
        window.addEventListener( 'keyup', keyup, false );
    
        this.handleResize();
    
    };
    
    THREE.TrackballControls.prototype = Object.create( THREE.EventDispatcher.prototype );

    4)dem3d_map.html

    <html lang="en">
    <head>
        <title>DEM threejs Browser</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
        <style> body { margin: 0; overflow: hidden; }</style>
    </head>
    <body>
        <div id="dem-map"></div>
        <script src="../js/three.min.js"></script>
        <script src="../js/TrackballControls.js"></script>
        <script src="../js/TerrainLoader.js"></script>
        <script>
    
            var width  = window.innerWidth,
                height = window.innerHeight;
    
            var scene = new THREE.Scene();
    
            var axes = new THREE.AxisHelper(200);
            scene.add(axes);
    
            var camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);
            camera.position.set(0, -50, 50);
    
            var renderer = new THREE.WebGLRenderer();
            renderer.setSize(width, height);
    
            var terrainLoader = new THREE.TerrainLoader();
            terrainLoader.load('../../geodata/whistler2.bin', function(data) {
    
                var geometry = new THREE.PlaneGeometry(60, 60, 199, 199);
    
                for (var i = 0, l = geometry.vertices.length; i < l; i++) {
                    geometry.vertices[i].z = data[i] / 65535 * 10;
                }
    
                var material = new THREE.MeshPhongMaterial({
                    color: 0xdddddd,
                    wireframe: true
                });
    
                /*
                //加载纹理

                var material = new THREE.MeshPhongMaterial({
                  map: THREE.ImageUtils.loadTexture('../../geodata/whistler_ortho_f.jpg')
                });

                */

                var plane = new THREE.Mesh(geometry, material);
                scene.add(plane);
    
            });
    
            var controls = new THREE.TrackballControls(camera);
    
            document.getElementById('dem-map').appendChild(renderer.domElement);
    
            render();
    
            function render() {
                controls.update();
                requestAnimationFrame(render);
                renderer.render(scene, camera);
            }
    
        </script>
    </body>
    </html>
  • 相关阅读:
    概念理解及常用方法
    WeX5触发事件
    前端页面问题小结
    高性能 CSS3 动画
    IScroll5中文API整理,用法与参考
    关于浏览器兼容之mate标签
    HTMl5的sessionStorage和localStorage
    web app iphone4 iphone5 iphone6 响应式布局 适配代码
    Javascript模板引擎分享
    css3 media媒体查询器用法总结
  • 原文地址:https://www.cnblogs.com/gispathfinder/p/5792238.html
Copyright © 2020-2023  润新知