【问题标题】:How to scale given coordinates correctly to fit into the boundaries of a View without distortion?如何正确缩放给定坐标以适应视图的边界而不会失真?
【发布时间】:2018-06-02 18:32:23
【问题描述】:

我有一个 [纬度/经度] 对的列表,并希望将它们描述为连接线的路径绘制到我的自定义视图中,但比例正确。

到目前为止,我的解决方案如下所示: 找到纬度和经度的最小值和最大值,将每个值描述为 0 到 1 之间的数字,然后将它们乘以我的视图的高度/宽度。 结果,我的道路总是被拉长到我的视野范围内。

因此,例如,如果我从北向南走 10000 米,那时只有 1 米向西,然后向东 2 米,我的路径看起来像这样(向四面八方延伸):

---------------
|      *      |
|  ****       |
|**           |
| *           |
|  ***        |
|     ****    |
|         **  |
|           **|
---------------

但我希望它看起来像这样:

---------------
|      *      |
|      *      |
|     *       |
|     **      |
|       *     |
|       *     |
|       *     |
|       *     |
---------------

所以当我大部分时间从北向南走时,路线应该填满我的视野的整个高度,并且应该相应地显示 x 位置。当我大部分时间从西向东走时,它应该占据它的整个宽度。

我不知道如何正确缩放我的值,以便在我的视图中绘制它们。我希望你能理解我的问题并能帮助我解决它。提前致谢! :)

【问题讨论】:

    标签: java coordinates android-view


    【解决方案1】:

    假设您有以下具有明确含义的变量: screenWidthscreenHeightminLonmaxLonminLatmaxLat。 您想要的是 lonlat 到屏幕坐标 xy 的映射,这样宽度或高度都被填充,但圆圈仍然是圆圈(距离的水平和垂直比例相同)。

    经纬度差的距离之比几乎是纬度的余弦值。 “几乎”只是因为地球不是一个完美的球体。余弦在纬度 0° 处为 1,因此,例如,在赤道,经度和纬度的相同差异对应于相同的距离。对于“足够”小区域的地图,可以使用平均纬度来计算比例。

    如果您只有水平约束,则必须使x==0 对应于minLonx==screenWidth-1 对应于maxLon。其线性变换(即映射)将是:

    x = lon * lonScale + x0;
    

    deltaLon = maxLon - minLon;
    lonScale = (screenWidth - 1) / deltaLon;
    x0 = - minLon * lonScale;
    

    同样,如果你只有一个垂直约束:

    y = y0 - lat * latScale; // i.e.: lat * (-latScale) + y0
    

    deltaLat = maxLat - minLat;
    latScale = (screenHeight - 1) / deltaLat;
    y0 = maxLat * latScale;
    

    (减号是因为屏幕坐标的原点位于左上角。)

    现在,如果deltaLon == 0.0deltaLat == 0.0,您不想除以0,而只想将有意义的映射应用于您正在处理的直线。如果两者都是!= 0.0,你想在latScalelonScale之间选择较小的一个,记住它们的比率是平均纬度的余弦:

    lonScale == latScale * Math.cos((maxLat + minLat) / 2.0)
    

    所以:

    avgLat = (maxLat + minLat) / 2.0;
    cosFactor = Math.cos(avgLat);
    if (deltaLon != 0.0 && deltaLat != 0.0) {
        if (lonScale > latScale * cosFactor) {
            lonScale = latScale * cosFactor;
            x0 = ((screenWidth - 1) - (minLon + maxLon) * lonScale) / 2.0;
        } else {
            latScale = lonScale / cosFactor;
            y0 = ((screenHeight - 1) + (minLat + maxLat) * latScale) / 2.0;
        }
    }
    

    其中x0y0 已通过求解将(maxL* - minL*) / 2.0 置于屏幕中间的方程进行了调整(*onat)。


    编辑

    根据您的评论,您不使用真正的经度和纬度值,而只是使用东到西和南到北方向的距离。这有点像在赤道,余弦因子变为1。我将继续调用输入坐标lonlat,但它们可以用任何长度单位表示,只要它是相同的单位两者兼得。

    所以计算变成:

    deltaLon = maxLon - minLon;
    deltaLat = maxLat - minLat;
    if (deltaLon != 0.0 && deltaLat != 0.0) {
        lonScale = (screenWidth - 1) / deltaLon;
        latScale = (screenHeight - 1) / deltaLat;
        x0 = - minLon * lonScale;
        y0 = maxLat * latScale;
        if (lonScale > latScale) {
            lonScale = latScale;
            x0 = ((screenWidth - 1) - (minLon + maxLon) * lonScale) / 2.0;
        } else {
            latScale = lonScale;
            y0 = ((screenHeight - 1) + (minLat + maxLat) * latScale) / 2.0;
        }
    } else if (deltaLon != 0.0) {
        lonScale = (screenWidth - 1) / deltaLon;
        latScale = 0;
        x0 = - minLon * lonScale;
        y0 = (screenHeight - 1) / 2.0;
    } else if (deltaLat != 0.0) {
        lonScale = 0;
        latScale = (screenHeight - 1) / deltaLat;
        x0 = (screenWidth - 1) / 2.0;
        y0 = maxLat * latScale;
    } else {
        lonScale = 0;
        latScale = 0;
        x0 = (screenHeight - 1) / 2.0;
        y0 = (screenWidth - 1) / 2.0;
    }
    

    屏幕值的计算与一般情况完全相同,即:

    x = x0 + lon * lonScale;
    y = y0 - lat * latScale;
    

    【讨论】:

    • 如果你愿意,我可以编写一个完整的类,用于从地理坐标到屏幕坐标的转换(给定 screenWidth、screenHeight、minLon、maxLon、minLat、maxLat)。但是由于我会将其添加到答案中,因此我现在避免这样做,以免答案太长
    • 首先非常感谢您的详细解答!但我不得不说这对于我的问题来说有点太复杂了。让我们假设,我没有纬度/经度值,而是具有相同比率的简单 x 和 y 坐标,因此 x 方向上的 1m 等于 y 方向上的 1m。我试图采用您的解决方案,但我的 x 坐标值很奇怪。你能相应地简化你的答案吗?非常感谢! :)
    • 好的@Apocabal,但是你的输入y坐标是向上还是向下增加?向上就像纬度和“正常”坐标系,而向下就像标准屏幕坐标(这是我猜你的目标)。两者中的哪一个?
    • 顺便说一句,希望你在计算x0时没有忽略-(减号)。
    • 我的 y 坐标向上增加。所以y的最大值应该显示在视图的顶部。我没有忽略减号。我尝试了有无它,但无论哪种方式我都得到了奇怪的结果。
    【解决方案2】:

    我会定义一个最大纵横比。例如:

    min_aspect_ratio = 5; // Minimum aspect ratio of 5:1
    x_scale = screen_width/max_x_diff;
    y_scale = screen_height/max_y_diff;
    
    if(x_scale>y_scale)
        if (x_scale/y_scale < min_aspect_ratio)
            y_scale = x_scale/min_aspect_ratio;
    else
        if (y_scale/x_scale < min_aspect_ratio)
            x_scale = y_scale/min_aspect_ratio;
    

    免责声明:我没有测试此代码。

    【讨论】:

      猜你喜欢
      • 2018-08-01
      • 2021-04-07
      • 2012-04-17
      • 2019-10-03
      • 1970-01-01
      • 1970-01-01
      • 2016-03-24
      • 1970-01-01
      • 2012-08-25
      相关资源
      最近更新 更多