/// <summary> /// Calculate the visual magintude from a viewpoint. /// </summary> /// <param name="viewpoint">Viewpoint</param> /// <param name="losMap">LOS map</param> private void CalculateVisualMagnitude(SpatialUtils.ViewpointProps viewpoint, GeoMap losMap) { double visualMagnitude; spatialUtils.Viewpoint = viewpoint; viewpoint.Elevation = elevationMap[viewpoint.Y, viewpoint.X] + viewpoint.ElevationOffset; losMap[viewpoint.Y, viewpoint.X] = GeoMap.UndefinedValue; //initialize LOS of the viewpoint //find out the maximum distance to the edge of map int maxDistance = GetMaximumDistance(viewpoint); for (int i = 1 + omittedRings; i < maxDistance; i++) { GeoMap.Ring ring = elevationMap.GetRing(viewpoint.Y, viewpoint.X, i); foreach (int[] item in ring) { if (spatialUtils.IsCellVisible(losMap, item[0], item[1])) { visualMagnitude = spatialUtils.GetVisualMagnitude(item[0], item[1]); if (visualMagnitude > 0) { sumator.AddResult(new Sumator.VisualMagnitudeResult() { Y = item[0], X = item[1], VisualMagnitude = visualMagnitude, Weight = viewpoint.Weight }); } } } } }
/// <summary> /// Contructor to initialize the worker. /// </summary> /// <param name="workQueue">Work queue share amon workers.</param> /// <param name="elevationMap">Elevation map</param> /// <param name="sumator">Sumator instance</param> /// <param name="parent">Work manager</param> public VisualMagnitudeWorker(ref ConcurrentQueue <SpatialUtils.ViewpointProps> workQueue, ref GeoMap elevationMap, ref Sumator sumator, WorkManager parent) { spatialUtils = new SpatialUtils(ref elevationMap); this.workQueue = workQueue; this.elevationMap = elevationMap; this.sumator = sumator; this.parent = parent; }
/// <summary> /// Start processing the work queue. /// </summary> public void Start() { GeoMap losMap = new GeoMap(elevationMap.GetLength(0), elevationMap.GetLength(1)); while (workQueue.TryDequeue(out SpatialUtils.ViewpointProps viewpoint)) { System.Diagnostics.Debug.WriteLine(workQueue.Count + " left"); CalculateVisualMagnitude(viewpoint, losMap); } System.Diagnostics.Debug.WriteLine("ThreadDone"); parent.ThreadFinished(); }
/// <summary> /// Create a mock map for testing purposes. /// </summary> /// <param name="dimensionY">Y length</param> /// <param name="dimensionX">X length</param> /// <returns>Mocked GeoMap</returns> public static GeoMap CreateMock(int dimensionY, int dimensionX) { GeoMap map = new GeoMap(dimensionY, dimensionX); Random rnd = new Random(); for (int y = 0; y < dimensionY; y++) { for (int x = 0; x < dimensionX; x++) { map[y, x] = y; } } return(map); }
/// <summary> /// Start calculationg the visual magnitude. /// </summary> /// <param name="elevationMap">Elevation map</param> public void StartWorking(ref GeoMap elevationMap) { sumator = new Sumator(elevationMap.GetLength(0), elevationMap.GetLength(1)); sumator.Start(); startingQueueSize = workQueue.Count; runningThreads = threadCount; for (int i = 0; i < threadCount; i++) { VisualMagnitudeWorker worker = new VisualMagnitudeWorker(ref workQueue, ref elevationMap, ref sumator, this); Thread thread = new Thread(worker.Start); threads[i] = thread; thread.Start(); } }
/// <summary> /// Convert the DEM raster to internal elevation map. /// </summary> /// <param name="raster">DEM raster</param> /// <returns>Elevation map</returns> private GeoMap CreateElevationMap(Raster raster) { PixelBlock currentPixelBlock = raster.CreatePixelBlock(raster.GetWidth(), raster.GetHeight()); raster.Read(0, 0, currentPixelBlock); Array pixels = currentPixelBlock.GetPixelData(0, false); GeoMap elevationMap = new GeoMap(raster.GetHeight(), raster.GetWidth()); Tuple <double, double> cellSize = raster.GetMeanCellSize(); /*if (Math.Abs(cellSize.Item1-cellSize.Item2) < cellSize.Item1*0.05) { * MessageBox.Show("Cells are not squares. Using X size of cells.", "Rectuangular cells", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Information); * }*/ elevationMap.CellSize = cellSize.Item1; elevationMap.ImportData(pixels); return(elevationMap); }
/// <summary> /// Determine if the cell is visible from the viewpoint. All cells in LOS cell-viewpoint have to be previously calculated and present in LOS map. /// </summary> /// <param name="losMap">Map which contains LOS values of the cells</param> /// <param name="cellY">Y coordinate of the cell</param> /// <param name="cellX">X coordinate of the cell</param> /// <returns></returns> public bool IsCellVisible(GeoMap losMap, int cellY, int cellX) { Orientation cellOrientation = GetCellOrientation(cellY, cellX); GetNeighborCells(cellY, cellX, cellOrientation, out int adjacentY, out int adjacentX, out int offsetY, out int offsetX); double adjacentWeight = InterpolateWeight(cellY, cellX, cellOrientation); double viewingLos = GetViewingSlope(cellY, cellX); double cellLos = losMap[adjacentY, adjacentX] * adjacentWeight + losMap[offsetY, offsetX] * (1 - adjacentWeight); if (viewingLos > cellLos) { losMap[cellY, cellX] = cellLos; return(false); } else { losMap[cellY, cellX] = viewingLos; return(true); } }
/// <summary> /// Constructor. Initialize the class with an elevation map. /// </summary> /// <param name="elevationMap">Elevtion map</param> public SpatialUtils(ref GeoMap elevationMap) { cellResolution = elevationMap.CellSize; ElevationMap = elevationMap; }
/// <summary> /// Asynchrnously start the visual magnitude analysis. /// </summary> public async void StartAnalysis() { if (!ValidateInputLayers()) { return; } outputFolder = CreateOutputDirectory(outputFolderName); if (File.Exists(outputFolder + "/" + SettingsManager.Instance.CurrentSettings.OutputFilename)) { System.Windows.MessageBoxResult messageResult = MessageBox.Show("The output file already exists and will be overwritten. Continue?", "File exists!", System.Windows.MessageBoxButton.OKCancel, System.Windows.MessageBoxImage.Warning); if (messageResult == System.Windows.MessageBoxResult.OK) { GarbageHelper.Instance.AddGarbage(outputFolder + "/" + SettingsManager.Instance.CurrentSettings.OutputFilename); GarbageHelper.Instance.CleanUp(); } else { return; } } if (File.Exists(outputFolder + "/" + tmpRasterName)) { GarbageHelper.Instance.AddGarbage(outputFolder + "/" + tmpRasterName); GarbageHelper.Instance.CleanUp(); } ///most tasks have to run on MCT thread await QueuedTask.Run(async() => { outputFolder = CreateOutputDirectory(outputFolderName); FileSystemDatastore outputDataStore = CreateNewDatastore(); //get viewpoints Raster raster = SettingsManager.Instance.SelectedDemLayer.GetRaster(); Projection projection = new Projection(raster, outputFolder); //make the detection automatic try { if (await projection.CalculateViewpoints(SettingsManager.Instance.SelectedViewpointLayer) == false) { MessageBox.Show("Invalid viewpoint layer type.\nOnly points, lines and polylines are supported.", "Error", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error); return; } } catch (Exception) { if (!SettingsManager.Instance.CurrentSettings.OffsetGlobal) { MessageBox.Show("Invalid viewpoint data. Do the viewpoints have OFFSET column specified?", "Error", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error); } else if (SettingsManager.Instance.CurrentSettings.WeightedViewpoints) { MessageBox.Show("Invalid viewpoint data. Do the viewpoints have WEIGHT column specified?", "Error", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error); } else { MessageBox.Show("Invalid viewpoint data.", "Error", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error); } return; } GeoMap elevationMap = CreateElevationMap(raster); //initialize work manager WorkManager workManager = new WorkManager(SettingsManager.Instance.CurrentSettings.WorkerThreads); int invalidViewpointsCount = 0; foreach (SpatialUtils.ViewpointProps viewpoint in projection) { if (viewpoint.Y < 0 || viewpoint.X < 0) { invalidViewpointsCount++; } else { if (SettingsManager.Instance.CurrentSettings.OffsetGlobal) { viewpoint.ElevationOffset = SettingsManager.Instance.CurrentSettings.AltOffset; } workManager.AddWork(viewpoint); } } if (invalidViewpointsCount > 0) { string message = invalidViewpointsCount.ToString() + (invalidViewpointsCount == 1 ? " viewpoint was invalid or failed to process." : " viewpoints were invalid or failed to process."); MessageBox.Show(message, "Ignored viewpoints", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Warning); } //wait for the calculation of the visual magnitude for all viewpoints to finish var watch = System.Diagnostics.Stopwatch.StartNew(); workManager.StartWorking(ref elevationMap); WorkManager.AutoEvent.WaitOne(); GeoMap result = workManager.GetResult(); MessageBox.Show("Computation finished\n------------\nTime: " + watch.ElapsedMilliseconds / 1000 + " seconds\nViewpoints: " + projection.GetViewpointsCount(), "Finished", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Information); //save and display the result try { WriteToRaster(raster, outputDataStore, result); } catch (Exception) { MessageBox.Show("Cannot write data to raster.", "Error", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error); } LayerFactory.Instance.CreateLayer(new Uri(Path.Combine(outputFolder, SettingsManager.Instance.CurrentSettings.OutputFilename)), MapView.Active.Map); //clean up temporary files GarbageHelper.Instance.CleanUp(); }); }
/// <summary> /// Write results to a rester. /// </summary> /// <param name="raster">DEM raster</param> /// <param name="outputDataStore">Output datastore</param> /// <param name="result">Elevation map with resuluts</param> private void WriteToRaster(Raster raster, FileSystemDatastore outputDataStore, GeoMap result) { raster.SetNoDataValue(0); raster.SetPixelType(RasterPixelType.DOUBLE); RasterDataset resultRasterDataset = raster.SaveAs(tmpRasterName, outputDataStore, rasterFormat); GarbageHelper.Instance.AddGarbage(Path.Combine(outputFolder, tmpRasterName)); Raster resultRaster = resultRasterDataset.CreateRaster(new int[1] { 0 }); resultRaster.Refresh(); if (!resultRaster.CanEdit()) { MessageBox.Show("Cannot write to raster", "Error", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error); return; } PixelBlock pixelBlock = resultRaster.CreatePixelBlock(resultRaster.GetWidth(), resultRaster.GetHeight()); resultRaster.Read(0, 0, pixelBlock); pixelBlock.Clear(0); Array pixel = new double[resultRaster.GetWidth(), resultRaster.GetHeight()]; pixelBlock.SetPixelData(0, result.Transpose()); resultRaster.Write(0, 0, pixelBlock); resultRaster.Refresh(); resultRaster.SaveAs(SettingsManager.Instance.CurrentSettings.OutputFilename, outputDataStore, rasterFormat); }
/// <summary> /// Constructor. /// </summary> /// <param name="dimensionY">Y size of the map</param> /// <param name="dimensionX">X size of the map</param> public Sumator(int dimensionY, int dimensionX) { VisualMagnitudeMap = new GeoMap(dimensionY, dimensionX); VisualMagnitudeMap.Initialize(); }