wpf-18071-controls-and-libraries-map-control-examples-vector-data-customize-data-appearance-how-to-implement-a-custom-map-projection.md
In this example, the Hammer-Atoff map projection is implemented.
To create a custom projection, inherit the ProjectionBase class and override the following methods of the base class.
Imports DevExpress.Xpf.Map
Imports System
Imports System.Windows
Namespace CustomProjection
Friend Class HammerAitoffProjection
Inherits ProjectionBase
Public Overrides Property OffsetX() As Double
Get
Return 0.5
End Get
Set(value As Double)
End Set
End Property
Public Overrides Property OffsetY() As Double
Get
Return 0.5
End Get
Set(value As Double)
End Set
End Property
Public Overrides Property ScaleX() As Double
Get
Return 0.5
End Get
Set(value As Double)
End Set
End Property
Public Overrides Property ScaleY() As Double
Get
Return -0.25
End Get
Set(value As Double)
End Set
End Property
Private Const minLatitude As Double = -90.0
Private Const maxLatitude As Double = 90.0
Private Const minLongitude As Double = -180.0
Private Const maxLongitude As Double = 180.0
Private Shared Function RadianToDegree(ByVal value As Double) As Double
Return value * 180.0 / Math.PI
End Function
Private Shared Function DegreeToRadian(ByVal value As Double) As Double
Return value * Math.PI / 180.0
End Function
Private Function IsValidPoint(ByVal x As Double, ByVal y As Double) As Boolean
If Math.Pow(x, 2) + Math.Pow(y, 2) > 1 Then
Return False
End If
Return True
End Function
Public Overrides Function GeoPointToMapUnit(ByVal geoPoint As GeoPoint) As MapUnit
Dim lonInRadian As Double = DegreeToRadian(Math.Min(maxLongitude, Math.Max(minLongitude, geoPoint.Longitude)))
Dim latInRadian As Double = DegreeToRadian(Math.Min(maxLatitude, Math.Max(minLatitude, geoPoint.Latitude)))
Dim z As Double = Math.Sqrt(1 + Math.Cos(latInRadian) * Math.Cos(lonInRadian / 2))
Dim x As Double = Math.Cos(latInRadian) * Math.Sin(lonInRadian / 2) / z
Dim y As Double = Math.Sin(latInRadian) / z
Return New MapUnit(x * ScaleX + OffsetX, y * ScaleY + OffsetY)
End Function
Public Overrides Function MapUnitToGeoPoint(ByVal mapUnit As MapUnit) As GeoPoint
Dim x As Double = (mapUnit.X - OffsetX) / ScaleX
Dim y As Double = Math.Min(1, Math.Max(-1, (mapUnit.Y - OffsetY) / ScaleY))
If IsValidPoint(x, y) Then
Dim z As Double = Math.Sqrt(1 - 0.5 * Math.Pow(x, 2) - 0.5 * Math.Pow(y, 2))
Dim c As Double = Math.Sqrt(2) * z * x / (2 * Math.Pow(z, 2) - 1)
Dim lon As Double = 2 * Math.Atan(c)
Dim lat As Double = Math.Asin(Math.Min(Math.Max(Math.Sqrt(2) * z * y, -1), 1))
Dim latInDegree As Double = lat * maxLongitude / Math.PI
Dim lonInDegree As Double = lon * maxLongitude / Math.PI
Return New GeoPoint(Math.Min(maxLatitude, Math.Max(minLatitude, RadianToDegree(lat))), Math.Min(maxLongitude, Math.Max(minLongitude, RadianToDegree(lon))))
Else
Dim signX As Integer = If(x < 0, -1, 1)
Dim signY As Integer = If(y < 0, -1, 1)
Return New GeoPoint(maxLatitude * signY, maxLongitude * signX)
End If
End Function
Public Overrides Function GeoToKilometersSize(ByVal anchorPoint As GeoPoint, ByVal size As Size) As Size
Return New Size(size.Width * LonToKilometersRatio * Math.Cos(DegreeToRadian(anchorPoint.Latitude)), size.Height * LatToKilometersRatio)
End Function
Public Overrides Function KilometersToGeoSize(ByVal anchorPoint As GeoPoint, ByVal size As Size) As Size
Return New Size(size.Width / LonToKilometersRatio / Math.Cos(DegreeToRadian(anchorPoint.Latitude)), size.Height / LatToKilometersRatio)
End Function
End Class
End Namespace
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomProjection"
xmlns:dxm="http://schemas.devexpress.com/winfx/2008/xaml/map"
x:Class="CustomProjection.MainWindow"
Title="MainWindow" Height="549" Width="526">
<Grid>
<dxm:MapControl>
<dxm:MapControl.CoordinateSystem>
<dxm:GeoMapCoordinateSystem>
<dxm:GeoMapCoordinateSystem.Projection>
<local:HammerAitoffProjection/>
</dxm:GeoMapCoordinateSystem.Projection>
</dxm:GeoMapCoordinateSystem>
</dxm:MapControl.CoordinateSystem>
<dxm:VectorLayer>
<dxm:ShapefileDataAdapter FileUri="Data/Countries.shp" />
</dxm:VectorLayer>
</dxm:MapControl>
</Grid>
</Window>
<Application x:Class="CustomProjection.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
using DevExpress.Xpf.Map;
using System;
using System.Windows;
namespace CustomProjection {
class HammerAitoffProjection : ProjectionBase {
override public double OffsetX { get { return 0.5; } }
override public double OffsetY { get { return 0.5; } }
override public double ScaleX { get { return 0.5; } }
override public double ScaleY { get { return -0.25; } }
const double minLatitude = -90.0;
const double maxLatitude = 90.0;
const double minLongitude = -180.0;
const double maxLongitude = 180.0;
static double RadianToDegree(double value) {
return value * 180.0 / Math.PI;
}
static double DegreeToRadian(double value) {
return value * Math.PI / 180.0;
}
bool IsValidPoint(double x, double y) {
if (Math.Pow(x, 2) + Math.Pow(y, 2) > 1)
return false;
return true;
}
public override MapUnit GeoPointToMapUnit(GeoPoint geoPoint) {
double lonInRadian = DegreeToRadian(
Math.Min(
maxLongitude,
Math.Max(minLongitude, geoPoint.Longitude)
)
);
double latInRadian = DegreeToRadian(
Math.Min(
maxLatitude,
Math.Max(minLatitude, geoPoint.Latitude)
)
);
double z = Math.Sqrt(1 + Math.Cos(latInRadian) * Math.Cos(lonInRadian / 2));
double x = Math.Cos(latInRadian) * Math.Sin(lonInRadian / 2) / z;
double y = Math.Sin(latInRadian) / z;
return new MapUnit(x * ScaleX + OffsetX, y * ScaleY + OffsetY);
}
public override GeoPoint MapUnitToGeoPoint(MapUnit mapUnit) {
double x = (mapUnit.X - OffsetX) / ScaleX;
double y = Math.Min(1, Math.Max(-1, (mapUnit.Y - OffsetY) / ScaleY));
if (IsValidPoint(x, y)) {
double z = Math.Sqrt(1 - 0.5 * Math.Pow(x, 2) - 0.5 * Math.Pow(y, 2));
double c = Math.Sqrt(2) * z * x / (2 * Math.Pow(z, 2) - 1);
double lon = 2 * Math.Atan(c);
double lat = Math.Asin(Math.Min(Math.Max(Math.Sqrt(2) * z * y, -1), 1));
double latInDegree = lat * maxLongitude / Math.PI;
double lonInDegree = lon * maxLongitude / Math.PI;
return new GeoPoint(
Math.Min(
maxLatitude,
Math.Max(minLatitude, RadianToDegree(lat))
),
Math.Min(
maxLongitude,
Math.Max(minLongitude, RadianToDegree(lon))
)
);
}
else {
int signX = (x < 0) ? -1 : 1;
int signY = (y < 0) ? -1 : 1;
return new GeoPoint(maxLatitude * signY, maxLongitude * signX);
}
}
public override Size GeoToKilometersSize(GeoPoint anchorPoint, Size size) {
return new Size(
size.Width * LonToKilometersRatio * Math.Cos(DegreeToRadian(anchorPoint.Latitude)),
size.Height * LatToKilometersRatio
);
}
public override Size KilometersToGeoSize(GeoPoint anchorPoint, Size size) {
return new Size(
size.Width / LonToKilometersRatio / Math.Cos(DegreeToRadian(anchorPoint.Latitude)),
size.Height / LatToKilometersRatio
);
}
}
}