当前位置: 首页 > 知识库问答 >
问题:

谷歌街景:FOV和Zoom之间的转换

毋城
2023-03-14

有人知道一个公式,我可以用它来转换从谷歌地图缩放Android API:https://developers.google.com/maps/documentation/android-api/streetview到谷歌街景图像API中使用的FOV:https://developers.google.com/maps/documentation/streetview/intro?

我在这里找到了几个旧公式:

  1. https://groups.google.com/forum/#!topic/google-maps-js-api-v3/uqKfg0ZBhWc
  2. https://groups.google.com/forum/#!msg/google maps图像API/O_Odb0A7_0c/Q74cHCoRuscJ

然后,我对他们进行了测试,在我的android手机上缩放相机时,会计算FOV:

FOV1:3.9018*Math.pow(StreetViewPanoramaCamera.zoom,2) - 42.432*StreetViewPanoramaCamera.zoom123

FOV2:数学。abs(街景全景摄像头。变焦/5*108-120

FOV3:180/数学。pow(2,街景全景摄像头。缩放)

@Override
public void onStreetViewPanoramaCameraChange(StreetViewPanoramaCamera streetViewPanoramaCamera) {

    Log.e("Pano", "Bearing: " + streetViewPanoramaCamera.bearing + " Tilt: " + streetViewPanoramaCamera.tilt +
            " Zoom: " + streetViewPanoramaCamera.zoom +
    " FOV1: "+ String.valueOf(3.9018*Math.pow(streetViewPanoramaCamera.zoom,2) - 42.432*streetViewPanoramaCamera.zoom + 123) +" FOV2: "+
            Math.abs(streetViewPanoramaCamera.zoom/5*108-120) +" FOV3: "+ 180/Math.pow(2,streetViewPanoramaCamera.zoom) ) ;

}

在Android上缩放0.0,我们返回以下FOV值:

09-27 15:53:52.322东/帕诺﹕ 方位:228.28955倾斜:14.516191变焦:0.0FOV1:123.0FOV2:120.0FOV3:180.0

由于FOV的最大值为120,FOV2的公式起初似乎很有希望,但当我放大2倍时,它给出的值为76.8,这与实际值相差甚远:

09-27 16:01:48.235东/帕诺﹕ 方位:223.11241倾斜:1.852709变焦:2.0 FOV1:53.7432 FOV2:76.8 FOV3:45.0

这是我手机经过两次缩放后的图像:

这是从FOV 76.8的谷歌街景图像API下载的图像(https://maps.googleapis.com/maps/api/streetview?size=600x300

这是从FOV 45的谷歌街景图像API下载的图像(https://maps.googleapis.com/maps/api/streetview?size=600x300

我得到的最接近的是如果我做了FOV 26,但那是我猜测的,没有使用公式——下面的图片是FOV 26:

共有3个答案

陶锋
2023-03-14

在谷歌上搜索这个答案,我只能找到网络的公式。网络不同,因为首先,在留档中有一个缩放和视场图表:https://developers.google.com/maps/documentation/javascript/streetview#TilingPanoramas

因此我们可以想出一个公式。Android文档中没有这样的表格,人们在web上使用的公式也不适用于Android(甚至不接近)。

不过也有一些好消息,Android API中提供了这个功能:StreetViewPanorama。方向点(街景全景方向)

这个函数允许我们提供一个方向,它将返回屏幕上该方向所在的点。这个函数的文档说,如果方向离开屏幕,它将返回null,但在实践中,我发现它将给出屏幕可视区域之外的一个点;然而,如果我们知道屏幕尺寸,我们可以检查该点是否有效。通过180度的循环,我们可以发现该范围内有多少区域是可见的,因此视野也是可见的。

警告:此功能效率低下。只有当变焦改变时才打电话!

注:下面的示例代码计算水平视野。要计算垂直视野,需要进行一些更改。例如,改变点。x指向点。y、 更改屏幕大小的所有实例。x到屏幕大小。y、 更改街景全景摄像头的所有实例。指向街景全景摄像头。倾斜,并更改以下所有实例:

orientation = new StreetViewPanoramaOrientation(streetViewPanoramaCamera.tilt, angleCheck);

至:

orientation = new StreetViewPanoramaOrientation(angleCheck, streetViewPanoramaCamera.bearing);

这里是一个简单的方法,我们只需从-90度循环到90度,看看180度中有多少在可视区域(注意:这只适用于倾斜为0度的情况,即用户没有向上或向下看):

private StreetViewPanorama mStreetViewPanorama;

private float mZoom = -1f;
private int mFieldOfViewDegrees;

private void onOrientationUpdate(StreetViewPanoramaCamera streetViewPanoramaCamera) {

    if (streetViewPanoramaCamera.zoom != mZoom) {
        mFieldOfViewDegrees = getFieldOfView(streetViewPanoramaCamera);
        mZoom = streetViewPanoramaCamera.zoom;
    }
}

private int getFieldOfView(StreetViewPanoramaCamera streetViewPanoramaCamera) {

    WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
    Display display = windowManager.getDefaultDisplay();
    Point screenSize = new Point();
    display.getSize(screenSize);

    StreetViewPanoramaOrientation orientation;
    Integer lowerFovDegrees = null;
    Integer upperFovDegrees = null;

    for (int angleCheck = (int) streetViewPanoramaCamera.bearing - 90;
         angleCheck <= (int) streetViewPanoramaCamera.bearing + 90;
         angleCheck++) {
        orientation = new StreetViewPanoramaOrientation(streetViewPanoramaCamera.tilt, angleCheck);
        Point point = mStreetViewPanorama.orientationToPoint(orientation);

        if (lowerFovDegrees == null) {
            if ((point != null) && (point.x >= 0)) {
                lowerFovDegrees = angleCheck;
            }
        } else if (upperFovDegrees == null) {
            if ((point == null) || (point.x > screenSize.x)) {
                upperFovDegrees = angleCheck - 1;
                break;
            }
        }
    }

    int fieldOfViewDegrees = upperFovDegrees - lowerFovDegrees;

    return fieldOfViewDegrees;
}

然而,OrientationPoint()非常昂贵,它最多可以调用180次。我们可以使用二进制搜索来提高效率(尽管更复杂),并将其减少到大约13个调用。在本例中,我使用了递归函数来执行二进制搜索。此外,还存在一个限制,即水平赤道必须在视野范围内,否则无法准确计算FOV;在本例中,它返回null。只要用户没有一直向下或向上倾斜,这不是一个真正的问题,但如果他们这样做,FOV将返回null,您需要隐藏任何依赖于FOV是否准确的UI元素(否则它们将不在正确的位置)。

private StreetViewPanorama mStreetViewPanorama;

private float mZoom = -1f;
private float mFieldOfViewDegrees;

private void onOrientationUpdate(StreetViewPanoramaCamera streetViewPanoramaCamera) {

    if (streetViewPanoramaCamera.zoom != mZoom) {
        Float fieldOfViewDegrees = getFieldOfView(streetViewPanoramaCamera);
        if (fieldOfViewDegrees == null) { // If FOV cannot be determined, hide any overlay UI overlay elements so they don't display misaligned
            mZoom = -1f;
            // Hide UI overlay elements
        } else {
            mFieldOfViewDegrees = fieldOfViewDegrees;
            mZoom = streetViewPanoramaCamera.zoom;
            // Show UI overlay elements
        }
    }
}


/*
 * Determine field of view. Must use roundabout way since the info is not provided directly.
 * StreetViewPanorama.orientationToPoint() will tell us if a particular orientation is visible on the
 * screen, so we can loop from looking left (-90) to right (+90) and see how much of the 180 degrees is in
 * the field of view.
 *
 * This is is CPU intensive, so instead of a simple loop we use a recursive binary search, and make sure
 * to only call this function when the zoom has changed.
 *
 * WARNING: This method of getting the FOV only works if the equator is still in view. If the user tilts
 * too far down or up, it will return null.
 */
private Float getFieldOfView(StreetViewPanoramaCamera streetViewPanoramaCamera) {

    WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
    Display display = windowManager.getDefaultDisplay();
    Point screenSize = new Point();
    display.getSize(screenSize);

    // If the equator is no longer in view, FOV cannot be accurately calculated
    StreetViewPanoramaOrientation orientation =
            new StreetViewPanoramaOrientation(0, streetViewPanoramaCamera.bearing);
    Point point = mStreetViewPanorama.orientationToPoint(orientation);
    if (point.y < 0 || point.y > screenSize.y) {
        return null;
    }

    Float lowerFovDegrees = getLowerFieldOfViewDegrees(
            streetViewPanoramaCamera,
            screenSize,
            streetViewPanoramaCamera.bearing - 90f,
            streetViewPanoramaCamera.bearing + 90f);
    Float upperFovDegrees = getUpperFieldOfViewDegrees(
            streetViewPanoramaCamera,
            screenSize,
            streetViewPanoramaCamera.bearing - 90f,
            streetViewPanoramaCamera.bearing + 90f);

    if ((lowerFovDegrees == null) || (upperFovDegrees == null)) return null;

    float fieldOfViewDegrees = upperFovDegrees - lowerFovDegrees;

    return fieldOfViewDegrees;
}

// Recursive binary search function
private Float getLowerFieldOfViewDegrees(StreetViewPanoramaCamera streetViewPanoramaCamera,
                                         Point screenSize,
                                         float startDegrees,
                                         float endDegrees) {
    StreetViewPanoramaOrientation orientation;
    float midpointDegrees = (int) (startDegrees + ((endDegrees - startDegrees) / 2f));

    orientation = new StreetViewPanoramaOrientation(0, midpointDegrees);
    Point point = mStreetViewPanorama.orientationToPoint(orientation);

    if ((point == null) || (point.x < 0)) {
        if (endDegrees - midpointDegrees <= 1f) {
            return endDegrees;
        }

        return getLowerFieldOfViewDegrees(
                streetViewPanoramaCamera,
                screenSize,
                midpointDegrees,
                endDegrees);
    } else {
        if (midpointDegrees - startDegrees <= 1f) {
            return midpointDegrees;
        }

        return getLowerFieldOfViewDegrees(
                streetViewPanoramaCamera,
                screenSize,
                startDegrees,
                midpointDegrees);
    }
}

// Recursive binary search function
private Float getUpperFieldOfViewDegrees(StreetViewPanoramaCamera streetViewPanoramaCamera,
                                         Point screenSize,
                                         float startDegrees,
                                         float endDegrees) {
    StreetViewPanoramaOrientation orientation;
    float midpointDegrees = (int) (startDegrees + ((endDegrees - startDegrees) / 2f));

    orientation = new StreetViewPanoramaOrientation(0, midpointDegrees);
    Point point = mStreetViewPanorama.orientationToPoint(orientation);

    if ((point == null) || (point.x > screenSize.x)) {
        if (midpointDegrees - startDegrees <= 1f) {
            return startDegrees;
        }

        return getUpperFieldOfViewDegrees(
                streetViewPanoramaCamera,
                screenSize,
                startDegrees,
                midpointDegrees);
    } else {
        if (endDegrees - midpointDegrees <= 1f) {
            return midpointDegrees;
        }

        return getUpperFieldOfViewDegrees(
                streetViewPanoramaCamera,
                screenSize,
                midpointDegrees,
                endDegrees);
    }
}

上面函数的粒度是最接近的程度。你可以修改它以给出更小的粒度,但是它需要更长的时间来运行。由于它已经会导致图形结巴,我没有这样做。

我希望这对其他人有所帮助,因为这似乎是一个常见的问题,我还没有看到Android解决了这个问题。

解鸿运
2023-03-14

您可以使用以下公式(JavaScript代码):

>

  • 将视野转换为缩放:

    缩放=Math.log(180/fov)/(Math.log(2))

    将缩放转换为视野:

    fov=180/数学。pow(2,缩放

  • 轩辕成天
    2023-03-14

    最后,我不得不倒退这条线来得到指数公式。

    我使用这个网站,手动输入FOV和缩放值的数据点组合,这些数据点很好地结合在一起:http://www.had2know.com/academics/regression-calculator-statistics-best-fit.html

    我的数据点是:

    视野(放大视野旁边的括号)

    120 (0)

    50(1)

    45(1.2)

    37(1.5)

    26 (2)

    69 (0.5)

    78 (0.3)

    31 (1.9)

    为了得到一个更接近的公式,你可以不断地添加更多“合适”的数据点,但我只是没有时间——任何想完善这个公式的人都可以尝试。

    我得到了下面的公式,其中Y是视场,X是变焦。

    Y=103.7587(0.5051^X)

    相关性=-0.9882

     类似资料:
    • 我需要完全按照用户选择的方式保存街景图像(包括全景、航向、俯仰和视野)。我有以下代码: 问题是我想保存缩放为fov值https://developers.google.com/maps/documentation/streetview/intro(看fov可选参数) fov(默认值为90)确定图像的水平视野。视野以度表示,最大允许值为120。当处理固定大小的视口时,就像处理设置大小的街景图像一样,

    • 我正在尝试使用谷歌街景API获取街景图像。我的代码适用于除法国以外的所有国家。出于某种原因,当我试图从法国任何地方获取数据时,我只会得到一张灰色图像,上面写着“对不起,我们这里没有图像。”但是,我知道那里有图像,因为我可以通过你的谷歌网站使用谷歌街景,看到街景。 例如,如果我去https://maps.google.com/maps/ms?ie=UTF8 知道我做错了什么吗?为什么会这样?

    • 我们使用谷歌街景图像应用编程接口(而不是Javascript应用编程接口)来构建一个返回街景图像的网址。问题是,有时它会返回建筑物内部的图像,而不是提供地址的建筑物图像,例如: https://maps.googleapis.com/maps/api/streetview?size=600x300 是否有只显示户外图像的解决方案/解决方法?

    • 我知道可以使用谷歌API视觉在网上找到类似的图片。我的目标是根据我的图像数据库找到类似的图像。我不想在网上看到类似的图片。 可能吗? 如果是,你能给我一些链接或建议吗? 谢谢

    • 问题内容: 说我有一个这样定义的: 根据我的阅读 (如果我错了,请纠正我): 如果将值写入0:00,则应在60秒后将其移至“准备退出”状态。从中实际删除值将在下一次 缓存修改 时发生(究竟是什么缓存修改?)。 那正确吗? 另外,我不确定和方法之间有什么区别,有人可以提供解释吗? 问题答案: *此链接的 *第一部分 :Guava如何使CacheBuilder中的条目到期? 我将重点讨论 expire

    • 我对谷歌地图API完全是个新手,我想开发一款将谷歌地图上的街道作为路径的游戏。我很清楚我需要什么,但我甚至不知道从API文档中开始搜索什么。。。 如何获得城市街道或谷歌地图其他部分的图形表示,例如交叉点的顶点和边的街道名称(或任何我可以转换为类似的内容),并将顶点映射到(lat,long)协调点…甚至街道的多段线对象,以便从中提取所需内容?(路由查找算法当然可以访问这样的数据结构,所以它就在那里的