计算Google地图街景视图的滚动角度

时间:2014-11-07 21:56:19

标签: javascript google-maps google-maps-api-3 google-street-view

序言:Google地图API有一个issue logged,要求能够更正街景图块的滚动角度以补偿山丘。我想出了一个客户端解决方法,涉及到tile容器上的一些css魔法。这是我的旋转功能:

rotate: function() {
    var tilesLoaded = setInterval(function() {
        var tiles = $('map-canvas').getElementsByTagName('img');
        for (var i=0; i<tiles.length; i++) {
            if (tiles[i].src.indexOf(maps.panorama.getPano()) > -1) {
                if (typeof maps.panorama.getPhotographerPov != 'undefined') {
                    var pov = maps.panorama.getPhotographerPov(),
                    pitch = pov.pitch,
                    cameraHeading = pov.heading;

                    /**************************
                    // I need help with my logic here.
                    **************************/
                    var yaw = pov.heading - 90;
                    if (yaw < 0) yaw += 360;
                    var scale = ((Math.abs(maps.heading - yaw) / 90) - 1) * -1;
                    pitch = pov.pitch * scale;


                    tiles[i].parentNode.parentNode.style.transform = 'rotate(' + pitch + 'deg)';
                    clearInterval(tilesLoaded);
                    return;
                }
            }
        }
    }, 20);
}

完整(且更全面评论)的概念验证是this JSFiddle。奇怪的是,如果我在JSFiddle中的示例中根本没有计算,那么地平线就是完全水平的,但是这个结果对于每个Lat / Lng都不一致。这只是巧合。

所以,我需要根据客户标题,摄影师的标题和摄影师的音调来计算客户端标题的滚动。假设摄影师面朝上坡或下坡,pov.pitch是最高级的(最小或最大限制)。如何在一定程度上计算出面向侧面的所需音高?

编辑:我发现了一个似乎运作良好的等式。我更新了代码和小提琴。虽然它似乎非常接近答案,但我的算法是线性的。我相信正确的方程应该是对数的,导致更接近相机前进和相反的更微妙的调整,而相机的左右调整更大。

1 个答案:

答案 0 :(得分:1)

我找到了我想要的答案。计算涉及球形三角学,在研究这个问题之前我甚至都不知道。如果有人注意到任何问题,请发表评论。或者,如果你有一个比我找到的解决方案更好的解决方案,请随意添加你的答案,如果它比我自己更可靠或效率更高,我可能会接受它。

无论如何,如果瓷砖画布是一个球体,0间距(地平线)是一个平面,而相机间距是另一个与摄影师相交的平面,两个平面将spherical lune投影到画布上。此lune可用于计算spherical triangle其中:

  1. 极角= Math.abs(摄像机俯仰)
  2. base =相机标题 - 客户标题
  3. 一个角度= 90°(对于平坦的地平线)
  4. 有两个角度和一个边可用,可以使用spherical law of sines计算球面三角形的其他属性。不需要整个三角形 - 只有与极角相对的一侧。因为这是超出我技能的数学,我不得不借用this spherical triangle calculator的逻辑。特别感谢emfril!

    The jsfiddle已更新。我的生产辊吸气剂已更新如下:

    function $(what) { return document.getElementById(what); }
    
    var maps = {
    
        get roll() {
            function acos(what) {
                return (Math.abs(Math.abs(what) - 1) < 0.0000000001)
                ? Math.round(Math.acos(what)) : Math.acos(what);
            }
            function sin(what) { return Math.sin(what); }
            function cos(what) { return Math.cos(what); }
            function abs(what) { return Math.abs(what); }
            function deg2rad(what) { return what * Math.PI / 180; }
            function rad2deg(what) { return what * 180 / Math.PI; }
            var roll=0;
    
            if (typeof maps.panorama.getPhotographerPov() != 'undefined') {
                var pov = maps.panorama.getPhotographerPov(),
                clientHeading = maps.panorama.getPov().heading;
                while (clientHeading < 0) clientHeading += 360;
                while (clientHeading > 360) clientHeading -= 360;
    
                // Spherical trigonometry method
                a1 = deg2rad(abs(pov.pitch));
                a2 = deg2rad(90);
                yaw = deg2rad((pov.heading < 0 ? pov.heading + 360 : pov.heading) - clientHeading);
                b1 = acos((cos(a1) * cos(a2)) + (sin(a1) * sin(a2) * cos(yaw)));
    
                if (sin(a1) * sin(a2) * sin(b1) !== 0) {
    
                    roll = acos((cos(a1) - (cos(a2) * cos(b1))) / (sin(a2) * sin(b1)));
                    direction = pov.heading - clientHeading;
                    if (direction < 0) direction += 360;
                    if (pov.pitch < 0)
                        roll = (direction < 180) ? rad2deg(roll) * -1 : rad2deg(roll);
                    else
                        roll = (direction > 180) ? rad2deg(roll) * -1 : rad2deg(roll);
    
                } else {
    
                    // Fall back to algebraic estimate to avoid divide-by-zero
                    var yaw = pov.heading - 90;
                    if (yaw < 0) yaw += 360;
                    var scale = ((abs(clientHeading - yaw) / 90) - 1) * -1;
                    roll = pov.pitch * scale;
                    if (abs(roll) > abs(pov.pitch)) {
                        var diff = (abs(roll) - abs(pov.pitch)) * 2;
                        roll = (roll < 0) ? roll + diff : roll - diff;
                    }
    
                }
            }
            return roll;        
        }, // end maps.roll getter
    
        // ... rest of maps object...
    
    } // end maps{}
    

    旋转全景图块容器后,还需要展开容器以隐藏空白角。我最初使用的是二维正弦法,但我发现了more efficient shortcut。谢谢谭先生!

    function deg2rad(what) { return what * Math.PI / 180; }
    function cos(what) { return Math.cos(deg2rad(what)); }
    function sin(what) { return Math.sin(deg2rad(what)); }
    
    var W = $('map-canvas').clientWidth,
    H = $('map-canvas').clientHeight,
    Rot = Math.abs(maps.originPitch);
    
    // pixels per side
    maps.growX = Math.round(((W * cos(Rot) + H * cos(90 - Rot)) - W) / 2);
    maps.growY = Math.round(((W * sin(Rot) + H * sin(90 - Rot)) - H) / 2);
    

    此答案将不再编辑,因为我不希望将其转换为社区维基答案。当我发生更新时,它们将应用于fiddle