/Main/src/DynamicDataDisplay.Maps/Charts/NewMap.cs
C# | 613 lines | 503 code | 99 blank | 11 comment | 76 complexity | 443c320682ccfb99a7bfd01c264b26cb MD5 | raw file
Possible License(s): CC-BY-SA-3.0
1using System; 2using System.Collections.Specialized; 3using System.ComponentModel; 4using System.Diagnostics; 5using System.Globalization; 6using System.Linq; 7using System.Windows; 8using System.Windows.Controls; 9using System.Windows.Input; 10using System.Windows.Markup; 11using System.Windows.Media; 12using System.Windows.Media.Imaging; 13using Microsoft.Research.DynamicDataDisplay; 14using Microsoft.Research.DynamicDataDisplay.Charts.Navigation; 15using Microsoft.Research.DynamicDataDisplay.Maps.Servers.Network; 16using Microsoft.Research.DynamicDataDisplay.ViewportConstraints; 17using Microsoft.Research.DynamicDataDisplay.Maps; 18using System.Collections.Generic; 19using Microsoft.Research.DynamicDataDisplay.Common; 20using Microsoft.Research.DynamicDataDisplay.Maps.Charts; 21using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary; 22 23 24namespace Microsoft.Research.DynamicDataDisplay.Charts.Maps 25{ 26 [ContentProperty("SourceTileServer")] 27 public class NewMap : FrameworkElement, IPlotterElement 28 { 29 public NewMap() 30 { 31 server.ImageLoaded += OnTileLoaded; 32 server.SourceServerChanged += OnNetworkServerChanged; 33 34#if DEBUG 35 drawDebugBounds = false; 36#endif 37 } 38 39 private TileProvider tileProvider = new MapTileProvider(); 40 public TileProvider TileProvider 41 { 42 get { return tileProvider; } 43 set 44 { 45 if (value == null) 46 throw new ArgumentNullException("value"); 47 tileProvider = value; 48 } 49 } 50 51 private readonly TileServerSystem server = new TileServerSystem(); 52 public TileServerSystem TileSystem 53 { 54 get { return server; } 55 } 56 57 [NotNull] 58 public SourceTileServer SourceTileServer 59 { 60 get { return server.SourceServer; } 61 set { server.SourceServer = value; } 62 } 63 64 public TileSystemMode Mode 65 { 66 get { return server.Mode; } 67 set { server.Mode = value; } 68 } 69 70 public IWriteableTileServer FileTileServer 71 { 72 get { return server.FileServer; } 73 set { server.FileServer = value; } 74 } 75 76 private readonly ViewportHostPanel panel = new ViewportHostPanel(); 77 public ViewportHostPanel Panel 78 { 79 get { return panel; } 80 } 81 82 protected virtual void OnTileLoaded(object sender, TileLoadResultEventArgs e) 83 { 84 if (e.Result == TileLoadResult.Success) 85 { 86 DataRect tileBounds = tileProvider.GetTileBounds(e.ID); 87 88 bool containsInDrawn = drawnBounds.Contains(tileBounds) || drawnBounds.IsEmpty; 89 bool shouldDraw = containsInDrawn && !invalidatePending && e.ID.Level <= tileProvider.Level; 90 if (shouldDraw) 91 { 92 DrawImage(e.Image, tileBounds, e.ID); 93 } 94 } 95 } 96 97 private int tileWidth = 256; 98 private int tileHeight = 256; 99 protected virtual void OnNetworkServerChanged(object sender, ValueChangedEventArgs<SourceTileServer> e) 100 { 101 if (e.PreviousValue != null) 102 { 103 RemoveLogicalChild(e.PreviousValue); 104 } 105 if (e.CurrentValue != null) 106 { 107 AddLogicalChild(e.CurrentValue); 108 } 109 110 SourceTileServer networkServer = server.SourceServer; 111 if (networkServer != null) 112 { 113 tileProvider.MinLevel = networkServer.MinLevel; 114 tileProvider.MaxLevel = networkServer.MaxLevel; 115 116 MapTileProvider mapProvider = tileProvider as MapTileProvider; 117 if (mapProvider != null) 118 { 119 mapProvider.MaxLatitude = networkServer.MaxLatitude; 120 } 121 tileWidth = networkServer.TileWidth; 122 tileHeight = networkServer.TileHeight; 123 } 124 125 BeginInvalidateVisual(); 126 } 127 128 private bool drawDebugBounds = false; 129 public bool DrawDebugBounds 130 { 131 get { return drawDebugBounds; } 132 set { drawDebugBounds = value; } 133 } 134 135 private readonly Pen debugBoundsPen = new Pen(Brushes.Red.MakeTransparent(0.5), 1); 136 137 bool showsWholeMap = false; 138 DataRect drawnBounds = DataRect.Empty; 139 DataRect visibleBounds; 140 bool invalidatePending = false; 141 bool rendering = false; 142 protected void OnRender() 143 { 144 if (DesignerProperties.GetIsInDesignMode(this)) 145 return; 146 147 if (plotter == null) 148 return; 149 150 panel.Children.Clear(); 151 152 freeChildren = new List<MapElement>(panel.Children.Count); 153 for (int i = 0; i < panel.Children.Count; i++) 154 { 155 freeChildren.Add((MapElement)panel.Children[i]); 156 } 157 158 rendering = true; 159 invalidatePending = false; 160 161 var transform = plotter.Viewport.Transform; 162 Rect output = plotter.Viewport.Output; 163 DataRect visible = plotter.Viewport.Visible; 164 visibleBounds = visible; 165 166 drawnBounds = DataRect.Empty; 167 168 var tileInfos = GetVisibleTiles(); 169 170 var lowerTilesList = GetLoadedLowerTiles(tileInfos); 171 // displaying lower tiles 172 foreach (var tile in lowerTilesList) 173 { 174 if (server.IsLoaded(tile)) 175 { 176 var bmp = server[tile]; 177 DataRect visibleRect = tileProvider.GetTileBounds(tile); 178 DrawImage(bmp, visibleRect, tile); 179 } 180 else 181 { 182 server.BeginLoadImage(tile); 183 } 184 } 185 186 int count = 0; 187 foreach (var tileInfo in tileInfos) 188 { 189 drawnBounds.Union(tileInfo.VisibleBounds); 190 191 count++; 192 if (server.IsLoaded(tileInfo.Tile)) 193 { 194 var bmp = server[tileInfo.Tile]; 195 DrawImage(bmp, tileInfo.VisibleBounds, tileInfo.Tile); 196 } 197 else 198 { 199 server.BeginLoadImage(tileInfo.Tile); 200 } 201 } 202 showsWholeMap = count == MapTileProvider.GetTotalTilesCount(tileProvider.Level); 203 204 foreach (var item in freeChildren) 205 { 206 panel.Children.Remove(item); 207 pool.Put(item); 208 } 209 210 foreach (MapElement item in panel.Children) 211 { 212 if (item.Bitmap == null) 213 { 214 panel.Children.Remove(item); 215 pool.Put(item); 216 } 217 } 218 219 rendering = false; 220 221 Debug.WriteLine("Drawn " + Environment.TickCount); 222 } 223 224 private List<MapElement> freeChildren = new List<MapElement>(); 225 private readonly ResourcePool<MapElement> pool = new ResourcePool<MapElement>(); 226 private void DrawImage(BitmapSource bmp, DataRect bounds, TileIndex id) 227 { 228 MapElement element = null; 229 bool onPanel = false; 230 if (freeChildren.Count > 0) 231 { 232 element = (MapElement)freeChildren[freeChildren.Count - 1]; 233 freeChildren.RemoveAt(freeChildren.Count - 1); 234 onPanel = true; 235 } 236 else 237 { 238 element = pool.GetOrCreate(); 239 } 240 241 element.Bitmap = bmp; 242 ViewportPanel.SetViewportBounds(element, bounds); 243 System.Windows.Controls.Panel.SetZIndex(element, (int)id.Level); 244 245 if (!onPanel) 246 { 247 panel.Children.Add(element); 248 panel.InvalidateMeasure(); 249 } 250 } 251 252 protected virtual DataRect Transform(DataRect visibleRect) 253 { 254 return visibleRect; 255 } 256 257 protected virtual DataRect TransformRegion(DataRect visibleRect) 258 { 259 return visibleRect; 260 } 261 262 protected List<VisibleTileInfo> GetVisibleTiles() 263 { 264 var transform = plotter.Viewport.Transform; 265 Rect output = plotter.Viewport.Output; 266 DataRect visible = plotter.Viewport.Visible; 267 268 var tileInfos = (from tile in tileProvider.GetTilesForRegion(TransformRegion(visible), tileProvider.Level) 269 let visibleRect = Transform(tileProvider.GetTileBounds(tile)) 270 let screenRect = visibleRect.ViewportToScreen(transform) 271 where output.IntersectsWith(screenRect) 272 select new VisibleTileInfo { Tile = tile, ScreenBounds = screenRect, VisibleBounds = visibleRect }).ToList(); 273 274 if (tileInfos.Count > MapTileProvider.GetTotalTilesCount(tileProvider.Level)) 275 { 276 //Debugger.Break(); 277 } 278 279 return tileInfos; 280 } 281 282 protected virtual IEnumerable<TileIndex> GetLoadedLowerTiles(IEnumerable<VisibleTileInfo> visibleTiles) 283 { 284 Set<TileIndex> result = new Set<TileIndex>(); 285 286 foreach (var tileInfo in visibleTiles) 287 { 288 if (!server.IsLoaded(tileInfo.Tile)) 289 { 290 bool found = false; 291 var tile = tileInfo.Tile; 292 do 293 { 294 if (tile.HasLowerTile) 295 { 296 tile = tile.GetLowerTile(); 297 if (server.IsLoaded(tile) || tile.Level == 1) 298 { 299 found = true; 300 result.TryAdd(tile); 301 } 302 } 303 else 304 { 305 found = true; 306 } 307 } 308 while (!found); 309 } 310 } 311 312 return result.OrderBy(id => id.Level); 313 } 314 315 protected Rect EnlargeRect(Rect rect) 316 { 317 double coeff = 1 + 1.0 / tileWidth; 318 return EnlargeRect(rect, coeff); 319 } 320 321 protected Rect EnlargeRect(Rect rect, double rectZoomCoeff) 322 { 323 Rect res = rect; 324 res = res.Zoom(res.GetCenter(), rectZoomCoeff); 325 return res; 326 } 327 328 private FormattedText CreateText(TileIndex tileIndex) 329 { 330 FormattedText text = new FormattedText(tileIndex.ToString(), 331 CultureInfo.CurrentCulture, 332 FlowDirection.LeftToRight, new Typeface("Arial"), 8, Brushes.Red); 333 return text; 334 } 335 336 #region ChangesTextFormat property 337 338 public bool ChangesTextFormat 339 { 340 get { return (bool)GetValue(ChangesTextFormatProperty); } 341 set { SetValue(ChangesTextFormatProperty, value); } 342 } 343 344 public static readonly DependencyProperty ChangesTextFormatProperty = DependencyProperty.Register( 345 "ChangesTextFormat", 346 typeof(bool), 347 typeof(NewMap), 348 new FrameworkPropertyMetadata(true, OnChangesTextFormatChanged)); 349 350 private static void OnChangesTextFormatChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 351 { 352 NewMap map = (NewMap)d; 353 map.OnChangesTextFormatChanged(); 354 } 355 356 private void OnChangesTextFormatChanged() 357 { 358 // do nothing if disconnected 359 if (plotter == null) 360 return; 361 362 if (ChangesTextFormat) 363 { 364 plotter.Children.CollectionChanged += PlotterChildren_CollectionChanged; 365 ChangeTextFormat(); 366 } 367 else 368 { 369 plotter.Children.CollectionChanged -= PlotterChildren_CollectionChanged; 370 RevertTextFormat(); 371 } 372 } 373 374 #endregion 375 376 protected virtual void BeginInvalidateVisual() 377 { 378 if (!rendering) 379 { 380 invalidatePending = true; 381 OnRender(); 382 } 383 else 384 { 385 Dispatcher.BeginInvoke(((Action)(() => { OnRender(); }))); 386 } 387 } 388 389 #region IPlotterElement Members 390 391 Func<double, string> prevXMapping; 392 Func<double, string> prevYMapping; 393 PhysicalProportionsConstraint proportionsConstraint = new PhysicalProportionsConstraint(2.0); 394 protected PhysicalProportionsConstraint ProportionsConstraint 395 { 396 get { return proportionsConstraint; } 397 } 398 399 MaximalSizeConstraint maxSizeConstraint = new MaximalSizeConstraint(); 400 protected MaximalSizeConstraint MaxSizeConstraint 401 { 402 get { return maxSizeConstraint; } 403 } 404 405 void IPlotterElement.OnPlotterAttached(Plotter plotter) 406 { 407 this.plotter = (Plotter2D)plotter; 408 this.plotter.Viewport.PropertyChanged += Viewport_PropertyChanged; 409 410 this.plotter.Viewport.Constraints.Add(maxSizeConstraint); 411 this.plotter.Viewport.Constraints.Add(proportionsConstraint); 412 413 this.plotter.KeyDown += new KeyEventHandler(plotter_KeyDown); 414 415 if (ChangesTextFormat) 416 { 417 plotter.Children.CollectionChanged += PlotterChildren_CollectionChanged; 418 // changing text mappings of CursorCoordinateGraph, if it exists, 419 // to display text labels with degrees. 420 ChangeTextFormat(); 421 } 422 423 plotter.Children.BeginAdd(panel); 424 425 OnPlotterAttached(plotter); 426 427 BeginInvalidateVisual(); 428 } 429 430 protected virtual void OnPlotterAttached(Plotter plotter) 431 { 432 } 433 434 private void plotter_KeyDown(object sender, KeyEventArgs e) 435 { 436 if (e.Key == Key.Q) 437 { 438 tileProvider.IncreaseLevel(); 439 BeginInvalidateVisual(); 440 } 441 else if (e.Key == Key.W) 442 { 443 tileProvider.DecreaseLevel(); 444 BeginInvalidateVisual(); 445 } 446 } 447 448 void PlotterChildren_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 449 { 450 if (ChangesTextFormat) 451 { 452 ChangeTextFormat(); 453 } 454 } 455 456 bool changedTextFormat = false; 457 private void ChangeTextFormat() 458 { 459 if (changedTextFormat) 460 return; 461 462 463 // todo discover why sometimes we arrive here from PlotterChildren_CollectionChanged when we have removed this handler from 464 // plotter.Children.CollectionChanged invocation list. 465 if (plotter == null) 466 return; 467 468 var cursorGraph = plotter.Children.OfType<CursorCoordinateGraph>().FirstOrDefault<CursorCoordinateGraph>(); 469 if (cursorGraph != null) 470 { 471 changedTextFormat = true; 472 473 // saving previous text mappings 474 prevXMapping = cursorGraph.XTextMapping; 475 prevYMapping = cursorGraph.YTextMapping; 476 477 478 // setting new text mappings 479 cursorGraph.XTextMapping = value => 480 { 481 if (Double.IsNaN(value)) 482 return ""; 483 484 if (-180 <= value && value <= 180) 485 { 486 Degree degree = Degree.CreateLongitude(value); 487 return degree.ToString(); 488 } 489 else return null; 490 }; 491 492 cursorGraph.YTextMapping = value => 493 { 494 if (Double.IsNaN(value)) 495 return ""; 496 497 var mapTileProvider = tileProvider as MapTileProvider; 498 if (mapTileProvider != null) 499 { 500 if (mapTileProvider.MinLatitude <= value && value <= mapTileProvider.MaxLatitude) 501 { 502 Degree degree = Degree.CreateLatitude(value); 503 return degree.ToString(); 504 } 505 } 506 return null; 507 }; 508 } 509 } 510 511 512 private void Viewport_PropertyChanged(object sender, ExtendedPropertyChangedEventArgs e) 513 { 514 var transform = plotter.Viewport.Transform; 515 516 double prevLevel = tileProvider.Level; 517 UpdateLevel(transform); 518 519 bool shouldRedraw = prevLevel != tileProvider.Level; 520 521 if (e.PropertyName == "Visible") 522 { 523 DataRect currVisible = (DataRect)e.NewValue; 524 shouldRedraw |= !(drawnBounds.Contains(currVisible) || showsWholeMap); 525 } 526 else if (e.PropertyName == "Transform") { } 527 528 if (shouldRedraw) 529 { 530 BeginInvalidateVisual(); 531 } 532 } 533 534 protected virtual void UpdateLevel(CoordinateTransform transform) 535 { 536 bool ok = false; 537 do 538 { 539 double width = tileProvider.GetTileWidth(tileProvider.Level); 540 double height = tileProvider.GetTileHeight(tileProvider.Level); 541 542 DataRect size = new DataRect(new Size(width, height)); 543 Rect onScreen = size.ViewportToScreen(transform); 544 545 // todo написать нормально 546 if (onScreen.Width > tileWidth * 1.45) 547 { 548 if (tileProvider.IncreaseLevel()) 549 { 550 continue; 551 } 552 } 553 else if (onScreen.Width < tileWidth / 1.45) 554 { 555 if (tileProvider.DecreaseLevel()) 556 { 557 continue; 558 } 559 } 560 ok = true; 561 } while (!ok); 562 } 563 564 protected virtual void OnPlotterDetaching(Plotter plotter) { } 565 566 void IPlotterElement.OnPlotterDetaching(Plotter plotter) 567 { 568 OnPlotterDetaching(plotter); 569 570 visibleBounds = new Rect(); 571 572 this.plotter.Children.BeginRemove(panel); 573 this.plotter.Viewport.PropertyChanged -= Viewport_PropertyChanged; 574 575 this.plotter.Viewport.Constraints.Remove(proportionsConstraint); 576 this.plotter.Viewport.Constraints.Remove(maxSizeConstraint); 577 578 this.plotter.Children.CollectionChanged -= PlotterChildren_CollectionChanged; 579 580 RevertTextFormat(); 581 582 this.plotter = null; 583 } 584 585 private void RevertTextFormat() 586 { 587 if (changedTextFormat) 588 { 589 // revert test mappings of CursorCoordinateGraph, if it exists. 590 var cursorGraph = plotter.Children.OfType<CursorCoordinateGraph>().FirstOrDefault<CursorCoordinateGraph>(); 591 if (cursorGraph != null) 592 { 593 cursorGraph.XTextMapping = prevXMapping; 594 cursorGraph.YTextMapping = prevYMapping; 595 } 596 changedTextFormat = false; 597 } 598 } 599 600 private Plotter2D plotter; 601 public Plotter2D Plotter 602 { 603 get { return plotter; } 604 } 605 606 Plotter IPlotterElement.Plotter 607 { 608 get { return plotter; } 609 } 610 611 #endregion 612 } 613}