谷歌搜索这个答案我只能找到网络公式。 Web 是不同的,因为首先,文档中有一个 Zoom vs FOV 图表:
https://developers.google.com/maps/documentation/javascript/streetview#TilingPanoramas
因此可以想出一个公式。 Android 文档没有这样的表格,人们用于 web 的公式不适用于 Android(甚至不能关闭)。
不过有一些好消息,Android API 中提供了这个功能:
StreetViewPanorama.orientationToPoint(StreetViewPanoramaOrientation orientation)
这个函数允许我们提供一个方向,它会返回屏幕上那个方向所在的一个点。该函数的文档说,如果该方向不在屏幕上,它将返回 null,但实际上我发现它会在屏幕的可视区域之外给出一个点;但是,如果我们知道屏幕尺寸,我们可以检查该点是否有效。通过循环 180 度,我们可以知道该范围有多少是可见的,因此是视野。
警告:此功能效率低下。仅在缩放已更改时才调用!
注意:下面的示例代码计算水平视野。要计算垂直视野,您需要进行一些更改。例如,将 point.x 更改为 point.y,将 screenSize.x 的所有实例更改为 screenSize.y,将 streetViewPanoramaCamera.bearing 的所有实例更改为 streetViewPanoramaCamera.tilt,并更改以下所有实例:
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;
}
但是,orientationToPoint() 很昂贵,最多可以调用 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 上得到解决。