====== Source Code WpfDesktopEditionSample CustomRotationProjection CS 110127.zip ====== ====App.xaml.cs==== using System; using System.Collections.Generic; using System.Configuration; using System.Data; using System.Linq; using System.Windows; namespace CustomRotationProjection { /// /// Interaction logic for App.xaml /// public partial class App : Application { } } ====CustomRotationProjection.cs==== using System; using System.Collections.Generic; using System.Linq; using System.Text; using ThinkGeo.MapSuite.Core; namespace Projections { //This projection class allows to project and rotate at the same time. class CustomRotationProjection : Projection, IDisposable { private ManagedProj4Projection proj4 = new ManagedProj4Projection(); private RotationProjection rotateProjection = new RotationProjection(); private double angle; private Vertex pivotVertex; public CustomRotationProjection() : base() { } public CustomRotationProjection( String InternalProjectionString, String ExternalProjectionString) : base() { proj4.InternalProjectionParametersString = InternalProjectionString; proj4.ExternalProjectionParametersString = ExternalProjectionString; } public string InternalProjectionString { get { return proj4.InternalProjectionParametersString; } set { proj4.InternalProjectionParametersString = value; } } public string ExternalProjectionString { get { return proj4.ExternalProjectionParametersString; } set { proj4.ExternalProjectionParametersString = value; } } public double Angle { get { return angle; } set { angle = value; rotateProjection.Angle = angle; } } public Vertex PivotVertex { get { return pivotVertex; } set { pivotVertex = value; } } protected override Vertex[] ConvertToExternalProjectionCore(double[] x, double[] y) { //First, converts to the external projection Vertex[] projVertices = new Vertex[x.Length]; proj4.Open(); for (int i = 0; i < projVertices.Length; i++) { projVertices[i] = proj4.ConvertToExternalProjection(x[i], y[i]); } proj4.Close(); Vertex[] rotateVertices = new Vertex[x.Length]; //Second, rotates based on angle and pivot point. for (int i = 0; i < rotateVertices.Length; i++) { rotateVertices[i] = RotateVertex(projVertices[i].X, projVertices[i].Y, angle); } return rotateVertices; } private Vertex RotateVertex(double x, double y, double angle) { Vertex rotatedVertex = new Vertex(x, y); if ((angle % 360) != 0) { double rotatedX = x; double rotatedY = y; double distanceToPivot = Math.Sqrt(Math.Pow((x - pivotVertex.X), 2) + Math.Pow((y - pivotVertex.Y), 2)); if (distanceToPivot != 0) { double beta = Math.Atan((y - pivotVertex.Y) / (x - pivotVertex.X)); if ((beta <= 0 | y < pivotVertex.Y) && x < pivotVertex.X) { beta = Math.PI + beta; } double radiantAngle = angle * Math.PI / 180; rotatedX = (distanceToPivot * Math.Cos(radiantAngle + beta)) + pivotVertex.X; rotatedY = (distanceToPivot * Math.Sin(radiantAngle + beta)) + pivotVertex.Y; } rotatedVertex = new Vertex(rotatedX, rotatedY); } return rotatedVertex; } protected override Vertex[] ConvertToInternalProjectionCore(double[] x, double[] y) { //Converts back to internal projection with the rotation. Vertex[] vertices = new Vertex[x.Length]; proj4.Open(); for (int i = 0; i < vertices.Length; i++) { vertices[i] = proj4.ConvertToInternalProjection(x[i], y[i]); } proj4.Close(); return vertices; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { Close(); } } } ====TestWindow.xaml.cs==== using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; using System.Collections.ObjectModel; using ThinkGeo.MapSuite.Core; using ThinkGeo.MapSuite.WpfDesktopEdition; namespace CustomRotationProjection { /// /// Interaction logic for TestWindow.xaml /// public partial class TestWindow : Window { private Projections.CustomRotationProjection customRotationProjection; public TestWindow() { InitializeComponent(); } private void Window_Loaded(object sender, RoutedEventArgs e) { //Sets the correct map unit and the extent of the map. wpfMap1.MapUnit = GeographyUnit.Meter; wpfMap1.CurrentExtent = new RectangleShape(-10782920,3912077,-10779783,3910188); wpfMap1.Background = new SolidColorBrush(Color.FromRgb(148, 196, 243)); GoogleMapsOverlay googleMapsOverlay = new GoogleMapsOverlay(); wpfMap1.Overlays.Add(googleMapsOverlay); //Custom projection that will allow to project from State Plane Central North Texas to Spherical Mercator while applying a rotation. customRotationProjection = new Projections.CustomRotationProjection(ManagedProj4Projection.GetEsriParametersString(102738), ManagedProj4Projection.GetGoogleMapParametersString()); ShapeFileFeatureLayer streetLayer = new ShapeFileFeatureLayer(@"../../data/Streets_subset.shp"); streetLayer.DrawingMarginPercentage = 100; streetLayer.FeatureSource.Projection = customRotationProjection; streetLayer.ZoomLevelSet.ZoomLevel01.DefaultLineStyle = LineStyles.CreateSimpleLineStyle (GeoColor.StandardColors.Red, 3, GeoColor.FromArgb(255, GeoColor.StandardColors.Black), 5, true); streetLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20; LayerOverlay layerOverlay = new LayerOverlay(); layerOverlay.Layers.Add("StreetLayer", streetLayer); wpfMap1.Overlays.Add("StreetOverlay", layerOverlay); //Sets the pivot point to be the center of the layer. streetLayer.Open(); customRotationProjection.PivotVertex = new Vertex(streetLayer.GetBoundingBox().GetCenterPoint()); streetLayer.Close(); customRotationProjection.Angle = 20; wpfMap1.Refresh(); } private void btnRotateClockWise_Click(object sender, RoutedEventArgs e) { LayerOverlay streetLayerOverlay = (LayerOverlay)wpfMap1.Overlays["StreetOverlay"]; ShapeFileFeatureLayer streetLayer = (ShapeFileFeatureLayer)streetLayerOverlay.Layers["StreetLayer"]; //Set the Angle property of RotationProjection. Substract 5 to go clockwise. streetLayer.Open(); customRotationProjection.Angle = customRotationProjection.Angle - 5; streetLayer.Close(); wpfMap1.Refresh(streetLayerOverlay); } private void btnRotateCounterClockWise_Click(object sender, RoutedEventArgs e) { LayerOverlay streetLayerOverlay = (LayerOverlay)wpfMap1.Overlays["StreetOverlay"]; ShapeFileFeatureLayer streetLayer = (ShapeFileFeatureLayer)streetLayerOverlay.Layers["StreetLayer"]; //Set the Angle property of RotationProjection. Adds 5 to go clockwise. streetLayer.Open(); customRotationProjection.Angle = customRotationProjection.Angle + 5; streetLayer.Close(); wpfMap1.Refresh(streetLayerOverlay); } private void wpfMap1_MouseMove(object sender, MouseEventArgs e) { //Gets the PointShape in world coordinates from screen coordinates. Point point = e.MouseDevice.GetPosition(null); ScreenPointF screenPointF = new ScreenPointF((float)point.X, (float)point.Y); PointShape pointShape = ExtentHelper.ToWorldCoordinate(wpfMap1.CurrentExtent, screenPointF, (float)wpfMap1.Width, (float)wpfMap1.Height); textBox1.Text = "X: " + Math.Round(pointShape.X) + " Y: " + Math.Round(pointShape.Y); } } }