对于我目前正在进行的项目,我正在尝试使用3维的二维地图。对于这个项目,我需要能够在三维空间中使用地图上的点(以二维定义)。
地图在原点处围绕矢量(-5,1,1)旋转。我无法将XY平面上的点旋转到三维的适当位置。
这就是我正在做的事情:
private Point3D MapXY2Screen3D
(
MapPoint mapPoint
)
{
// Width and Height of the map, both 600
double Xmax = Map.ActualWidth;
double Ymax = Map.ActualHeight;
// Convert ESRI map coordinates to screen coordinates
Point ScreenCoordinates = Map.MapToScreen(mapPoint);
// Normalize the screen coordinates so they fall in the range -1..1
ScreenCoordinates.X = ((2*ScreenCoordinates.X)/Xmax) - 1;
ScreenCoordinates.Y = 1 - ((2*ScreenCoordinates.Y)/Ymax);
// Create a Quaternion from the original location: Petzold
// Chapter 8 - Low-Level Quaternion Rotation
var originQuaternion =
new Quaternion(ScreenCoordinates.X, ScreenCoordinates.Y, 0, 0);
// Multiply rotation quaternion by origin by conjugate of rotation quaternion
// to get the rotated point as a quaternion.
var rotatedPoint = RotationQuaternion *
originQuaternion *
RotationQuaternionConjugate;
// Return the X, Y, Z of the rotated quaternion as a 3D point
return new Point3D(rotatedPoint.X, rotatedPoint.Y, rotatedPoint.Z);
}
我可以看到一些潜在的问题,这些问题可能会导致我所创造的点的位置偏离它们应该存在的位置。
首先,如果地图坐标的标准化不正确,旋转后该点将明显位于错误的位置。对我来说,这些公式是有意义的,从一些简单的调试,结果看起来是正确的。
其次,在我看的例子中,代码看起来有点不同:
// Create a Quaternion from the original location: Petzold
// Chapter 8 - Low-Level Quaternion Rotation
var originQuaternion =
new Quaternion(ScreenCoordinates.X, ScreenCoordinates.Y, 0, 0);
*originQuaternion -= center;*
// Multiply rotation quaternion by origin by conjugate of rotation quaternion
// to get the rotated point as a quaternion.
var rotatedPoint = RotationQuaternion *
originQuaternion *
RotationQuaternionConjugate;
*rotatedPoint += center;*
center是指示旋转中心的四元数。我在原点周围旋转,所以我认为我不需要这样做。
以下是一些展示展示位置的图片:
在2d。兴趣点是NE部分的灰色方块。
在3d中。您仍然可以在地图平面上看到该点。
在3D中我正在创造。灰色方块上的预期红色圆圈。
using System;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Media3D;
using ESRI.ArcGIS.Client;
using ESRI.ArcGIS.Client.Geometry;
using ESRI.ArcGIS.Client.Symbols;
using Petzold.Media3D;
namespace Map3D
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : INotifyPropertyChanged
{
private Quaternion _rotationQuaternion;
private Quaternion _rotationQuaternionConjugate;
private MapPoint _testMapPoint;
#region PropertyChanged
// General property changed event
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
public MainWindow()
{
InitializeComponent();
DataContext = this;
SetQuaternions();
}
public Quaternion RotationQuaternion
{
get { return _rotationQuaternion; }
set
{
_rotationQuaternion = value;
RaisePropertyChanged("RotationQuaternion");
}
}
public Quaternion RotationQuaternionConjugate
{
get { return _rotationQuaternionConjugate; }
set
{
_rotationQuaternionConjugate = value;
RaisePropertyChanged("RotationQuaternionConjugate");
}
}
public MapPoint TestMapPoint
{
get { return _testMapPoint; }
set
{
_testMapPoint = value;
RaisePropertyChanged("TestMapPoint");
}
}
private void SetQuaternions()
{
var rotationAxis = new Vector3D(-5, 1, 1);
rotationAxis.Normalize();
RotationQuaternion = new Quaternion(rotationAxis, 65);
RotationQuaternionConjugate = RotationQuaternion;
RotationQuaternionConjugate.Conjugate();
if (!RotationQuaternion.IsNormalized)
{
RotationQuaternion.Normalize();
}
if (!RotationQuaternionConjugate.IsNormalized)
{
RotationQuaternionConjugate.Normalize();
}
}
private void Timeline_OnCompleted
(
object sender,
EventArgs e
)
{
Axes axes = new Axes();
axes.Color = Colors.Red;
axes.Extent = 1;
axes.SetValue(Panel.ZIndexProperty, 100);
MyModelVisual3D.Children.Add(axes);
}
private void Layer_OnInitialized
(
object sender,
EventArgs e
)
{
var graphic = new Graphic();
var symbol = new SimpleMarkerSymbol
{
Style = SimpleMarkerSymbol.SimpleMarkerStyle.Circle,
Size = 10,
Color = new SolidColorBrush(Colors.DarkGray)
};
graphic.Symbol = symbol;
var center = Map.Extent.GetCenter().Clone();
MapPoint mapPoint = new MapPoint
{
X = 3*(Map.Extent.Width / 4) + Map.Extent.XMin,
Y = 3*(Map.Extent.Height / 4) + Map.Extent.YMin
};
TestMapPoint = mapPoint;
graphic.Geometry = mapPoint;
var graphicsLayer = new GraphicsLayer
{
ID = "GraphicsLayer"
};
graphicsLayer.Graphics.Add(graphic);
Map.Layers.Add(graphicsLayer);
}
private void PointButtonClick
(
object sender,
RoutedEventArgs e
)
{
GraphicsLayer graphicsLayer =
(GraphicsLayer) Map.Layers["GraphicsLayer"];
MapPoint mapPoint =
(MapPoint) graphicsLayer.Graphics.First().Geometry;
MyModelVisual3D.Children.Add(
CreateSphere(0.025, Colors.Red, MapXY2Screen3D(mapPoint)));
}
private Visual3D CreateSphere
(
double radius,
Color color,
Point3D pt
)
{
return new Sphere
{
Radius = radius,
BackMaterial = new DiffuseMaterial
{
Brush = new SolidColorBrush(color)
},
Center = pt,
};
}
private Point3D MapXY2Screen3D
(
MapPoint mapPoint
)
{
// Width and Height of the map, both 600
double Xmax = Map.ActualWidth;
double Ymax = Map.ActualHeight;
// Convert ESRI map coordinates to screen coordinates
Point ScreenCoordinates = Map.MapToScreen(mapPoint);
// Normalize the screen coordinates so they fall in the range -1..1
ScreenCoordinates.X = ((2*ScreenCoordinates.X)/Xmax) - 1;
ScreenCoordinates.Y = 1 - ((2*ScreenCoordinates.Y)/Ymax);
// Create a Quaternion from the original location: Petzold
// Chapter 8 - Low-Level Quaternion Rotation
var originQuaternion = new Quaternion(
ScreenCoordinates.X, ScreenCoordinates.Y, 0, 0);
// Multiply rotation quaternion by origin by conjugate of rotation
// quaternion to get the rotated point as a quaternion.
var rotatedPoint = RotationQuaternion *
originQuaternion *
RotationQuaternionConjugate;
// Return the X, Y, Z of the rotated quaternion as a 3D point
return new Point3D(rotatedPoint.X, rotatedPoint.Y, rotatedPoint.Z);
}
private void TiltButtonClick
(
object sender,
RoutedEventArgs e
)
{
var sb = Resources["MyStoryboard"] as Storyboard;
sb.Begin();
}
}
}
<Window x:Class="Map3D.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:esri="http://schemas.esri.com/arcgis/client/2009"
Title="MainWindow"
Width="300" Height="300" mc:Ignorable="d" SizeToContent="WidthAndHeight">
<Window.Resources>
<Storyboard x:Key="MyStoryboard">
<QuaternionAnimation Storyboard.TargetName="MyQuaternionRotation3D"
Storyboard.TargetProperty="Quaternion"
To="{Binding RotationQuaternion}"
Completed="Timeline_OnCompleted"
Duration="0:0:1" />
</Storyboard>
</Window.Resources>
<Grid Width="800" Height="800">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Viewport3D Grid.Row="0">
<Viewport3D.Camera>
<PerspectiveCamera Position="0,0,4"
LookDirection="0,0,-1"
UpDirection="0,1,0" />
</Viewport3D.Camera>
<ModelVisual3D x:Name="MyModelVisual3D">
<ModelVisual3D.Content>
<AmbientLight Color="White" />
</ModelVisual3D.Content>
</ModelVisual3D>
<ModelVisual3D>
<Viewport2DVisual3D>
<Viewport2DVisual3D.Transform>
<RotateTransform3D CenterX="0" CenterY="0" CenterZ="0">
<RotateTransform3D.Rotation>
<QuaternionRotation3D x:Name="MyQuaternionRotation3D"
Quaternion="0,0,0,0.5" />
</RotateTransform3D.Rotation>
</RotateTransform3D>
</Viewport2DVisual3D.Transform>
<Viewport2DVisual3D.Geometry>
<MeshGeometry3D Positions="-1 1 0, -1 -1 0, 1 -1 0, 1 1 0 "
TextureCoordinates="0 0, 0 1, 1 1, 1 0"
TriangleIndices="0 1 2 0 2 3" />
</Viewport2DVisual3D.Geometry>
<Viewport2DVisual3D.Material>
<DiffuseMaterial Viewport2DVisual3D.IsVisualHostMaterial="True"
Brush=White" />
</Viewport2DVisual3D.Material>
<!-- Map -->
<Grid x:Name="MapGrid"
Width="600" Height="600"
Panel.ZIndex="-1" Opacity="1"
ClipToBounds="True">
<Border Background="Black">
<Border.Effect>
<BlurEffect Radius="15" />
</Border.Effect>
</Border>
<esri:Map x:Name="Map" Extent="5071751, 2615619, 6622505, 3609912">
<esri:Map.Layers>
<esri:ArcGISTiledMapServiceLayer
Url="http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer"
Initialized="Layer_OnInitialized"
Visible="True" />
</esri:Map.Layers>
</esri:Map>
</Grid>
</Viewport2DVisual3D>
</ModelVisual3D>
</Viewport3D>
<UniformGrid Grid.Row="1"
Rows="1" Columns="2">
<Button Margin="5" HorizontalAlignment="Center" VerticalAlignment="Center" Click="PointButtonClick">Point</Button>
<Button Margin="5" HorizontalAlignment="Center" VerticalAlignment="Center" Click="TiltButtonClick">Tilt</Button>
</UniformGrid>
</Grid>
</Window>
答案 0 :(得分:0)
在我上面发布的代码中,旋转四元数的共轭没有被正确计算或存储,所以我基本上是乘法:
rotated point = rotationQ * origin * rotationQ
这给出了反射,而不是旋转。为了解决这个问题,我将我的SetQuaternions方法更改为:
private void SetQuaternions()
{
var rotationAxis = new Vector3D(-5f, 1f, 1f);
RotationQuaternion = new Quaternion(rotationAxis, 65f);
RotationQuaternion.Normalize();
var q = RotationQuaternion;
q.Conjugate();
RotationQuaternionConjugate = q;
if (!RotationQuaternion.IsNormalized)
{
RotationQuaternion.Normalize();
}
if (!RotationQuaternionConjugate.IsNormalized)
{
RotationQuaternionConjugate.Normalize();
}
}
通过此更改,我的Sphere对象将在地图平面上,在感兴趣的位置创建。