namespace GMap
.NET.WindowsPresentation
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Effects;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Threading;
using GMap.NET;
using GMap.NET.Internals;
using System.Diagnostics;
using GMap.NET.MapProviders;
using System.Windows.Media.Animation;
using GMap.NET.Projections;
/// <summary>
/// GMap.NET control for Windows Presentation
/// </summary>
public partial class GMapControl
: ItemsControl,
Interface, IDisposable
{
#region DependencyProperties and related stuff
public System.Windows.Point MapPoint
{
get
{
return (System.Windows.Point)GetValue
(MapPointProperty
);
}
set
{
SetValue
(MapPointProperty,
value);
}
}
// Using a DependencyProperty as the backing store for point. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MapPointProperty
=
DependencyProperty
.Register("MapPoint",
typeof(System.Windows.Point),
typeof(GMapControl
),
new PropertyMetadata
(new Point
(), OnMapPointPropertyChanged
));
private static void OnMapPointPropertyChanged
(DependencyObject source,
DependencyPropertyChangedEventArgs e
)
{
Point temp
= (Point
)e
.NewValue;
(source
as GMapControl
).Position = new PointLatLng
(temp
.X, temp
.Y);
}
public static readonly DependencyProperty MapProviderProperty
= DependencyProperty
.Register("MapProvider",
typeof(GMapProvider
),
typeof(GMapControl
),
new UIPropertyMetadata
(EmptyProvider
.Instance,
new PropertyChangedCallback
(MapProviderPropertyChanged
)));
/// <summary>
/// type of map
/// </summary>
[Browsable
(false)]
public GMapProvider MapProvider
{
get
{
return GetValue
(MapProviderProperty
) as GMapProvider
;
}
set
{
SetValue
(MapProviderProperty,
value);
}
}
private static void MapProviderPropertyChanged
(DependencyObject d, DependencyPropertyChangedEventArgs e
)
{
GMapControl map
= (GMapControl
)d
;
if(map
!= null && e
.NewValue != null)
{
Debug
.WriteLine("MapType: " + e
.OldValue + " -> " + e
.NewValue);
RectLatLng viewarea
= map
.SelectedArea;
if(viewarea
!= RectLatLng
.Empty)
{
map
.Position = new PointLatLng
(viewarea
.Lat - viewarea
.HeightLat / 2, viewarea
.Lng + viewarea
.WidthLng / 2);
}
else
{
viewarea
= map
.ViewArea;
}
map
.Core.Provider = e
.NewValue as GMapProvider
;
map
.Copyright = null;
if(!string.IsNullOrEmpty(map
.Core.Provider.Copyright))
{
map
.Copyright = new FormattedText
(map
.Core.Provider.Copyright, CultureInfo
.CurrentUICulture, FlowDirection
.LeftToRight,
new Typeface
("GenericSansSerif"),
9, Brushes
.Navy);
}
if(map
.Core.IsStarted && map
.Core.zoomToArea)
{
// restore zoomrect as close as possible
if(viewarea
!= RectLatLng
.Empty && viewarea
!= map
.ViewArea)
{
int bestZoom
= map
.Core.GetMaxZoomToFitRect(viewarea
);
if(bestZoom
> 0 && map
.Zoom != bestZoom
)
{
map
.Zoom = bestZoom
;
}
}
}
}
}
public static readonly DependencyProperty ZoomProperty
= DependencyProperty
.Register("Zoom",
typeof(double),
typeof(GMapControl
),
new UIPropertyMetadata
(0.0,
new PropertyChangedCallback
(ZoomPropertyChanged
),
new CoerceValueCallback
(OnCoerceZoom
)));
/// <summary>
/// map zoom
/// </summary>
[Category
("GMap.NET")]
public double Zoom
{
get
{
return (double)(GetValue
(ZoomProperty
));
}
set
{
SetValue
(ZoomProperty,
value);
}
}
private static object OnCoerceZoom
(DependencyObject o,
object value)
{
GMapControl map
= o
as GMapControl
;
if(map
!= null)
{
double result
= (double)value;
if(result
> map
.MaxZoom)
{
result
= map
.MaxZoom;
}
if(result
< map
.MinZoom)
{
result
= map
.MinZoom;
}
return result
;
}
else
{
return value;
}
}
private ScaleModes scaleMode
= ScaleModes
.Integer;
/// <summary>
/// Specifies, if a floating map scale is displayed using a
/// stretched, or a narrowed map.
/// If <code>ScaleMode</code> is <code>ScaleDown</code>,
/// then a scale of 12.3 is displayed using a map zoom level of 13
/// resized to the lower level. If the parameter is <code>ScaleUp</code> ,
/// then the same scale is displayed using a zoom level of 12 with an
/// enlarged scale. If the value is <code>Dynamic</code>, then until a
/// remainder of 0.25 <code>ScaleUp</code> is applied, for bigger
/// remainders <code>ScaleDown</code>.
/// </summary>
[Category
("GMap.NET")]
[Description
("map scale type")]
public ScaleModes ScaleMode
{
get
{
return scaleMode
;
}
set
{
scaleMode
= value;
InvalidateVisual
();
}
}
private static void ZoomPropertyChanged
(DependencyObject d, DependencyPropertyChangedEventArgs e
)
{
GMapControl map
= (GMapControl
)d
;
if(map
!= null && map
.MapProvider.Projection != null)
{
double value = (double)e
.NewValue;
Debug
.WriteLine("Zoom: " + e
.OldValue + " -> " + value);
double remainder
= value % 1;
if(map
.ScaleMode != ScaleModes
.Integer && remainder
!= 0 && map
.ActualWidth > 0)
{
bool scaleDown
;
switch(map
.ScaleMode)
{
case ScaleModes
.ScaleDown:
scaleDown
= true;
break;
case ScaleModes
.Dynamic:
scaleDown
= remainder
> 0.25;
break;
default:
scaleDown
= false;
break;
}
if(scaleDown
)
remainder
--;
double scaleValue
= Math
.Pow(2d, remainder
);
{
if(map
.MapScaleTransform == null)
{
map
.MapScaleTransform = map
.lastScaleTransform;
}
map
.MapScaleTransform.ScaleX = scaleValue
;
map
.MapScaleTransform.ScaleY = scaleValue
;
map
.Core.scaleX = 1 / scaleValue
;
map
.Core.scaleY = 1 / scaleValue
;
map
.MapScaleTransform.CenterX = map
.ActualWidth / 2;
map
.MapScaleTransform.CenterY = map
.ActualHeight / 2;
}
map
.Core.Zoom = Convert
.ToInt32(scaleDown
? Math
.Ceiling(value) : value - remainder
);
}
else
{
map
.MapScaleTransform = null;
map
.Core.scaleX = 1;
map
.Core.scaleY = 1;
map
.Core.Zoom = (int)Math
.Floor(value);
}
if(map
.IsLoaded)
{
map
.ForceUpdateOverlays();
map
.InvalidateVisual(true);
}
}
}
readonly ScaleTransform lastScaleTransform
= new ScaleTransform
();
#endregion
readonly Core Core
= new Core
();
//GRect region;
delegate void MethodInvoker
();
PointLatLng selectionStart
;
PointLatLng selectionEnd
;
Typeface tileTypeface
= new Typeface
("Arial");
bool showTileGridLines
= false;
FormattedText Copyright
;
/// <summary>
/// enables filling empty tiles using lower level images
/// </summary>
[Browsable
(false)]
public bool FillEmptyTiles
{
get
{
return Core
.fillEmptyTiles;
}
set
{
Core
.fillEmptyTiles = value;
}
}
/// <summary>
/// max zoom
/// </summary>
[Category
("GMap.NET")]
[Description
("maximum zoom level of map")]
public int MaxZoom
{
get
{
return Core
.maxZoom;
}
set
{
Core
.maxZoom = value;
}
}
/// <summary>
/// min zoom
/// </summary>
[Category
("GMap.NET")]
[Description
("minimum zoom level of map")]
public int MinZoom
{
get
{
return Core
.minZoom;
}
set
{
Core
.minZoom = value;
}
}
/// <summary>
/// pen for empty tile borders
/// </summary>
public Pen EmptyTileBorders
= new Pen
(Brushes
.White,
1.0);
/// <summary>
/// pen for Selection
/// </summary>
public Pen SelectionPen
= new Pen
(Brushes
.Blue,
2.0);
/// <summary>
/// background of selected area
/// </summary>
public Brush SelectedAreaFill
= new SolidColorBrush
(Color
.FromArgb(33, Colors
.RoyalBlue.R, Colors
.RoyalBlue.G, Colors
.RoyalBlue.B));
/// <summary>
/// /// <summary>
/// pen for empty tile background
/// </summary>
public Brush EmptytileBrush
= Brushes
.Navy;
/// <summary>
/// text on empty tiles
/// </summary>
public FormattedText EmptyTileText
= new FormattedText
("We are sorry, but we don't\nhave imagery at this zoom\n level for this region.",
System.Globalization.CultureInfo.CurrentUICulture, FlowDirection
.LeftToRight,
new Typeface
("Arial"),
16, Brushes
.Blue);
/// <summary>
/// map zooming type for mouse wheel
/// </summary>
[Category
("GMap.NET")]
[Description
("map zooming type for mouse wheel")]
public MouseWheelZoomType MouseWheelZoomType
{
get
{
return Core
.MouseWheelZoomType;
}
set
{
Core
.MouseWheelZoomType = value;
}
}
/// <summary>
/// enable map zoom on mouse wheel
/// </summary>
[Category
("GMap.NET")]
[Description
("enable map zoom on mouse wheel")]
public bool MouseWheelZoomEnabled
{
get
{
return Core
.MouseWheelZoomEnabled;
}
set
{
Core
.MouseWheelZoomEnabled = value;
}
}
/// <summary>
/// map dragg button
/// </summary>
[Category
("GMap.NET")]
public MouseButton DragButton
= MouseButton
.Right;
/// <summary>
/// use circle for selection
/// </summary>
public bool SelectionUseCircle
= false;
/// <summary>
/// shows tile gridlines
/// </summary>
[Category
("GMap.NET")]
public bool ShowTileGridLines
{
get
{
return showTileGridLines
;
}
set
{
showTileGridLines
= value;
InvalidateVisual
();
}
}
/// <summary>
/// retry count to get tile
/// </summary>
[Browsable
(false)]
public int RetryLoadTile
{
get
{
return Core
.RetryLoadTile;
}
set
{
Core
.RetryLoadTile = value;
}
}
/// <summary>
/// how many levels of tiles are staying decompresed in memory
/// </summary>
[Browsable
(false)]
public int LevelsKeepInMemmory
{
get
{
return Core
.LevelsKeepInMemmory;
}
set
{
Core
.LevelsKeepInMemmory = value;
}
}
/// <summary>
/// current selected area in map
/// </summary>
private RectLatLng selectedArea
;
[Browsable
(false)]
public RectLatLng SelectedArea
{
get
{
return selectedArea
;
}
set
{
selectedArea
= value;
InvalidateVisual
();
}
}
/// <summary>
/// is touch control enabled
/// </summary>
public bool TouchEnabled
= true;
/// <summary>
/// map boundaries
/// </summary>
public RectLatLng
? BoundsOfMap
= null;
/// <summary>
/// occurs when mouse selection is changed
/// </summary>
public event SelectionChange OnSelectionChange
;
/// <summary>
/// list of markers
/// </summary>
public readonly ObservableCollection
<GMapMarker
> Markers
= new ObservableCollection
<GMapMarker
>();
/// <summary>
/// current markers overlay offset
/// </summary>
internal readonly TranslateTransform MapTranslateTransform
= new TranslateTransform
();
internal readonly TranslateTransform MapOverlayTranslateTransform
= new TranslateTransform
();
internal ScaleTransform MapScaleTransform
= new ScaleTransform
();
internal RotateTransform MapRotateTransform
= new RotateTransform
();
protected bool DesignModeInConstruct
{
get
{
return System.ComponentModel.DesignerProperties.GetIsInDesignMode(this);
}
}
Canvas mapCanvas
= null;
/// <summary>
/// markers overlay
/// </summary>
internal Canvas MapCanvas
{
get
{
if(mapCanvas
== null)
{
if(this.VisualChildrenCount > 0)
{
Border border
= VisualTreeHelper
.GetChild(this,
0) as Border
;
ItemsPresenter items
= border
.Child as ItemsPresenter
;
DependencyObject target
= VisualTreeHelper
.GetChild(items,
0);
mapCanvas
= target
as Canvas
;
mapCanvas
.RenderTransform = MapTranslateTransform
;
}
}
return mapCanvas
;
}
}
public GMaps Manager
{
get
{
return GMaps
.Instance;
}
}
static DataTemplate DataTemplateInstance
;
static ItemsPanelTemplate ItemsPanelTemplateInstance
;
static Style StyleInstance
;
public GMapControl
()
{
if(!DesignModeInConstruct
)
{
#region -- templates --
#region -- xaml --
// <ItemsControl Name="figures">
// <ItemsControl.ItemTemplate>
// <DataTemplate>
// <ContentPresenter Content="{Binding Path=Shape}" />
// </DataTemplate>
// </ItemsControl.ItemTemplate>
// <ItemsControl.ItemsPanel>
// <ItemsPanelTemplate>
// <Canvas />
// </ItemsPanelTemplate>
// </ItemsControl.ItemsPanel>
// <ItemsControl.ItemContainerStyle>
// <Style>
// <Setter Property="Canvas.Left" Value="{Binding Path=LocalPositionX}"/>
// <Setter Property="Canvas.Top" Value="{Binding Path=LocalPositionY}"/>
// </Style>
// </ItemsControl.ItemContainerStyle>
//</ItemsControl>
#endregion
if(DataTemplateInstance
== null)
{
DataTemplateInstance
= new DataTemplate
(typeof(GMapMarker
));
{
FrameworkElementFactory fef
= new FrameworkElementFactory
(typeof(ContentPresenter
));
fef
.SetBinding(ContentPresenter
.ContentProperty,
new Binding
("Shape"));
DataTemplateInstance
.VisualTree = fef
;
}
}
ItemTemplate
= DataTemplateInstance
;
if(ItemsPanelTemplateInstance
== null)
{
var factoryPanel
= new FrameworkElementFactory
(typeof(Canvas
));
{
factoryPanel
.SetValue(Canvas
.IsItemsHostProperty,
true);
ItemsPanelTemplateInstance
= new ItemsPanelTemplate
();
{
ItemsPanelTemplateInstance
.VisualTree = factoryPanel
;
}
}
}
ItemsPanel
= ItemsPanelTemplateInstance
;
if(StyleInstance
== null)
{
StyleInstance
= new Style
();
{
StyleInstance
.Setters.Add(new Setter
(Canvas
.LeftProperty,
new Binding
("LocalPositionX")));
StyleInstance
.Setters.Add(new Setter
(Canvas
.TopProperty,
new Binding
("LocalPositionY")));
StyleInstance
.Setters.Add(new Setter
(Canvas
.ZIndexProperty,
new Binding
("ZIndex")));
}
}
ItemContainerStyle
= StyleInstance
;
#endregion
ClipToBounds
= true;
SnapsToDevicePixels
= true;
Core
.SystemType = "WindowsPresentation";
Core
.RenderMode = GMap
.NET.RenderMode.WPF;
Core
.OnMapZoomChanged += new MapZoomChanged
(ForceUpdateOverlays
);
Loaded
+= new RoutedEventHandler
(GMapControl_Loaded
);
Dispatcher
.ShutdownStarted += new EventHandler
(Dispatcher_ShutdownStarted
);
SizeChanged
+= new SizeChangedEventHandler
(GMapControl_SizeChanged
);
// by default its internal property, feel free to use your own
if(ItemsSource
== null)
{
ItemsSource
= Markers
;
}
Core
.Zoom = (int)((double)ZoomProperty
.DefaultMetadata.DefaultValue);
}
}
static GMapControl
()
{
GMapImageProxy
.Enable();
#if !PocketPC
GMaps
.Instance.SQLitePing();
#endif
}
void invalidatorEngage
(object sender, ProgressChangedEventArgs e
)
{
base.InvalidateVisual();
}
/// <summary>
/// enque built-in thread safe invalidation
/// </summary>
public new void InvalidateVisual
()
{
if(Core
.Refresh != null)
{
Core
.Refresh.Set();
}
}
/// <summary>
/// Invalidates the rendering of the element, and forces a complete new layout
/// pass. System.Windows.UIElement.OnRender(System.Windows.Media.DrawingContext)
/// is called after the layout cycle is completed. If not forced enques built-in thread safe invalidation
/// </summary>
/// <param name="forced"></param>
public void InvalidateVisual
(bool forced
)
{
if(forced
)
{
lock(Core
.invalidationLock)
{
Core
.lastInvalidation = DateTime
.Now;
}
base.InvalidateVisual();
}
else
{
InvalidateVisual
();
}
}
protected override void OnItemsChanged
(System.Collections.Specialized.NotifyCollectionChangedEventArgs e
)
{
base.OnItemsChanged(e
);
if(e
.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
ForceUpdateOverlays
(e
.NewItems);
}
else
{
InvalidateVisual
();
}
}
/// <summary>
/// inits core system
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void GMapControl_Loaded
(object sender, RoutedEventArgs e
)
{
if(!Core
.IsStarted)
{
if(lazyEvents
)
{
lazyEvents
= false;
if(lazySetZoomToFitRect
.HasValue)
{
SetZoomToFitRect
(lazySetZoomToFitRect
.Value);
lazySetZoomToFitRect
= null;
}
}
Core
.OnMapOpen().ProgressChanged += new ProgressChangedEventHandler
(invalidatorEngage
);
ForceUpdateOverlays
();
if(Application
.Current != null)
{
loadedApp
= Application
.Current;
loadedApp
.Dispatcher.Invoke(DispatcherPriority
.ApplicationIdle,
new Action
(delegate()
{
loadedApp
.SessionEnding += new SessionEndingCancelEventHandler
(Current_SessionEnding
);
}
));
}
}
}
Application loadedApp
;
void Current_SessionEnding
(object sender, SessionEndingCancelEventArgs e
)
{
GMaps
.Instance.CancelTileCaching();
}
void Dispatcher_ShutdownStarted
(object sender, EventArgs e
)
{
Dispose
();
}
/// <summary>
/// recalculates size
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void GMapControl_SizeChanged
(object sender, SizeChangedEventArgs e
)
{
System.Windows.Size constraint
= e
.NewSize;
// 50px outside control
//region = new GRect(-50, -50, (int)constraint.Width + 100, (int)constraint.Height + 100);
Core
.OnMapSizeChanged((int)constraint
.Width,
(int)constraint
.Height);
if(Core
.IsStarted)
{
if(IsRotated
)
{
UpdateRotationMatrix
();
}
ForceUpdateOverlays
();
}
}
void ForceUpdateOverlays
()
{
ForceUpdateOverlays
(ItemsSource
);
}
void ForceUpdateOverlays
(System.Collections.IEnumerable items
)
{
using(Dispatcher
.DisableProcessing())
{
UpdateMarkersOffset
();
foreach(GMapMarker i
in items
)
{
if(i
!= null)
{
i
.ForceUpdateLocalPosition(this);
if(i
is IShapable
)
{
(i
as IShapable
).RegenerateShape(this);
}
}
}
}
InvalidateVisual
();
}
/// <summary>
/// updates markers overlay offset
/// </summary>
void UpdateMarkersOffset
()
{
if(MapCanvas
!= null)
{
if(MapScaleTransform
!= null)
{
var tp
= MapScaleTransform
.Transform(new System.Windows.Point(Core
.renderOffset.X, Core
.renderOffset.Y));
MapOverlayTranslateTransform
.X = tp
.X;
MapOverlayTranslateTransform
.Y = tp
.Y;
// map is scaled already
MapTranslateTransform
.X = Core
.renderOffset.X;
MapTranslateTransform
.Y = Core
.renderOffset.Y;
}
else
{
MapTranslateTransform
.X = Core
.renderOffset.X;
MapTranslateTransform
.Y = Core
.renderOffset.Y;
MapOverlayTranslateTransform
.X = MapTranslateTransform
.X;
MapOverlayTranslateTransform
.Y = MapTranslateTransform
.Y;
}
}
}
public Brush EmptyMapBackground
= Brushes
.WhiteSmoke;
/// <summary>
/// render map in WPF
/// </summary>
/// <param name="g"></param>
void DrawMap
(DrawingContext g
)
{
if(MapProvider
== EmptyProvider
.Instance || MapProvider
== null)
{
return;
}
Core
.tileDrawingListLock.AcquireReaderLock();
Core
.Matrix.EnterReadLock();
try
{
foreach(var tilePoint
in Core
.tileDrawingList)
{
Core
.tileRect.Location = tilePoint
.PosPixel;
Core
.tileRect.OffsetNegative(Core
.compensationOffset);
//if(region.IntersectsWith(Core.tileRect) || IsRotated)
{
bool found
= false;
Tile t
= Core
.Matrix.GetTileWithNoLock(Core
.Zoom, tilePoint
.PosXY);
if(t
.NotEmpty)
{
foreach(GMapImage img
in t
.Overlays)
{
if(img
!= null && img
.Img != null)
{
if(!found
)
found
= true;
var imgRect
= new Rect
(Core
.tileRect.X + 0.6, Core
.tileRect.Y + 0.6, Core
.tileRect.Width + 0.6, Core
.tileRect.Height + 0.6);
if(!img
.IsParent)
{
g
.DrawImage(img
.Img, imgRect
);
}
else
{
// TODO: move calculations to loader thread
var geometry
= new RectangleGeometry
(imgRect
);
var parentImgRect
= new Rect
(Core
.tileRect.X - Core
.tileRect.Width * img
.Xoff + 0.6, Core
.tileRect.Y - Core
.tileRect.Height * img
.Yoff + 0.6, Core
.tileRect.Width * img
.Ix + 0.6, Core
.tileRect.Height * img
.Ix + 0.6);
g
.PushClip(geometry
);
g
.DrawImage(img
.Img, parentImgRect
);
g
.Pop();
geometry
= null;
}
}
}
}
else if(FillEmptyTiles
&& MapProvider
.Projection is MercatorProjection
)
{
#region -- fill empty tiles --
int zoomOffset
= 1;
Tile parentTile
= Tile
.Empty;
long Ix
= 0;
while(!parentTile
.NotEmpty && zoomOffset
< Core
.Zoom && zoomOffset
<= LevelsKeepInMemmory
)
{
Ix
= (long)Math
.Pow(2, zoomOffset
);
parentTile
= Core
.Matrix.GetTileWithNoLock(Core
.Zoom - zoomOffset
++,
new GMap
.NET.GPoint((int)(tilePoint
.PosXY.X / Ix
),
(int)(tilePoint
.PosXY.Y / Ix
)));
}
if(parentTile
.NotEmpty)
{
long Xoff
= Math
.Abs(tilePoint
.PosXY.X - (parentTile
.Pos.X * Ix
));
long Yoff
= Math
.Abs(tilePoint
.PosXY.Y - (parentTile
.Pos.Y * Ix
));
var geometry
= new RectangleGeometry
(new Rect
(Core
.tileRect.X + 0.6, Core
.tileRect.Y + 0.6, Core
.tileRect.Width + 0.6, Core
.tileRect.Height + 0.6));
var parentImgRect
= new Rect
(Core
.tileRect.X - Core
.tileRect.Width * Xoff
+ 0.6, Core
.tileRect.Y - Core
.tileRect.Height * Yoff
+ 0.6, Core
.tileRect.Width * Ix
+ 0.6, Core
.tileRect.Height * Ix
+ 0.6);
// render tile
{
foreach(GMapImage img
in parentTile
.Overlays)
{
if(img
!= null && img
.Img != null && !img
.IsParent)
{
if(!found
)
found
= true;
g
.PushClip(geometry
);
g
.DrawImage(img
.Img, parentImgRect
);
g
.DrawRectangle(SelectedAreaFill,
null, geometry
.Bounds);
g
.Pop();
}
}
}
geometry
= null;
}
#endregion
}
// add text if tile is missing
if(!found
)
{
lock(Core
.FailedLoads)
{
var lt
= new LoadTask
(tilePoint
.PosXY, Core
.Zoom);
if(Core
.FailedLoads.ContainsKey(lt
))
{
g
.DrawRectangle(EmptytileBrush, EmptyTileBorders,
new Rect
(Core
.tileRect.X, Core
.tileRect.Y, Core
.tileRect.Width, Core
.tileRect.Height));
var ex
= Core
.FailedLoads[lt
];
FormattedText TileText
= new FormattedText
("Exception: " + ex
.Message,
System.Globalization.CultureInfo.CurrentUICulture, FlowDirection
.LeftToRight, tileTypeface,
14, Brushes
.Red);
TileText
.MaxTextWidth = Core
.tileRect.Width - 11;
g
.DrawText(TileText,
new System.Windows.Point(Core
.tileRect.X + 11, Core
.tileRect.Y + 11));
g
.DrawText(EmptyTileText,
new System.Windows.Point(Core
.tileRect.X + Core
.tileRect.Width / 2 - EmptyTileText
.Width / 2, Core
.tileRect.Y + Core
.tileRect.Height / 2 - EmptyTileText
.Height / 2));
}
}
}
if(ShowTileGridLines
)
{
g
.DrawRectangle(null, EmptyTileBorders,
new Rect
(Core
.tileRect.X, Core
.tileRect.Y, Core
.tileRect.Width, Core
.tileRect.Height));
if(tilePoint
.PosXY == Core
.centerTileXYLocation)
{
FormattedText TileText
= new FormattedText
("CENTER:" + tilePoint
.ToString(),
System.Globalization.CultureInfo.CurrentUICulture, FlowDirection
.LeftToRight, tileTypeface,
16, Brushes
.Red);
TileText
.MaxTextWidth = Core
.tileRect.Width;
g
.DrawText(TileText,
new System.Windows.Point(Core
.tileRect.X + Core
.tileRect.Width / 2 - EmptyTileText
.Width / 2, Core
.tileRect.Y + Core
.tileRect.Height / 2 - TileText
.Height / 2));
}
else
{
FormattedText TileText
= new FormattedText
("TILE: " + tilePoint
.ToString(),
System.Globalization.CultureInfo.CurrentUICulture, FlowDirection
.LeftToRight, tileTypeface,
16, Brushes
.Red);
TileText
.MaxTextWidth = Core
.tileRect.Width;
g
.DrawText(TileText,
new System.Windows.Point(Core
.tileRect.X + Core
.tileRect.Width / 2 - EmptyTileText
.Width / 2, Core
.tileRect.Y + Core
.tileRect.Height / 2 - TileText
.Height / 2));
}
}
}
}
}
finally
{
Core
.Matrix.LeaveReadLock();
Core
.tileDrawingListLock.ReleaseReaderLock();
}
}
/// <summary>
/// gets image of the current view
/// </summary>
/// <returns></returns>
public ImageSource ToImageSource
()
{
FrameworkElement obj
= this;
// Save current canvas transform
Transform transform
= obj
.LayoutTransform;
obj
.LayoutTransform = null;
// fix margin offset as well
Thickness margin
= obj
.Margin;
obj
.Margin = new Thickness
(0,
0,
margin
.Right - margin
.Left, margin
.Bottom - margin
.Top);
// Get the size of canvas
Size size
= new Size
(obj
.ActualWidth, obj
.ActualHeight);
// force control to Update
obj
.Measure(size
);
obj
.Arrange(new Rect
(size
));
RenderTargetBitmap bmp
= new RenderTargetBitmap
(
(int)size
.Width,
(int)size
.Height,
96,
96, PixelFormats
.Pbgra32);
bmp
.Render(obj
);
if(bmp
.CanFreeze)
{
bmp
.Freeze();
}
// return values as they were before
obj
.LayoutTransform = transform
;
obj
.Margin = margin
;
return bmp
;
}
/// <summary>
/// creates path from list of points, for performance set addBlurEffect to false
/// </summary>
/// <param name="pl"></param>
/// <returns></returns>
public virtual Path CreateRoutePath
(List
<Point
> localPath
)
{
return CreateRoutePath
(localPath,
true ,
null);
}
/// <summary>
/// creates path from list of points, for performance set addBlurEffect to false
/// </summary>
/// <param name="pl"></param>
/// <returns></returns>
public virtual Path CreateRoutePath
(List
<Point
> localPath,
bool addBlurEffect, Brush brush
)
{
// Create a StreamGeometry to use to specify myPath.
StreamGeometry geometry
= new StreamGeometry
();
using(StreamGeometryContext ctx
= geometry
.Open())
{
ctx
.BeginFigure(localPath
[0],
false,
false);
// Draw a line to the next specified point.
ctx
.PolyLineTo(localPath,
true,
true);
}
// Freeze the geometry (make it unmodifiable)
// for additional performance benefits.
geometry
.Freeze();
// Create a path to draw a geometry with.
Path myPath
= new Path
();
{
// Specify the shape of the Path using the StreamGeometry.
myPath
.Data = geometry
;
if (addBlurEffect
)
{
BlurEffect ef
= new BlurEffect
();
{
ef
.KernelType = KernelType
.Gaussian;
ef
.Radius = 3.0;
ef
.RenderingBias = RenderingBias
.Performance;
}
myPath
.Effect = ef
;
}
if(brush
!=null)
myPath
.Stroke = brush
;
else
myPath
.Stroke = Brushes
.Magenta;
myPath
.StrokeThickness = 5;
myPath
.StrokeLineJoin = PenLineJoin
.Round;
myPath
.StrokeStartLineCap = PenLineCap
.Triangle;
myPath
.StrokeEndLineCap = PenLineCap
.Square;
myPath
.Opacity = 0.6;
myPath
.IsHitTestVisible = false;
}
return myPath
;
}
/// <summary>
/// creates path from list of points, for performance set addBlurEffect to false
/// </summary>
/// <param name="pl"></param>
/// <returns></returns>
public virtual Path CreatePolygonPath
(List
<Point
> localPath
)
{
return CreatePolygonPath
(localPath,
false);
}
/// <summary>
/// creates path from list of points, for performance set addBlurEffect to false
/// </summary>
/// <param name="pl"></param>
/// <returns></returns>
public virtual Path CreatePolygonPath
(List
<Point
> localPath,
bool addBlurEffect
)
{
// Create a StreamGeometry to use to specify myPath.
StreamGeometry geometry
= new StreamGeometry
();
using(StreamGeometryContext ctx
= geometry
.Open())
{
ctx
.BeginFigure(localPath
[0],
true,
true);
// Draw a line to the next specified point.
ctx
.PolyLineTo(localPath,
true,
true);
}
// Freeze the geometry (make it unmodifiable)
// for additional performance benefits.
geometry
.Freeze();
// Create a path to draw a geometry with.
Path myPath
= new Path
();
{
// Specify the shape of the Path using the StreamGeometry.
myPath
.Data = geometry
;
if (addBlurEffect
)
{
BlurEffect ef
= new BlurEffect
();
{
ef
.KernelType = KernelType
.Gaussian;
ef
.Radius = 3.0;
ef
.RenderingBias = RenderingBias
.Performance;
}
myPath
.Effect = ef
;
}
myPath
.Stroke = Brushes
.MidnightBlue;
myPath
.StrokeThickness = 5;
myPath
.StrokeLineJoin = PenLineJoin
.Round;
myPath
.StrokeStartLineCap = PenLineCap
.Triangle;
myPath
.StrokeEndLineCap = PenLineCap
.Square;
myPath
.Fill = Brushes
.AliceBlue;
myPath
.Opacity = 0.6;
myPath
.IsHitTestVisible = false;
}
return myPath
;
}
/// <summary>
/// sets zoom to max to fit rect
/// </summary>
/// <param name="rect">area</param>
/// <returns></returns>
public bool SetZoomToFitRect
(RectLatLng rect
)
{
if(lazyEvents
)
{
lazySetZoomToFitRect
= rect
;
}
else
{
int maxZoom
= Core
.GetMaxZoomToFitRect(rect
);
if(maxZoom
> 0)
{
PointLatLng center
= new PointLatLng
(rect
.Lat - (rect
.HeightLat / 2), rect
.Lng + (rect
.WidthLng / 2));
Position
= center
;
if(maxZoom
> MaxZoom
)
{
maxZoom
= MaxZoom
;
}
if(Core
.Zoom != maxZoom
)
{
Zoom
= maxZoom
;
}
return true;
}
}
return false;
}
RectLatLng
? lazySetZoomToFitRect
= null;
bool lazyEvents
= true;
/// <summary>
/// sets to max zoom to fit all markers and centers them in map
/// </summary>
/// <param name="ZIndex">z index or null to check all</param>
/// <returns></returns>
public bool ZoomAndCenterMarkers
(int? ZIndex
)
{
RectLatLng
? rect
= GetRectOfAllMarkers
(ZIndex
);
if(rect
.HasValue)
{
return SetZoomToFitRect
(rect
.Value);
}
return false;
}
/// <summary>
/// gets rectangle with all objects inside
/// </summary>
/// <param name="ZIndex">z index or null to check all</param>
/// <returns></returns>
public RectLatLng
? GetRectOfAllMarkers
(int? ZIndex
)
{
RectLatLng
? ret
= null;
double left
= double.MaxValue;
double top
= double.MinValue;
double right
= double.MinValue;
double bottom
= double.MaxValue;
IEnumerable
<GMapMarker
> Overlays
;
if(ZIndex
.HasValue)
{
Overlays
= ItemsSource
.Cast<GMapMarker
>().Where(p
=> p
!= null && p
.ZIndex == ZIndex
);
}
else
{
Overlays
= ItemsSource
.Cast<GMapMarker
>();
}
if(Overlays
!= null)
{
foreach(var m
in Overlays
)
{
if(m
.Shape != null && m
.Shape.Visibility == System.Windows.Visibility.Visible)
{
// left
if(m
.Position.Lng < left
)
{
left
= m
.Position.Lng;
}
// top
if(m
.Position.Lat > top
)
{
top
= m
.Position.Lat;
}
// right
if(m
.Position.Lng > right
)
{
right
= m
.Position.Lng;
}
// bottom
if(m
.Position.Lat < bottom
)
{
bottom
= m
.Position.Lat;
}
}
}
}
if(left
!= double.MaxValue && right
!= double.MinValue && top
!= double.MinValue && bottom
!= double.MaxValue)
{
ret
= RectLatLng
.FromLTRB(left, top, right, bottom
);
}
return ret
;
}
/// <summary>
/// offset position in pixels
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
public void Offset
(int x,
int y
)
{
if(IsLoaded
)
{
if(IsRotated
)
{
Point p
= new Point
(x, y
);
p
= rotationMatrixInvert
.Transform(p
);
x
= (int)p
.X;
y
= (int)p
.Y;
Core
.DragOffset(new GPoint
(x, y
));
ForceUpdateOverlays
();
}
else
{
Core
.DragOffset(new GPoint
(x, y
));
UpdateMarkersOffset
();
InvalidateVisual
(true);
}
}
}
readonly RotateTransform rotationMatrix
= new RotateTransform
();
GeneralTransform rotationMatrixInvert
= new RotateTransform
();
/// <summary>
/// updates rotation matrix
/// </summary>
void UpdateRotationMatrix
()
{
System.Windows.Point center
= new System.Windows.Point(ActualWidth
/ 2.0, ActualHeight
/ 2.0);
rotationMatrix
.Angle = -Bearing
;
rotationMatrix
.CenterY = center
.Y;
rotationMatrix
.CenterX = center
.X;
rotationMatrixInvert
= rotationMatrix
.Inverse;
}
/// <summary>
/// returs true if map bearing is not zero
/// </summary>
public bool IsRotated
{
get
{
return Core
.IsRotated;
}
}
/// <summary>
/// bearing for rotation of the map
/// </summary>
[Category
("GMap.NET")]
public float Bearing
{
get
{
return Core
.bearing;
}
set
{
if(Core
.bearing != value)
{
bool resize
= Core
.bearing == 0;
Core
.bearing = value;
UpdateRotationMatrix
();
if(value != 0 && value % 360 != 0)
{
Core
.IsRotated = true;
if(Core
.tileRectBearing.Size == Core
.tileRect.Size)
{
Core
.tileRectBearing = Core
.tileRect;
Core
.tileRectBearing.Inflate(1,
1);
}
}
else
{
Core
.IsRotated = false;
Core
.tileRectBearing = Core
.tileRect;
}
if(resize
)
{
Core
.OnMapSizeChanged((int)ActualWidth,
(int)ActualHeight
);
}
if(Core
.IsStarted)
{
ForceUpdateOverlays
();
}
}
}
}
/// <summary>
/// apply transformation if in rotation mode
/// </summary>
System.Windows.Point ApplyRotation
(double x,
double y
)
{
System.Windows.Point ret
= new System.Windows.Point(x, y
);
if(IsRotated
)
{
ret
= rotationMatrix
.Transform(ret
);
}
return ret
;
}
/// <summary>
/// apply transformation if in rotation mode
/// </summary>
System.Windows.Point ApplyRotationInversion
(double x,
double y
)
{
System.Windows.Point ret
= new System.Windows.Point(x, y
);
if(IsRotated
)
{
ret
= rotationMatrixInvert
.Transform(ret
);
}
return ret
;
}
#region UserControl Events
protected override void OnRender
(DrawingContext drawingContext
)
{
if(!Core
.IsStarted)
return;
drawingContext
.DrawRectangle(EmptyMapBackground,
null,
new Rect
(RenderSize
));
if(IsRotated
)
{
drawingContext
.PushTransform(rotationMatrix
);
if(MapScaleTransform
!= null)
{
drawingContext
.PushTransform(MapScaleTransform
);
drawingContext
.PushTransform(MapTranslateTransform
);
{
DrawMap
(drawingContext
);
}
drawingContext
.Pop();
drawingContext
.Pop();
}
else
{
drawingContext
.PushTransform(MapTranslateTransform
);
{
DrawMap
(drawingContext
);
}
drawingContext
.Pop();
}
drawingContext
.Pop();
}
else
{
if(MapScaleTransform
!= null)
{
drawingContext
.PushTransform(MapScaleTransform
);
drawingContext
.PushTransform(MapTranslateTransform
);
{
DrawMap
(drawingContext
);
#if DEBUG
drawingContext
.DrawLine(VirtualCenterCrossPen,
new Point
(-20,
0),
new Point
(20,
0));
drawingContext
.DrawLine(VirtualCenterCrossPen,
new Point
(0,
-20),
new Point
(0,
20));
#endif
}
drawingContext
.Pop();
drawingContext
.Pop();
}
else
{
drawingContext
.PushTransform(MapTranslateTransform
);
{
DrawMap
(drawingContext
);
#if DEBUG
drawingContext
.DrawLine(VirtualCenterCrossPen,
new Point
(-20,
0),
new Point
(20,
0));
drawingContext
.DrawLine(VirtualCenterCrossPen,
new Point
(0,
-20),
new Point
(0,
20));
#endif
}
drawingContext
.Pop();
}
}
// selection
if(!SelectedArea
.IsEmpty)
{
GPoint p1
= FromLatLngToLocal
(SelectedArea
.LocationTopLeft);
GPoint p2
= FromLatLngToLocal
(SelectedArea
.LocationRightBottom);
long x1
= p1
.X;
long y1
= p1
.Y;
long x2
= p2
.X;
long y2
= p2
.Y;
if(SelectionUseCircle
)
{
drawingContext
.DrawEllipse(SelectedAreaFill, SelectionPen,
new System.Windows.Point(x1
+ (x2
- x1
) / 2, y1
+ (y2
- y1
) / 2),
(x2
- x1
) / 2,
(y2
- y1
) / 2);
}
else
{
drawingContext
.DrawRoundedRectangle(SelectedAreaFill, SelectionPen,
new Rect
(x1, y1, x2
- x1, y2
- y1
),
5,
5);
}
}
if(ShowCenter
)
{
drawingContext
.DrawLine(CenterCrossPen,
new System.Windows.Point((ActualWidth
/ 2) - 5, ActualHeight
/ 2),
new System.Windows.Point((ActualWidth
/ 2) + 5, ActualHeight
/ 2));
drawingContext
.DrawLine(CenterCrossPen,
new System.Windows.Point(ActualWidth
/ 2,
(ActualHeight
/ 2) - 5),
new System.Windows.Point(ActualWidth
/ 2,
(ActualHeight
/ 2) + 5));
}
if(renderHelperLine
)
{
var p
= Mouse
.GetPosition(this);
drawingContext
.DrawLine(HelperLinePen,
new Point
(p
.X,
0),
new Point
(p
.X, ActualHeight
));
drawingContext
.DrawLine(HelperLinePen,
new Point
(0, p
.Y),
new Point
(ActualWidth, p
.Y));
}
#region -- copyright --
if(Copyright
!= null)
{
drawingContext
.DrawText(Copyright,
new System.Windows.Point(5, ActualHeight
- Copyright
.Height - 5));
}
#endregion
base.OnRender(drawingContext
);
}
public Pen CenterCrossPen
= new Pen
(Brushes
.Red,
1);
public bool ShowCenter
= true;
#if DEBUG
readonly Pen VirtualCenterCrossPen
= new Pen
(Brushes
.Blue,
1);
#endif
HelperLineOptions helperLineOption
= HelperLineOptions
.DontShow;
/// <summary>
/// draw lines at the mouse pointer position
/// </summary>
[Browsable
(false)]
public HelperLineOptions HelperLineOption
{
get
{
return helperLineOption
;
}
set
{
helperLineOption
= value;
renderHelperLine
= (helperLineOption
== HelperLineOptions
.ShowAlways);
if(Core
.IsStarted)
{
InvalidateVisual
();
}
}
}
public Pen HelperLinePen
= new Pen
(Brushes
.Blue,
1);
bool renderHelperLine
= false;
protected override void OnKeyUp
(KeyEventArgs e
)
{
base.OnKeyUp(e
);
if(HelperLineOption
== HelperLineOptions
.ShowOnModifierKey)
{
renderHelperLine
= !(e
.IsUp && (e
.Key == Key
.LeftShift || e
.SystemKey == Key
.LeftAlt));
if(!renderHelperLine
)
{
InvalidateVisual
();
}
}
}
protected override void OnKeyDown
(KeyEventArgs e
)
{
base.OnKeyDown(e
);
if(HelperLineOption
== HelperLineOptions
.ShowOnModifierKey)
{
renderHelperLine
= e
.IsDown && (e
.Key == Key
.LeftShift || e
.SystemKey == Key
.LeftAlt);
if(renderHelperLine
)
{
InvalidateVisual
();
}
}
}
/// <summary>
/// reverses MouseWheel zooming direction
/// </summary>
public bool InvertedMouseWheelZooming
= false;
/// <summary>
/// lets you zoom by MouseWheel even when pointer is in area of marker
/// </summary>
public bool IgnoreMarkerOnMouseWheel
= false;
protected override void OnMouseWheel
(MouseWheelEventArgs e
)
{
base.OnMouseWheel(e
);
if(MouseWheelZoomEnabled
&& (IsMouseDirectlyOver
|| IgnoreMarkerOnMouseWheel
) && !Core
.IsDragging)
{
System.Windows.Point p
= e
.GetPosition(this);
if(MapScaleTransform
!= null)
{
p
= MapScaleTransform
.Inverse.Transform(p
);
}
p
= ApplyRotationInversion
(p
.X, p
.Y);
if(Core
.mouseLastZoom.X != (int)p
.X && Core
.mouseLastZoom.Y != (int)p
.Y)
{
if(MouseWheelZoomType
== MouseWheelZoomType
.MousePositionAndCenter)
{
Core
.position = FromLocalToLatLng
((int)p
.X,
(int)p
.Y);
}
else if(MouseWheelZoomType
== MouseWheelZoomType
.ViewCenter)
{
Core
.position = FromLocalToLatLng
((int)ActualWidth
/ 2,
(int)ActualHeight
/ 2);
}
else if(MouseWheelZoomType
== MouseWheelZoomType
.MousePositionWithoutCenter)
{
Core
.position = FromLocalToLatLng
((int)p
.X,
(int)p
.Y);
}
Core
.mouseLastZoom.X = (int)p
.X;
Core
.mouseLastZoom.Y = (int)p
.Y;
}
// set mouse position to map center
if(MouseWheelZoomType
!= MouseWheelZoomType
.MousePositionWithoutCenter)
{
System.Windows.Point ps
= PointToScreen
(new System.Windows.Point(ActualWidth
/ 2, ActualHeight
/ 2));
Stuff
.SetCursorPos((int)ps
.X,
(int)ps
.Y);
}
Core
.MouseWheelZooming = true;
if(e
.Delta > 0)
{
if(!InvertedMouseWheelZooming
)
{
Zoom
= ((int)Zoom
) + 1;
}
else
{
Zoom
= ((int)(Zoom
+ 0.99)) - 1;
}
}
else
{
if(InvertedMouseWheelZooming
)
{
Zoom
= ((int)Zoom
) + 1;
}
else
{
Zoom
= ((int)(Zoom
+ 0.99)) - 1;
}
}
Core
.MouseWheelZooming = false;
}
}
bool isSelected
= false;
protected override void OnMouseDown
(MouseButtonEventArgs e
)
{
base.OnMouseDown(e
);
if(CanDragMap
&& e
.ChangedButton == DragButton
)
{
Point p
= e
.GetPosition(this);
if(MapScaleTransform
!= null)
{
p
= MapScaleTransform
.Inverse.Transform(p
);
}
p
= ApplyRotationInversion
(p
.X, p
.Y);
Core
.mouseDown.X = (int)p
.X;
Core
.mouseDown.Y = (int)p
.Y;
InvalidateVisual
();
}
else
{
if(!isSelected
)
{
Point p
= e
.GetPosition(this);
isSelected
= true;
SelectedArea
= RectLatLng
.Empty;
selectionEnd
= PointLatLng
.Empty;
selectionStart
= FromLocalToLatLng
((int)p
.X,
(int)p
.Y);
}
}
}
int onMouseUpTimestamp
= 0;
protected override void OnMouseUp
(MouseButtonEventArgs e
)
{
base.OnMouseUp(e
);
if(isSelected
)
{
isSelected
= false;
}
if(Core
.IsDragging)
{
if(isDragging
)
{
onMouseUpTimestamp
= e
.Timestamp & Int32
.MaxValue;
isDragging
= false;
Debug
.WriteLine("IsDragging = " + isDragging
);
Cursor
= cursorBefore
;
Mouse
.Capture(null);
}
Core
.EndDrag();
if(BoundsOfMap
.HasValue && !BoundsOfMap
.Value.Contains(Position
))
{
if(Core
.LastLocationInBounds.HasValue)
{
Position
= Core
.LastLocationInBounds.Value;
}
}
}
else
{
if(e
.ChangedButton == DragButton
)
{
Core
.mouseDown = GPoint
.Empty;
}
if(!selectionEnd
.IsEmpty && !selectionStart
.IsEmpty)
{
bool zoomtofit
= false;
if(!SelectedArea
.IsEmpty && Keyboard
.Modifiers == ModifierKeys
.Shift)
{
zoomtofit
= SetZoomToFitRect
(SelectedArea
);
}
if(OnSelectionChange
!= null)
{
OnSelectionChange
(SelectedArea, zoomtofit
);
}
}
else
{
InvalidateVisual
();
}
}
}
Cursor cursorBefore
= Cursors
.Arrow;
protected override void OnMouseMove
(MouseEventArgs e
)
{
if (CanDragMap
)
{
base.OnMouseMove(e
);
// wpf generates to many events if mouse is over some visual
// and OnMouseUp is fired, wtf, anyway...
// http://greatmaps.codeplex.com/workitem/16013
if ((e
.Timestamp & Int32
.MaxValue) - onMouseUpTimestamp
< 55)
{
Debug
.WriteLine("OnMouseMove skipped: " + ((e
.Timestamp & Int32
.MaxValue) - onMouseUpTimestamp
) + "ms");
return;
}
if (!Core
.IsDragging && !Core
.mouseDown.IsEmpty)
{
Point p
= e
.GetPosition(this);
if (MapScaleTransform
!= null)
{
p
= MapScaleTransform
.Inverse.Transform(p
);
}
p
= ApplyRotationInversion
(p
.X, p
.Y);
// cursor has moved beyond drag tolerance
if (Math
.Abs(p
.X - Core
.mouseDown.X) * 2 >= SystemParameters
.MinimumHorizontalDragDistance || Math
.Abs(p
.Y - Core
.mouseDown.Y) * 2 >= SystemParameters
.MinimumVerticalDragDistance)
{
Core
.BeginDrag(Core
.mouseDown);
}
}
if (Core
.IsDragging)
{
if (!isDragging
)
{
isDragging
= true;
Debug
.WriteLine("IsDragging = " + isDragging
);
cursorBefore
= Cursor
;
Cursor
= Cursors
.SizeAll;
Mouse
.Capture(this);
}
if (BoundsOfMap
.HasValue && !BoundsOfMap
.Value.Contains(Position
))
{
// ...
}
else
{
Point p
= e
.GetPosition(this);
if (MapScaleTransform
!= null)
{
p
= MapScaleTransform
.Inverse.Transform(p
);
}
p
= ApplyRotationInversion
(p
.X, p
.Y);
Core
.mouseCurrent.X = (int)p
.X;
Core
.mouseCurrent.Y = (int)p
.Y;
{
Core
.Drag(Core
.mouseCurrent);
}
if (IsRotated
|| scaleMode
!= ScaleModes
.Integer)
{
ForceUpdateOverlays
();
}
else
{
UpdateMarkersOffset
();
}
}
InvalidateVisual
(true);
}
else
{
if (isSelected
&& !selectionStart
.IsEmpty && (Keyboard
.Modifiers == ModifierKeys
.Shift || Keyboard
.Modifiers == ModifierKeys
.Alt || DisableAltForSelection
))
{
System.Windows.Point p
= e
.GetPosition(this);
selectionEnd
= FromLocalToLatLng
((int)p
.X,
(int)p
.Y);
{
GMap
.NET.PointLatLng p1
= selectionStart
;
GMap
.NET.PointLatLng p2
= selectionEnd
;
double x1
= Math
.Min(p1
.Lng, p2
.Lng);
double y1
= Math
.Max(p1
.Lat, p2
.Lat);
double x2
= Math
.Max(p1
.Lng, p2
.Lng);
double y2
= Math
.Min(p1
.Lat, p2
.Lat);
SelectedArea
= new RectLatLng
(y1, x1, x2
- x1, y1
- y2
);
}
}
if (renderHelperLine
)
{
InvalidateVisual
(true);
}
}
}
}
/// <summary>
/// if true, selects area just by holding mouse and moving
/// </summary>
public bool DisableAltForSelection
= false;
protected override void OnStylusDown
(StylusDownEventArgs e
)
{
base.OnStylusDown(e
);
if(TouchEnabled
&& CanDragMap
&& !e
.InAir)
{
Point p
= e
.GetPosition(this);
if(MapScaleTransform
!= null)
{
p
= MapScaleTransform
.Inverse.Transform(p
);
}
p
= ApplyRotationInversion
(p
.X, p
.Y);
Core
.mouseDown.X = (int)p
.X;
Core
.mouseDown.Y = (int)p
.Y;
InvalidateVisual
();
}
}
protected override void OnStylusUp
(StylusEventArgs e
)
{
base.OnStylusUp(e
);
if(TouchEnabled
)
{
if(isSelected
)
{
isSelected
= false;
}
if(Core
.IsDragging)
{
if(isDragging
)
{
onMouseUpTimestamp
= e
.Timestamp & Int32
.MaxValue;
isDragging
= false;
Debug
.WriteLine("IsDragging = " + isDragging
);
Cursor
= cursorBefore
;
Mouse
.Capture(null);
}
Core
.EndDrag();
if(BoundsOfMap
.HasValue && !BoundsOfMap
.Value.Contains(Position
))
{
if(Core
.LastLocationInBounds.HasValue)
{
Position
= Core
.LastLocationInBounds.Value;
}
}
}
else
{
Core
.mouseDown = GPoint
.Empty;
InvalidateVisual
();
}
}
}
protected override void OnStylusMove
(StylusEventArgs e
)
{
base.OnStylusMove(e
);
if(TouchEnabled
&& CanDragMap
)
{
// wpf generates to many events if mouse is over some visual
// and OnMouseUp is fired, wtf, anyway...
// http://greatmaps.codeplex.com/workitem/16013
if((e
.Timestamp & Int32
.MaxValue) - onMouseUpTimestamp
< 55)
{
Debug
.WriteLine("OnMouseMove skipped: " + ((e
.Timestamp & Int32
.MaxValue) - onMouseUpTimestamp
) + "ms");
return;
}
if(!Core
.IsDragging && !Core
.mouseDown.IsEmpty)
{
Point p
= e
.GetPosition(this);
if(MapScaleTransform
!= null)
{
p
= MapScaleTransform
.Inverse.Transform(p
);
}
p
= ApplyRotationInversion
(p
.X, p
.Y);
// cursor has moved beyond drag tolerance
if(Math
.Abs(p
.X - Core
.mouseDown.X) * 2 >= SystemParameters
.MinimumHorizontalDragDistance || Math
.Abs(p
.Y - Core
.mouseDown.Y) * 2 >= SystemParameters
.MinimumVerticalDragDistance)
{
Core
.BeginDrag(Core
.mouseDown);
}
}
if(Core
.IsDragging)
{
if(!isDragging
)
{
isDragging
= true;
Debug
.WriteLine("IsDragging = " + isDragging
);
cursorBefore
= Cursor
;
Cursor
= Cursors
.SizeAll;
Mouse
.Capture(this);
}
if(BoundsOfMap
.HasValue && !BoundsOfMap
.Value.Contains(Position
))
{
// ...
}
else
{
Point p
= e
.GetPosition(this);
if(MapScaleTransform
!= null)
{
p
= MapScaleTransform
.Inverse.Transform(p
);
}
p
= ApplyRotationInversion
(p
.X, p
.Y);
Core
.mouseCurrent.X = (int)p
.X;
Core
.mouseCurrent.Y = (int)p
.Y;
{
Core
.Drag(Core
.mouseCurrent);
}
if(IsRotated
)
{
ForceUpdateOverlays
();
}
else
{
UpdateMarkersOffset
();
}
}
InvalidateVisual
();
}
}
}
#endregion
#region IGControl Members
/// <summary>
/// Call it to empty tile cache & reload tiles
/// </summary>
public void ReloadMap
()
{
Core
.ReloadMap();
}
/// <summary>
/// sets position using geocoder
/// </summary>
/// <param name="keys"></param>
/// <returns></returns>
public GeoCoderStatusCode SetPositionByKeywords
(string keys
)
{
GeoCoderStatusCode status
= GeoCoderStatusCode
.Unknow;
GeocodingProvider gp
= MapProvider
as GeocodingProvider
;
if(gp
== null)
{
gp
= GMapProviders
.OpenStreetMap as GeocodingProvider
;
}
if(gp
!= null)
{
var pt
= gp
.GetPoint(keys,
out status
);
if(status
== GeoCoderStatusCode
.G_GEO_SUCCESS && pt
.HasValue)
{
Position
= pt
.Value;
}
}
return status
;
}
public PointLatLng FromLocalToLatLng
(int x,
int y
)
{
if(MapScaleTransform
!= null)
{
var tp
= MapScaleTransform
.Inverse.Transform(new System.Windows.Point(x, y
));
x
= (int)tp
.X;
y
= (int)tp
.Y;
}
if(IsRotated
)
{
var f
= rotationMatrixInvert
.Transform(new System.Windows.Point(x, y
));
x
= (int)f
.X;
y
= (int)f
.Y;
}
return Core
.FromLocalToLatLng(x, y
);
}
public GPoint FromLatLngToLocal
(PointLatLng point
)
{
GPoint ret
= Core
.FromLatLngToLocal(point
);
if(MapScaleTransform
!= null)
{
var tp
= MapScaleTransform
.Transform(new System.Windows.Point(ret
.X, ret
.Y));
ret
.X = (int)tp
.X;
ret
.Y = (int)tp
.Y;
}
if(IsRotated
)
{
var f
= rotationMatrix
.Transform(new System.Windows.Point(ret
.X, ret
.Y));
ret
.X = (int)f
.X;
ret
.Y = (int)f
.Y;
}
return ret
;
}
public bool ShowExportDialog
()
{
var dlg
= new Microsoft.Win32.SaveFileDialog();
{
dlg
.CheckPathExists = true;
dlg
.CheckFileExists = false;
dlg
.AddExtension = true;
dlg
.DefaultExt = "gmdb";
dlg
.ValidateNames = true;
dlg
.Title = "GMap.NET: Export map to db, if file exsist only new data will be added";
dlg
.FileName = "DataExp";
dlg
.InitialDirectory = Environment
.GetFolderPath(Environment
.SpecialFolder.MyDocuments);
dlg
.Filter = "GMap.NET DB files (*.gmdb)|*.gmdb";
dlg
.FilterIndex = 1;
dlg
.RestoreDirectory = true;
if(dlg
.ShowDialog() == true)
{
bool ok
= GMaps
.Instance.ExportToGMDB(dlg
.FileName);
if(ok
)
{
MessageBox
.Show("Complete!",
"GMap.NET", MessageBoxButton
.OK, MessageBoxImage
.Information);
}
else
{
MessageBox
.Show(" Failed!",
"GMap.NET", MessageBoxButton
.OK, MessageBoxImage
.Warning);
}
return ok
;
}
}
return false;
}
public bool ShowImportDialog
()
{
var dlg
= new Microsoft.Win32.OpenFileDialog();
{
dlg
.CheckPathExists = true;
dlg
.CheckFileExists = false;
dlg
.AddExtension = true;
dlg
.DefaultExt = "gmdb";
dlg
.ValidateNames = true;
dlg
.Title = "GMap.NET: Import to db, only new data will be added";
dlg
.FileName = "DataImport";
dlg
.InitialDirectory = Environment
.GetFolderPath(Environment
.SpecialFolder.MyDocuments);
dlg
.Filter = "GMap.NET DB files (*.gmdb)|*.gmdb";
dlg
.FilterIndex = 1;
dlg
.RestoreDirectory = true;
if(dlg
.ShowDialog() == true)
{
Cursor
= Cursors
.Wait;
bool ok
= GMaps
.Instance.ImportFromGMDB(dlg
.FileName);
if(ok
)
{
MessageBox
.Show("Complete!",
"GMap.NET", MessageBoxButton
.OK, MessageBoxImage
.Information);
ReloadMap
();
}
else
{
MessageBox
.Show(" Failed!",
"GMap.NET", MessageBoxButton
.OK, MessageBoxImage
.Warning);
}
Cursor
= Cursors
.Arrow;
return ok
;
}
}
return false;
}
/// <summary>
/// current coordinates of the map center
/// </summary>
[Browsable
(false)]
public PointLatLng Position
{
get
{
return Core
.Position;
}
set
{
Core
.Position = value;
if(Core
.IsStarted)
{
ForceUpdateOverlays
();
}
}
}
[Browsable
(false)]
public GPoint PositionPixel
{
get
{
return Core
.PositionPixel;
}
}
[DesignerSerializationVisibility
(DesignerSerializationVisibility
.Hidden)]
[Browsable
(false)]
public string CacheLocation
{
get
{
return CacheLocator
.Location;
}
set
{
CacheLocator
.Location = value;
}
}
bool isDragging
= false;
[Browsable
(false)]
public bool IsDragging
{
get
{
return isDragging
;
}
}
[Browsable
(false)]
public RectLatLng ViewArea
{
get
{
if(!IsRotated
)
{
return Core
.ViewArea;
}
else if(Core
.Provider.Projection != null)
{
var p
= FromLocalToLatLng
(0,
0);
var p2
= FromLocalToLatLng
((int)Width,
(int)Height
);
return RectLatLng
.FromLTRB(p
.Lng, p
.Lat, p2
.Lng, p2
.Lat);
}
return RectLatLng
.Empty;
}
}
[Category
("GMap.NET")]
public bool CanDragMap
{
get
{
return Core
.CanDragMap;
}
set
{
Core
.CanDragMap = value;
}
}
public GMap
.NET.RenderMode RenderMode
{
get
{
return GMap
.NET.RenderMode.WPF;
}
}
#endregion
#region IGControl event Members
public event PositionChanged OnPositionChanged
{
add
{
Core
.OnCurrentPositionChanged += value;
}
remove
{
Core
.OnCurrentPositionChanged -= value;
}
}
public event TileLoadComplete OnTileLoadComplete
{
add
{
Core
.OnTileLoadComplete += value;
}
remove
{
Core
.OnTileLoadComplete -= value;
}
}
public event TileLoadStart OnTileLoadStart
{
add
{
Core
.OnTileLoadStart += value;
}
remove
{
Core
.OnTileLoadStart -= value;
}
}
public event MapDrag OnMapDrag
{
add
{
Core
.OnMapDrag += value;
}
remove
{
Core
.OnMapDrag -= value;
}
}
public event MapZoomChanged OnMapZoomChanged
{
add
{
Core
.OnMapZoomChanged += value;
}
remove
{
Core
.OnMapZoomChanged -= value;
}
}
/// <summary>
/// occures on map type changed
/// </summary>
public event MapTypeChanged OnMapTypeChanged
{
add
{
Core
.OnMapTypeChanged += value;
}
remove
{
Core
.OnMapTypeChanged -= value;
}
}
/// <summary>
/// occurs on empty tile displayed
/// </summary>
public event EmptyTileError OnEmptyTileError
{
add
{
Core
.OnEmptyTileError += value;
}
remove
{
Core
.OnEmptyTileError -= value;
}
}
#endregion
#region IDisposable Members
public virtual void Dispose
()
{
if(Core
.IsStarted)
{
Core
.OnMapZoomChanged -= new MapZoomChanged
(ForceUpdateOverlays
);
Loaded
-= new RoutedEventHandler
(GMapControl_Loaded
);
Dispatcher
.ShutdownStarted -= new EventHandler
(Dispatcher_ShutdownStarted
);
SizeChanged
-= new SizeChangedEventHandler
(GMapControl_SizeChanged
);
if(loadedApp
!= null)
{
loadedApp
.SessionEnding -= new SessionEndingCancelEventHandler
(Current_SessionEnding
);
}
Core
.OnMapClose();
}
}
#endregion
}
public enum HelperLineOptions
{
DontShow
= 0,
ShowAlways
= 1,
ShowOnModifierKey
= 2
}
public enum ScaleModes
{
/// <summary>
/// no scaling
/// </summary>
Integer,
/// <summary>
/// scales to fractional level using a stretched tiles, any issues -> http://greatmaps.codeplex.com/workitem/16046
/// </summary>
ScaleUp,
/// <summary>
/// scales to fractional level using a narrowed tiles, any issues -> http://greatmaps.codeplex.com/workitem/16046
/// </summary>
ScaleDown,
/// <summary>
/// scales to fractional level using a combination both stretched and narrowed tiles, any issues -> http://greatmaps.codeplex.com/workitem/16046
/// </summary>
Dynamic
}
public delegate void SelectionChange
(RectLatLng Selection,
bool ZoomToFit
);
}