Cesium如何在放大/缩小时缩放多边形以匹配Lat-Lon位置 [英] Cesium how to scale a polygon to match Lat-Lon positions while zoom-in/zoom-out

查看:1608
本文介绍了Cesium如何在放大/缩小时缩放多边形以匹配Lat-Lon位置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在寻找一位Cesium Guru,以帮助我找到我需要的东西。我是Cesium的新手,但我一直在使用我继承的教程和一些现有代码。

I'm looking for a Cesium Guru for a little help finding what I need. I'm new to Cesium but I've been working with the tutorials and some existing code that I've inherited.

在我的Cesium应用程序中,我输入我的地址,视图放大到我的街道。好极了!然后我放大了,所以我可以在我的房子周围绘制一个多边形。现有代码非常好。然而当我缩小然后再放大时,我的多边形不能保持我房子的Lat-Lon位置

In my Cesium app, I enter my address and the view zooms in to my street. Yay! Then I zoom in closer, so I can draw a polygon around my house. The existing code does this very well. However when I zoom out and then zoom in again, my polygon does not stay true to the Lat-Lon position of my house.


Cesium是否包含将像素缩放到lat-lon坐标的实用程序或者我是否需要使用
distanceToBoundingSphere(boundingSphere)之类的东西并自行计算?我
只想要x,y坐标;我根本不关心身高。

Does Cesium contain a utility to scale pixels to lat-lon coordinates or do I need to use something like distanceToBoundingSphere(boundingSphere) and calculate it myself? I only want the x,y coordinates; I don't care about height at all.

我一直在寻找演示和教程,到目前为止还没找到我认为我在寻找什么。也许我已经发现了一些接近但我还不知道是否已经找到它。帮助!

I have been looking around in the demos and tutorials and so far haven't found what I think I'm looking for. Maybe I have found something close but I don't know enough yet to know whether I've found it or not. Help!

============================ CODE = =================================

收集多边形的位置:

singleClick仅捕获该点的坐标,并在用户将鼠标拖动到新点时绘制折线。因此,一堆折线和每个点的坐标集合。

    positionHandler.setInputAction(function (click) {
        cartesian = scene.camera.pickEllipsoid(click.position, ellipsoid);
        if (cartesian) {
            var setCartographic = ellipsoid.cartesianToCartographic(cartesian);
            asset.latlonalt.push(
                Cesium.Math.toDegrees(setCartographic.latitude).toFixed(15),
                Cesium.Math.toDegrees(setCartographic.longitude).toFixed(15),
                Cesium.Math.toDegrees(setCartographic.height).toFixed(15)
            );
            lla.push(Cesium.Math.toDegrees(setCartographic.longitude), Cesium.Math.toDegrees(setCartographic.latitude));
            if (lla.length >= 4) {
                self.loggingMessage((lla.length / 2) + ' Points Added');
            }
            Cesium.sampleTerrain(terrainProvider, 11, [cartographic])
                .then(function (updatedPositions) {
                    asset.latlonalt[2] = updatedPositions[0].height;
                    stage = 1;
                });
        }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

然后doubleClick获取在singleClick函数中捕获的坐标并调用self.createAsset('添加',asset)来创建多边形。

    positionHandler.setInputAction(function (doubleClick){
        if (asset.shape == 'Polygon') {
            var len = asset.latlonalt.length;
            if(len > 9) {
                asset.rad = (len / 3);
                console.log("Creating Asset");
                self.loggingMessage("Creating Asset");
                socket.emit('newElement', asset.cType, asset);
                self.createAsset('add', asset);
                viewer.entities.remove(entity);
                viewer.entities.remove(newCircle);
                viewer.entities.remove(newPolygon);
                viewer.entities.remove(newOutline);
                positionHandler = positionHandler && positionHandler.destroy();
            }else{
                console.log('3+ Positions Required');
                loggingMessage('3+ Positions Required.');
            }
        }
    }, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK)

创建多边形:

            var newPolygon = viewer.entities.add({
                name : asset.id,
                polygon : {
                    hierarchy : Cesium.Cartesian3.fromDegreesArray(vertices),
                    material : rgba[0],
                    outline : true,
                    outlineColor : rgba[1]
                }
            });
            var newLabel = viewer.entities.add({
                position: Cesium.Cartesian3.fromDegrees(asset.latlonalt[1], asset.latlonalt[0], 1000),
                name: asset.id,
                label: {
                    text: asset.name,
                    font : '16px Helvetica'
                }
            });
            var newPoint = viewer.entities.add({
                position: Cesium.Cartesian3.fromDegrees(asset.latlonalt[1], asset.latlonalt[0], 0),
                name: asset.id,
                point : {
                pixelSize : 5,
                    color : Cesium.Color.RED,
                    outlineColor : Cesium.Color.RED,
                    outlineWidth : 2
                }
            });
            self.currentGeometry[asset.id] = {shape: newPolygon, label: newLabel, point: newPoint};

看起来我们正在使用Terrain(我认为)。

It looks like we're using Terrain (I think).

我应该对哪些数字感兴趣:

Which numbers should I be interested in:

收集坐标时,只有第一个z不是零:

When the coordinates are collected, only the first z is not zeros:

我取这个值并填充其他z值:

I take that value and populated the other z values:

现在我已经添加了z值,在createAsset方法中出现错误。我需要跟踪该问题以查看结果。现在,它看起来像这样:

Now that I have added the z values something is erroring out in the createAsset method. I need to track that problem down to see the results. Right now, it looks like this:

真的很大,轮廓也没有被删除。

REALLY BIG and the outlines are not getting removed.

推荐答案

一个问题还有很多,但我会尝试解决了这个问题的关键部分。

There's a lot going on for one question, but I'll try to address the key parts of this.

首先, Cartesian3 类本身。在内部,构造之后,此类包含 x,y,z 成员,其中包含 314923.1 等值。您应该将这些视为黑盒,不透明的值。事实上,它们代表了从行星中心开始的笛卡尔位置,以米为单位,这是渲染引擎所需要的,但通常对人类制图师无用。要理解的关键是 z 将始终使用实际值填充,这并不意味着在创建值时会考虑高度或不考虑高度。

First, the Cartesian3 class itself. Internally, after construction, this class contains x, y, z members containing values like 314923.1. You should probably think of these as black-box, opaque values. In fact they represent a Cartesian position, in meters, from the center of the planet, which is needed by the rendering engine but not normally useful to human cartographers. The key thing to understand is that z will always be populated with a real value, and this does NOT imply that height was taken into account or not when creating the value.

有一个单独的类 制图 ,包含熟悉的经度(弧度),纬度(弧度)和海拔高度(以米为单位)。通常这些必须转换为 Cartesian3 ,然后才能传递给渲染引擎。此转换需要了解 Ellipsoid (默认为WGS84椭圆体),因此,零高度值表示坐在该椭球上的一个点(通常意味着在打开地形时该点位于地下)。

There is a separate class Cartographic that contains the familiar Longitude (Radians), Latitude (Radians), and Altitude (in meters) values. Generally these must be converted to Cartesian3 before being handed off to the rendering engine. This conversion requires knowledge of the Ellipsoid (which defaults to the WGS84 ellipsoid), and as such, a zero-altitude value indicates a point sitting on that ellipsoid (typically meaning the point is underground when terrain is turned on).

各种辅助函数采用常用值(例如lon / lat(以度为单位))并转换为这两种格式中的任何一种。其中一些辅助函数不包括高度参数,其他包括它。这两个类的文档列举了这些帮助器。

An assortment of helper functions take common values (such as lon/lat in degrees) and convert to either of these two formats. Some of these helper functions leave off the altitude parameter, others include it. The documentation for these two classes enumerates these helpers.

当关闭地形时,从鼠标单击获取精确的lon / lat很简单,但是当它打开时更复杂,由于使用了透视3D相机。没有地形,您可以像上面第一个代码示例中那样调用 scene.camera.pickEllipsoid ,并获得确切的位置。但是当地形开启时,即使点击平原也会计算错误的坐标,找到你正在看的地形下方和后方的地下点。

Getting the exact lon/lat from a mouse click is simple when terrain is turned off, but more complex when it's on, due to the use of the perspective 3D camera. Without terrain, you can just call scene.camera.pickEllipsoid as you've done in the first code sample above, and get the exact spot. But when terrain is on, even a click on a flat plain will calculate the wrong coordinates, finding an underground spot below and behind the terrain you're looking at.

A随意搜索没有找到正确的代码,但黄金标准似乎是在这里烘焙到现有的相机控制器。它看起来像这样:

A casual search doesn't turn up the correct code for this, but the gold standard appears to be baked into the existing camera controller here. It looks like this:

    var depthIntersection;
    if (scene.pickPositionSupported) {
        depthIntersection = scene.pickPosition(mousePosition, scratchDepthIntersection);
    }

    var ray = camera.getPickRay(mousePosition, pickGlobeScratchRay);
    var rayIntersection = globe.pick(ray, scene, scratchRayIntersection);

    var pickDistance = defined(depthIntersection) ?
        Cartesian3.distance(depthIntersection, camera.positionWC) :
        Number.POSITIVE_INFINITY;
    var rayDistance = defined(rayIntersection) ?
        Cartesian3.distance(rayIntersection, camera.positionWC) :
        Number.POSITIVE_INFINITY;

    if (pickDistance < rayDistance) {
        return Cartesian3.clone(depthIntersection, result);
    }

    return Cartesian3.clone(rayIntersection, result);

此代码尝试采用双管齐下的方法:它尝试像以前一样选择椭圆体,并且也尝试从深度缓冲区中进行选择,深度缓冲区是3D图形系统的一部分,它允许Cesium在渲染时检查多边形距离相机的距离。两个结果进行了比较,并且更接近相机的人被宣布为获胜者。这样就完全不需要 sampleTerrain 调用了,因为鼠标位置已用于直接在渲染多边形的空间中选取一个笛卡尔点(这可能是地形,但它甚至可能是建筑物的顶部等。)

This code is attempting a two-pronged approach: It tries to pick the ellipsoid, as before, and is also attempts to pick from the "depth buffer", which is part of the 3D graphics system that allows Cesium to inspect how far polygons were from the camera when rendered. The two results are compared, and whichever is closer to the camera is declared the winner. This avoids the need for a sampleTerrain call at all, because the mouse location has been used to directly pick a Cartesian point in space where a polygon was rendered (which is likely terrain, but could even be the top of a building, etc).

在下一个代码块中,您使用 asset.latlonalt 填充 lon lat ,但是你有 alt 硬编码为 0 1000 而不是来自相同的数据结构。这可能是高度信息丢失的地方,如果它是从那里开始的(如果你只是选择椭球本身,它不是,虽然它可能是由 sampleTerrain 稍后。请注意,由于从服务器加载地形图块, sampleTerrain 是异步的。如果您决定尝试深度采摘技术,那将直接产生 Cartesian3 ,因此您不必担心转换它或保持高度等。

In your next code block, you use asset.latlonalt to populate the lon and the lat, but then you have alt hard-coded to 0 or 1000 rather than coming from the same data structure. This may be where the altitude information is getting lost, if it was there to begin with (which it is not, if you're just picking the ellipsoid itself, although it may be being added by sampleTerrain later. Beware that sampleTerrain is asynchronous due to terrain tiles being loaded from the server). If you decide to try the depth-picking technique, that will yield Cartesian3 directly, so you won't have to worry about converting it or preserving altitude etc.

最后一条评论,最近版本的Cesium确实支持多边形以覆盖地形。这不会让你不需要选择正确的lon / lat开始(考虑透视地形),但是会允许多边形放置在块状表面上而不会穿过。

One last comment, recent versions of Cesium do support a GroundPrimitive that allows polygons to drape on terrain. This won't save you from the need to "pick" the right lon/lat to begin with (taking perspective terrain into account), but will allow a polygon to lay on a lumpy surface and not stick through.

这篇关于Cesium如何在放大/缩小时缩放多边形以匹配Lat-Lon位置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆