SuperMap组件开发二三维联动的坐标转换问题

SuperMap组件开发二三维联动的坐标转换问题

超图组件开发包supermap-iobjectsdotnet-9.0.1-15628-65435-all\SampleCode\Realspace\AssociatingMapAndScene目录demo演示了同一地理坐标系下的二三维联动问题。效果如下图所示:
图1 超图二三维联动示例图

问题:如果数据在二维地图控件中加载的是投影坐标,而三维场景中显示的是地理坐标怎么办?

       这个时候需要在二三维联动的时候进行投影坐标和地理坐标的转换,简言之,需要进行双向坐标转换,即在二维地图控件中操作时,需要把平面投影坐标转换为地理坐标进行联动显示;而在三维场景中进行操作时,需要把地理坐标转换为平面投影坐标进行联动显示。如果二三维数据在同一地理坐标系下,以上两种操作分别用到了SuperMap.Data 命名空间下静态类CoordSysTranslator的Inverse 方法和Forward方法;如果二三维数据不是在同一地理坐标系下,则需要利用静态类CoordSysTranslator的Convert方法并设置相应的转换参数。

       以下是针对二维点对象进行投影坐标和地理坐标相互转换的两个函数:

/// <summary>
      /// 投影坐标转地理坐标
      /// </summary>
      /// <param name="point2D"></param>
      private void ProjectionToGeo(ref Point2D point2D)
      {
          Point2Ds point2Ds = new Point2Ds(point2D);
          PrjCoordSys prjCoordSys = new PrjCoordSys(ClsStaticFields.prjCoordSysType);
          //投影坐标
          if (point2D.X > 180)
          {
              bool b = CoordSysTranslator.Inverse(point2Ds, prjCoordSys);
          }
          point2D = point2Ds[0];
      }
  	/// <summary>
      /// 地理坐标转投影坐标
      /// </summary>
      /// <param name="point2D"></param>
      private void GeoToProjection(ref Point2D point2D)
      {
          Point2Ds point2Ds = new Point2Ds(point2D);
          PrjCoordSys prjCoordSys = new PrjCoordSys(ClsStaticFields.prjCoordSysType);
          //投影坐标
          if (point2D.X < 180)
          {
              bool b = CoordSysTranslator.Forward(point2Ds, prjCoordSys);
          }
          point2D = point2Ds[0];
      }

解决方法:

       当地图重绘时,把投影坐标转换为地理坐标控制三维场景的同步。

/// <summary>
        /// 当地图重绘的时候,控制三维场景的可视效果也相应变化
        /// </summary>
        private void MapDrawnHandler(object sender, MapDrawnEventArgs e)
        {
            try
            {
                if (m_workControlType != WorkControlType.Map)
                {
                    return;
                }
                //backup
                //Camera camera = new Camera(m_mapControl.Map.Center.X, m_mapControl.Map.Center.Y, CalculateAltitudeFromBounds(m_mapControl.Map.ViewBounds));
                //TODO:yjw要把地理坐标全部转换为投影坐标
                Point2D point2D = m_mapControl.Map.Center;
                ProjectionToGeo(ref point2D);

                Point2D leftBottom = new Point2D(m_mapControl.Map.ViewBounds.Left, m_mapControl.Map.ViewBounds.Bottom);
                Point2D rightTop = new Point2D(m_mapControl.Map.ViewBounds.Right, m_mapControl.Map.ViewBounds.Top);
                ProjectionToGeo(ref leftBottom);
                ProjectionToGeo(ref rightTop);
                Rectangle2D rec = new Rectangle2D(leftBottom, rightTop);

                Camera camera = new Camera(point2D.X, point2D.Y, CalculateAltitudeFromBounds(rec));
                camera.Heading = m_mapControl.Map.Angle;
                m_sceneControl.Scene.Camera = camera;
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.Message);
            }
        }

       	/// <summary>
        /// 根据给定的地图范围计算场景的高度
        /// Calculate the scene altitude according to the given map extent
        /// </summary>
        /// <param name="bounds">Map Extent</param>
        /// <returns>Scene Altitude</returns>
        private Double CalculateAltitudeFromBounds(Rectangle2D bounds)
        {
            Double altitude = EarthRadius;
            try
            {
                if (bounds.Width >= 120)
                {
                    Double mapWidth = bounds.Width;
                    altitude = EarthRadius * mapWidth / 60 - EarthRadius;
                }
                else if (bounds.Width != 0)
                {
                    Double angle1 = bounds.Width / 2 / 180 * 3.1415926;
                    Double height = Math.Sin(angle1) * EarthRadius;
                    Double a = height / Math.Tan(angle1);
                    Double b = height / Math.Tan(3.1415926 / 6);
                    altitude = a + b - EarthRadius;
                }
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.Message);
            }
            return altitude;
        }

       当三维场景刷新时,把地理坐标转换为投影坐标控制二维地图控件进行同步。

/// <summary>
        /// 当三维场景刷新时,控制地图可视效果也相应变化
        /// When you refresh the scene, the map displaying effect will change
        /// </summary>
        private void SceneTimerTickHandler(object sender, EventArgs e)
        {
            try
            {
                if (m_workControlType != WorkControlType.Scene)
                {
                    return;
                }

                Camera camera = m_sceneControl.Scene.Camera;
                Point2D point = new Point2D(camera.Longitude, camera.Latitude);
                Size2D size = CalculateSizeFromAltitude(camera.Altitude);
                Rectangle2D bounds = new Rectangle2D(point, size);

                //TODO:YJW把地理坐标范围转为投影坐标范围
                Point2D leftBottom = new Point2D(bounds.Left, bounds.Bottom);
                Point2D rightTop = new Point2D(bounds.Right, bounds.Top);
                GeoToProjection(ref leftBottom);
                GeoToProjection(ref rightTop);

                Rectangle2D newBounds = new Rectangle2D(leftBottom, rightTop);

                //if (bounds != m_mapControl.Map.Bounds || m_mapControl.Map.Angle != camera.Heading)
                if (newBounds != m_mapControl.Map.Bounds || m_mapControl.Map.Angle != camera.Heading)
                {
                    //m_mapControl.Map.ViewBounds = bounds;
                    m_mapControl.Map.ViewBounds = newBounds;

                    m_mapControl.Map.Angle = camera.Heading;
                    m_mapControl.Map.Refresh();
                }
                m_sceneControl.Scene.Refresh();
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// 根据给定的场景高度计算地图中显示范围的宽度
        /// </summary>
        /// <param name="altitude">Altitude</param>
        /// <returns>Extent Size</returns>
        private Size2D CalculateSizeFromAltitude(Double altitude)
        {
            Size2D size = new Size2D(0, 0);

            try
            {
                // 当场景高度大于可全幅显示整球的高度时
                if (altitude >= EarthRadius)
                {
                    Double ratio = (altitude + EarthRadius) / 2;
                    Double longitudeWidth = 120 * ratio / EarthRadius;
                    Double latitudeWidth = 120 * ratio / EarthRadius;

                    size = new Size2D(longitudeWidth, latitudeWidth);
                }
                // 当场景高度小于可全幅显示整球的高度时,即无法看到整球时
                else
                {
                    Double tan30 = Math.Tan(3.1415926 / 6);
                    // 设置二元一次方程组的a、b、c值
                    Double a = (Math.Pow(tan30, 2) + 1) * Math.Pow(EarthRadius, 2);
                    Double b = -2 * (EarthRadius + altitude) * EarthRadius * Math.Pow(tan30, 2);
                    Double c = Math.Pow(tan30, 2) * Math.Pow((EarthRadius + altitude), 2) - Math.Pow(EarthRadius, 2.0);
                    // 解一元二次方程,取锐角,因此余弦值较大
                    Double cosd = (-b + Math.Sqrt(Math.Pow(b, 2) - 4 * a * c)) / 2 / a;
                    Double d = Math.Acos(cosd);
                    Double widthd = 2 * d * EarthRadius;
                    Double width = (widthd / 3.1415926 / EarthRadius) * 180;

                    size = new Size2D(width, width);
                }
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.Message);
            }
            return size;
        }

       最终效果如下图所示。