世界风点标记航向

9
在NASA WorldWind Java中,我正在使用PointPlacemark来表示图像,因为它在缩放级别不同的情况下保持相同的大小。问题是,我想设置Point Placemark的航向,并使其保持在该罗盘航向上,即使相机被倾斜也是如此。当查看未倾斜的地球时,它的工作方式与我想要的完全相同,但是当我倾斜时,标记继续面向屏幕而不是随着地球倾斜,这会导致它表现奇怪。
这里有一个GIF说明我看到的情况: https://giphy.com/embed/3o7WIqZUceR8xh6BOg 我希望Point Placemark图像相对于地球保持一个航向,即使倾斜也是如此--因此,随着视图的倾斜,图像实质上被"平铺",同时仍然保持相同的大小,无论缩放级别如何。
这是我正在使用的代码片段。我在相关的PointPlacemarkAttributes上设置attrs.setHeadingReference(AVKey.RELATIVE_TO_GLOBE); 在这个例子中,我将方向设置为135度。
import gov.nasa.worldwind.WorldWind;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.geom.Position;
import gov.nasa.worldwind.layers.RenderableLayer;
import gov.nasa.worldwind.render.Offset;
import gov.nasa.worldwind.render.PointPlacemark;
import gov.nasa.worldwind.render.PointPlacemarkAttributes;

public class Placemarks extends ApplicationTemplate {
    public static class AppFrame extends ApplicationTemplate.AppFrame {

        public AppFrame() {
            super(true, true, false);

            final RenderableLayer layer = new RenderableLayer();

            PointPlacemark pp = new PointPlacemark(Position.fromDegrees(28, -102, 30000));
            pp.setLabelText("Airplane");
            pp.setLineEnabled(false);
            pp.setAltitudeMode(WorldWind.ABSOLUTE);
            PointPlacemarkAttributes attrs = new PointPlacemarkAttributes();
            attrs.setImageAddress("images/airplane.png");
            attrs.setScale(0.05);
            attrs.setImageOffset(Offset.CENTER);

            //Point to 135.0
            attrs.setHeading(135.0);
            attrs.setHeadingReference(AVKey.RELATIVE_TO_GLOBE);

            pp.setAttributes(attrs);

            layer.addRenderable(pp);

            // Add the layer to the model.
            insertBeforeCompass(getWwd(), layer);
        }
    }

    public static void main(String[] args) {
        ApplicationTemplate.start("WorldWind Placemarks", AppFrame.class);
    }

}

我也尝试过使用带有纹理的多边形。它的定位方式是我想要的,但我希望图标的大小不随缩放级别而改变(就像PointPlacemark所做的那样)。
这是一个GIF,说明了我在使用多边形时看到的情况。请注意,当地球倾斜时它的行为方式:https://giphy.com/embed/xThta4USlDzd8Ii5ZS 这是我用于多边形的源代码:
import java.awt.geom.AffineTransform;
import java.util.Arrays;
import java.util.List;

import gov.nasa.worldwind.WorldWind;
import gov.nasa.worldwind.geom.Position;
import gov.nasa.worldwind.layers.RenderableLayer;
import gov.nasa.worldwind.render.BasicShapeAttributes;
import gov.nasa.worldwind.render.Polygon;

public class TexturedPolygon extends ApplicationTemplate {

    public static Polygon createPolygonTexturedImage(String filePath, Position pos, double heading, double scale) {

        double offsetDist = 1.0D * scale;

        Position p1 = Position.fromDegrees(pos.getLatitude().addDegrees(-offsetDist).getDegrees(),
                pos.getLongitude().addDegrees(-offsetDist).getDegrees(), pos.getAltitude());
        Position p2 = Position.fromDegrees(pos.getLatitude().addDegrees(offsetDist).getDegrees(),
                pos.getLongitude().addDegrees(-offsetDist).getDegrees());
        Position p3 = Position.fromDegrees(pos.getLatitude().addDegrees(offsetDist).getDegrees(),
                pos.getLongitude().addDegrees(offsetDist).getDegrees());
        Position p4 = Position.fromDegrees(pos.getLatitude().addDegrees(-offsetDist).getDegrees(),
                pos.getLongitude().addDegrees(offsetDist).getDegrees());

        double[] points = new double[] { p1.getLatitude().getDegrees(), p1.getLongitude().getDegrees(),
                p2.getLatitude().getDegrees(), p2.getLongitude().getDegrees(), p3.getLatitude().getDegrees(),
                p3.getLongitude().getDegrees(), p4.getLatitude().getDegrees(), p4.getLongitude().getDegrees() };

        double[] transformedPoints = new double[8];
        AffineTransform rotation = new AffineTransform();
        rotation.rotate(Math.toRadians(heading), pos.getLatitude().getDegrees(), pos.getLongitude().getDegrees());
        rotation.transform(points, 0, transformedPoints, 0, 4);

        double altitude = pos.getAltitude();
        p1 = Position.fromDegrees(transformedPoints[0], transformedPoints[1], altitude);
        p2 = Position.fromDegrees(transformedPoints[2], transformedPoints[3], altitude);
        p3 = Position.fromDegrees(transformedPoints[4], transformedPoints[5], altitude);
        p4 = Position.fromDegrees(transformedPoints[6], transformedPoints[7], altitude);

        List<Position> positions = Arrays.asList(p1, p2, p3, p4);
        Polygon polygon = new Polygon(positions);
        polygon.setAltitudeMode(WorldWind.ABSOLUTE);

        BasicShapeAttributes mattr = new BasicShapeAttributes();
        mattr.setDrawOutline(false);
        mattr.setDrawInterior(true);
        polygon.setAttributes(mattr);
        polygon.setTextureImageSource(filePath, new float[] { 0.0F, 0.0F, 1.0F, 0.0F, 1.0F, 1.0F, 0.0F, 1.0F }, 4);

        return polygon;
    }

    public static class AppFrame extends ApplicationTemplate.AppFrame {
        public AppFrame() {
            super(true, true, false);

            final RenderableLayer layer = new RenderableLayer();

            Position pos = Position.fromDegrees(28, -102, 30000);
            String url = "images/airplane.png";

            layer.addRenderable(createPolygonTexturedImage(url, pos, 135.0, 1.05));

            // Add the layer to the model.
            insertBeforeCompass(getWwd(), layer);
        }
    }

    public static void main(String[] args) {
        ApplicationTemplate.start("WorldWind Placemarks", AppFrame.class);
    }

}

为了完整起见 -- 这是我用作飞机.png的图像:

总的来说,我需要以下内容:

  • 一个由图标图片表示的可渲染对象
  • 无论缩放级别如何,图标大小保持不变
  • 即使相机视图倾斜,图标方向仍然与地球罗盘方向一致

一个大致的图示或图像可能会让你想要的东西更加清晰。你试过使用 setPitch() 吗?如果你能确定一个切平面相对于地球上地标所在点的夹角和地标角度之间的差异,那么你可以将该差异应用于 setPitch() - Aflah Bhari
谢谢您的回复--我添加了一些GIF动画来说明我的问题,并对其进行了重新调整。我尝试了一下setPitch,但我不清楚它是如何工作的,或者它是否有助于我想要做的事情。 - mainstringargs
2个回答

7
通过将此问题的解决方案与将屏幕倾斜与俯仰相关联的指南针图层逻辑相结合。
请将此方法添加到PointPlacemark.java中(从CompassLayer中获取):
protected double computePitch(View view)
{
    if (view == null)
        return 0.0;

    if (!(view instanceof OrbitView))
        return 0.0;

    OrbitView orbitView = (OrbitView) view;
    return orbitView.getPitch().getDegrees();
}

在doDrawOrderedRenderable(DrawContext dc, PickSupport pickCandidates, OrderedPlacemark opm)方法中,使用以下逻辑:
protected void doDrawOrderedRenderable(DrawContext dc, PickSupport pickCandidates, OrderedPlacemark opm)
{
    if (this.isDrawLine(dc, opm))
        this.drawLine(dc, pickCandidates, opm);

    if (this.activeTexture == null)
    {
        if (this.isDrawPoint(dc))
            this.drawPoint(dc, pickCandidates, opm);
        return;
    }

    GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.

    OGLStackHandler osh = new OGLStackHandler();
    try
    {
        if (dc.isPickingMode())
        {
            // Set up to replace the non-transparent texture colors with the single pick color.
            gl.glEnable(GL.GL_TEXTURE_2D);
            gl.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_COMBINE);
            gl.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_SRC0_RGB, GL2.GL_PREVIOUS);
            gl.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_COMBINE_RGB, GL2.GL_REPLACE);

            Color pickColor = dc.getUniquePickColor();
            pickCandidates.addPickableObject(this.createPickedObject(dc, pickColor));
            gl.glColor3ub((byte) pickColor.getRed(), (byte) pickColor.getGreen(), (byte) pickColor.getBlue());
        }
        else
        {
            gl.glEnable(GL.GL_TEXTURE_2D);
            Color color = this.getActiveAttributes().getImageColor();
            if (color == null)
                color = PointPlacemarkAttributes.DEFAULT_IMAGE_COLOR;
            gl.glColor4ub((byte) color.getRed(), (byte) color.getGreen(), (byte) color.getBlue(),
                (byte) color.getAlpha());
        }

        // This was relocated from the check in version.
        // Compute the scale
        double xscale;
        Double scale = this.getActiveAttributes().getScale();
        if (scale != null)
            xscale = scale * this.activeTexture.getWidth(dc);
        else
            xscale = this.activeTexture.getWidth(dc);

        double yscale;
        if (scale != null)
            yscale = scale * this.activeTexture.getHeight(dc);
        else
            yscale = this.activeTexture.getHeight(dc);
        double maxwh = Math.max(xscale, yscale);

        // The image is drawn using a parallel projection.
        // This came from the fix in https://dev59.com/uarka4cB1Zd3GeqPb1OS
        osh.pushProjectionIdentity(gl);
        gl.glOrtho(0d, dc.getView().getViewport().width, 0d, dc.getView().getViewport().height, -0.6 * maxwh, 0.6 * maxwh);

        // Apply the depth buffer but don't change it (for screen-space shapes).
        if ((!dc.isDeepPickingEnabled()))
            gl.glEnable(GL.GL_DEPTH_TEST);
        gl.glDepthMask(false);

        // Suppress any fully transparent image pixels.
        gl.glEnable(GL2.GL_ALPHA_TEST);
        gl.glAlphaFunc(GL2.GL_GREATER, 0.001f);

        // Adjust depth of image to bring it slightly forward
        double depth = opm.screenPoint.z - (8d * 0.00048875809d);
        depth = depth < 0d ? 0d : (depth > 1d ? 1d : depth);
        gl.glDepthFunc(GL.GL_LESS);
        gl.glDepthRange(depth, depth);

        // The image is drawn using a translated and scaled unit quad.
        // Translate to screen point and adjust to align hot spot.
        osh.pushModelviewIdentity(gl);
        gl.glTranslated(opm.screenPoint.x + this.dx, opm.screenPoint.y + this.dy, 0);

        Double heading = getActiveAttributes().getHeading();
        Double pitch =          this.computePitch(dc.getView());

        // Adjust heading to be relative to globe or screen
        if (heading != null)
        {
            if (AVKey.RELATIVE_TO_GLOBE.equals(this.getActiveAttributes().getHeadingReference()))
                heading = dc.getView().getHeading().degrees - heading;
            else
                heading = -heading;
        }

        // Apply the heading and pitch if specified.
        if (heading != null || pitch != null)
        {
            gl.glTranslated(xscale / 2, yscale / 2, 0);
            if (pitch != null)
                gl.glRotated(pitch, 1, 0, 0);
            if (heading != null)
                gl.glRotated(heading, 0, 0, 1);
            gl.glTranslated(-xscale / 2, -yscale / 2, 0);
        }

        // Scale the unit quad
        gl.glScaled(xscale, yscale, 1);

        if (this.activeTexture.bind(dc))
            dc.drawUnitQuad(activeTexture.getTexCoords());

        gl.glDepthRange(0, 1); // reset depth range to the OGL default

        if (this.mustDrawLabel())
        {
            if (!dc.isPickingMode() || this.isEnableLabelPicking())
                this.drawLabel(dc, pickCandidates, opm);
        }
    }
    finally
    {
        if (dc.isPickingMode())
        {
            gl.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, OGLUtil.DEFAULT_TEX_ENV_MODE);
            gl.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_SRC0_RGB, OGLUtil.DEFAULT_SRC0_RGB);
            gl.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_COMBINE_RGB, OGLUtil.DEFAULT_COMBINE_RGB);
        }

        gl.glDisable(GL.GL_TEXTURE_2D);
        osh.pop(gl);
    }
}

它会看起来像这样:

enter image description here


1
感谢您的关注。我同意指南针的行为方式正是我所需要的。我在WorldWind论坛上发布了帖子,希望他们能提供帮助:https://forum.worldwindcentral.com/forum/world-wind-java-forums/development-help/158255-pointplacemark-heading-pitch - mainstringargs
所以在另一个stackoverflow用户的帮助下,音高问题已经得到解决:https://dev59.com/uarka4cB1Zd3GeqPb1OS#49756209 我认为通过这个修复,将其与屏幕的音高绑定就会变得容易,就像指南针的工作原理一样。我会去看看的。 - mainstringargs

5

您想要实现的是根据相机的眼睛位置缩放多边形,并保持多边形在地图上的方向。

您可以尝试更新第二种解决方案并添加RenderingListener以在渲染之前更新多边形的大小:

wwd.addRenderingListener(new RenderingListener()
{
    public void stageChanged(RenderingEvent event)
    {
        if (RenderingEvent.BEFORE_RENDERING.equals(event.getStage())
        {
             if (wwd.getView() != null && wwd.getView().getEyePosition() != null) {
                 // compute distance between eyePosition and object position, and set the scale.
             }

        }
    }
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接