Google街景:FOV和缩放之间的转换

时间:2015-09-27 14:10:59

标签: android google-maps google-street-view

有没有人知道我可以用来将Google地图中的放大Android API https://developers.google.com/maps/documentation/android-api/streetview转换为Google StreetView Image 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-image-apis/O_Odb0A7_0c/Q74cHCoRuscJ
  3. 然后我将它们进行测试,当我在Android手机上放大相机时,将计算FOV:

    FOV1: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)

    @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 E / Pano:方位:228.28955倾斜:14.516191放大:   0.0 FOV1:123.0 FOV2:120.0 FOV3:180.0

    由于FOV的最大值为120,FOV2的公式最初看起来很有希望,但当我缩放2次时,它给出的值为76.8,远远低于实际值:

      

    09-27 16:01:48.235 E / Pano:方位:223.11241倾斜:1.852709放大:2.0   FOV1:53.7432 FOV2:76.8 FOV3:45.0

    这是2倍变焦后手机上的图像:

    Google Map Android Streetview

    这是从FST 76.8(https://maps.googleapis.com/maps/api/streetview?size=600x300&location=-33.87365,151.20689&heading=223.11241&pitch=1.852709&fov=76.8&key=APIKEY)的Google Streetview Image API下载的图片:

    GoogleStreetView Image API 76.8

    这是在FOV 45(https://maps.googleapis.com/maps/api/streetview?size=600x300&location=-33.87365,151.20689&heading=223.11241&pitch=1.852709&fov=45&key=APIKEY)从Google Streetview Image API下载的图片:

    GoogleStreetView Image API 45

    我得到的最接近的是如果我做了FOV 26,但这是我猜测而不是使用公式 - 下图是在FOV 26:

    GoogleStreetView Image API 26

4 个答案:

答案 0 :(得分:1)

我最终不得不对该行进行回归以获得指数公式。

我使用这个网站并手动输入FOV和缩放值的数据点组合,这些数据点可以很好地协同工作:http://www.had2know.com/academics/regression-calculator-statistics-best-fit.html

我的数据点是:

FOV(FOV旁边的括号中的ZOOM)

120(0)

50(1)

45(1.2)

37(1.5)

26(2)

69(0.5)

78(0.3)

31(1.9)

为了获得更接近的公式,你可以不断增加更多"良好的适应性"数据点,但我没有时间 - 任何想要完善公式的人都可以尝试。

我得到了下面的公式,其中Y是FOV,X是缩放。

Y = 103.7587(0.5051 ^ X)

correlation = -0.9882

答案 1 :(得分:0)

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

  • fov 转换为缩放

    File out = new File("Trina_NEW.xls") <config evaluator="node-type" condition="acme:document"> <forms> <form> <field-visibility> <show id="acme:anycustomproperty"/> </field-visibility> <appearance> <field id="acme:anycustomproperty" label-id="xqw"> <control template='/org/alfresco/components/form/controls/textfield.ftl' /> <control-param name='maxlength'>5000</control-param> <control template='/org/alfresco/components/form/controls/textarea.ftl' /> <control-param name='rows'>10</control-param> <control-param name='columns'>10</control-param> </field> </appearance> </form> </forms> </config> zoom = Math.log(180/

  • 缩放转换为 fov

    fov )/(Math.log(2)) fov = 180 / Math.pow(2,

答案 2 :(得分:0)

谷歌搜索这个答案我只能找到网络的公式。 Web是不同的,因为首先,文档中有一个Zoom vs FOV图表: https://developers.google.com/maps/documentation/javascript/streetview#TilingPanoramas

因此,人们可以想出一个公式。 Android文档没有这样的表格,人们用于网络的公式不适用于Android(甚至没有关闭)。

然而,有一些好消息,Android API中提供了此功能: StreetViewPanorama.orientationToPoint(StreetViewPanoramaOrientation orientation)

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

警告:此功能效率低下。仅在变焦已更改时调用!

注意:下面的示例代码计算HORIZONTAL视野。要计算垂直视野,您需要进行一些更改。例如,将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。

答案 3 :(得分:0)

我找到了一个更好的方法,JavaScript 如下:

import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;

public class CourseAdapter extends RecyclerView.Adapter<CourseAdapter.Viewholder>{
    private Context context;
    private ArrayList<CourseModel> courseModelArrayList;

    // Constructor
    public CourseAdapter(Context context, ArrayList<CourseModel> courseModelArrayList) {
        this.context = context;
        this.courseModelArrayList = courseModelArrayList;
    }

    @NonNull
    @Override
    public CourseAdapter.Viewholder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        // to inflate the layout for each item of recycler view.
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_recycle_courses, parent, false);
        return new Viewholder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull CourseAdapter.Viewholder holder, int position) {
        // to set data to textview and imageview of each card layout
        CourseModel model = courseModelArrayList.get(position);
        holder.courseNameTV.setText(model.getCourse_name());
        holder.courseDescriptionTV.setText("" + model.getCourse_description());
        holder.courseIV.setImageResource(model.getCourse_image());
    }

    @Override
    public int getItemCount() {
        // this method is used for showing number
        // of card items in recycler view.
        return courseModelArrayList.size();
    }

    // View holder class for initializing of
    // your views such as TextView and Imageview.
    public class Viewholder extends RecyclerView.ViewHolder {
        private ImageView courseIV;
        private TextView courseNameTV, courseDescriptionTV;

        public Viewholder(@NonNull View itemView) {
            super(itemView);
            courseIV = itemView.findViewById(R.id.idIVCourseImage);
            courseNameTV = itemView.findViewById(R.id.idTVCourseName);
            courseDescriptionTV = itemView.findViewById(R.id.idTVCourseDescription);
        }
    }


}

为我在移动设备上运行的 React Native 工作。但目前只在 iOS 上测试。

作者提供:https://ramblings.mcpher.com/ephemeral-exchange/playing-around-with-maps-and-streetview/convert-field-of-view-to-zoom-and-back-again/