// Copyright (C) Stichting Deltares 2021. All rights reserved.
//
// This file is part of Riskeer.
//
// Riskeer is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see .
//
// All names, logos, and references to "Deltares" are registered trademarks of
// Stichting Deltares and remain full property of Stichting Deltares at all times.
// All rights reserved.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.AccessControl;
using System.Threading;
using System.Windows.Forms;
using BruTile;
using BruTile.Predefined;
using Core.Common.Base.Data;
using Core.Common.Base.Geometry;
using Core.Common.TestUtil;
using Core.Common.Util.Reflection;
using Core.Common.Util.TestUtil.Settings;
using Core.Components.BruTile.Configurations;
using Core.Components.BruTile.TestUtil;
using Core.Components.DotSpatial.Layer.BruTile;
using Core.Components.DotSpatial.MapFunctions;
using Core.Components.Gis.Data;
using Core.Components.Gis.Exceptions;
using Core.Components.Gis.Features;
using Core.Components.Gis.Forms;
using Core.Components.Gis.Geometries;
using Core.Components.Gis.TestUtil;
using DotSpatial.Controls;
using DotSpatial.Projections;
using DotSpatial.Symbology;
using NUnit.Extensions.Forms;
using NUnit.Framework;
using Rhino.Mocks;
using Extent = DotSpatial.Data.Extent;
using IMapView = DotSpatial.Controls.IMap;
namespace Core.Components.DotSpatial.Forms.Test
{
[TestFixture]
public class MapControlTest
{
private const double padding = 0.05;
private DirectoryDisposeHelper directoryDisposeHelper;
private TestSettingsHelper testSettingsHelper;
private string settingsDirectory;
[Test]
public void Constructor_DefaultValues()
{
// Call
using (var mapControl = new MapControl())
{
// Assert
Assert.IsInstanceOf(mapControl);
Assert.IsInstanceOf(mapControl);
Assert.IsNull(mapControl.Data);
Assert.IsNull(mapControl.BackgroundMapData);
}
}
[Test]
[Apartment(ApartmentState.STA)]
public void Constructor_MapCorrectlyInitialized()
{
using (var form = new Form())
{
// Call
var mapControl = new MapControl();
form.Controls.Add(mapControl);
form.Show();
Map map = GetMap(mapControl);
// Assert
Assert.AreEqual(MapDataConstants.FeatureBasedMapDataCoordinateSystem, map.Projection);
Assert.AreEqual(ActionMode.Never, map.ProjectionModeDefine);
Assert.AreEqual(9, map.MapFunctions.Count);
Assert.AreEqual(1, map.MapFunctions.OfType().Count());
Assert.AreEqual(1, map.MapFunctions.OfType().Count());
Assert.AreEqual(KnownCoordinateSystems.Projected.NationalGrids.Rijksdriehoekstelsel, map.Projection);
Assert.AreEqual(ActionMode.Never, map.ProjectionModeDefine);
Assert.IsTrue(map.ZoomOutFartherThanMaxExtent);
}
}
[Test]
public void RemoveAllData_Always_SetDataAndBackgroundMapDataNull()
{
// Setup
WmtsMapData backgroundMapData = WmtsMapDataTestHelper.CreateDefaultPdokMapData();
using (new UseCustomSettingsHelper(testSettingsHelper))
using (new UseCustomTileSourceFactoryConfig(backgroundMapData))
using (var mapControl = new MapControl())
{
mapControl.BackgroundMapData = backgroundMapData;
var mapDataCollection = new MapDataCollection("A");
mapDataCollection.Add(new MapPointData("Points")
{
Features = new[]
{
new MapFeature(new[]
{
new MapGeometry(new[]
{
new[]
{
new Point2D(1.1, 2.2)
}
})
})
}
});
mapControl.Data = mapDataCollection;
// Precondition
Assert.IsNotNull(mapControl.Data);
Assert.IsNotNull(mapControl.BackgroundMapData);
// Call
mapControl.RemoveAllData();
// Assert
Assert.IsNull(mapControl.Data);
Assert.IsNull(mapControl.BackgroundMapData);
}
}
[Test]
public void GivenMapControlWithoutData_WhenDataSetToMapDataCollection_ThenMapControlUpdated()
{
// Given
using (var mapControl = new MapControl())
{
// When
mapControl.Data = CreateTestMapDataCollection();
// Then
Map map = GetMap(mapControl);
Assert.AreEqual(3, map.Layers.Count);
List featureLayers = map.Layers.Cast().ToList();
Assert.AreEqual("Points", featureLayers[0].Name);
Assert.IsTrue(map.Projection.Equals(featureLayers[0].Projection));
Assert.AreEqual(1.1, featureLayers[0].FeatureSet.Features[0].Geometry.Coordinates[0].X);
Assert.AreEqual(2.2, featureLayers[0].FeatureSet.Features[0].Geometry.Coordinates[0].Y);
Assert.AreEqual("Lines", featureLayers[1].Name);
Assert.IsTrue(map.Projection.Equals(featureLayers[1].Projection));
Assert.AreEqual("Polygons", featureLayers[2].Name);
Assert.IsTrue(map.Projection.Equals(featureLayers[2].Projection));
}
}
[Test]
public void GivenMapControlWithData_WhenDataSetToOtherMapDataCollection_ThenMapControlUpdated()
{
// Given
using (var mapControl = new MapControl())
{
Map map = GetMap(mapControl);
var mapPointData = new MapPointData("Points");
var mapLineData = new MapLineData("Lines");
var mapPolygonData = new MapPolygonData("Polygons");
var mapDataCollection1 = new MapDataCollection("Collection 1");
var mapDataCollection2 = new MapDataCollection("Collection 2");
mapDataCollection1.Add(mapPointData);
mapDataCollection2.Add(mapLineData);
mapDataCollection2.Add(mapPolygonData);
mapControl.Data = mapDataCollection1;
// Precondition
Assert.AreEqual(1, map.Layers.Count);
Assert.AreEqual("Points", ((FeatureLayer) map.Layers[0]).Name);
// When
mapControl.Data = mapDataCollection2;
// Then
Assert.AreEqual(2, map.Layers.Count);
List featureLayers = map.Layers.Cast().ToList();
Assert.AreEqual("Lines", featureLayers[0].Name);
Assert.AreEqual("Polygons", featureLayers[1].Name);
}
}
[Test]
public void GivenMapControlWithData_WhenMapDataNotifiesChange_ThenAllLayersReused()
{
// Given
using (var mapControl = new MapControl())
{
Map map = GetMap(mapControl);
MapDataCollection testMapDataCollection = CreateTestMapDataCollection();
FeatureBasedMapData featureBasedMapData = testMapDataCollection.Collection
.OfType()
.First();
mapControl.Data = testMapDataCollection;
IMapLayer[] layersBeforeUpdate = map.Layers.ToArray();
// When
featureBasedMapData.Features = new MapFeature[0];
featureBasedMapData.NotifyObservers();
// Then
CollectionAssert.AreEqual(layersBeforeUpdate, map.Layers);
}
}
[Test]
public void GivenMapControlWithData_WhenMapDataRemoved_ThenCorrespondingLayerRemovedAndOtherLayersReused()
{
// Given
using (var mapControl = new MapControl())
{
Map map = GetMap(mapControl);
MapDataCollection testMapDataCollection = CreateTestMapDataCollection();
MapDataCollection nestedMapDataCollection = testMapDataCollection.Collection
.OfType()
.First();
MapLineData nestedMapLineData = nestedMapDataCollection.Collection
.OfType()
.First();
mapControl.Data = testMapDataCollection;
List layersBeforeUpdate = map.Layers.ToList();
// Precondition
Assert.AreEqual(3, layersBeforeUpdate.Count);
// When
nestedMapDataCollection.Remove(nestedMapLineData);
nestedMapDataCollection.NotifyObservers();
// Then
Assert.AreEqual(2, map.Layers.Count);
List featureLayers = map.Layers.Cast().ToList();
Assert.AreEqual("Points", featureLayers[0].Name);
Assert.AreEqual("Polygons", featureLayers[1].Name);
Assert.AreEqual(0, map.Layers.Except(layersBeforeUpdate).Count());
}
}
[Test]
public void GivenMapControlWithData_WhenMapDataAdded_ThenCorrespondingLayerAddedAndOtherLayersReused()
{
// Given
using (var mapControl = new MapControl())
{
Map map = GetMap(mapControl);
MapDataCollection testMapDataCollection = CreateTestMapDataCollection();
MapDataCollection nestedMapDataCollection = testMapDataCollection.Collection
.OfType()
.First();
mapControl.Data = testMapDataCollection;
List layersBeforeUpdate = map.Layers.ToList();
// Precondition
Assert.AreEqual(3, layersBeforeUpdate.Count);
// When
nestedMapDataCollection.Insert(0, new MapPolygonData("Additional polygons"));
nestedMapDataCollection.NotifyObservers();
// Then
Assert.AreEqual(4, map.Layers.Count);
List featureLayers = map.Layers.Cast().ToList();
Assert.AreEqual("Points", featureLayers[0].Name);
Assert.AreEqual("Additional polygons", featureLayers[1].Name);
Assert.AreEqual("Lines", featureLayers[2].Name);
Assert.AreEqual("Polygons", featureLayers[3].Name);
Assert.AreEqual(0, layersBeforeUpdate.Except(map.Layers).Count());
}
}
[Test]
public void GivenMapControlWithData_WhenMapDataMoved_ThenCorrespondingLayerMovedAndAllLayersReused()
{
// Given
using (var mapControl = new MapControl())
{
Map map = GetMap(mapControl);
MapDataCollection testMapDataCollection = CreateTestMapDataCollection();
MapDataCollection nestedMapDataCollection = testMapDataCollection.Collection
.OfType()
.Last();
MapPointData nestedMapPointData = testMapDataCollection.Collection
.OfType()
.First();
mapControl.Data = testMapDataCollection;
List layersBeforeUpdate = map.Layers.Cast().ToList();
// Precondition
Assert.AreEqual(3, layersBeforeUpdate.Count);
Assert.AreEqual("Points", layersBeforeUpdate[0].Name);
Assert.AreEqual("Lines", layersBeforeUpdate[1].Name);
Assert.AreEqual("Polygons", layersBeforeUpdate[2].Name);
// When
testMapDataCollection.Remove(nestedMapPointData);
nestedMapDataCollection.Add(nestedMapPointData);
nestedMapDataCollection.NotifyObservers();
// Then
Assert.AreEqual(3, map.Layers.Count);
List featureLayers = map.Layers.Cast().ToList();
Assert.AreEqual("Lines", featureLayers[0].Name);
Assert.AreEqual("Polygons", featureLayers[1].Name);
Assert.AreEqual("Points", featureLayers[2].Name);
Assert.AreEqual(0, layersBeforeUpdate.Except(featureLayers).Count());
}
}
[OneTimeSetUp]
public void OneTimeSetUp()
{
directoryDisposeHelper = new DirectoryDisposeHelper(TestHelper.GetScratchPadPath(), nameof(MapControlTest));
testSettingsHelper = new TestSettingsHelper
{
ApplicationLocalUserSettingsDirectory = TestHelper.GetScratchPadPath(nameof(MapControlTest))
};
settingsDirectory = testSettingsHelper.GetApplicationLocalUserSettingsDirectory();
}
[OneTimeTearDown]
public void OneTimeTearDown()
{
directoryDisposeHelper.Dispose();
}
private static MapDataCollection GetTestData()
{
var mapDataCollection = new MapDataCollection("Test data");
mapDataCollection.Add(new MapPointData("Test data")
{
Features = new[]
{
new MapFeature(new[]
{
new MapGeometry(new[]
{
new[]
{
new Point2D(1.5, 2)
}
}),
new MapGeometry(new[]
{
new[]
{
new Point2D(1.1, 1)
}
}),
new MapGeometry(new[]
{
new[]
{
new Point2D(0.8, 0.5)
}
})
})
}
});
mapDataCollection.Add(new MapLineData("Test data")
{
Features = new[]
{
new MapFeature(new[]
{
new MapGeometry(new[]
{
new[]
{
new Point2D(0.0, 1.1),
new Point2D(1.0, 2.1),
new Point2D(1.6, 1.6)
}
})
})
}
});
mapDataCollection.Add(new MapPolygonData("Test data")
{
Features = new[]
{
new MapFeature(new[]
{
new MapGeometry(new[]
{
new[]
{
new Point2D(1.0, 1.3),
new Point2D(3.0, 2.6),
new Point2D(5.6, 1.6)
}
})
})
},
IsVisible = false
});
return mapDataCollection;
}
private static void ExtendWithExpectedMargin(Extent expectedExtent)
{
double smallestDimension = Math.Min(expectedExtent.Height, expectedExtent.Width);
expectedExtent.ExpandBy(smallestDimension * padding);
}
private static Extent GetExpectedExtent(FeatureBasedMapData visibleMapData)
{
var minX = double.MaxValue;
var maxX = double.MinValue;
var minY = double.MaxValue;
var maxY = double.MinValue;
foreach (MapFeature feature in visibleMapData.Features)
{
foreach (MapGeometry geometry in feature.MapGeometries)
{
foreach (IEnumerable pointCollection in geometry.PointCollections)
{
foreach (Point2D point in pointCollection)
{
minX = Math.Min(minX, point.X);
maxX = Math.Max(maxX, point.X);
minY = Math.Min(minY, point.Y);
maxY = Math.Max(maxY, point.Y);
}
}
}
}
return new Extent(minX, minY, maxX, maxY);
}
private static void SetTestExtents(IMapView mapView)
{
mapView.ViewExtents.MinX = 0.2;
mapView.ViewExtents.MaxX = 2.2;
mapView.ViewExtents.MinY = 3.2;
mapView.ViewExtents.MaxY = 5.7;
}
private static void SetReprojectedExtents(Map map)
{
map.ViewExtents.MinX = 523413.98162662971;
map.ViewExtents.MaxX = 523415.89786963863;
map.ViewExtents.MinY = 5313601.4625104629;
map.ViewExtents.MaxY = 5313604.0206882581;
}
private static void AssertReprojectedTo28992TestExtents(IMapView mapView)
{
Assert.AreEqual(523401.48494492332, mapView.ViewExtents.MinX, 1e-1,
"Coordinate does not match. (Ball park expected value can be calculated from https://epsg.io/transform#s_srs=28992&t_srs=25831&x=0.2000000&y=3.2000000).");
Assert.AreEqual(5313546.326155385, mapView.ViewExtents.MinY, 1e-1,
"Coordinate does not match. (Estimate of expected value can be calculated from https://epsg.io/transform#s_srs=28992&t_srs=25831&x=0.2000000&y=3.2000000).");
Assert.AreEqual(523403.40096895653, mapView.ViewExtents.MaxX, 1e-1,
"Coordinate does not match. (Estimate of expected value can be calculated from https://epsg.io/transform#s_srs=28992&t_srs=25831&x=2.2000000&y=5.7000000).");
Assert.AreEqual(5313548.8840212682, mapView.ViewExtents.MaxY, 1e-1,
"Coordinate does not match. (Estimate of expected value can be calculated from https://epsg.io/transform#s_srs=28992&t_srs=25831&x=2.2000000&y=5.7000000).");
}
private static void AssertReprojectedTo3857TestExtents(IMapView mapView)
{
Assert.AreEqual(368863.7429899415, mapView.ViewExtents.MinX, 1e-6,
"Coordinate does not match. (Ball park expected value can be calculated from https://epsg.io/transform#s_srs=28992&t_srs=3857&x=0.2000000&y=3.2000000).");
Assert.AreEqual(6102662.6528704129, mapView.ViewExtents.MinY, 1e-6,
"Coordinate does not match. (Estimate of expected value can be calculated from https://epsg.io/transform#s_srs=28992&t_srs=3857&x=0.2000000&y=3.2000000).");
Assert.AreEqual(368866.61636522325, mapView.ViewExtents.MaxX, 1e-6,
"Coordinate does not match. (Estimate of expected value can be calculated from https://epsg.io/transform#s_srs=28992&t_srs=3857&x=2.2000000&y=5.7000000).");
Assert.AreEqual(6102666.467949939, mapView.ViewExtents.MaxY, 1e-6,
"Coordinate does not match. (Estimate of expected value can be calculated from https://epsg.io/transform#s_srs=28992&t_srs=3857&x=2.2000000&y=5.7000000).");
}
private static void AssertOriginalExtents(IMapView mapView)
{
Assert.AreEqual(14.45, mapView.ViewExtents.MinX, 1e-2);
Assert.AreEqual(16.45, mapView.ViewExtents.MaxX, 1e-2);
Assert.AreEqual(57.96, mapView.ViewExtents.MinY, 1e-2);
Assert.AreEqual(60.46, mapView.ViewExtents.MaxY, 1e-2);
}
private static MapDataCollection CreateTestMapDataCollection()
{
var mapPointData = new MapPointData("Points")
{
Features = new[]
{
new MapFeature(new[]
{
new MapGeometry(new[]
{
new[]
{
new Point2D(1.1, 2.2)
}
})
})
}
};
var mapLineData = new MapLineData("Lines");
var mapPolygonData = new MapPolygonData("Polygons");
var mapDataCollection = new MapDataCollection("Root collection");
var nestedMapDataCollection1 = new MapDataCollection("Nested collection 1");
var nestedMapDataCollection2 = new MapDataCollection("Nested collection 2");
mapDataCollection.Add(mapPointData);
mapDataCollection.Add(nestedMapDataCollection1);
nestedMapDataCollection1.Add(mapLineData);
nestedMapDataCollection1.Add(nestedMapDataCollection2);
nestedMapDataCollection2.Add(mapPolygonData);
return mapDataCollection;
}
///
/// Creates a with a that has no dimensions. This way aspect ratio related issues can be bypassed.
///
/// The created .
private static MapControl CreateMapControlWithDimensionlessMap()
{
var mapControl = new MapControl();
Map map = GetMap(mapControl);
map.Dock = DockStyle.None;
map.Width = 0;
map.Height = 0;
return mapControl;
}
private static Map GetMap(MapControl mapControl)
{
return mapControl.Controls[0].Controls.OfType