Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

There is no possibility to get canvas coordinates of some geographic location #28

Closed
ComBatVision opened this issue Apr 18, 2019 · 3 comments · Fixed by #31
Closed

There is no possibility to get canvas coordinates of some geographic location #28

ComBatVision opened this issue Apr 18, 2019 · 3 comments · Fixed by #31
Assignees

Comments

@ComBatVision
Copy link
Member

There is no possibility to get canvas coordinates of some geographic location - oposite procedure to picking of some location by canvas coordinates.

This functionality is required when somebody requires to move some HTML UI elements according to Position on globe determining its pixel coordinates.

Android version has this functionality. So I propose to port it on JS in the following way...

  1. Add project function in Matrix:
        /**
         * Projects a Cartesian point to screen coordinates. This method assumes this matrix represents an inverse
         * modelview-projection matrix. The result of this method is undefined if this matrix is not an inverse
         * modelview-projection matrix.
         * <p/>
         * The resultant screen point is in OpenGL screen coordinates, with the origin in the bottom-left corner and axes
         * that extend up and to the right from the origin.
         * <p/>
         * This stores the projected point in the result argument, and returns a boolean value indicating whether or not the
         * projection is successful. This returns false if the Cartesian point is clipped by the near clipping plane or the
         * far clipping plane.
         *
         * @param {Number}    x the Cartesian point's X component
         * @param {Number}    y the Cartesian point's y component
         * @param {Number}    z the Cartesian point's z component
         * @param {Rectangle} viewport the viewport defining the screen point's coordinate system
         * @param {Vec3}    result a pre-allocated {@link Vec3} in which to return the projected point
         *
         * @return {boolean} true if the transformation is successful, otherwise false
         *
         * @throws {ArgumentError} If any argument is null
         */
        Matrix.prototype.project = function (x, y, z, viewport, result) {
            if (!viewport) {
                throw new ArgumentError(Logger.logMessage(Logger.ERROR, "Matrix", "project",
                    "missingViewport"));
            }

            if (!result) {
                throw new ArgumentError(Logger.logMessage(Logger.ERROR, "Matrix", "project",
                    "missingResult"));
            }

            // Transform the model point from model coordinates to eye coordinates then to clip coordinates. This inverts
            // the Z axis and stores the negative of the eye coordinate Z value in the W coordinate.
            var sx = this[0] * x + this[1] * y + this[2] * z + this[3];
            var sy = this[4] * x + this[5] * y + this[6] * z + this[7];
            var sz = this[8] * x + this[9] * y + this[10] * z + this[11];
            var sw = this[12] * x + this[13] * y + this[14] * z + this[15];

            if (sw === 0) {
                return false;
            }

            // Complete the conversion from model coordinates to clip coordinates by dividing by W. The resultant X, Y
            // and Z coordinates are in the range [-1,1].
            sx /= sw;
            sy /= sw;
            sz /= sw;

            // Clip the point against the near and far clip planes.
            if (sz < -1 || sz > 1) {
                return false;
            }

            // Convert the point from clip coordinate to the range [0,1]. This enables the X and Y coordinates to be
            // converted to screen coordinates, and the Z coordinate to represent a depth value in the range[0,1].
            sx = sx * 0.5 + 0.5;
            sy = sy * 0.5 + 0.5;
            sz = sz * 0.5 + 0.5;

            // Convert the X and Y coordinates from the range [0,1] to screen coordinates.
            sx = sx * viewport.width + viewport.x;
            sy = sy * viewport.height + viewport.y;

            result[0] = sx;
            result[1] = sy;
            result[2] = sz;

            return true;
        };
  1. Add scratch point to WorldWindow attributes:
            // Internal. Intentionally not documented.
            this.scratchPoint = new Vec3(0, 0, 0);
  1. Add geograpficToScreen and cartesianToScreen functions to WorldWindow:
        /**
         * Transforms a Cartesian coordinate point to coordinates relative to this WorldWindow's canvas.
         * <p/>
         * This stores the converted point in the result argument, and returns a boolean value indicating whether or not the
         * converted is successful. This returns false if the Cartesian point is clipped by either the WorldWindow's near
         * clipping plane or far clipping plane.
         *
         * @param {Number} x      the Cartesian point's x component in meters
         * @param {Number} y      the Cartesian point's y component in meters
         * @param {Number} z      the Cartesian point's z component in meters
         * @param {Vec2}   result a pre-allocated {@link Vec2} in which to return the screen point
         *
         * @return {boolean} true if the transformation is successful, otherwise false
         *
         * @throws {ArgumentError} If the result is null
         */
        WorldWindow.prototype.cartesianToScreenPoint = function (x, y, z, result) {
            if (!result) {
                throw new ArgumentError(Logger.logMessage(Logger.ERROR, "WorldWindow", "cartesianToScreenPoint",
                    "missingResult"));
            }

            // Compute the WorldWindow's modelview-projection matrix.
            this.computeViewingTransform(this.scratchProjection, this.scratchModelview);
            this.scratchProjection.multiplyMatrix(this.scratchModelview);

            // Transform the Cartesian point to OpenGL screen coordinates. Complete the transformation by converting to
            // Android screen coordinates and discarding the screen Z component.
            if (this.scratchProjection.project(x, y, z, this.viewport, this.scratchPoint)) {
                result[0] = this.scratchPoint[0];
                result[1] = this.viewport.height - this.scratchPoint[1];
                return true;
            }

            return false;
        };

        /**
         * Transforms a geographic position to coordinates relative to this WorldWindow's canvas.
         * <p/>
         * This stores the converted point in the result argument, and returns a boolean value indicating whether or not the
         * converted is successful. This returns false if the Cartesian point is clipped by either of the WorldWindow's
         * near clipping plane or far clipping plane.
         *
         * @param {Number} latitude  the position's latitude in degrees
         * @param {Number} longitude the position's longitude in degrees
         * @param {Number} altitude  the position's altitude in meters
         * @param {Vec2}   result    a pre-allocated {@link Vec2} in which to return the screen point
         *
         * @return {boolean} true if the transformation is successful, otherwise false
         *
         * @throws {ArgumentError} If the result is null
         */
        WorldWindow.prototype.geographicToScreenPoint = function (latitude, longitude, altitude, result) {
            if (!result) {
                throw new ArgumentError(Logger.logMessage(Logger.ERROR, "WorldWindow", "geographicToScreenPoint",
                    "missingResult"));
            }

            // Convert the position from geographic coordinates to Cartesian coordinates.
            this.globe.computePointFromPosition(latitude, longitude, altitude, this.scratchPoint);

            // Convert the position from Cartesian coordinates to screen coordinates.
            return this.cartesianToScreenPoint(this.scratchPoint[0], this.scratchPoint[1], this.scratchPoint[2], result);
        };
@monkeytroy
Copy link

I am using this geographicToScreenPoint function but I am trying to get the screen coords for a point that I think is getting clipped by the near clip plane. Is there any way to ignore the clip plane to get the x,y anyway? Or translate it to the x,y coord on the near plane? Maybe using a ray? Havent worked that out yet.

I'm drawing a line on a canvas representing the same view as my viewport. Using the lat, lon end points of the line, I grab the screen x,y from geographicToScreenPoint and render the lines (though the line ends are off the canvas...) it all lines up and works well... until I tilt the viewport and zoom in. It works to a point, then the lat, lon point of one end is behind the eye position enough that I think it's being clipped and the function returns false. Any thoughts or way to support this in this function?

@ComBatVision
Copy link
Member Author

ComBatVision commented Feb 3, 2021

@monkeytroy geographicToScreenPoint function uses cartesianToScreenPoint function which has following description:
This returns false if the Cartesian point is clipped by either the WorldWindow's near clipping plane or far clipping plane.

So the only way is to override cartesianToScreenPoint logic to be able to calculate points outside frustum.
I think Matrix.project is not able to do this.

I think those people who write cartesianToScreenPoint function faced issue with universal calculation and that's why added boolean result.

@monkeytroy
Copy link

Ok. I was able to get what I needed by:

  • Use computePointFromPosition on both ends of my line to give me v1 and v2;
  • Use the clip function on the near clipping plane worldWindow.drawContext.frustumInModelCoordinates.near(v1, v2) to clip the line so it does not extend beyond the near clipping plane.
  • Then project.. though I still had to override it. The clipped vector is sometimes right on the plane and the project still will not calculate it. I overrode project to remove the (z < -1 || z > 1) check and now it works all the time.

Just posting in case someone else runs into this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants