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

Added rotation support for viewport and WPF renderers #11

Merged
merged 3 commits into from
Sep 18, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@

*.java

*.v12.suo

*.csproj.user

Mapsui.Rendering.Android/obj/Debug/library_project_imports/__res_name_case_map.txt

Mapsui.Rendering.Android/obj/Debug/library_project_imports/res/values/strings.xml

Mapsui.Rendering.Android/obj/Debug/Mapsui.Rendering.Android.csproj.FilesWrittenAbsolute.txt

Samples/Mapsui.Samples.Silverlight.Web/ClientBin

obj/

bin/
Expand Down
46 changes: 32 additions & 14 deletions Mapsui.Rendering.Xaml/GeometryRenderer.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Linq;
using Mapsui.Geometries;
using Mapsui.Styles;
using Point = Mapsui.Geometries.Point;
Expand Down Expand Up @@ -30,7 +31,7 @@ namespace Mapsui.Rendering.Xaml
/// namespace aliases. I will use 'Xaml' in namespace and method names to refer to
/// all .net framework classes related to Xaml, even if they are not.
/// </remarks>
static class GeometryRenderer
public static class GeometryRenderer
{
public static UIElement RenderPoint(Point point, IStyle style, IViewport viewport)
{
Expand Down Expand Up @@ -114,6 +115,10 @@ private static XamlMedia.Matrix CreateTransformMatrix(Point point, IViewport vie
var mapCenterY = viewport.Height * 0.5;

MatrixHelper.Translate(ref matrix, mapCenterX - viewport.Center.X, mapCenterY - viewport.Center.Y);
if (viewport.IsRotated)
{
MatrixHelper.RotateAt(ref matrix, -viewport.Rotation);
}
MatrixHelper.ScaleAt(ref matrix, 1 / viewport.Resolution, 1 / viewport.Resolution, mapCenterX, mapCenterY);

// This will invert the Y axis, but will also put images upside down
Expand All @@ -128,6 +133,10 @@ private static XamlMedia.Matrix CreateTransformMatrix1(IViewport viewport)
var mapCenterY = viewport.Height * 0.5;

MatrixHelper.Translate(ref matrix, mapCenterX - viewport.Center.X, mapCenterY - viewport.Center.Y);
if (viewport.IsRotated)
{
MatrixHelper.RotateAt(ref matrix, -viewport.Rotation);
}
MatrixHelper.ScaleAt(ref matrix, 1 / viewport.Resolution, 1 / viewport.Resolution, mapCenterX, mapCenterY);

// This will invert the Y axis, but will also put images upside down
Expand Down Expand Up @@ -341,7 +350,8 @@ public static XamlShapes.Path RenderMultiPolygon(MultiPolygon geometry, IStyle s
public static XamlShapes.Path RenderRaster(IRaster raster, IStyle style, IViewport viewport)
{
var path = CreateRasterPath(style, raster.Data);
path.Data = ConvertRaster(raster.GetBoundingBox(), viewport);
path.Data = new XamlMedia.RectangleGeometry();
PositionRaster(path, raster.GetBoundingBox(), viewport);

// path.Stroke = new XamlMedia.SolidColorBrush(XamlColors.Red);
// path.StrokeThickness = 6;
Expand Down Expand Up @@ -394,15 +404,6 @@ private static async Task<IRandomAccessStream> ByteArrayToRandomAccessStream(byt
}

#endif
private static XamlMedia.Geometry ConvertRaster(BoundingBox boundingBox, IViewport viewport)
{
return new XamlMedia.RectangleGeometry
{
Rect = RoundToPixel(new Rect(
viewport.WorldToScreen(boundingBox.Min).ToXaml(),
viewport.WorldToScreen(boundingBox.Max).ToXaml()))
};
}

public static Rect RoundToPixel(Rect dest)
{
Expand All @@ -428,10 +429,27 @@ public static void PositionPoint(UIElement renderedGeometry, Point point, IStyle

public static void PositionRaster(UIElement renderedGeometry, BoundingBox boundingBox, IViewport viewport)
{
UpdateRenderTransform(renderedGeometry, viewport);

// since the render transform will take care of the rotation, calculate top-left using unrotated viewport
var topLeft = viewport.WorldToScreenUnrotated(boundingBox.TopLeft);
var rectWidthPixels = boundingBox.Width / viewport.Resolution;
var rectHeightPixels = boundingBox.Height / viewport.Resolution;
((XamlMedia.RectangleGeometry)((XamlShapes.Path)renderedGeometry).Data).Rect =
RoundToPixel(new Rect(
viewport.WorldToScreen(boundingBox.Min).ToXaml(),
viewport.WorldToScreen(boundingBox.Max).ToXaml()));
RoundToPixel(new Rect(topLeft.X, topLeft.Y, rectWidthPixels, rectHeightPixels));
}

private static void UpdateRenderTransform(UIElement renderedGeometry, IViewport viewport)
{
var matrix = new XamlMedia.Matrix();

if (viewport.IsRotated)
{
var center = viewport.WorldToScreen(viewport.Center);
MatrixHelper.RotateAt(ref matrix, viewport.Rotation, center.X, center.Y);
}

renderedGeometry.RenderTransform = new XamlMedia.MatrixTransform { Matrix = matrix };
}

public static void PositionGeometry(UIElement renderedGeometry, IViewport viewport)
Expand Down
7 changes: 5 additions & 2 deletions Mapsui.Rendering.Xaml/MatrixHelper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using Mapsui.Utilities;
#if !NETFX_CORE
using System.Windows.Media;
#else
Expand Down Expand Up @@ -117,7 +118,8 @@ internal enum MatrixTypes
public static void Rotate(ref Matrix matrix, double angle)
{
angle = angle % 360.0;
Multiply(ref matrix, CreateRotationRadians(angle * 0.017453292519943295));
var radians = Algorithms.DegreesToRadians(angle);
Multiply(ref matrix, CreateRotationRadians(radians));
}

internal static Matrix CreateRotationRadians(double angle)
Expand All @@ -139,7 +141,8 @@ internal static Matrix CreateRotationRadians(double angle, double centerX, doubl
public static void RotateAt(ref Matrix matrix, double angle, double centerX = 0, double centerY = 0)
{
angle = angle % 360.0;
Multiply(ref matrix, CreateRotationRadians(angle * 0.017453292519943295, centerX, centerY));
var radians = Algorithms.DegreesToRadians(angle);
Multiply(ref matrix, CreateRotationRadians(radians, centerX, centerY));
}
}
}
25 changes: 13 additions & 12 deletions Mapsui.Rendering.Xaml/StackedLabelLayerRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Mapsui.Providers;
using Mapsui.Styles;
using Mapsui.Styles.Thematics;
using Point = Mapsui.Geometries.Point;
#if !NETFX_CORE
using System.Windows.Controls;
using System.Windows.Media;
Expand Down Expand Up @@ -79,7 +80,10 @@ public static Canvas Render(IViewport viewport, LabelLayer layer)
};
labelStyle.Offset.Y += stackOffsetY;

var position = new Geometries.Point(cluster.Box.GetCentroid().X, cluster.Box.Bottom);
// since the box can be rotated, find the minimal Y value of all 4 corners
var rotatedBox = cluster.Box.Rotate(-viewport.Rotation);
var minY = rotatedBox.Vertices.Select(v => v.Y).Min();
var position = new Geometries.Point(cluster.Box.GetCentroid().X, minY);

canvas.Children.Add(SingleLabelRenderer.RenderLabel(position, labelStyle, viewport));
}
Expand All @@ -92,22 +96,19 @@ private static UIElement RenderBox(BoundingBox box, IViewport viewport)
const int symbolSize = 32; // todo: determine margin by symbol size
const int boxMargin = symbolSize / 2;

var p1 = viewport.WorldToScreen(box.Min);
var p2 = viewport.WorldToScreen(box.Max);

var rectangle = new Rectangle
var path = new Path
{
Width = p2.X - p1.X + symbolSize,
Height = p1.Y - p2.Y + symbolSize
Stroke = new SolidColorBrush(Colors.White),
StrokeThickness = 2,
Data = new RectangleGeometry()
};

Canvas.SetLeft(rectangle, p1.X - boxMargin);
Canvas.SetTop(rectangle, p2.Y - boxMargin);
// offset the bounding box left and up by the box margin
var offsetBox = box.Grow(boxMargin * viewport.Resolution);

rectangle.Stroke = new SolidColorBrush(Colors.White);
rectangle.StrokeThickness = 2;
GeometryRenderer.PositionRaster(path, offsetBox, viewport);

return rectangle;
return path;
}

private static void ClusterFeatures(
Expand Down
5 changes: 5 additions & 0 deletions Mapsui.UI.Xaml/MapControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ void MapPropertyChanged(object sender, PropertyChangedEventArgs e)
InitializeViewport();
_map.ViewChanged(true);
}
else if (e.PropertyName == "Rotation")
{
_map.ViewChanged(true);
OnViewChanged();
}
}

public FpsCounter FpsCounter
Expand Down
21 changes: 20 additions & 1 deletion Mapsui/Geometries/BoundingBox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using System.Collections.ObjectModel;
using System.Globalization;
using System.Collections.Generic;
using Mapsui.Utilities;

namespace Mapsui.Geometries
{
Expand Down Expand Up @@ -478,6 +479,24 @@ public BoundingBox Grow(double amountInX, double amountInY)
return box;
}

/// <summary>
/// Calculates a new quad by rotating this bounding box about its center by the
/// specified angle clockwise
/// </summary>
/// <param name="degrees">Angle about which to rotate (degrees)</param>
/// <returns>Returns the calculated quad</returns>
public Quad Rotate(double degrees)
{
var bottomLeft = new Point(MinX, MinY);
var topLeft = new Point(MinX, MaxY);
var topRight = new Point(MaxX, MaxY);
var bottomRight = new Point(MaxX, MinY);
var quad = new Quad(bottomLeft, topLeft, topRight, bottomRight);
var center = GetCentroid();

return quad.Rotate(degrees, center.X, center.Y);
}

/// <summary>
/// Checks whether a point lies within the bounding box
/// </summary>
Expand Down Expand Up @@ -540,7 +559,7 @@ public virtual double Distance(Point p)
/// </summary>
public Point GetCentroid()
{
return (min + max)*.5f;
return (min + max)*.5;
}

/// <summary>
Expand Down
36 changes: 35 additions & 1 deletion Mapsui/Geometries/Point.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

using System;
using Mapsui.Utilities;

namespace Mapsui.Geometries
{
Expand Down Expand Up @@ -378,5 +379,38 @@ public override bool Contains(Geometry geom)
return false;
}

}
/// <summary>
/// Calculates a new point by rotating this point clockwise about the specified center point
/// </summary>
/// <param name="degrees">Angle to rotate clockwise (degrees)</param>
/// <param name="centerX">X coordinate of point about which to rotate</param>
/// <param name="centerY">Y coordinate of point about which to rotate</param>
/// <returns>Returns the rotated point</returns>
public Point Rotate(double degrees, double centerX, double centerY)
{
// translate this point back to the center
var newX = x - centerX;
var newY = y - centerY;

// rotate the values
var p = Algorithms.RotateClockwiseDegrees(newX, newY, degrees);

// translate back to original reference frame
newX = p.X + centerX;
newY = p.Y + centerY;

return new Point(newX, newY);
}

/// <summary>
/// Calculates a new point by rotating this point clockwise about the specified center point
/// </summary>
/// <param name="degrees">Angle to rotate clockwise (degrees)</param>
/// <param name="center">Point about which to rotate</param>
/// <returns>Returns the rotated point</returns>
public Point Rotate(double degrees, Point center)
{
return Rotate(degrees, center.X, center.Y);
}
}
}
Loading