using System;
using System.Drawing;
using GeoAPI.Geometries;
namespace SharpMap
{
///
/// Utility class that checks Viewport min/max Zoom and constraint
///
[Serializable]
public class MapViewPortGuard
{
private double _minimumZoom;
private double _maximumZoom;
private Envelope _maximumExtents;
private double _pixelAspectRatio;
const double MinMinZoomValue = 2d * double.Epsilon;
///
/// Gets or sets a value indicating the minimum zoom level.
///
public double MinimumZoom
{
get { return _minimumZoom; }
set
{
if (value < MinMinZoomValue)
value = MinMinZoomValue;
_minimumZoom = value;
}
}
///
/// Gets or sets a value indicating the maximum zoom level.
///
public double MaximumZoom
{
get { return _maximumZoom; }
set
{
if (value < _minimumZoom)
value = _minimumZoom;
_maximumZoom = value;
}
}
///
/// Gets or sets a value indicating the maximum extents
///
public Envelope MaximumExtents
{
get { return _maximumExtents ?? (_maximumExtents = new Envelope()); }
set { _maximumExtents = value; }
}
///
/// Gets or sets the size of the Map in device units (Pixel)
///
public Size Size { get; set; }
///
/// Gets or sets the aspect-ratio of the pixel scales. A value less than
/// 1 will make the map streach upwards, and larger than 1 will make it smaller.
///
/// Throws an argument exception when value is 0 or less.
public double PixelAspectRatio
{
get { return _pixelAspectRatio; }
set
{
if (value <= 0)
throw new ArgumentException("Invalid Pixel Aspect Ratio");
_pixelAspectRatio = value;
}
}
///
/// Creates an instance of this class
///
internal MapViewPortGuard(Size size, double minZoom, double maxZoom)
{
Size = size;
MinimumZoom = minZoom;
MaximumZoom = maxZoom;
PixelAspectRatio = 1d;
}
///
/// Gets or sets a value indicating if should be enforced or not.
///
public bool EnforceMaximumExtents { get; set; }
///
/// Verifies the zoom level and center of the map
///
/// The zoom level to test
/// The center of the map. This coordinate might change so you must provide a copy if you want to preserve the old value
/// The zoom level, might have changed
public double VerifyZoom(double zoom, Coordinate center)
{
// Zoom within valid region
if (zoom < _minimumZoom)
zoom = _minimumZoom;
else if (zoom > _maximumZoom)
zoom = _maximumZoom;
if (!EnforceMaximumExtents)
return zoom;
double arWidth = (double)Size.Width/Size.Height;
if (zoom > _maximumExtents.Width)
zoom = _maximumExtents.Width;
if (zoom > arWidth * _maximumExtents.Height)
zoom = arWidth * _maximumExtents.Height;
zoom = VerifyValidViewport(zoom, center);
return zoom;
}
///
/// Verifies the valid viewport, makes adjustments if required
///
/// The current zoom
/// The
/// The verified zoom level
private double VerifyValidViewport(double zoom, Coordinate center)
{
var maxExtents = MaximumExtents ?? new Envelope();
if (maxExtents.IsNull)
return zoom;
double halfWidth = 0.5d * zoom;
double halfHeight = halfWidth * PixelAspectRatio * ((double)Size.Height / Size.Width);
double maxZoomHeight = _maximumZoom < double.MaxValue ? _maximumZoom : double.MaxValue;
if (2 * halfHeight > maxZoomHeight)
{
halfHeight = 0.5d*maxZoomHeight;
halfWidth = halfHeight / (_pixelAspectRatio * ((double)Size.Height / Size.Width));
zoom = 2 * halfWidth;
}
var testEnvelope = new Envelope(center.X - halfWidth, center.X + halfWidth,
center.Y - halfHeight, center.Y + halfHeight);
if (maxExtents.Contains(testEnvelope))
return zoom;
double dx = testEnvelope.MinX < maxExtents.MinX
? maxExtents.MinX - testEnvelope.MinX
: testEnvelope.MaxX > maxExtents.MaxX
? maxExtents.MaxX - testEnvelope.MaxX
: 0;
double dy = testEnvelope.MinY < maxExtents.MinY
? maxExtents.MinY - testEnvelope.MinY
: testEnvelope.MaxY > maxExtents.MaxY
? maxExtents.MaxY - testEnvelope.MaxY
: 0;
center.X += dx;
center.Y += dy;
return zoom;
}
}
///
/// Utility class to lock a map's viewport so it cannot be changed
///
public class MapViewportLock
{
private readonly Map _map;
private double _minimumZoom;
private double _maximumZoom;
private Envelope _maximumExtents;
private bool _enforce;
///
/// Creates an instance of this class
///
///
public MapViewportLock(Map map)
{
_map = map;
}
///
/// Lock the viewport of the map
///
public void Lock()
{
if (IsLocked)
return;
// Signal the viewport as locked
IsLocked = true;
// store the current extent settings
_minimumZoom = _map.MinimumZoom;
_maximumZoom = _map.MaximumZoom;
_maximumExtents = _map.MaximumExtents;
_enforce = _map.EnforceMaximumExtents;
// Lock the viewport
_map.MinimumZoom = _map.MaximumZoom = _map.Zoom;
_map.MaximumExtents = _map.Envelope;
_map.EnforceMaximumExtents = true;
}
///
/// Gets a value indicating that the map's viewport is locked
///
public bool IsLocked { get; private set; }
///
/// Unlock the viewport of the map
///
public void Unlock()
{
// Unlock the viewport
_map.EnforceMaximumExtents = _enforce;
_map.MaximumExtents = _maximumExtents;
_map.MinimumZoom = _minimumZoom;
_map.MaximumZoom = _maximumZoom;
// Signal the viewport as unlocked
IsLocked = false;
}
}
}