diff --git a/.gitignore b/.gitignore index d0eae807..321e5da0 100644 --- a/.gitignore +++ b/.gitignore @@ -69,6 +69,8 @@ ipch/ # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper +*.idea +*.vs # TeamCity is a build add-in _TeamCity* @@ -136,8 +138,7 @@ UpgradeLog*.htm # SQL Server files App_Data/*.mdf -App_Data/*.ldf - +App_Data/*.ldf #LightSwitch generated files GeneratedArtifacts/ @@ -160,10 +161,22 @@ $RECYCLE.BIN/ # Mac desktop service store files .DS_Store +__MACOSX/ + +# GDAL SDK +support/GDAL_SDK/licenses/*.rtf +support/GDAL_SDK/**/*.dll +support/GDAL_SDK/**/*.exe +support/GDAL_SDK/**/*.lib +support/GDAL_SDK/**/*.h +support/GDAL_SDK/**/*.hpp +support/GDAL_SDK/**/*.c +support/GDAL_SDK/**/*.inc +support/GDAL_SDK/**/*.proto +support/GDAL_SDK/**/*.pc # Custom *.dll -support/GDAL_SDK/* docs/output demo/.nuget/ *.zip @@ -172,11 +185,8 @@ unittests/MapWinGISTests/AkkerwebTests.cs *.mwd *.mwx unittests/packages/ -unittests/.vs/unittests/v15/Server/sqlite3/storage.ide -unittests/.vs/unittests/v15/Server/sqlite3/db.lock -docs/.vs/MapWinGis Documentation/v15/Server/sqlite3/storage.ide-wal -docs/.vs/MapWinGis Documentation/v15/Server/sqlite3/storage.ide-shm -docs/.vs/MapWinGis Documentation/v15/Server/sqlite3/storage.ide -docs/.vs/MapWinGis Documentation/v15/Server/sqlite3/db.lock -src/MapWinGIS.vcxproj.filters -unittests/.vs/ +*.VC.db +*.VC.opendb +docs/Assets/Syncfusion Logos/ +tmp/ +docs/OptimizedImages/ diff --git a/docs/AxInterop.MapWinGIS.XML b/docs/AxInterop.MapWinGIS.XML index b4bd2478..b363c6bf 100644 --- a/docs/AxInterop.MapWinGIS.XML +++ b/docs/AxInterop.MapWinGIS.XML @@ -1042,7 +1042,9 @@ \addtogroup map_drawing_layers Drawing layers - Here is a list of methods and properties to interact with the drawing layers of the map. This module is a part of the documentation of AxMap class. + Here is a list of methods and properties to interact with the drawing layers of the map. + The drawing layers are more transient that the standard layers, intended for fast drawing of temporary elements on top of the map. + This module is a part of the documentation of AxMap class. \dot digraph map_drawing_layers { splines = true; @@ -1071,18 +1073,21 @@ - Clears all the drawings on the drawing layer specified. + Clears all drawings on the specified drawing layer, and removes the drawing layer. The drawing handle will no longer be valid. + Call AxMap.NewDrawing again to create a new drawing layer to continue adding new elements. - Drawing handle of the drawing layer for which all drawings are to be cleared. + Drawing handle of the drawing layer to be cleared (and removed). - Clears all drawings on all drawing layers. This method is slower than using ClearDrawing on a specific layer + Clears all drawings on all drawing layers, and removes all drawing layers. + This method is slower than using ClearDrawing on a specific layer. + Call AxMap.NewDrawing again to create a new drawing layer to continue adding new elements. - Draws a circle on the last drawing layer created by NewDrawing + Draws a circle on the last drawing layer created by AxMap.NewDrawing Center x coordinate for the circle to be drawn. Center y coordinate for the circle to be drawn. @@ -1103,7 +1108,7 @@ - Draws a line on the last drawing layer created using NewDrawing. + Draws a line on the last drawing layer created using AxMap.NewDrawing. X coordinate of the first point used to draw the line Y coordinate of the first point used to draw the line. @@ -1126,7 +1131,7 @@ - Draws a point on the last drawing layer created by NewDrawing. + Draws a point on the last drawing layer created by AxMap.NewDrawing. The x coordinate of the point to draw The y coordinate of the point to draw. @@ -1145,7 +1150,7 @@ - Draws a polygon on the last drawing layer created using NewDrawing. + Draws a polygon on the last drawing layer created using AxMap.NewDrawing. An array containing x-coordinates for each point in the polygon. An array containing y-coordinates for each point in the polygon. @@ -1166,7 +1171,7 @@ - Draws a circle with custom outline width on the last drawing layer created by NewDrawing. + Draws a circle with custom outline width on the last drawing layer created by AxMap.NewDrawing. Center x coordinate for the circle to be drawn. Center y coordinate for the circle to be drawn. @@ -1189,7 +1194,7 @@ - Draws a polygon with custom width of outline on the last drawing layer created using NewDrawing. + Draws a polygon with custom width of outline on the last drawing layer created using AxMap.NewDrawing. An array containing x-coordinates for each point in the polygon. An array containing y-coordinates for each point in the polygon. @@ -1200,7 +1205,7 @@ - Draws a polygon with custom width of outline on the last drawing layer created using NewDrawing. + Draws a polygon with custom width of outline on the last drawing layer created using AxMap.NewDrawing. The handle of the drawing layer created with AxMap.NewDrawing call. An array containing x-coordinates for each point in the polygon. @@ -1214,8 +1219,8 @@ Creates a new drawing layer on the map returning its handle. - Sets the coordinate system to use for the new drawing layer to be created. (ScreenReferenced - uses pixels in screen coordinates. SpatiallyReferenced uses projected map units.) + Sets the coordinate system to use for the new drawing layer to be created. (tkDrawReferenceList.dlScreenReferencedList + uses pixels in screen coordinates. tkDrawReferenceList.dlSpatiallyReferencedList uses projected map units.) The handle for the new drawing layer in the map. diff --git a/docs/AxInterop.MapWinGIS/AxMap.cs b/docs/AxInterop.MapWinGIS/AxMap.cs index 8081488f..9502aa41 100644 --- a/docs/AxInterop.MapWinGIS/AxMap.cs +++ b/docs/AxInterop.MapWinGIS/AxMap.cs @@ -1,4 +1,9 @@ -using System; +// ReSharper disable ArrangeAccessorOwnerBody +// ReSharper disable DelegateSubtraction +// ReSharper disable PossibleInvalidCastExceptionInForeachLoop +// ReSharper disable CheckNamespace + +using System; using MapWinGIS; #pragma warning disable 67 // Never used warning @@ -1470,7 +1475,9 @@ public bool get_LayerVisibleAtCurrentScale(int layerHandle) #region Drawing layer /// \addtogroup map_drawing_layers Drawing layers - /// Here is a list of methods and properties to interact with the drawing layers of the map. This module is a part of the documentation of AxMap class. + /// Here is a list of methods and properties to interact with the drawing layers of the map. + /// The drawing layers are more transient that the standard layers, intended for fast drawing of temporary elements on top of the map. + /// This module is a part of the documentation of AxMap class. /// \dot /// digraph map_drawing_layers { /// splines = true; @@ -1506,16 +1513,19 @@ public Labels get_DrawingLabels(int DrawingLayerIndex) } /// - /// Clears all the drawings on the drawing layer specified. + /// Clears all drawings on the specified drawing layer, and removes the drawing layer. The drawing handle will no longer be valid. + /// Call AxMap.NewDrawing again to create a new drawing layer to continue adding new elements. /// - /// Drawing handle of the drawing layer for which all drawings are to be cleared. + /// Drawing handle of the drawing layer to be cleared (and removed). public void ClearDrawing(int DrawHandle) { throw new NotImplementedException(); } /// - /// Clears all drawings on all drawing layers. This method is slower than using ClearDrawing on a specific layer + /// Clears all drawings on all drawing layers, and removes all drawing layers. + /// This method is slower than using ClearDrawing on a specific layer. + /// Call AxMap.NewDrawing again to create a new drawing layer to continue adding new elements. /// public void ClearDrawings() { @@ -1523,7 +1533,7 @@ public void ClearDrawings() } /// - /// Draws a circle on the last drawing layer created by NewDrawing + /// Draws a circle on the last drawing layer created by AxMap.NewDrawing /// /// Center x coordinate for the circle to be drawn. /// Center y coordinate for the circle to be drawn. @@ -1550,7 +1560,7 @@ public void DrawCircleEx(int LayerHandle, double x, double y, double pixelRadius } /// - /// Draws a line on the last drawing layer created using NewDrawing. + /// Draws a line on the last drawing layer created using AxMap.NewDrawing. /// /// X coordinate of the first point used to draw the line /// Y coordinate of the first point used to draw the line. @@ -1579,7 +1589,7 @@ public void DrawLineEx(int LayerHandle, double x1, double y1, double x2, double } /// - /// Draws a point on the last drawing layer created by NewDrawing. + /// Draws a point on the last drawing layer created by AxMap.NewDrawing. /// /// The x coordinate of the point to draw /// The y coordinate of the point to draw. @@ -1604,7 +1614,7 @@ public void DrawPointEx(int LayerHandle, double x, double y, int pixelSize, uint } /// - /// Draws a polygon on the last drawing layer created using NewDrawing. + /// Draws a polygon on the last drawing layer created using AxMap.NewDrawing. /// /// An array containing x-coordinates for each point in the polygon. /// An array containing y-coordinates for each point in the polygon. @@ -1631,7 +1641,7 @@ public void DrawPolygonEx(int LayerHandle, ref object xPoints, ref object yPoint } /// - /// Draws a circle with custom outline width on the last drawing layer created by NewDrawing. + /// Draws a circle with custom outline width on the last drawing layer created by AxMap.NewDrawing. /// /// Center x coordinate for the circle to be drawn. /// Center y coordinate for the circle to be drawn. @@ -1660,7 +1670,7 @@ public void DrawWideCircleEx(int LayerHandle, double x, double y, double radius, } /// - /// Draws a polygon with custom width of outline on the last drawing layer created using NewDrawing. + /// Draws a polygon with custom width of outline on the last drawing layer created using AxMap.NewDrawing. /// /// An array containing x-coordinates for each point in the polygon. /// An array containing y-coordinates for each point in the polygon. @@ -1674,7 +1684,7 @@ public void DrawWidePolygon(ref object xPoints, ref object yPoints, int numPoint } /// - /// Draws a polygon with custom width of outline on the last drawing layer created using NewDrawing. + /// Draws a polygon with custom width of outline on the last drawing layer created using AxMap.NewDrawing. /// /// The handle of the drawing layer created with AxMap.NewDrawing call. /// An array containing x-coordinates for each point in the polygon. @@ -1691,10 +1701,10 @@ public void DrawWidePolygonEx(int LayerHandle, ref object xPoints, ref object yP /// /// Creates a new drawing layer on the map returning its handle. /// - /// Sets the coordinate system to use for the new drawing layer to be created. (ScreenReferenced - /// uses pixels in screen coordinates. SpatiallyReferenced uses projected map units.) + /// Sets the coordinate system to use for the new drawing layer to be created. (tkDrawReferenceList.dlScreenReferencedList + /// uses pixels in screen coordinates. tkDrawReferenceList.dlSpatiallyReferencedList uses projected map units.) /// The handle for the new drawing layer in the map. - public int NewDrawing(MapWinGIS.tkDrawReferenceList Projection) + public int NewDrawing(tkDrawReferenceList projection) { throw new NotImplementedException(); } diff --git a/docs/AxInterop.MapWinGIS/AxMap_Deprecated.cs b/docs/AxInterop.MapWinGIS/AxMap_Deprecated.cs index d0092aa9..309777b6 100644 --- a/docs/AxInterop.MapWinGIS/AxMap_Deprecated.cs +++ b/docs/AxInterop.MapWinGIS/AxMap_Deprecated.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; #if nsp namespace AxMapWinGIS diff --git a/docs/AxInterop.MapWinGIS/Enums.cs b/docs/AxInterop.MapWinGIS/Enums.cs index 4086ea86..6c038581 100644 --- a/docs/AxInterop.MapWinGIS/Enums.cs +++ b/docs/AxInterop.MapWinGIS/Enums.cs @@ -1,4 +1,10 @@ -/// +// ReSharper disable ArrangeAccessorOwnerBody +// ReSharper disable DelegateSubtraction +// ReSharper disable PossibleInvalidCastExceptionInForeachLoop +// ReSharper disable CheckNamespace + + +/// /// Justification of the labels /// public enum tkHJustification { } diff --git a/docs/AxInterop.MapWinGIS/Properties/AssemblyInfo.cs b/docs/AxInterop.MapWinGIS/Properties/AssemblyInfo.cs index 04f7f8b6..98afea40 100644 --- a/docs/AxInterop.MapWinGIS/Properties/AssemblyInfo.cs +++ b/docs/AxInterop.MapWinGIS/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/docs/Examples/Description.cs b/docs/Examples/Description.cs index c629ad1c..4f3c7e77 100644 --- a/docs/Examples/Description.cs +++ b/docs/Examples/Description.cs @@ -1,10 +1,12 @@ - +// ReSharper disable ArrangeAccessorOwnerBody +// ReSharper disable DelegateSubtraction +// ReSharper disable PossibleInvalidCastExceptionInForeachLoop +// ReSharper disable CheckNamespace + +// ReSharper disable UseNullPropagation namespace Examples { - using System; using System.Collections.Generic; - using System.Linq; - using System.Text; /// /// The groups of the examples for GUI @@ -25,9 +27,8 @@ public enum ExampleGroup /// /// Holds description of the example to be displayed in the documentation and GUI of this project. /// - class Example + public class Example { - private string guiName; // in the interface public string function; // to call with reflection public string description; // to generate documentation page and show in the GUI public bool image; // whether an image should be attached to the page @@ -38,24 +39,22 @@ public Example() image = true; } - public string GuiName - { - get { return guiName; } - set { guiName = value; } - } + public string GuiName { get; set; } } - class Description + internal class Description { // list of the examples - public List examples = new List(); + public readonly List examples = new List(); // event declaration - delegate void ExampleAddedEventHandler(Example ex, int index); - event ExampleAddedEventHandler ExampleAdded; - void FireExampleAdded(Example ex, int index) + private delegate void ExampleAddedEventHandler(Example ex, int index); + + private event ExampleAddedEventHandler ExampleAdded; + + private void FireExampleAdded(Example ex, int index) { - if (this.ExampleAdded != null) + if (ExampleAdded != null) ExampleAdded(ex, index); } @@ -64,10 +63,10 @@ void FireExampleAdded(Example ex, int index) /// public Description() { - this.ExampleAdded += Description_ExampleAdded; - for (int i = 0; i < 30; i++) + ExampleAdded += Description_ExampleAdded; + for (var i = 0; i < 30; i++) { - Example ex = new Example(); + var ex = new Example(); FireExampleAdded(ex, i); examples.Add(ex); } @@ -76,7 +75,7 @@ public Description() /// /// Adds description of the examples /// - void Description_ExampleAdded(Example ex, int index) + private static void Description_ExampleAdded(Example ex, int index) { switch (index) { diff --git a/docs/Examples/MapEventHandler.cs b/docs/Examples/MapEventHandler.cs index 6300db4e..1b490fe1 100644 --- a/docs/Examples/MapEventHandler.cs +++ b/docs/Examples/MapEventHandler.cs @@ -1,18 +1,16 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +// ReSharper disable ArrangeAccessorOwnerBody +// ReSharper disable DelegateSubtraction +// ReSharper disable PossibleInvalidCastExceptionInForeachLoop +// ReSharper disable CheckNamespace -static class MapEvents +public static class MapEvents { - public static AxMapWinGIS._DMapEvents_MouseDownEventHandler _mouseDownDelegate; - public static AxMapWinGIS._DMapEvents_MouseMoveEventHandler _mouseMoveDelegate; - public static AxMapWinGIS._DMapEvents_MouseUpEventHandler _mouseUpDelegate; - public static AxMapWinGIS._DMapEvents_SelectBoxFinalEventHandler _selectBoxDelegate; - public static AxMapWinGIS._DMapEvents_ShapeHighlightedEventHandler _shapeHighlightedDelegate; - - private static Delegate[] m_delegates = new Delegate[5] { _mouseDownDelegate, _mouseMoveDelegate, _mouseUpDelegate, - _selectBoxDelegate, _shapeHighlightedDelegate }; + private static AxMapWinGIS._DMapEvents_MouseDownEventHandler _mouseDownDelegate; + private static AxMapWinGIS._DMapEvents_MouseMoveEventHandler _mouseMoveDelegate; + private static AxMapWinGIS._DMapEvents_MouseUpEventHandler _mouseUpDelegate; + private static AxMapWinGIS._DMapEvents_SelectBoxFinalEventHandler _selectBoxDelegate; + private static AxMapWinGIS._DMapEvents_ShapeHighlightedEventHandler _shapeHighlightedDelegate; #region Attach delegate /// @@ -22,31 +20,31 @@ public static void AttachMap(AxMapWinGIS.AxMap axMap1) { axMap1.MouseDownEvent += delegate(object sender, AxMapWinGIS._DMapEvents_MouseDownEvent e) { - object[] param = new object[2] { sender, e }; + var param = new[] { sender, e }; Invoke(_mouseDownDelegate, param); }; axMap1.MouseMoveEvent += delegate(object sender, AxMapWinGIS._DMapEvents_MouseMoveEvent e) { - object[] param = new object[2] { sender, e }; + var param = new[] { sender, e }; Invoke(_mouseMoveDelegate, param); }; axMap1.MouseUpEvent += delegate(object sender, AxMapWinGIS._DMapEvents_MouseUpEvent e) { - object[] param = new object[2] { sender, e }; + var param = new[] { sender, e }; Invoke(_mouseUpDelegate, param); }; axMap1.SelectBoxFinal += delegate(object sender, AxMapWinGIS._DMapEvents_SelectBoxFinalEvent e) { - object[] param = new object[2] { sender, e }; + var param = new[] { sender, e }; Invoke(_selectBoxDelegate, param); }; axMap1.ShapeHighlighted += delegate(object sender, AxMapWinGIS._DMapEvents_ShapeHighlightedEvent e) { - object[] param = new object[2] { sender, e }; + var param = new[] { sender, e }; Invoke(_shapeHighlightedDelegate, param); }; } diff --git a/docs/Examples/Program.cs b/docs/Examples/Program.cs index 7881f109..3584c2b5 100644 --- a/docs/Examples/Program.cs +++ b/docs/Examples/Program.cs @@ -1,18 +1,21 @@ -using MapWinGIS; +// ReSharper disable ArrangeAccessorOwnerBody +// ReSharper disable DelegateSubtraction +// ReSharper disable PossibleInvalidCastExceptionInForeachLoop +// ReSharper disable CheckNamespace + +using MapWinGIS; using System; -using System.Collections.Generic; -using System.Linq; using System.Windows.Forms; namespace Examples { - static class Program + public static class Program { /// /// The main entry point for the application. /// [STAThread] - static void Main() + private static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); @@ -20,10 +23,9 @@ static void Main() Application.Run(new StartForm()); } - static void InitGlobalSettings() + private static void InitGlobalSettings() { - var gs = new GlobalSettings(); - gs.ReprojectLayersOnAdding = true; + var globalSettings = new GlobalSettings {ReprojectLayersOnAdding = true}; } } diff --git a/docs/Examples/Properties/AssemblyInfo.cs b/docs/Examples/Properties/AssemblyInfo.cs index bcaee0dd..f8752bec 100644 --- a/docs/Examples/Properties/AssemblyInfo.cs +++ b/docs/Examples/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/docs/Examples/StartForm.cs b/docs/Examples/StartForm.cs index ffef509a..dd33cf21 100644 --- a/docs/Examples/StartForm.cs +++ b/docs/Examples/StartForm.cs @@ -1,10 +1,4 @@ using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; using System.Windows.Forms; namespace Examples @@ -18,7 +12,7 @@ public StartForm() Shown += StartForm_Shown; } - void StartForm_Shown(object sender, EventArgs e) + private void StartForm_Shown(object sender, EventArgs e) { using (var form = new TestForm()) { diff --git a/docs/Examples/TestForm.cs b/docs/Examples/TestForm.cs index 24d8eec9..e281896d 100644 --- a/docs/Examples/TestForm.cs +++ b/docs/Examples/TestForm.cs @@ -1,14 +1,18 @@ - +// ReSharper disable ArrangeAccessorOwnerBody +// ReSharper disable DelegateSubtraction +// ReSharper disable PossibleInvalidCastExceptionInForeachLoop +// ReSharper disable CheckNamespace + +// ReSharper disable MergeSequentialChecks +// ReSharper disable SuggestVarOrType_SimpleTypes +// ReSharper disable SuggestVarOrType_Elsewhere +// ReSharper disable SuggestVarOrType_BuiltInTypes namespace Examples { #region Usings using System; using System.Collections.Generic; - using System.ComponentModel; - using System.Data; - using System.Drawing; using System.Linq; - using System.Text; using System.Windows.Forms; using System.IO; using System.Reflection; @@ -18,11 +22,12 @@ namespace Examples public partial class TestForm : Form { - MapExamples m_examples; - Description m_description; + private readonly MapExamples m_examples; + private readonly Description m_description; private string m_dataPath; private string m_iconPath; + /// /// /// Creates a new instance of the form1 /// @@ -30,14 +35,11 @@ public TestForm() { InitializeComponent(); - - MapEvents.AttachMap(axMap1); - m_examples = new MapExamples(); - m_examples.axMap1 = axMap1; + m_examples = new MapExamples {axMap1 = axMap1}; m_description = new Description(); FillList(); - this.treeView1.SelectedNode = this.treeView1.Nodes[0]; + treeView1.SelectedNode = treeView1.Nodes[0]; GenerateExamples(); btnClear_Click_1(null, null); @@ -59,25 +61,24 @@ void axMap1_ProjectionMismatch(object sender, AxMapWinGIS._DMapEvents_Projection /// /// Fills the list from the examples directory. /// - public void FillList() + private void FillList() { m_dataPath = Path.GetDirectoryName(Application.ExecutablePath) + @"..\..\..\..\Data\"; m_iconPath = Path.GetDirectoryName(Application.ExecutablePath) + @"..\..\..\..\icons\"; if (!Directory.Exists(m_dataPath)) { - MessageBox.Show("The directory with the data wasn't found:\n" + m_dataPath); + MessageBox.Show(@"The directory with the data wasn't found:\n" + m_dataPath); return; } - this.treeView1.Nodes.Clear(); + treeView1.Nodes.Clear(); string[] names = Enum.GetNames(typeof(ExampleGroup)); foreach (string name in names) { - if (name != "None") - { - TreeNode node = this.treeView1.Nodes.Add(name); - node.Expand(); - } + if (name == "None") continue; + + TreeNode node = treeView1.Nodes.Add(name); + node.Expand(); } IEnumerable list = m_description.examples.Select(t => t.group.ToString()).Distinct(); @@ -87,7 +88,7 @@ public void FillList() foreach (Example ex in examples) { TreeNode node = new TreeNode() { Text = ex.GuiName, Tag = ex }; - foreach (TreeNode parent in this.treeView1.Nodes) + foreach (TreeNode parent in treeView1.Nodes) { if (parent.Text == ex.group.ToString()) { @@ -97,26 +98,25 @@ public void FillList() } } - this.treeView1.NodeMouseDoubleClick += delegate(object sender, TreeNodeMouseClickEventArgs e) + treeView1.NodeMouseDoubleClick += delegate { - this.BtnRunClick(null, null); + BtnRunClick(); }; - this.treeView1.AfterSelect += new TreeViewEventHandler(treeView1_AfterSelect); + treeView1.AfterSelect += treeView1_AfterSelect; } /// /// Shows description for the selected example /// - void treeView1_AfterSelect(object sender, TreeViewEventArgs e) + private void treeView1_AfterSelect(object sender, TreeViewEventArgs e) { - this.lblDescription.Text = "Description: "; - TreeNode node = this.treeView1.SelectedNode; - if (node != null && node.Tag != null) - { - Example ex = node.Tag as Example; - this.lblDescription.Text += "The example demonstrates how to " + ex.description; - } + lblDescription.Text = @"Description: "; + TreeNode node = treeView1.SelectedNode; + if (node == null || node.Tag == null) return; + + Example ex = node.Tag as Example; + if (ex != null) lblDescription.Text += @"The example demonstrates how to " + ex.description; } #endregion @@ -124,59 +124,57 @@ void treeView1_AfterSelect(object sender, TreeViewEventArgs e) /// /// Runs the selected example /// - private void BtnRunClick(object sender, EventArgs e) + private void BtnRunClick() { - this.btnClear_Click_1(null, null); + btnClear_Click_1(null, null); - TreeNode node = this.treeView1.SelectedNode; - if (node != null && node.Tag != null) + TreeNode node = treeView1.SelectedNode; + if (node == null || node.Tag == null) return; + + Example ex = node.Tag as Example; + if (ex == null) return; + + MethodInfo info = m_examples.GetType().GetMethod(ex.function); + if (info == null) + { + MessageBox.Show(@"Function wasn't found: " + ex.function); + return; + } + + ParameterInfo[] list = info.GetParameters(); + if (list.Any()) { - Example ex = node.Tag as Example; - if (ex != null) + object[] param = new object[list.Count()]; + for (int i = 0; i < list.Count(); i++) { - MethodInfo info = m_examples.GetType().GetMethod(ex.function); - if (info == null) + ParameterInfo item = list[i]; + switch (item.ParameterType.ToString()) { - MessageBox.Show("Function wasn't found: " + ex.function); - } - else - { - ParameterInfo[] list = info.GetParameters(); - if (list.Any()) - { - object[] param = new object[list.Count()]; - for (int i = 0; i < list.Count(); i++) + case "AxMapWinGIS.AxMap": + param[i] = axMap1; + break; + case "System.String": + if (item.Name == "dataPath") { - ParameterInfo item = list[i]; - switch (item.ParameterType.ToString()) - { - case "AxMapWinGIS.AxMap": - param[i] = this.axMap1; - break; - case "System.String": - if (item.Name == "dataPath") - { - param[i] = m_dataPath; - } - if (item.Name == "iconPath") - { - param[i] = m_iconPath; - } - break; - case "System.Windows.Forms.ToolStripStatusLabel": - param[i] = this.label1; - break; - } - //MessageBox.Show(item.ParameterType.ToString()); + param[i] = m_dataPath; } - info.Invoke(m_examples, param); - } - else - { - info.Invoke(m_examples, null); - } + + if (item.Name == "iconPath") + { + param[i] = m_iconPath; + } + + break; + case "System.Windows.Forms.ToolStripStatusLabel": + param[i] = label1; + break; } } + info.Invoke(m_examples, param); + } + else + { + info.Invoke(m_examples, null); } } #endregion @@ -186,7 +184,7 @@ private void BtnRunClick(object sender, EventArgs e) /// private void GenerateExamples() { - string path = System.IO.Path.GetDirectoryName(Application.ExecutablePath) + @"..\..\..\..\..\Interop.MapWinGIS\Related pages\examples_list.cs"; + string path = Path.GetDirectoryName(Application.ExecutablePath) + @"..\..\..\..\..\Interop.MapWinGIS\Related pages\examples_list.cs"; StreamWriter writer = new StreamWriter(path); foreach (Example item in m_description.examples) { @@ -207,7 +205,7 @@ private void btnClear_Click_1(object sender, EventArgs e) { axMap1.RemoveAllLayers(); axMap1.ClearDrawings(); - axMap1.CursorMode = MapWinGIS.tkCursorMode.cmZoomIn; + axMap1.CursorMode = tkCursorMode.cmZoomIn; MapEvents.Clear(); axMap1.Projection = tkMapProjection.PROJECTION_GOOGLE_MERCATOR; @@ -223,21 +221,18 @@ private void btnClear_Click_1(object sender, EventArgs e) /// private void btnSnapshot_Click(object sender, EventArgs e) { - TreeNode node = this.treeView1.SelectedNode; - if (node != null && node.Tag != null) - { - Example ex = node.Tag as Example; - if (ex != null) - { - MapWinGIS.Image img = (MapWinGIS.Image)this.axMap1.SnapShot(this.axMap1.Extents); - if (img != null) - { - string filename = Path.GetDirectoryName(Application.ExecutablePath) + - @"..\..\..\..\Resources\images\" + ex.function + ".png"; - img.Save(filename, false, MapWinGIS.ImageType.USE_FILE_EXTENSION, null); - } - } - } + TreeNode node = treeView1.SelectedNode; + if (node == null || node.Tag == null) return; + + Example ex = node.Tag as Example; + if (ex == null) return; + + Image img = axMap1.SnapShot(axMap1.Extents); + if (img == null) return; + + string filename = Path.GetDirectoryName(Application.ExecutablePath) + + @"..\..\..\..\Resources\images\" + ex.function + ".png"; + img.Save(filename, false, MapWinGIS.ImageType.USE_FILE_EXTENSION, null); } } } diff --git a/docs/Examples/src/AddCategoryRange.cs b/docs/Examples/src/AddCategoryRange.cs index 7282dbd4..dc9e0ab2 100644 --- a/docs/Examples/src/AddCategoryRange.cs +++ b/docs/Examples/src/AddCategoryRange.cs @@ -1,8 +1,14 @@ -using System; +// ReSharper disable ArrangeAccessorOwnerBody +// ReSharper disable DelegateSubtraction +// ReSharper disable PossibleInvalidCastExceptionInForeachLoop +// ReSharper disable CheckNamespace + using System.IO; using System.Windows.Forms; using AxMapWinGIS; using MapWinGIS; +// ReSharper disable SuggestVarOrType_BuiltInTypes +// ReSharper disable SuggestVarOrType_SimpleTypes namespace Examples { @@ -11,97 +17,95 @@ public partial class MapExamples // // Calculates area of polygons and sets 3 range of categories with different symbology // - public void AddCategoryRange(AxMap axMap1, string dataPath) + public void AddCategoryRange(AxMap axMap, string dataPath) { - axMap1.Projection = tkMapProjection.PROJECTION_GOOGLE_MERCATOR; + axMap.Projection = tkMapProjection.PROJECTION_GOOGLE_MERCATOR; string filename = dataPath + "landuse.shp"; if (File.Exists(filename) == false) { - MessageBox.Show("Failed to open file: " + filename); + MessageBox.Show(@"Failed to open file: " + filename); return; } Shapefile sf = new Shapefile(); - if (sf.Open(filename, null)) + if (!sf.Open(filename)) return; + + if (!sf.StartEditingTable()) { - if (!sf.StartEditingTable(null)) - { - MessageBox.Show("Failed to open editing mode."); - return; - } + MessageBox.Show(@"Failed to open editing mode."); + return; + } - int fieldIndex = sf.Table.FieldIndexByName["Area"]; + int fieldIndex = sf.Table.FieldIndexByName["Area"]; - if (fieldIndex == -1) - fieldIndex = sf.EditAddField("Area", FieldType.DOUBLE_FIELD, 15, 18); + if (fieldIndex == -1) + fieldIndex = sf.EditAddField("Area", FieldType.DOUBLE_FIELD, 15, 18); - for (int i = 0; i < sf.NumShapes; i++) - { - double area = sf.Shape[i].Area * 100000.0; - sf.EditCellValue(fieldIndex, i, area); - } + for (int i = 0; i < sf.NumShapes; i++) + { + double area = sf.Shape[i].Area * 100000.0; + sf.EditCellValue(fieldIndex, i, area); + } - // adding to map - int handle = axMap1.AddLayer(sf, true); - sf = axMap1.get_Shapefile(handle); // in case a copy of shapefile was created by GlobalSettings.ReprojectLayersOnAdding + // adding to map + int handle = axMap.AddLayer(sf, true); + sf = axMap.get_Shapefile(handle); // in case a copy of shapefile was created by GlobalSettings.ReprojectLayersOnAdding - double mean = sf.Table.MeanValue[fieldIndex]; - double stDev = sf.Table.StandardDeviation[fieldIndex]; - double min = (double)sf.Table.MinValue[fieldIndex]; - double max = (double)sf.Table.MaxValue[fieldIndex]; + double mean = sf.Table.MeanValue[fieldIndex]; + double stDev = sf.Table.StandardDeviation[fieldIndex]; + double min = (double)sf.Table.MinValue[fieldIndex]; + double max = (double)sf.Table.MaxValue[fieldIndex]; - var scheme = new ColorScheme(); + var scheme = new ColorScheme(); - // 1. the first range [min; mean - stDev] - Utils utils = new Utils(); - sf.DefaultDrawingOptions.FillType = tkFillType.ftHatch; - sf.DefaultDrawingOptions.FillHatchStyle = tkGDIPlusHatchStyle.hsDiagonalBrick; + // 1. the first range [min; mean - stDev] + Utils utils = new Utils(); + sf.DefaultDrawingOptions.FillType = tkFillType.ftHatch; + sf.DefaultDrawingOptions.FillHatchStyle = tkGDIPlusHatchStyle.hsDiagonalBrick; - bool res = sf.Categories.AddRange(fieldIndex, tkClassificationType.ctNaturalBreaks, 5, min, mean); - scheme.SetColors2(tkMapColor.Red, tkMapColor.Yellow); + sf.Categories.AddRange(fieldIndex, tkClassificationType.ctNaturalBreaks, 5, min, mean); + scheme.SetColors2(tkMapColor.Red, tkMapColor.Yellow); - // apply colors 0 and 4 are indices of categories, since 5 categories were added - from 0 to 4 - sf.Categories.ApplyColorScheme3(tkColorSchemeType.ctSchemeRandom, scheme, tkShapeElements.shElementFill, 0, 4); + // apply colors 0 and 4 are indices of categories, since 5 categories were added - from 0 to 4 + sf.Categories.ApplyColorScheme3(tkColorSchemeType.ctSchemeRandom, scheme, tkShapeElements.shElementFill, 0, 4); - // 2. the second range [mean - stDev; mean + stDev] - // the default drawing options will be copied to the new categories - sf.DefaultDrawingOptions.FillType = tkFillType.ftHatch; - sf.DefaultDrawingOptions.FillHatchStyle = tkGDIPlusHatchStyle.hsCross; - res = sf.Categories.AddRange(fieldIndex, tkClassificationType.ctEqualIntervals, 5, mean, mean + stDev); - scheme.SetColors2(tkMapColor.Green, tkMapColor.Blue); - sf.Categories.ApplyColorScheme3(tkColorSchemeType.ctSchemeGraduated, scheme, tkShapeElements.shElementFill, 5, 9); + // 2. the second range [mean - stDev; mean + stDev] + // the default drawing options will be copied to the new categories + sf.DefaultDrawingOptions.FillType = tkFillType.ftHatch; + sf.DefaultDrawingOptions.FillHatchStyle = tkGDIPlusHatchStyle.hsCross; + sf.Categories.AddRange(fieldIndex, tkClassificationType.ctEqualIntervals, 5, mean, mean + stDev); + scheme.SetColors2(tkMapColor.Green, tkMapColor.Blue); + sf.Categories.ApplyColorScheme3(tkColorSchemeType.ctSchemeGraduated, scheme, tkShapeElements.shElementFill, 5, 9); - // 3. the third range [mean + stDev; max] - // the default drawing options will be copied to the new categories - sf.DefaultDrawingOptions.FillType = tkFillType.ftGradient; - sf.DefaultDrawingOptions.FillColor2 = utils.ColorByName(tkMapColor.Gray); - res = sf.Categories.AddRange(fieldIndex, tkClassificationType.ctEqualIntervals, 5, mean + stDev, max); - scheme.SetColors2(tkMapColor.Pink, tkMapColor.Violet); - sf.Categories.ApplyColorScheme3(tkColorSchemeType.ctSchemeGraduated, scheme, tkShapeElements.shElementFill, 10, 14); + // 3. the third range [mean + stDev; max] + // the default drawing options will be copied to the new categories + sf.DefaultDrawingOptions.FillType = tkFillType.ftGradient; + sf.DefaultDrawingOptions.FillColor2 = utils.ColorByName(tkMapColor.Gray); + sf.Categories.AddRange(fieldIndex, tkClassificationType.ctEqualIntervals, 5, mean + stDev, max); + scheme.SetColors2(tkMapColor.Pink, tkMapColor.Violet); + sf.Categories.ApplyColorScheme3(tkColorSchemeType.ctSchemeGraduated, scheme, tkShapeElements.shElementFill, 10, 14); - // apply expresions should be called exlicitly - sf.Categories.ApplyExpressions(); - axMap1.Redraw(); + // apply expresions should be called exlicitly + sf.Categories.ApplyExpressions(); + axMap.Redraw(); - // saving options to see categories desription in XML - axMap1.SaveLayerOptions(handle, "categories_sample", true, ""); - } + // saving options to see categories desription in XML + axMap.SaveLayerOptions(handle, "categories_sample", true, ""); } // // To apply the same options on the next loading // - private void RestoreCategories(AxMap axMap1, string dataPath) + private void RestoreCategories(AxMap axMap, string dataPath) { string filename = dataPath + "landuse.shp"; Shapefile sf = new Shapefile(); - if (sf.Open(filename, null)) - { - int handle = axMap1.AddLayer(sf, true); - string description = ""; - axMap1.LoadLayerOptions(handle, "categories_sample", ref description); - } + if (!sf.Open(filename)) return; + + int handle = axMap.AddLayer(sf, true); + string description = ""; + axMap.LoadLayerOptions(handle, "categories_sample", ref description); } } } \ No newline at end of file diff --git a/docs/Interop.MapWinGIS.XML b/docs/Interop.MapWinGIS.XML index 0c2d93aa..7cb6197d 100644 --- a/docs/Interop.MapWinGIS.XML +++ b/docs/Interop.MapWinGIS.XML @@ -702,6 +702,138 @@ The index of the break. The new colour to set. + + + Implementation of the GDAL v2 librified functions. + Not all functions are implemented yet. + + \new495 Added in version 4.9.5 + + + + Retrieves the last error generated in the object. + + \new495 Added in version 4.9.5 + + + + The global callback is the interface used by MapWinGIS to pass progress and error events to interested applications. + + \new495 Added in version 4.9.5 + + + + The key may be used by the programmer to store any string data associated with the object. + + \new495 Added in version 4.9.5 + + + + Gets the detailed error message. + + \new495 Added in version 4.9.5 + + + + Image reprojection and warping utility. + Implementing the librified function of GDAL's gdalwarp.exe tool + + The source filename. + The destination filename. + The options, as a string array + See GDAL's documentation here: http://www.gdal.org/gdalwarp.html + \new495 Added in version 4.9.5 + + \code + // Example of creating VRT file from TIFF file. More options are possible: + var output = Path.GetTempPath() + "GdalWarp.vrt"; + var options = new[] + { + "-of", "vrt", + "-overwrite" + }; + var gdalUtils = new GdalUtils(); + if (!gdalUtils.GDALWarp("test.tif", output, options)) + { + Debug.WriteLine("GdalWarp failed: " + gdalUtils.ErrorMsg[gdalUtils.LastErrorCode] + " Detailed error: " + gdalUtils.DetailedErrorMsg); + } + \endcode + + \code + // Example of cutting a TIFF file with a border file: + var output = Path.GetTempPath() + "GdalWarpCutline.vrt"; + const string border = @"test.shp"; + var options = new[] + { + "-of", "vrt", + "-overwrite", + "-crop_to_cutline", + "-cutline", border + }; + var gdalUtils = new GdalUtils(); + if (!gdalUtils.GDALWarp("test.tif", output, options)) + { + Debug.WriteLine("GdalWarp failed: " + gdalUtils.ErrorMsg[gdalUtils.LastErrorCode] + " Detailed error: " + gdalUtils.DetailedErrorMsg); + } + \endcode + + + + Converts simple features data between file formats. + Implementing the librified function of GDAL's ogr2ogr.exe tool + + The source filename. + The destination filename. + The options, as a string array + If set to true improves performance but also might make it instable. + See GDAL's documentation here: http://www.gdal.org/ogr2ogr.html + \new495 Added in version 4.9.5 + + \code + // Converting shapefile to gml: + var outputFilename = Path.Combine(Path.GetTempPath(), "translated.gml"); + var options = new[] + { + "-f", "GML" + }; + var gdalUtils = new GdalUtils(); + if (!gdalUtils.GdalVectorTranslate(inputFilename, outputFilename, options, true)) + { + Debug.WriteLine("GdalVectorTranslate failed: " + gdalUtils.ErrorMsg[gdalUtils.LastErrorCode] + " Detailed error: " + gdalUtils.DetailedErrorMsg); + } + \endcode + + + + Clips the vector with another vector. + + The subject filename. + The overlay filename. + The destination filename. + If set to true improves performance but also might make it instable. + Uses GdalUtils.GdalVectorTranslate under the hood. + \new495 Added in version 4.9.5 + + \code + // Clipping large shapefile with border file + const string subjectFilename = @"D:\dev\GIS-Data\Issues\MWGIS-78 Clipper\Fishnet.shp"; + const string borderFilename = @"D:\dev\GIS-Data\Issues\MWGIS-78 Clipper\border.shp"; + var outputFilename = Path.Combine(tempFolder, "GdalVectorTranslate.shp"); + var gdalUtils = new GdalUtils(); + if (!gdalUtils.ClipVectorWithVector("LargeFile.shp", "Border.shp", outputFilename)) + { + Debug.WriteLine("GdalVectorTranslate failed: " + gdalUtils.ErrorMsg[gdalUtils.LastErrorCode] + " Detailed error: " + gdalUtils.DetailedErrorMsg); + } + \endcode + + + + Retrieves the error message associated with the specified error code. + + The error code for which the error message is required. + The error message description for the specified error code. + \new495 Added in version 4.9.5 + Represents a layer downloading and displaying the data from the particular WMS Server. @@ -2269,7 +2401,7 @@ \note The fully working implementation of the shape editor can be examined in the the Demo application included in MapWinGIS installation (starting from v4.9.3). The source code for this application is available in - the repository. + the repository. \new493 Added in version 4.9.3 @@ -3835,6 +3967,16 @@ \see OgrLayer.MaxFeatureCount \new493 Added in version 4.9.3 + + + Applies to conversion of OGR Shapefile layers to MapWinGIS Shapefiles (via GetBuffer), since + OGR Shapefiles do not support Logical fields. Instead, DBF Logical fields are read as single- + character strings. This property Gets or Sets a value which indicates whether to interpret + single-character Ogr strings as Logical/Boolean values, as long as the character contained + in the string is one of valid DBF logical characters (e.g. Y, N, T, F). The default value is true. + + \new495 Added in version 4.9.5 + Gets or sets a value which indicates whether OgrLayer.DynamicLoading mode will @@ -3950,16 +4092,14 @@ - Gets or sets API key to access Bing maps. Without API key Bing Maps provider isn't available. See - details here. + Gets or sets API key to access Bing maps. Without API key Bing Maps provider isn't available. \new493 Added in version 4.9.3 Sets application credentials for Here Maps online tiles. - Without these credentials Here Maps providers are not available. See - details here. + Without these credentials Here Maps providers are not available. Application Id. Can be obtained by registering on the site of the service. Application code. Can be obtained by registering on the site of the service. @@ -8033,7 +8173,7 @@ - Imports shape data from WKT format. + Exports shape data to WKT format. String in WKT format with shape data. \new490 Added in version 4.9.0 @@ -8139,6 +8279,23 @@ \new493 Added in version 4.9.3 + + + Return a point at the specified distance (or percentage) along the specified line + + Starting point along this line (specify Point[0] for beginning of line) + Distance along this line (or percentage of line length; if a percentage, specify a number between 0.0 and 1.0) + Optional value; if FALSE, 'distance' is actual distance; if TRUE, distance is percentage of length; defaults to FALSE + Returns a Point class representing the point along the sourceLine that is the specified distance (or percentage) along the line. + + Only applies to a Polyline Shape. If 'distance' is greater than the the line length, the line's endpoint is returned. + + 'startPoint' does not have to be on the line. If not on the line, actual starting point will be the nearest point to 'startPoint' that is on the line. + + \see Utils.LineInterpolatePoint + + \new500 Added in version 5.0.0 + @@ -8947,125 +9104,6 @@ ShapefileCategory.Expression property will change it to cvExpression. \new493 Added in version 4.9.3 - - - Defines a part of shapefile color scheme and specifies how a certain region of a shapefile will be colored. - - \deprecated in v. 4.8. Use ShapefileCategories, ShapefileCategory, ColorScheme classes instead. - \removed493 Removed in 4.9.3 - - - - Gets or sets the caption of the shapefile color break. - - - - - Gets or sets the color which will be used for drawing objects characterized by ShapefileColorBreak.EndValue. - - - - - Gets or sets the value which represent the end of shapefile color break. - - - - - Gets or sets the color which will be used for drawing objects characterized by ShapefileColorBreak.StartValue. - - - - - Gets or sets the value which represent the start of shapefile color break. - - - - - Gets or sets a boolean value which indicates whether the objects defined by color break will be displayed. - - - - - A shapefile color scheme defines how a shapefile will be colored. - - A shapefile color scheme consists of ShapefileColorBreak objects. - \deprecated in v. 4.8. Use ShapefileCategories, ShapefileCategory, ColorScheme instead. - \removed493 Removed in 4.9.3 - - - - Adds a color break to the color scheme. - - - - - - - Gets or sets the index in the attribute table the color scheme is associated with. - - - - - Gets or sets callback object to return the information about the errors. - - \deprecated v4.9.3 Use GlobalSettings.ApplicationCallback instead. - - - - Insert a color break in the specified position of the scheme. - - The position to insert color break at. - The color break to insert. - The actual position color break was inserted at. - - - - Gets or sets text string associated with the object. - - - - - Returns the code of the last error which took place inside this instance of class. - - - - - Gets or sets the layer handle the color scheme is associated with. - - - - - Gets the number of color breaks in the color scheme. - - The number of breaks. - - - - Removes specific color break from the scheme. - - The index of the break to remove. - - - - Gets a color break from the color scheme. - - The index of the break. - The color break object or NULL reference on failure. - - - - Gets the description of the error code returned by ShapefileColorScheme.LastErrorCode. - - The code of error. - The description of error. - - - - Replaces a color break in the color scheme. - - The index of color break to replace. - The reference to the new color break. - Represents a network built from the polyline shapefile. @@ -9887,12 +9925,21 @@ Type of cache to be cleared. + + + Clears cache of the specified to type for a given provider ID and scales. + + Type of cache to be cleared. + Tile provider ID to be cleared. -1 will clear tiles for all providers. + Minimal scale (zoom) to clear tiles for. + Maximum scale (zoom) to clear tiles for. + Clears cache of the specified to type for a given provider and scales. Type of cache to be cleared. - Tile provider to be cleared. ProviderNone will clear tiles for all providers. + Tile provider to be cleared. ProviderNone will clear tiles for all providers. Minimal scale (zoom) to clear tiles for. Maximum scale (zoom) to clear tiles for. @@ -9904,6 +9951,11 @@ Therefore in most cases tiles of particular zoom will be somewhat additionally scaled to fit the map, i.e. their display size on map won't be equal to the original 256 pixels. + + + Gets or sets the delay request timeout. + + Restores the state of Tiles class from string. @@ -9919,7 +9971,7 @@ Gets the bounds of specific tile in decimal degrees (for inner use/debug purposes). - Id of the provider. + Id of the provider. Zoom level for a tile. X coordinate of the tile within zoom level. Y coordinate of the tile within zoom level. @@ -9931,7 +9983,7 @@ Bounds in decimal degrees. Zoom level. - Id of the provider. + Id of the provider. Extents object with tile bounds or null on failure. Can be used at the first step of prefetching operation. @@ -9962,7 +10014,7 @@ Minimal longitude to cache within. Maximum longitude to cache within. Zoom level. - Id of the provider. + Id of the provider. StopExecution interface implementation to stop the operation prematurely. The number of tiles scheduled for caching. The operation is executed asynchronously. To get the progress information use Tiles.GlobalCallback property. @@ -9977,7 +10029,7 @@ Minimum Y index of tile to be cached (in coordinates of tile zoom level). Maximum Y index of tile to be cached (in coordinates of tile zoom level). Zoom level to be cached. - Id of the provider. + Id of the provider. StopExecution interface implementation to stop the operation prematurely. Number of tiles scheduled for caching. The operation is executed asynchronously. See details in Tiles.Prefetch. @@ -9988,8 +10040,8 @@ Extents to cache within in decimal degrees. Zoom level. - Id of the provider. - Directory to save files into. Nested folders for zoom levels, + Id of the provider. + Directory to save files into (must exists). Nested folders for zoom levels, X/Y coordinates will be created automatically. File extension to store tiles with. StopExecution interface implementation to stop the operation prematurely. @@ -10021,6 +10073,31 @@ Gets list of the available default and custom tile providers. + + + Gets the current size of cache. + + The type of cache to return size for. + The size of cache in MB. + + + + Gets the current size of cache used for specific provider and zoom level. + + The type of cache to return size for. + Provider. ProviderNone will return size for all providers. + Scale (zoom) level. -1 will return size for all zoom levels. + The size of cache in MB. + + + + Gets the current size of cache used for specific provider and zoom level. + + Type of the cache. + The provider. -1 will return size for all providers. + The scale. -1 will return size for all zoom levels. + The size of cache in MB. + Gets proxy server settings for tiles class including IP and port, e.g. 192.168.0.1:80. @@ -10059,22 +10136,6 @@ When set to false tiles won't be requested either from server or cache. - - - Gets the current size of cache. - - The type of cache to return size for. - The size of cache in MB. - - - - Gets the current size of cache used for specific provider and zoom level. - - The type of cache to return size for. - Provider. ProviderNone will return size for all providers. - Scale (zoom) level. -1 will return size for all zoom levels. - The size of cache in MB. - Gets the value indicating whether tiles requested from server will be automatically cached. @@ -10135,21 +10196,6 @@ \new491 Added in version 4.9.1 - - - Starts logging HTTP requests for tile server. - - Filename to write log into. New file will be created any existing file - overwritten. - Indicate whether only unsuccessful requests should logged. - True if log was opened, and false on failure. - \new491 Added in version 4.9.1 - - - - Stops logging of HTTP requests to a file. - - \new491 Added in version 4.9.1 - Gets the number of unsuccessful HTTP requests during prefetching operation (Tiles.Prefetch and overloads). @@ -10172,7 +10218,7 @@ Gets number of tiles stored in disk cache for a given provider, zoom and region. - Id of provider. + Id of provider. Zoom level. Min X index of tile. Max X index of tile. @@ -10200,7 +10246,7 @@ Gets the description of the specific error code. - The error code returned by LastErrorCode property. + The error code returned by LastErrorCode property. String with the description. @@ -10222,6 +10268,20 @@ This diagnostic value indicates whether tiles will be rendered without scaling and distortions and if they will be rendered at all. \new491 Added in version 4.9.1 + + + Gets or sets the proxy authentication scheme. + + \new491 Added in version 4.9.1 + + + + Gets a value indicating whether [projection is spherical mercator]. + + + true if [projection is spherical mercator]; otherwise, false. + + Gets projection used by specific tile service. @@ -10230,11 +10290,11 @@ - Clears user name and password set by Tiles.SetProxyAuthorization method. + Clears user name and password set by Tiles.SetProxyAuthentication method. \new493 Added in version 4.9.3 - + Sets credentials for proxy authorization. @@ -10488,6 +10548,7 @@ A utils object provides access to a set of utility functions to perform a variety of tasks on other objects such as grids, images, points, shapes, shapefiles, tins, etc. + Starting at v4.9.5 some specific GDAL methods are moved to GdalUtils. @@ -10825,6 +10886,7 @@ See documentation here: http://www.gdal.org/gdalwarp.html \new490 Added in version 4.9.0 + \deprecated v4.9.5 Use GdalUtils.GDALWarp instead. @@ -10913,6 +10975,7 @@ See documentation here: http://www.gdal.org/ogr2ogr.html \new490 Added in version 4.9.0 + \deprecated v4.9.5 Use GdalUtils.GdalVectorTranslate instead. @@ -11131,6 +11194,41 @@ \new495 Added in version 4.9.5 + + + Calculates the angle defined by the two specified coordinates + + + + Geographic Angle (in degrees) of the vector measured from the first point to the second point + + The angle returned is the so-called Geographic angle, measured in a clockwise direction + from the positive Y-axis, as opposed to the Arithmetic (Cartesian) angle measured in a + counter-clockwise direction from the positive X-axis. The returned value can be used + as-is to specify Shape or Label Rotation, which expect the Geographic angle for input. + + \see Shapefile.set_ShapeRotation + + \new500 Added in version 5.0.0 + + + + Return a point at the specified distance (or percentage) along the specified line + + Polyline shape to traverse + Starting point along 'sourceLine' (specify Point[0] for beginning of line) + Distance along line (or percentage of line length; if a percentage, specify a number between 0.0 and 1.0) + Optional value; if FALSE, 'distance' is actual distance; if TRUE, distance is percentage of length; defaults to FALSE + Returns a Point class representing the point along the sourceLine that is the specified distance (or percentage) along the line. + + 'sourceLine' must be a Polyline Shape. If 'distance' is greater than the source line length, the line's endpoint is returned. + + 'startPoint' does not have to be on 'sourceLine'. If not on the line, actual starting point will be the nearest point to 'startPoint' that is on 'sourceline'. + + \see Shape.InterpolatePoint + + \new500 Added in version 5.0.0 + A vector object is used to represent the light source for a grid color scheme. @@ -11729,7 +11827,7 @@ The objects of the drawing layer are specified in screen coordinates and are not moved after the changes of the extents. - To update such layers the full redraw is not needed. Therefore use AxMap.Refresh rather than AxMap.Redraw. + To update such layers the full redraw is not needed. Therefore use AxMap.Redraw2 rather than AxMap.Redraw. @@ -12566,7 +12664,7 @@ Map will be rendered from the main buffer. Only measurements and coordinate display will be rendered anew. - Corresponds to AxMap.Refresh/AxMap.Invalidate. + Corresponds to AxMap.Redraw2/AxMap.Invalidate. @@ -14162,7 +14260,8 @@ \addtogroup shapefile_geoprocessing Shapefile geoprocessing Here is a list of methods to perform geoprocessing tasks using shapefile data. - This module is a part of the documentation of Shapefile class. + This module is a part of the documentation of Shapefile class.\n\n + Use GlobalSettings.MinAreaToPerimeterRatio and GlobalSettings.MinPolygonArea to tweak which polygon needs to be included in the output file. \dot digraph shapefile_geoprocessing { splines = true; @@ -14703,7 +14802,13 @@ \addtogroup shapefile_selection Shapefile selection Here is a list of properties and methods for managing shapefile selection. - This module is a part of the documentation of Shapefile class. + This module is a part of the documentation of the Shapefile class. + + Historically, using the cmSelection and cmSelectByPolygon tools, you would specify a LayerHandle in the ChooseLayer map event + to indicate which single layer you were selecting shapes from. Starting in v5.0, you can select from multiple layers concurrently + by setting the 'Selectable' property of each layer you would like to select shapes from. If the LayerHandle returned from the + ChooseLayer event is left unspecified (-1), then instead, all layers having the 'Selectable' property = TRUE + will be selectable by the tool. \dot digraph shapefile_selection { splines = true; @@ -14791,6 +14896,17 @@ \new48 Added in version 4.8 + + + Gets or sets a value indicating whether this shapefile will be selectable by the cmSelection and cmSelectByPolygon tool. + + + This property allows for multiple shapefiles to be concurrently selectable. + If only selecting from one layer, you can still use the ChooseLayer map event to specify the selectable layer. + + \see AxMap.CursorMode + \new500 Added in version 5.0.0 + Gets or sets the way shapefile selection will be displayed. diff --git a/docs/Interop.MapWinGIS/Com Classes/Chart.cs b/docs/Interop.MapWinGIS/Com Classes/Chart.cs index 4098b2e8..482cb018 100644 --- a/docs/Interop.MapWinGIS/Com Classes/Chart.cs +++ b/docs/Interop.MapWinGIS/Com Classes/Chart.cs @@ -3,7 +3,6 @@ namespace MapWinGIS { #endif using System; - /// /// Represents a single chart on the map. /// @@ -59,7 +58,7 @@ namespace MapWinGIS /// \new48 Added in version 4.8 #if nsp #if upd - public class Chart : MapWinGIS.IChart + public class Chart : IChart #else public class IChart #endif @@ -107,9 +106,19 @@ public double PositionY /// This property will return NULL in case Chart.IsDrawn returns false. public Extents ScreenExtents { - get { throw new NotImplementedException(); } + get + { + throw new NotImplementedException(); + } } + + +// public Extents ScreenExtents +// { +// get { throw new NotImplementedException(); } +// } + /// /// Gets or sets the value which indicates whether the chart should be drawn on the map. /// diff --git a/docs/Interop.MapWinGIS/Com Classes/Charts.cs b/docs/Interop.MapWinGIS/Com Classes/Charts.cs index 1c614cb5..e27d9ddc 100644 --- a/docs/Interop.MapWinGIS/Com Classes/Charts.cs +++ b/docs/Interop.MapWinGIS/Com Classes/Charts.cs @@ -89,7 +89,8 @@ namespace MapWinGIS /// \new48 Added in version 4.8 #if nsp #if upd - public class Charts : MapWinGIS.ICharts + //public class Charts : MapWinGIS.ICharts + public class ICharts #else public class ICharts #endif @@ -692,7 +693,6 @@ public ChartField get_Field(int FieldIndex) { throw new NotImplementedException(); } - #endregion } #if nsp diff --git a/docs/Interop.MapWinGIS/Com Classes/GdalUtils.cs b/docs/Interop.MapWinGIS/Com Classes/GdalUtils.cs index 174c193a..17572225 100644 --- a/docs/Interop.MapWinGIS/Com Classes/GdalUtils.cs +++ b/docs/Interop.MapWinGIS/Com Classes/GdalUtils.cs @@ -158,15 +158,5 @@ public string get_ErrorMsg(int ErrorCode) { throw new NotImplementedException(); } - - /// - /// The global callback is the interface used by MapWinGIS to pass progress and error events to interested applications. - /// - /// \new495 Added in version 4.9.5 - ICallback IGdalUtils.GlobalCallback - { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } - } } } diff --git a/docs/Interop.MapWinGIS/Com Classes/Shape.cs b/docs/Interop.MapWinGIS/Com Classes/Shape.cs index a06e3cf6..bf8da318 100644 --- a/docs/Interop.MapWinGIS/Com Classes/Shape.cs +++ b/docs/Interop.MapWinGIS/Com Classes/Shape.cs @@ -704,7 +704,7 @@ public bool ImportFromWKT(string Serialized) } /// - /// Imports shape data from WKT format. + /// Exports shape data to WKT format. /// /// String in WKT format with shape data. /// \new490 Added in version 4.9.0 @@ -846,6 +846,27 @@ public bool IsEmpty { get { throw new NotImplementedException(); } } + + /// + /// Return a point at the specified distance (or percentage) along the specified line + /// + /// Starting point along this line (specify Point[0] for beginning of line) + /// Distance along this line (or percentage of line length; if a percentage, specify a number between 0.0 and 1.0) + /// Optional value; if FALSE, 'distance' is actual distance; if TRUE, distance is percentage of length; defaults to FALSE + /// Returns a Point class representing the point along the sourceLine that is the specified distance (or percentage) along the line. + /// + /// Only applies to a Polyline Shape. If 'distance' is greater than the the line length, the line's endpoint is returned. + /// + /// 'startPoint' does not have to be on the line. If not on the line, actual starting point will be the nearest point to 'startPoint' that is on the line. + /// + /// \see Utils.LineInterpolatePoint + /// + /// \new500 Added in version 5.0.0 + public Point InterpolatePoint(Point startPoint, double distance, bool normalized) + { + throw new NotImplementedException(); + } + } #if nsp } diff --git a/docs/Interop.MapWinGIS/Com Classes/Shapefile.cs b/docs/Interop.MapWinGIS/Com Classes/Shapefile.cs index 45b8856f..9eb741e8 100644 --- a/docs/Interop.MapWinGIS/Com Classes/Shapefile.cs +++ b/docs/Interop.MapWinGIS/Com Classes/Shapefile.cs @@ -956,7 +956,8 @@ public bool Snappable /// \addtogroup shapefile_geoprocessing Shapefile geoprocessing /// Here is a list of methods to perform geoprocessing tasks using shapefile data. - /// This module is a part of the documentation of Shapefile class. + /// This module is a part of the documentation of Shapefile class.\n\n + /// Use GlobalSettings.MinAreaToPerimeterRatio and GlobalSettings.MinPolygonArea to tweak which polygon needs to be included in the output file. /// \dot /// digraph shapefile_geoprocessing { /// splines = true; @@ -1665,7 +1666,13 @@ public bool RefreshShapeExtents(int ShapeId) #region Selection /// \addtogroup shapefile_selection Shapefile selection /// Here is a list of properties and methods for managing shapefile selection. - /// This module is a part of the documentation of Shapefile class. + /// This module is a part of the documentation of the Shapefile class. + /// + /// Historically, using the cmSelection and cmSelectByPolygon tools, you would specify a LayerHandle in the ChooseLayer map event + /// to indicate which single layer you were selecting shapes from. Starting in v5.0, you can select from multiple layers concurrently + /// by setting the 'Selectable' property of each layer you would like to select shapes from. If the LayerHandle returned from the + /// ChooseLayer event is left unspecified (-1), then instead, all layers having the 'Selectable' property = TRUE + /// will be selectable by the tool. /// \dot /// digraph shapefile_selection { /// splines = true; @@ -1781,6 +1788,21 @@ public void SelectNone() throw new NotImplementedException(); } + /// + /// Gets or sets a value indicating whether this shapefile will be selectable by the cmSelection and cmSelectByPolygon tool. + /// + /// + /// This property allows for multiple shapefiles to be concurrently selectable. + /// If only selecting from one layer, you can still use the ChooseLayer map event to specify the selectable layer. + /// + /// \see AxMap.CursorMode + /// \new500 Added in version 5.0.0 + public bool Selectable + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + /// /// Gets or sets the way shapefile selection will be displayed. /// diff --git a/docs/Interop.MapWinGIS/Com Classes/Utils.cs b/docs/Interop.MapWinGIS/Com Classes/Utils.cs index 6f1f3c36..39ff9882 100644 --- a/docs/Interop.MapWinGIS/Com Classes/Utils.cs +++ b/docs/Interop.MapWinGIS/Com Classes/Utils.cs @@ -872,6 +872,47 @@ public bool GetProjectionList(tkProjectionSet projectionSets, ref Object list) throw new NotImplementedException(); } + /// + /// Calculates the angle defined by the two specified coordinates + /// + /// + /// + /// Geographic Angle (in degrees) of the vector measured from the first point to the second point + /// + /// The angle returned is the so-called Geographic angle, measured in a clockwise direction + /// from the positive Y-axis, as opposed to the Arithmetic (Cartesian) angle measured in a + /// counter-clockwise direction from the positive X-axis. The returned value can be used + /// as-is to specify Shape or Label Rotation, which expect the Geographic angle for input. + /// + /// \see Shapefile.set_ShapeRotation + /// + /// \new500 Added in version 5.0.0 + public double GetAngle(Point firstPoint, Point secondPoint) + { + throw new NotImplementedException(); + } + + /// + /// Return a point at the specified distance (or percentage) along the specified line + /// + /// Polyline shape to traverse + /// Starting point along 'sourceLine' (specify Point[0] for beginning of line) + /// Distance along line (or percentage of line length; if a percentage, specify a number between 0.0 and 1.0) + /// Optional value; if FALSE, 'distance' is actual distance; if TRUE, distance is percentage of length; defaults to FALSE + /// Returns a Point class representing the point along the sourceLine that is the specified distance (or percentage) along the line. + /// + /// 'sourceLine' must be a Polyline Shape. If 'distance' is greater than the source line length, the line's endpoint is returned. + /// + /// 'startPoint' does not have to be on 'sourceLine'. If not on the line, actual starting point will be the nearest point to 'startPoint' that is on 'sourceline'. + /// + /// \see Shape.InterpolatePoint + /// + /// \new500 Added in version 5.0.0 + public Point LineInterpolatePoint(Shape sourceLine, Point startPoint, double distance, bool normalized) + { + throw new NotImplementedException(); + } + } #if nsp } diff --git a/docs/Interop.MapWinGIS/Enumerations/Enumerations.cs b/docs/Interop.MapWinGIS/Enumerations/Enumerations.cs index af58b1d6..2700f512 100644 --- a/docs/Interop.MapWinGIS/Enumerations/Enumerations.cs +++ b/docs/Interop.MapWinGIS/Enumerations/Enumerations.cs @@ -485,7 +485,7 @@ public enum tkDrawReferenceList /// /// The objects of the drawing layer are specified in screen coordinates and are not moved after the changes of the extents. /// - /// To update such layers the full redraw is not needed. Therefore use AxMap.Refresh rather than AxMap.Redraw. + /// To update such layers the full redraw is not needed. Therefore use AxMap.Redraw2 rather than AxMap.Redraw. dlScreenReferencedList = 0, /// @@ -1688,7 +1688,7 @@ public enum tkRedrawType /// /// Map will be rendered from the main buffer. Only measurements and coordinate display will be rendered anew. /// - /// Corresponds to AxMap.Refresh/AxMap.Invalidate. + /// Corresponds to AxMap.Redraw2/AxMap.Invalidate. RedrawMinimal = 3, } diff --git a/docs/Interop.MapWinGIS/Interop.MapWinGIS.csproj b/docs/Interop.MapWinGIS/Interop.MapWinGIS.csproj index d0030097..2572bd6a 100644 --- a/docs/Interop.MapWinGIS/Interop.MapWinGIS.csproj +++ b/docs/Interop.MapWinGIS/Interop.MapWinGIS.csproj @@ -151,7 +151,6 @@ - diff --git a/docs/Interop.MapWinGIS/Related Pages/Hints.cs b/docs/Interop.MapWinGIS/Related Pages/Hints.cs index 424bfbd3..675e7b0f 100644 --- a/docs/Interop.MapWinGIS/Related Pages/Hints.cs +++ b/docs/Interop.MapWinGIS/Related Pages/Hints.cs @@ -1,10 +1,10 @@ #pragma warning disable 1587 /// \page hints Hints /// -/// A. COM objects. \n\n +/// A. COM objects. \n /// MapWinGIS is COM-based, therefore it counts the references of particular objects to determine when they are no longer needed and can be released. /// -# Environments like .NET or VB6 add and release these references automatically. Calling the operations -/// explicitly (Marshal.AddRef, Marshal.Release for example) in most cases will cause problems and sometimes even crashes.\n\n +/// explicitly (Marshal.AddRef, Marshal.Release for example) in most cases will cause problems and sometimes even crashes.\n /// -# In the languages like unmanaged C++ the client is responsible for maintaining the number of reference, /// so AddRef(), Release() must be called explicitly.\n /// . @@ -26,8 +26,8 @@ /// \endcode /// MapWinGIS objects are not thread-safe. Therefore accessing the same object from several threads should be made with caution especially /// when the editing takes place.\n -/// -/// B. Error handling and progress information. \n\n +/// \n +/// B. Error handling and progress information. \n /// By design MapWinGIS doesn't throw exceptions to return the information about errors. If an unhandled exception is still thrown, /// in the most cases it should be treated as a bug and reported to the Issue tracker.\n /// @@ -60,7 +60,8 @@ /// // 1. Class.LastErrorCode and Class.get_ErrorMessage properties, which are defined for all major classes. /// If no error took place within this instance of class "No error" string will be return. /// Every call of LastErrorCode property will clear the error, i.e. reset it to the "No error" state. -/// \code Shapefile sf = some_shapefile; +/// \code +/// Shapefile sf = some_shapefile; /// Shape shp = sf.get_Shape(sf.NumShapes); // deliberately faulty line; the last index is NumShapes - 1 /// Debug.Print(sf.get_ErrorMessage(sf.LastErrorCode)); // "Index Out of Bounds" error will be reported /// Debug.Print(sf.get_ErrorMessage(sf.LastErrorCode)); // "No error" will be reported as the error was cleared by previous call @@ -87,28 +88,30 @@ /// } /// } /// \endcode -/// -/// C. Map redraw.\n\n +/// \n +/// C. Map redraw.\n /// Map control tracks some of the changes of its properties and data layers and trigger redraws to display them. It's usually applicable /// to all the AxMap members. But changes to the data layers made through API of other classes (like Shapefile or Image) will not be tracked. /// Therefore an explicit AxMap.Redraw() call will be needed to display the changes. In general it's a good practice to call redraw explicitly /// and not to rely on built-in tracking of state change. \n -/// \code AxMap axMap = map_instance; +/// \code +/// AxMap axMap = map_instance; /// Shapefile sf = some_shapefile; /// axMap.AddLayer(sf, true); // the map will be updated automatically /// sf.DefaultDrawingOptions.LineWidth = 3; // the map wasn't be updated /// axMap.Redraw(); // the thick lines will be shown only here /// \endcode -/// AxMap.Refresh rather AxMap.%Redraw should be called when there is need to update only temporary objects on map (so-called "drawing layers") +/// AxMap.Redraw2 rather AxMap.Redraw should be called when there is need to update only temporary objects on map (so-called "drawing layers") /// rather then to redraw the whole map. The former operation is fast and can be used for display of objects being dragged atop the map for example. -/// \code AxMap axMap = map_instance; -/// int handle = axMap.NewDrawing(tkScreenReferencedList); +/// \code +/// AxMap axMap = map_instance; +/// int handle = axMap.NewDrawing(tkDrawReferenceList.dlScreenReferencedList); /// axMap.DrawPolygon(arguments); -/// axMap.Refresh(); // redraw of drawing layer only to see the polygon (fast) +/// axMap.Redraw2(); // redraw of drawing layer only to see the polygon (fast) /// axMap.Redraw(); // complete redraw of the map (slow) /// \endcode -/// -/// D. Some aspects of interaction with .NET. \n\n +/// \n +/// D. Some aspects of interaction with .NET. /// 1. Interop libraries. In order to use MapWinGIS in .NET environment 2 interop assemblies (wrappers) must be generated: AxInteriop.MapWinGIS.dll /// and Interop.MapWinGIS dll. Visual Studio generates these assemblies automatically in the process of adding AxMap control on the form. Tlbimp.exe and /// Aximp.exe command line utilities can be used to do the same tasks manually.\n\n @@ -129,13 +132,14 @@ /// - to return *any* COM class with IDispath interface (see AxMap.get_GetObject); /// - to return the array of objects (see Shapefile.SelectShapes); /// - to get or set the values in the attribute table, which can be of either double, integer or string type (see Table.get_CellValue). -/// Use the documentation to find out the data type the output values should be cast to on the .NET side. -/// . +/// Use the documentation to find out the data type the output values should be cast to on the .NET side. \n +/// \n +/// 4. Create registrationless COM manifest file. Registrationless COM lets you use MapWinGIS without it being registered in the registry. +/// This means it's possible to deploy MapWinGIS along with your own application using plain xcopy semantics.\n +/// How to generate this manifest? +/// - In Visual Studio right click on the MapWinGIS reference and select Properties +/// - Click on the Isolated DropDown and select True +/// - Compile and that's all there's to it. Visual Studio will create a yourApp.exe.manifest file right alongside your application's EXE. /// -/// E. MapWindow 4 \n\n -/// MapWindow 4 is the largest application build upon MapWinGIS (http://www.mapwindow.org). -/// -# From version 4.8 (May 2011) MapWinGIS can load MapWindow 4 projects using AxMap.LoadMapState call. -/// It provides the the fastest way to setup the visualization options for the layers and pass them to custom application. \n\n -/// -# The functionality present in MapWindow can be more or less easily implemented in other custom application based on MapWinGIS. -/// MapWindow 4 repository is here. +/// When you copy the MapWinGIS files to your client, don't forget to include all files including the GDAL files and folders. #pragma warning restore 1587 \ No newline at end of file diff --git a/docs/MapWinGIS.doxyfile b/docs/MapWinGIS.doxyfile index c2c3a014..edd04387 100644 --- a/docs/MapWinGIS.doxyfile +++ b/docs/MapWinGIS.doxyfile @@ -245,6 +245,7 @@ ALIASES = "new48=\xrefitem newpage48 \"New API 4.8\" \"New API 4. "new493=\xrefitem newpage493 \"New API 4.9.3\" \"New API 4.9.3\"" \ "new494=\xrefitem newpage494 \"New API 4.9.4\" \"New API 4.9.4\"" \ "new495=\xrefitem newpage495 \"New API 4.9.5\" \"New API 4.9.5\"" \ + "new500=\xrefitem newpage500 \"New API 5.0.0\" \"New API 5.0.0\"" \ "removed493=\xrefitem removed493 \"Removed in 4.9.3\" \"Removed in 4.9.3\"" # This tag can be used to specify a number of word-keyword mappings (TCL only). diff --git a/src/COM classes/Expression.cpp b/src/COM classes/Expression.cpp index 3e50276c..49b3ade0 100644 --- a/src/COM classes/Expression.cpp +++ b/src/COM classes/Expression.cpp @@ -37,7 +37,7 @@ STDMETHODIMP CExpression::get_LastErrorMessage(BSTR* pVal) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *pVal = A2BSTR(_lastErrorMessage); + *pVal = W2BSTR(_lastErrorMessage); return S_OK; } @@ -104,7 +104,7 @@ STDMETHODIMP CExpression::Parse(BSTR expr, VARIANT_BOOL* retVal) Clear(); USES_CONVERSION; - *retVal = _expression.Parse(OLE2A(expr), true, _lastErrorMessage) ? VARIANT_TRUE : VARIANT_FALSE; + *retVal = _expression.Parse(OLE2W(expr), true, _lastErrorMessage) ? VARIANT_TRUE : VARIANT_FALSE; return S_OK; } @@ -131,7 +131,7 @@ STDMETHODIMP CExpression::ParseForTable(BSTR expr, ITable* table, VARIANT_BOOL* _expression.ReadFieldNames(table); USES_CONVERSION; - *retVal = _expression.Parse(OLE2A(expr), true, _lastErrorMessage) ? VARIANT_TRUE : VARIANT_FALSE; + *retVal = _expression.Parse(OLE2W(expr), true, _lastErrorMessage) ? VARIANT_TRUE : VARIANT_FALSE; return S_OK; } diff --git a/src/COM classes/Expression.h b/src/COM classes/Expression.h index 7ebf0ae4..cbabb7d4 100644 --- a/src/COM classes/Expression.h +++ b/src/COM classes/Expression.h @@ -65,7 +65,7 @@ class ATL_NO_VTABLE CExpression : private: CustomExpression _expression; - CString _lastErrorMessage; + CStringW _lastErrorMessage; int _lastErrorPosition; ITable* _table; diff --git a/src/COM classes/Function.cpp b/src/COM classes/Function.cpp index 18386df6..8d3fd172 100644 --- a/src/COM classes/Function.cpp +++ b/src/COM classes/Function.cpp @@ -29,7 +29,7 @@ STDMETHODIMP CFunction::get_Name(BSTR* pVal) } USES_CONVERSION; - *pVal = A2BSTR(_function->GetName()); + *pVal = W2BSTR(_function->GetName()); return S_OK; } @@ -45,7 +45,7 @@ STDMETHODIMP CFunction::get_Alias(long aliasIndex, BSTR* pVal) { aliasIndex++; // the first alias is name - vector* aliases = _function->getAliases(); + vector* aliases = _function->getAliases(); if (aliasIndex <= 0 || aliasIndex >= (long)aliases->size()) { @@ -53,7 +53,7 @@ STDMETHODIMP CFunction::get_Alias(long aliasIndex, BSTR* pVal) } else { - *pVal = A2BSTR((*aliases)[aliasIndex]); + *pVal = W2BSTR((*aliases)[aliasIndex]); return S_OK; } } @@ -75,7 +75,7 @@ STDMETHODIMP CFunction::get_NumAliases(long* pVal) return S_OK; } - vector* aliases = _function->getAliases(); + vector* aliases = _function->getAliases(); *pVal = aliases->size() - 1; diff --git a/src/COM classes/GeoProjection.cpp b/src/COM classes/GeoProjection.cpp index cad598a5..61841ab2 100644 --- a/src/COM classes/GeoProjection.cpp +++ b/src/COM classes/GeoProjection.cpp @@ -940,7 +940,7 @@ bool CGeoProjection::WriteToFileCore(CStringW filename, bool esri) if (proj.GetLength() != 0) { - fprintf(prjFile, "%s", proj); + fprintf(prjFile, "%s", (LPCSTR)proj); } fclose(prjFile); @@ -1073,7 +1073,7 @@ STDMETHODIMP CGeoProjection::SetNad83Projection(tkNad83Projection projection) STDMETHODIMP CGeoProjection::get_HasTransformation(VARIANT_BOOL* retval) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retval = _transformation != NULL; + *retval = (_transformation != NULL) ? VARIANT_TRUE : VARIANT_FALSE; return S_OK; } @@ -1320,8 +1320,10 @@ bool CGeoProjection::ParseLinearUnits(CString s, tkUnitsOfMeasure& units) { return false; } - else if (s.CompareNoCase("meters") == 0 || s.CompareNoCase("metre") == 0) - { + else if (s.CompareNoCase("meter") == 0 || s.CompareNoCase("metre") == 0 || s.CompareNoCase("meters") == 0 || s.CompareNoCase("metres") == 0) + { // jf: based on input on GitHub from ultraTCS, I'm adding the variations of 'meter(s)'; + // although I do not see any WKT UNIT references being plural 'meters', we had included + // that case before, so I am leaving it - (and the 'metres' variation for completeness) units = umMeters; return true; } diff --git a/src/COM classes/Labels.cpp b/src/COM classes/Labels.cpp index cd0db04f..b8187157 100644 --- a/src/COM classes/Labels.cpp +++ b/src/COM classes/Labels.cpp @@ -980,7 +980,7 @@ void CLabels::ApplyExpression_(long CategoryIndex) if (parsingIsNeeded) { // building list of expressions - std::vector expressions; + std::vector expressions; for (unsigned int i = 0; i < _categories.size(); i++) { if (i == CategoryIndex || CategoryIndex == -1 ) @@ -989,7 +989,7 @@ void CLabels::ApplyExpression_(long CategoryIndex) if (val->vt != VT_EMPTY && _classificationField != -1) { // we analyzed this one before, so just a dummy string here - CString str = ""; + CStringW str = L""; expressions.push_back(str); } else @@ -997,14 +997,14 @@ void CLabels::ApplyExpression_(long CategoryIndex) CComBSTR expr; _categories[i]->get_Expression(&expr); USES_CONVERSION; - CString str = OLE2CA(expr); + CStringW str = OLE2CW(expr); expressions.push_back(str); } } else { // we don't need this categories, so dummy strings for them - CString str = ""; + CStringW str = L""; expressions.push_back(str); } } @@ -1732,7 +1732,7 @@ STDMETHODIMP CLabels::put_Expression(BSTR newVal) return S_OK; } - CString str = OLE2CA(newVal); + CStringW str = OLE2CW(newVal); _labelExpression = str; if (!_synchronized && _labels.size() > 0) @@ -1748,7 +1748,7 @@ STDMETHODIMP CLabels::put_Expression(BSTR newVal) if (table) { // analyzes expression - CString strError; + CStringW strError; std::vector results; if (TableHelper::Cast(table)->CalculateCore(str, results, strError, floatFormat)) { @@ -2432,7 +2432,7 @@ STDMETHODIMP CLabels::Generate(BSTR Expression, tkLabelPositioning Method, VARIA CComPtr table = NULL; _shapefile->get_Table(&table); - CString error; + CStringW error; VARIANT_BOOL vbretval; TableHelper::Cast(table)->ParseExpressionCore(Expression, vtString, error, &vbretval); diff --git a/src/COM classes/OgrLayer.cpp b/src/COM classes/OgrLayer.cpp index 4a4d43de..0e6cbe11 100644 --- a/src/COM classes/OgrLayer.cpp +++ b/src/COM classes/OgrLayer.cpp @@ -1575,8 +1575,15 @@ STDMETHODIMP COgrLayer::get_AvailableShapeTypes(VARIANT* pVal) { vector shapeTypes; + // querying the MSSQL datasource for available types doesn't work if + // we're looking at a query result set rather than an actual table. + // instead, we'll let it fall into the 'else' logic, which iterates the + // local layer instead of the database to determine which types are present. + CStringW layerName = Utility::ConvertFromUtf8(_layer->GetName()); + if (m_globalSettings.ogrListAllGeometryTypes && - OgrHelper::IsMsSqlDatasource(_dataset)) + OgrHelper::IsMsSqlDatasource(_dataset) && + (layerName != "SELECT" && layerName != "UPDATE")) { GetMsSqlShapeTypes(shapeTypes); } @@ -1665,11 +1672,19 @@ STDMETHODIMP COgrLayer::put_ActiveShapeType(ShpfileType newVal) ShpfileType shpType; get_ShapeType(&shpType); + // SHP_NULLSHAPE indicates multiple shape types if (shpType == SHP_NULLSHAPE) { - _activeShapeType = newVal; - } - else + // if re-assigning the active type + if (_activeShapeType != newVal) + { + // clear current buffer to force new + CloseShapefile(); + } + // assign active type + _activeShapeType = newVal; + } + else { CallbackHelper::ErrorMsg("OGR layer has single geometry type. ActiveShapeType provided will be ignored."); } diff --git a/src/COM classes/Shape.cpp b/src/COM classes/Shape.cpp index 84a1d5d4..879dbcba 100644 --- a/src/COM classes/Shape.cpp +++ b/src/COM classes/Shape.cpp @@ -716,7 +716,7 @@ STDMETHODIMP CShape::DeletePoint(long PointIndex, VARIANT_BOOL *retval) STDMETHODIMP CShape::get_XY(long PointIndex, double* x, double* y, VARIANT_BOOL* retval) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *retval = get_XY(PointIndex, x, y); + *retval = get_XY(PointIndex, x, y) ? VARIANT_TRUE : VARIANT_FALSE; return S_OK; } @@ -726,7 +726,7 @@ STDMETHODIMP CShape::get_XY(long PointIndex, double* x, double* y, VARIANT_BOOL* STDMETHODIMP CShape::put_XY(LONG pointIndex, double x, double y, VARIANT_BOOL* retVal) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retVal = (VARIANT_BOOL)_shp->put_PointXY(pointIndex, x, y); + *retVal = _shp->put_PointXY(pointIndex, x, y) ? VARIANT_TRUE : VARIANT_FALSE; if (*retVal == VARIANT_FALSE) { ErrorMessage(_shp->get_LastErrorCode()); @@ -740,7 +740,7 @@ STDMETHODIMP CShape::put_XY(LONG pointIndex, double x, double y, VARIANT_BOOL* r STDMETHODIMP CShape::put_M(LONG pointIndex, double m, VARIANT_BOOL* retVal) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retVal = (VARIANT_BOOL)_shp->put_PointM(pointIndex, m); + *retVal = _shp->put_PointM(pointIndex, m) ? VARIANT_TRUE : VARIANT_FALSE; if (!(*retVal)) { ErrorMessage(_shp->get_LastErrorCode()); @@ -754,7 +754,7 @@ STDMETHODIMP CShape::put_M(LONG pointIndex, double m, VARIANT_BOOL* retVal) STDMETHODIMP CShape::put_Z(LONG pointIndex, double z, VARIANT_BOOL* retVal) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retVal = (VARIANT_BOOL)_shp->put_PointZ(pointIndex, z); + *retVal = _shp->put_PointZ(pointIndex, z) ? VARIANT_TRUE : VARIANT_FALSE; if (!(*retVal)) { ErrorMessage(_shp->get_LastErrorCode()); @@ -768,7 +768,7 @@ STDMETHODIMP CShape::put_Z(LONG pointIndex, double z, VARIANT_BOOL* retVal) STDMETHODIMP CShape::get_Z(long PointIndex, double* z, VARIANT_BOOL* retval) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *retval = get_Z(PointIndex, z); + *retval = get_Z(PointIndex, z) ? VARIANT_TRUE : VARIANT_FALSE; return S_OK; } @@ -778,7 +778,7 @@ STDMETHODIMP CShape::get_Z(long PointIndex, double* z, VARIANT_BOOL* retval) STDMETHODIMP CShape::get_M(long PointIndex, double* m, VARIANT_BOOL* retval) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *retval = get_M(PointIndex, m); + *retval = get_M(PointIndex, m) ? VARIANT_TRUE : VARIANT_FALSE; return S_OK; } @@ -1190,7 +1190,7 @@ STDMETHODIMP CShape::Relates(IShape* Shape, tkSpatialRelation Relation, VARIANT_ case srCrosses: res = oGeom1->Crosses(oGeom2); break; case srDisjoint: res = oGeom1->Disjoint(oGeom2); break; case srEquals: res = oGeom1->Equals(oGeom2); break; - case srIntersects: res = oGeom1->Intersect(oGeom2); break; + case srIntersects: res = oGeom1->Intersects(oGeom2); break; case srOverlaps: res = oGeom1->Overlaps(oGeom2); break; case srTouches: res = oGeom1->Touches(oGeom2); break; case srWithin: res = oGeom1->Within(oGeom2); break; @@ -1261,9 +1261,9 @@ STDMETHODIMP CShape::Within(IShape* Shape, VARIANT_BOOL* retval) STDMETHODIMP CShape::Clip(IShape* Shape, tkClipOperation Operation, IShape** retval) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retval = NULL; + *retval = nullptr; - if( Shape == NULL) + if( Shape == nullptr) { ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); return S_OK; @@ -1275,23 +1275,23 @@ STDMETHODIMP CShape::Clip(IShape* Shape, tkClipOperation Operation, IShape** ret return S_OK; } - OGRGeometry* oGeom1 = NULL; - OGRGeometry* oGeom2 = NULL; + OGRGeometry* oGeom1 = nullptr; + OGRGeometry* oGeom2 = nullptr; oGeom1 = OgrConverter::ShapeToGeometry(this); - if (oGeom1 == NULL) + if (oGeom1 == nullptr) return S_OK; OGRwkbGeometryType oReturnType = oGeom1->getGeometryType(); oGeom2 = OgrConverter::ShapeToGeometry(Shape); - if (oGeom2 == NULL) + if (oGeom2 == nullptr) { OGRGeometryFactory::destroyGeometry(oGeom1); return S_OK; } - OGRGeometry* oGeom3 = NULL; + OGRGeometry* oGeom3 = nullptr; switch (Operation) { @@ -1306,7 +1306,7 @@ STDMETHODIMP CShape::Clip(IShape* Shape, tkClipOperation Operation, IShape** ret oGeom3 = oGeom1->Intersection(oGeom2); break; case clSymDifference: - oGeom3 = oGeom1->SymmetricDifference(oGeom2); + oGeom3 = oGeom1->SymDifference(oGeom2); break; default: break; @@ -2575,8 +2575,9 @@ STDMETHODIMP CShape::ExportToWKT(BSTR * retVal) geom->exportToWkt(&s); (*retVal) = A2BSTR(s); OGRGeometryFactory::destroyGeometry(geom); - delete[] s; - } + // allocated in GDAL; free using CPLFree + CPLFree(s); + } else { (*retVal) = A2BSTR(""); } @@ -2868,3 +2869,12 @@ STDMETHODIMP CShape::Clear() return S_OK; } + +//***************************************************************** +//* InterpolatePoint() +//***************************************************************** +STDMETHODIMP CShape::InterpolatePoint(IPoint* startPoint, double distance, VARIANT_BOOL normalized, IPoint **retVal) +{ + // simply call Utility function + return GetUtils()->LineInterpolatePoint(this, startPoint, distance, normalized, retVal); +} diff --git a/src/COM classes/Shape.h b/src/COM classes/Shape.h index b3c9d046..d75ba6a6 100644 --- a/src/COM classes/Shape.h +++ b/src/COM classes/Shape.h @@ -145,6 +145,7 @@ class ATL_NO_VTABLE CShape : STDMETHOD(get_IsEmpty)(VARIANT_BOOL* pVal); STDMETHOD(Clear)(); STDMETHOD(FixUp2)(tkUnitsOfMeasure units, IShape** retVal); + STDMETHOD(InterpolatePoint)(IPoint* startPoint, double distance, VARIANT_BOOL normalized, IPoint **retVal); private: BSTR _key; diff --git a/src/COM classes/Shapefile.cpp b/src/COM classes/Shapefile.cpp index 60365864..5a0969a3 100644 --- a/src/COM classes/Shapefile.cpp +++ b/src/COM classes/Shapefile.cpp @@ -15,8 +15,12 @@ //Utah State University and the Idaho National Engineering and Environmental Lab that were released as //public domain in March 2004. //******************************************************************************************************** - -#include "stdafx.h" +// +//Contributor(s): (Open source contributors should list themselves and their modifications here). +// ------------------------------------------------------------------------------------------------------- +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper + +#include "StdAfx.h" #include #include "Shapefile.h" #include "Labels.h" @@ -24,16 +28,13 @@ #include "GeoProjection.h" #include "Templates.h" #include -#include "ShapeValidator.h" #include "ShapefileCategories.h" #include "Shape.h" -#include "UndoList.h" #include "GeosConverter.h" #include "ShapefileHelper.h" #include "LabelsHelper.h" #include "ShapeStyleHelper.h" #include "TableClass.h" -#include "TableHelper.h" #ifdef _DEBUG #define new DEBUG_NEW @@ -42,201 +43,206 @@ #endif CShapefile::CShapefile() -{ - _pUnkMarshaler = NULL; - - _sortingChanged = true; - _sortAscending = VARIANT_FALSE; - _sortField = SysAllocString(L""); - - _appendStartShapeCount = -1; - _appendMode = VARIANT_FALSE; - _snappable = VARIANT_TRUE; - _interactiveEditing = VARIANT_FALSE; - _hotTracking = VARIANT_TRUE; - _geosGeometriesRead = false; - _stopExecution = NULL; - - _selectionTransparency = 180; - _selectionAppearance = saSelectionColor; - _selectionColor = RGB(255, 255, 0); - _collisionMode = tkCollisionMode::LocalList; - - _geometryEngine = m_globalSettings.geometryEngine; - - _sourceType = sstUninitialized; - - _writing = false; - _reading = false; - - _isEditingShapes = FALSE; - _fastMode = m_globalSettings.shapefileFastMode ? TRUE : FALSE; - _minDrawingSize = 1; - _volatile = false; +{ + _pUnkMarshaler = nullptr; + + _sortingChanged = true; + _sortAscending = VARIANT_FALSE; + _sortField = SysAllocString(L""); + + _appendStartShapeCount = -1; + _appendMode = VARIANT_FALSE; + _snappable = VARIANT_TRUE; + _interactiveEditing = VARIANT_FALSE; + _hotTracking = VARIANT_TRUE; + _selectable = VARIANT_FALSE; + _geosGeometriesRead = false; + _stopExecution = nullptr; + + _selectionTransparency = 180; + _selectionAppearance = saSelectionColor; + _selectionColor = RGB(255, 255, 0); + _collisionMode = tkCollisionMode::LocalList; + + _geometryEngine = m_globalSettings.geometryEngine; + + _sourceType = sstUninitialized; + + _writing = false; + _reading = false; + + _isEditingShapes = FALSE; + _fastMode = m_globalSettings.shapefileFastMode ? TRUE : FALSE; + _minDrawingSize = 1; + _volatile = false; _useSpatialIndex = TRUE; _hasSpatialIndex = FALSE; _spatialIndexLoaded = FALSE; - _spatialIndexMaxAreaPercent = 0.5; - _spatialIndexNodeCapacity = 100; - - //Neio 20090721 - _useQTree = VARIANT_FALSE; - _cacheExtents = FALSE; - _qtree = NULL; - _tempTree = NULL; - - _shpfile = NULL; - _shxfile = NULL; - - _shpfiletype = SHP_NULLSHAPE; - _nextShapeHandle = 0; - - _minX = 0.0; - _minY = 0.0; - _minZ = 0.0; - _maxX = 0.0; - _maxY = 0.0; - _maxZ = 0.0; - _minM = 0.0; - _maxM = 0.0; - - _key = SysAllocString(L""); - _expression = SysAllocString(L""); - _globalCallback = NULL; - _lastErrorCode = tkNO_ERROR; - _table = NULL; - - // creation of children classes - _selectDrawOpt = NULL; - _defaultDrawOpt = NULL; - _labels = NULL; - _categories = NULL; - _charts = NULL; - _geoProjection = NULL; - - ComHelper::CreateInstance(idShapeValidationInfo, (IDispatch**)&_inputValidation); - ComHelper::CreateInstance(idShapeValidationInfo, (IDispatch**)&_outputValidation); - - CoCreateInstance(CLSID_ShapeDrawingOptions,NULL,CLSCTX_INPROC_SERVER,IID_IShapeDrawingOptions,(void**)&_selectDrawOpt); - CoCreateInstance(CLSID_ShapeDrawingOptions,NULL,CLSCTX_INPROC_SERVER,IID_IShapeDrawingOptions,(void**)&_defaultDrawOpt); - CoCreateInstance(CLSID_ShapefileCategories,NULL,CLSCTX_INPROC_SERVER,IID_IShapefileCategories,(void**)&_categories); - CoCreateInstance(CLSID_Labels,NULL,CLSCTX_INPROC_SERVER,IID_ILabels,(void**)&_labels); - CoCreateInstance(CLSID_Charts,NULL,CLSCTX_INPROC_SERVER,IID_ICharts,(void**)&_charts); - CoCreateInstance(CLSID_GeoProjection,NULL,CLSCTX_INPROC_SERVER,IID_IGeoProjection,(void**)&_geoProjection); - - this->put_ReferenceToLabels(); - this->put_ReferenceToCategories(); - this->put_ReferenceToCharts(); - - ComHelper::CreateInstance(idUndoList, (IDispatch**)&_undoList); - - gReferenceCounter.AddRef(tkInterface::idShapefile); + _spatialIndexMaxAreaPercent = 0.5; + _spatialIndexNodeCapacity = 100; + + //Neio 20090721 + _useQTree = VARIANT_FALSE; + _cacheExtents = FALSE; + _qtree = nullptr; + _tempTree = nullptr; + + _shpfile = nullptr; + _shxfile = nullptr; + + _shpfiletype = SHP_NULLSHAPE; + _nextShapeHandle = 0; + + _minX = 0.0; + _minY = 0.0; + _minZ = 0.0; + _maxX = 0.0; + _maxY = 0.0; + _maxZ = 0.0; + _minM = 0.0; + _maxM = 0.0; + + _key = SysAllocString(L""); + _expression = SysAllocString(L""); + _globalCallback = nullptr; + _lastErrorCode = tkNO_ERROR; + _table = nullptr; + + // creation of children classes + _selectDrawOpt = nullptr; + _defaultDrawOpt = nullptr; + _labels = nullptr; + _categories = nullptr; + _charts = nullptr; + _geoProjection = nullptr; + + ComHelper::CreateInstance(idShapeValidationInfo, (IDispatch**)&_inputValidation); + ComHelper::CreateInstance(idShapeValidationInfo, (IDispatch**)&_outputValidation); + + CoCreateInstance(CLSID_ShapeDrawingOptions, nullptr, CLSCTX_INPROC_SERVER, IID_IShapeDrawingOptions, + (void**)&_selectDrawOpt); + CoCreateInstance(CLSID_ShapeDrawingOptions, nullptr, CLSCTX_INPROC_SERVER, IID_IShapeDrawingOptions, + (void**)&_defaultDrawOpt); + CoCreateInstance(CLSID_ShapefileCategories, nullptr, CLSCTX_INPROC_SERVER, IID_IShapefileCategories, + (void**)&_categories); + CoCreateInstance(CLSID_Labels, nullptr, CLSCTX_INPROC_SERVER, IID_ILabels, (void**)&_labels); + CoCreateInstance(CLSID_Charts, nullptr, CLSCTX_INPROC_SERVER, IID_ICharts, (void**)&_charts); + CoCreateInstance(CLSID_GeoProjection, nullptr, CLSCTX_INPROC_SERVER, IID_IGeoProjection, (void**)&_geoProjection); + + this->put_ReferenceToLabels(); + this->put_ReferenceToCategories(); + this->put_ReferenceToCharts(); + + ComHelper::CreateInstance(idUndoList, (IDispatch**)&_undoList); + + gReferenceCounter.AddRef(tkInterface::idShapefile); } CShapefile::~CShapefile() -{ - VARIANT_BOOL vbretval; - this->Close(&vbretval); - - ::SysFreeString(_key); - ::SysFreeString(_expression); - ::SysFreeString(_sortField); - - if (_selectDrawOpt != NULL) - _selectDrawOpt->Release(); - - if (_defaultDrawOpt != NULL) - _defaultDrawOpt->Release(); - - if (_labels != NULL) - { - put_ReferenceToLabels(true); // labels class maybe referenced by client and won't be deleted as a result - _labels->Release(); // therefore we must clear the reference to the parent as it will be invalid - } - - if (_categories != NULL) - { - put_ReferenceToCategories(true); - _categories->Release(); - } - - if (_charts != NULL) - { - put_ReferenceToCharts(true); - _charts->Release(); - } - - if (_stopExecution) - _stopExecution->Release(); - - if (_geoProjection) - _geoProjection->Release(); - - if (_undoList) { - _undoList->Release(); - } - gReferenceCounter.Release(tkInterface::idShapefile); +{ + VARIANT_BOOL vbretval; + this->CShapefile::Close(&vbretval); + + SysFreeString(_key); + SysFreeString(_expression); + SysFreeString(_sortField); + + if (_selectDrawOpt != nullptr) + _selectDrawOpt->Release(); + + if (_defaultDrawOpt != nullptr) + _defaultDrawOpt->Release(); + + if (_labels != nullptr) + { + put_ReferenceToLabels(true); // labels class maybe referenced by client and won't be deleted as a result + _labels->Release(); // therefore we must clear the reference to the parent as it will be invalid + } + + if (_categories != nullptr) + { + put_ReferenceToCategories(true); + _categories->Release(); + } + + if (_charts != nullptr) + { + put_ReferenceToCharts(true); + _charts->Release(); + } + + if (_stopExecution) + _stopExecution->Release(); + + if (_geoProjection) + _geoProjection->Release(); + + if (_undoList) + { + _undoList->Release(); + } + gReferenceCounter.Release(tkInterface::idShapefile); } std::vector* CShapefile::get_ShapeVector() { - return &_shapeData; + return &_shapeData; } IShapeWrapper* CShapefile::get_ShapeWrapper(int ShapeIndex) { - return ((CShape*)_shapeData[ShapeIndex]->shape)->get_ShapeWrapper(); + return ((CShape*)_shapeData[ShapeIndex]->shape)->get_ShapeWrapper(); } IShapeData* CShapefile::get_ShapeRenderingData(int ShapeIndex) { - return _shapeData[ShapeIndex]->get_RenderingData(); + return _shapeData[ShapeIndex]->get_RenderingData(); } void CShapefile::put_ShapeRenderingData(int ShapeIndex, CShapeData* data) { - return _shapeData[ShapeIndex]->put_RenderingData(data); + return _shapeData[ShapeIndex]->put_RenderingData(data); } void CShapefile::SetValidationInfo(IShapeValidationInfo* info, tkShapeValidationType validationType) { - ComHelper::SetRef(info, - (IDispatch**)&(validationType == svtInput ? _inputValidation : _outputValidation), true); + ComHelper::SetRef(info, + (IDispatch**)&(validationType == svtInput ? _inputValidation : _outputValidation), true); } -#pragma region Properties +#pragma region Properties // ************************************************************ // get_EditingShapes() // ************************************************************ -STDMETHODIMP CShapefile::get_EditingShapes(VARIANT_BOOL *pVal) +STDMETHODIMP CShapefile::get_EditingShapes(VARIANT_BOOL* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *pVal = _isEditingShapes?VARIANT_TRUE:VARIANT_FALSE; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + *pVal = _isEditingShapes ? VARIANT_TRUE : VARIANT_FALSE; + return S_OK; } // ************************************************************ // get_LastErrorCode() // ************************************************************ -STDMETHODIMP CShapefile::get_LastErrorCode(long *pVal) +STDMETHODIMP CShapefile::get_LastErrorCode(long* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *pVal = _lastErrorCode; - _lastErrorCode = tkNO_ERROR; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + *pVal = _lastErrorCode; + _lastErrorCode = tkNO_ERROR; + return S_OK; } // ************************************************************ // get_CdlgFilter() // ************************************************************ -STDMETHODIMP CShapefile::get_CdlgFilter(BSTR *pVal) +STDMETHODIMP CShapefile::get_CdlgFilter(BSTR* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - USES_CONVERSION; - *pVal = A2BSTR("ESRI Shapefiles (*.shp)|*.shp"); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + USES_CONVERSION; + *pVal = A2BSTR("ESRI Shapefiles (*.shp)|*.shp"); + return S_OK; } // ************************************************************ @@ -244,11 +250,11 @@ STDMETHODIMP CShapefile::get_CdlgFilter(BSTR *pVal) // ************************************************************ STDMETHODIMP CShapefile::get_LastInputValidation(IShapeValidationInfo** retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if (_inputValidation) - _inputValidation->AddRef(); - *retVal = _inputValidation; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + if (_inputValidation) + _inputValidation->AddRef(); + *retVal = _inputValidation; + return S_OK; } // ************************************************************ @@ -256,33 +262,34 @@ STDMETHODIMP CShapefile::get_LastInputValidation(IShapeValidationInfo** retVal) // ************************************************************ STDMETHODIMP CShapefile::get_LastOutputValidation(IShapeValidationInfo** retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if (_outputValidation) - _outputValidation->AddRef(); - *retVal = _outputValidation; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + if (_outputValidation) + _outputValidation->AddRef(); + *retVal = _outputValidation; + return S_OK; } // ************************************************************ // get/put_GlobalCallback() // ************************************************************ -STDMETHODIMP CShapefile::get_GlobalCallback(ICallback **pVal) +STDMETHODIMP CShapefile::get_GlobalCallback(ICallback** pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) + AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *pVal = _globalCallback; - if( _globalCallback != NULL ) - _globalCallback->AddRef(); - return S_OK; + *pVal = _globalCallback; + if (_globalCallback != nullptr) + _globalCallback->AddRef(); + return S_OK; } -STDMETHODIMP CShapefile::put_GlobalCallback(ICallback *newVal) + +STDMETHODIMP CShapefile::put_GlobalCallback(ICallback* newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - ComHelper::SetRef(newVal, (IDispatch**)&_globalCallback); - if( _table != NULL ) - _table->put_GlobalCallback(newVal); + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + ComHelper::SetRef(newVal, (IDispatch**)&_globalCallback); + if (_table != nullptr) + _table->put_GlobalCallback(newVal); - return S_OK; + return S_OK; } // ************************************************************ @@ -290,142 +297,147 @@ STDMETHODIMP CShapefile::put_GlobalCallback(ICallback *newVal) // ************************************************************ STDMETHODIMP CShapefile::put_StopExecution(IStopExecution* stopper) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - ComHelper::SetRef((IDispatch*)stopper, (IDispatch**)&_stopExecution, true); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + ComHelper::SetRef((IDispatch*)stopper, (IDispatch**)&_stopExecution, true); + return S_OK; } // ************************************************************ // get/put_Key() // ************************************************************ -STDMETHODIMP CShapefile::get_Key(BSTR *pVal) +STDMETHODIMP CShapefile::get_Key(BSTR* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - USES_CONVERSION; - *pVal = OLE2BSTR(_key); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + USES_CONVERSION; + *pVal = OLE2BSTR(_key); + return S_OK; } + STDMETHODIMP CShapefile::put_Key(BSTR newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - ::SysFreeString(_key); - _key = OLE2BSTR(newVal); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + SysFreeString(_key); + _key = OLE2BSTR(newVal); + return S_OK; } // ************************************************************ // get/put_VisibilityExpression // ************************************************************ -STDMETHODIMP CShapefile::get_VisibilityExpression(BSTR *pVal) +STDMETHODIMP CShapefile::get_VisibilityExpression(BSTR* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - USES_CONVERSION; - *pVal = OLE2BSTR(_expression); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + USES_CONVERSION; + *pVal = OLE2BSTR(_expression); + return S_OK; } + STDMETHODIMP CShapefile::put_VisibilityExpression(BSTR newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - ::SysFreeString(_expression); - _expression = OLE2BSTR(newVal); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + SysFreeString(_expression); + _expression = OLE2BSTR(newVal); + return S_OK; } // ************************************************************ // get/put_Volatile // ************************************************************ -STDMETHODIMP CShapefile::get_Volatile(VARIANT_BOOL *pVal) +STDMETHODIMP CShapefile::get_Volatile(VARIANT_BOOL* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if (_interactiveEditing) { - *pVal = VARIANT_TRUE; - } - else { - *pVal = _volatile ? VARIANT_TRUE : VARIANT_FALSE; - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + if (_interactiveEditing) + { + *pVal = VARIANT_TRUE; + } + else + { + *pVal = _volatile ? VARIANT_TRUE : VARIANT_FALSE; + } + return S_OK; } + STDMETHODIMP CShapefile::put_Volatile(VARIANT_BOOL newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - _volatile = newVal == VARIANT_TRUE; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + _volatile = newVal == VARIANT_TRUE; + return S_OK; } // ***************************************************************** // get_NumShapes() // ***************************************************************** -STDMETHODIMP CShapefile::get_NumShapes(long *pVal) +STDMETHODIMP CShapefile::get_NumShapes(long* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *pVal = _shapeData.size(); //_numShapes; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + *pVal = _shapeData.size(); //_numShapes; + return S_OK; } // ************************************************************** // get_NumFields() // ************************************************************** -STDMETHODIMP CShapefile::get_NumFields(long *pVal) +STDMETHODIMP CShapefile::get_NumFields(long* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if( _table != NULL ) - _table->get_NumFields(pVal); - else - { - ErrorMessage(tkFILE_NOT_OPEN); - *pVal = 0; - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + if (_table != nullptr) + _table->get_NumFields(pVal); + else + { + ErrorMessage(tkFILE_NOT_OPEN); + *pVal = 0; + } + return S_OK; } // ************************************************************ // get_ShapefileType() // ************************************************************ -STDMETHODIMP CShapefile::get_ShapefileType(ShpfileType *pVal) +STDMETHODIMP CShapefile::get_ShapefileType(ShpfileType* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *pVal = _shpfiletype; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + *pVal = _shpfiletype; + return S_OK; } // ***************************************************************** // get_ErrorMsg() // ***************************************************************** -STDMETHODIMP CShapefile::get_ErrorMsg(long ErrorCode, BSTR *pVal) +STDMETHODIMP CShapefile::get_ErrorMsg(long ErrorCode, BSTR* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - USES_CONVERSION; - *pVal = A2BSTR(ErrorMsg(ErrorCode)); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + USES_CONVERSION; + *pVal = A2BSTR(ErrorMsg(ErrorCode)); + return S_OK; } // ***************************************************************** // get_FileHandle() // ***************************************************************** -STDMETHODIMP CShapefile::get_FileHandle(long * pVal) +STDMETHODIMP CShapefile::get_FileHandle(long* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if( _shpfile != NULL ) - { - int handle = _fileno(_shpfile); - *pVal = _dup(handle); - } - else - *pVal = -1; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + if (_shpfile != nullptr) + { + const int handle = _fileno(_shpfile); + *pVal = _dup(handle); + } + else + *pVal = -1; - return S_OK; + return S_OK; } // ************************************************************** // get_Filename() // ************************************************************** -STDMETHODIMP CShapefile::get_Filename(BSTR *pVal) +STDMETHODIMP CShapefile::get_Filename(BSTR* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) + AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *pVal = W2BSTR(_shpfileName); + *pVal = W2BSTR(_shpfileName); - return S_OK; + return S_OK; } // ************************************************************** @@ -433,15 +445,16 @@ STDMETHODIMP CShapefile::get_Filename(BSTR *pVal) // ************************************************************** void CShapefile::ErrorMessage(long ErrorCode) { - _lastErrorCode = ErrorCode; - CallbackHelper::ErrorMsg("Shapefile", _globalCallback, _key, ErrorMsg(_lastErrorCode)); + _lastErrorCode = ErrorCode; + CallbackHelper::ErrorMsg("Shapefile", _globalCallback, _key, ErrorMsg(_lastErrorCode)); } + void CShapefile::ErrorMessage(long ErrorCode, ICallback* cBack) { - _lastErrorCode = ErrorCode; - CallbackHelper::ErrorMsg("Shapefile", _globalCallback, _key, ErrorMsg(_lastErrorCode)); - if (cBack != _globalCallback) - CallbackHelper::ErrorMsg("Shapefile", cBack, _key, ErrorMsg(_lastErrorCode)); + _lastErrorCode = ErrorCode; + CallbackHelper::ErrorMsg("Shapefile", _globalCallback, _key, ErrorMsg(_lastErrorCode)); + if (cBack != _globalCallback) + CallbackHelper::ErrorMsg("Shapefile", cBack, _key, ErrorMsg(_lastErrorCode)); } // ************************************************************ @@ -449,15 +462,16 @@ void CShapefile::ErrorMessage(long ErrorCode, ICallback* cBack) // ************************************************************ STDMETHODIMP CShapefile::get_MinDrawingSize(LONG* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *pVal = _minDrawingSize; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *pVal = _minDrawingSize; + return S_OK; } + STDMETHODIMP CShapefile::put_MinDrawingSize(LONG newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - _minDrawingSize = newVal; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + _minDrawingSize = newVal; + return S_OK; } // ************************************************************ @@ -465,9 +479,9 @@ STDMETHODIMP CShapefile::put_MinDrawingSize(LONG newVal) // ************************************************************ STDMETHODIMP CShapefile::get_SourceType(tkShapefileSourceType* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *pVal = _sourceType; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *pVal = _sourceType; + return S_OK; } #pragma endregion @@ -479,378 +493,377 @@ STDMETHODIMP CShapefile::get_SourceType(tkShapefileSourceType* pVal) // LoadDataFrom() // ************************************************************ // Loads shape and DBF data from disk file into in-memory mode -STDMETHODIMP CShapefile::LoadDataFrom(BSTR ShapefileName, ICallback *cBack, VARIANT_BOOL *retval) -{ - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - - USES_CONVERSION; - *retval = VARIANT_FALSE; - if (_sourceType != sstInMemory) - { - ErrorMessage(tkINMEMORY_SHAPEFILE_EXPECTED); - return S_OK; - } - - if (OpenCore(OLE2CA(ShapefileName), cBack)) - { - // loading data in-memory - VARIANT_BOOL vb; - _isEditingShapes = false; - StartEditingShapes(VARIANT_TRUE, cBack, &vb); - - // this will trigger loading of all DBF values into the memory - long numFields; - this->get_NumFields(&numFields); - if (numFields > 0) - { - CComVariant var; - for(size_t i = 0; i < _shapeData.size(); i++) - { - _table->get_CellValue(0, i, &var); - } - } - - // closing disk file despite the result success or failure - _shpfileName = ""; - _shxfileName = ""; - _dbffileName = ""; - - if( _shpfile != NULL) - fclose(_shpfile); - _shpfile = NULL; - - if( _shxfile != NULL) - fclose(_shxfile); - _shxfile = NULL; - - if( _table != NULL ) - ((CTableClass*)_table)->CloseUnderlyingFile(); - - *retval = vb; - } - return S_OK; +STDMETHODIMP CShapefile::LoadDataFrom(BSTR ShapefileName, ICallback* cBack, VARIANT_BOOL* retval) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + + USES_CONVERSION; + *retval = VARIANT_FALSE; + if (_sourceType != sstInMemory) + { + ErrorMessage(tkINMEMORY_SHAPEFILE_EXPECTED); + return S_OK; + } + + if (OpenCore(OLE2CA(ShapefileName), cBack)) + { + // loading data in-memory + VARIANT_BOOL vb; + _isEditingShapes = false; + StartEditingShapes(VARIANT_TRUE, cBack, &vb); + + // this will trigger loading of all DBF values into the memory + long numFields; + this->get_NumFields(&numFields); + if (numFields > 0) + { + CComVariant var; + for (size_t i = 0; i < _shapeData.size(); i++) + { + _table->get_CellValue(0, i, &var); + } + } + + // closing disk file despite the result success or failure + _shpfileName = ""; + _shxfileName = ""; + _dbffileName = ""; + + if (_shpfile != nullptr) + fclose(_shpfile); + _shpfile = nullptr; + + if (_shxfile != nullptr) + fclose(_shxfile); + _shxfile = nullptr; + + if (_table != nullptr) + ((CTableClass*)_table)->CloseUnderlyingFile(); + + *retval = vb; + } + return S_OK; } // ************************************************************ // OpenCore() // ************************************************************ -bool CShapefile::OpenCore(CStringW tmp_shpfileName, ICallback* cBack) -{ - USES_CONVERSION; - VARIANT_BOOL vbretval; - - // saving the provided names; - // from now on we must clean the class variables in case the operation won't succeed - _shpfileName = tmp_shpfileName; - _shxfileName = tmp_shpfileName.Left(tmp_shpfileName.GetLength() - 3) + L"shx"; - _dbffileName = tmp_shpfileName.Left(tmp_shpfileName.GetLength() - 3) + L"dbf"; - _prjfileName = tmp_shpfileName.Left(tmp_shpfileName.GetLength() - 3) + L"prj"; - - // read mode - _shpfile = _wfopen( _shpfileName, L"rb"); - _shxfile = _wfopen(_shxfileName,L"rb"); - - // opening DBF - if (!_table) - { - CoCreateInstance(CLSID_Table,NULL,CLSCTX_INPROC_SERVER,IID_ITable,(void**)&_table); - } - else - { - VARIANT_BOOL vb; - _table->Close(&vb); - } - - _table->put_GlobalCallback(_globalCallback); - ((CTableClass*)_table)->InjectShapefile(this); - - CComBSTR bstrDbf(_dbffileName); - _table->Open(bstrDbf, cBack, &vbretval); - - if( _shpfile == NULL ) - { - ErrorMessage(tkCANT_OPEN_SHP); - this->Close(&vbretval); - } - else if( _shxfile == NULL ) - { - ErrorMessage(tkCANT_OPEN_SHX); - this->Close(&vbretval); - } - else if( vbretval == VARIANT_FALSE ) - { - _table->get_LastErrorCode(&_lastErrorCode); - ErrorMessage(_lastErrorCode); - this->Close(&vbretval); - } - else - { - if( !ReadShx()) // shapefile header is read here as well - { - ErrorMessage(tkINVALID_SHX_FILE); - this->Close(&vbretval); - } - else - { - //Check for supported types - if( _shpfiletype != SHP_NULLSHAPE && - _shpfiletype != SHP_POINT && - _shpfiletype != SHP_POLYLINE && - _shpfiletype != SHP_POLYGON && - _shpfiletype != SHP_POINTZ && - _shpfiletype != SHP_POLYLINEZ && - _shpfiletype != SHP_POLYGONZ && - _shpfiletype != SHP_MULTIPOINT && - _shpfiletype != SHP_MULTIPOINTZ && - _shpfiletype != SHP_POLYLINEM && - _shpfiletype != SHP_POLYGONM && - _shpfiletype != SHP_POINTM && - _shpfiletype != SHP_MULTIPOINTM ) - { - ErrorMessage(tkUNSUPPORTED_SHAPEFILE_TYPE); - this->Close(&vbretval); - } - else - { - _shapeData.reserve(_shpOffsets.size()); - for (size_t i = 0; i < _shpOffsets.size(); i++) - { - _shapeData.push_back(new ShapeRecord()); - } - return true; - } - } - } - return false; +bool CShapefile::OpenCore(CStringW tmpShpfileName, ICallback* cBack) +{ + USES_CONVERSION; + VARIANT_BOOL vbretval; + + // saving the provided names; + // from now on we must clean the class variables in case the operation won't succeed + _shpfileName = tmpShpfileName; + _shxfileName = tmpShpfileName.Left(tmpShpfileName.GetLength() - 3) + L"shx"; + _dbffileName = tmpShpfileName.Left(tmpShpfileName.GetLength() - 3) + L"dbf"; + _prjfileName = tmpShpfileName.Left(tmpShpfileName.GetLength() - 3) + L"prj"; + + // read mode + _shpfile = _wfopen(_shpfileName, L"rb"); + _shxfile = _wfopen(_shxfileName, L"rb"); + + // opening DBF + if (!_table) + { + CoCreateInstance(CLSID_Table, nullptr, CLSCTX_INPROC_SERVER, IID_ITable, (void**)&_table); + } + else + { + VARIANT_BOOL vb; + _table->Close(&vb); + } + + _table->put_GlobalCallback(_globalCallback); + ((CTableClass*)_table)->InjectShapefile(this); + + const CComBSTR bstrDbf(_dbffileName); + _table->Open(bstrDbf, cBack, &vbretval); + + if (_shpfile == nullptr) + { + ErrorMessage(tkCANT_OPEN_SHP); + this->Close(&vbretval); + } + else if (_shxfile == nullptr) + { + ErrorMessage(tkCANT_OPEN_SHX); + this->Close(&vbretval); + } + else if (vbretval == VARIANT_FALSE) + { + _table->get_LastErrorCode(&_lastErrorCode); + ErrorMessage(_lastErrorCode); + this->Close(&vbretval); + } + else + { + if (!ReadShx()) // shapefile header is read here as well + { + ErrorMessage(tkINVALID_SHX_FILE); + this->Close(&vbretval); + } + else + { + //Check for supported types + if (_shpfiletype != SHP_NULLSHAPE && + _shpfiletype != SHP_POINT && + _shpfiletype != SHP_POLYLINE && + _shpfiletype != SHP_POLYGON && + _shpfiletype != SHP_POINTZ && + _shpfiletype != SHP_POLYLINEZ && + _shpfiletype != SHP_POLYGONZ && + _shpfiletype != SHP_MULTIPOINT && + _shpfiletype != SHP_MULTIPOINTZ && + _shpfiletype != SHP_POLYLINEM && + _shpfiletype != SHP_POLYGONM && + _shpfiletype != SHP_POINTM && + _shpfiletype != SHP_MULTIPOINTM) + { + ErrorMessage(tkUNSUPPORTED_SHAPEFILE_TYPE); + this->Close(&vbretval); + } + else + { + _shapeData.reserve(_shpOffsets.size()); + for (size_t i = 0; i < _shpOffsets.size(); i++) + { + _shapeData.push_back(new ShapeRecord()); + } + return true; + } + } + } + return false; } // ************************************************************ // Open() // ************************************************************ -STDMETHODIMP CShapefile::Open(BSTR ShapefileName, ICallback *cBack, VARIANT_BOOL *retval) -{ - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *retval = VARIANT_FALSE; - VARIANT_BOOL vbretval; - - if(_globalCallback == NULL && cBack !=NULL) - { - _globalCallback = cBack; - _globalCallback->AddRef(); - } - - USES_CONVERSION; - CStringW tmp_shpfileName = OLE2CW(ShapefileName); - - if (tmp_shpfileName.GetLength() == 0) - { - // better to use CreateNew directly, but this call will be preserved for backward compatibility - this->CreateNew(m_globalSettings.emptyBstr, _shpfiletype, &vbretval); - } - else if( tmp_shpfileName.GetLength() <= 3 ) - { - ErrorMessage(tkINVALID_FILENAME); - } - else - { - // close the opened shapefile - this->Close(&vbretval); - - if( vbretval == VARIANT_FALSE ) - { - // error code in the function - return S_OK; - } - - if (OpenCore(tmp_shpfileName, cBack)) - { - _sourceType = sstDiskBased; - - // reading projection - CComBSTR bstrPrj(_prjfileName); - _geoProjection->ReadFromFileEx(bstrPrj, VARIANT_TRUE, &vbretval); - - ShapeStyleHelper::ApplyRandomDrawingOptions(this); - LabelsHelper::UpdateLabelsPositioning(this); - *retval = VARIANT_TRUE; - } - } - return S_OK; +STDMETHODIMP CShapefile::Open(BSTR ShapefileName, ICallback* cBack, VARIANT_BOOL* retval) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + *retval = VARIANT_FALSE; + VARIANT_BOOL vbretval; + + if (_globalCallback == nullptr && cBack != nullptr) + { + _globalCallback = cBack; + _globalCallback->AddRef(); + } + + USES_CONVERSION; + CStringW tmp_shpfileName = OLE2CW(ShapefileName); + + if (tmp_shpfileName.GetLength() == 0) + { + // better to use CreateNew directly, but this call will be preserved for backward compatibility + this->CreateNew(m_globalSettings.emptyBstr, _shpfiletype, &vbretval); + } + else if (tmp_shpfileName.GetLength() <= 3) + { + ErrorMessage(tkINVALID_FILENAME); + } + else + { + // close the opened shapefile + this->Close(&vbretval); + + if (vbretval == VARIANT_FALSE) + { + // error code in the function + return S_OK; + } + + if (OpenCore(tmp_shpfileName, cBack)) + { + _sourceType = sstDiskBased; + + // reading projection + const CComBSTR bstrPrj(_prjfileName); + _geoProjection->ReadFromFileEx(bstrPrj, VARIANT_TRUE, &vbretval); + + ShapeStyleHelper::ApplyRandomDrawingOptions(this); + LabelsHelper::UpdateLabelsPositioning(this); + *retval = VARIANT_TRUE; + } + } + return S_OK; } // ********************************************************* // CreateNew() // ********************************************************* -STDMETHODIMP CShapefile::CreateNew(BSTR ShapefileName, ShpfileType ShapefileType, VARIANT_BOOL *retval) +STDMETHODIMP CShapefile::CreateNew(BSTR ShapefileName, ShpfileType ShapefileType, VARIANT_BOOL* retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - return this->CreateNewCore(ShapefileName, ShapefileType, true, retval); + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + return this->CreateNewCore(ShapefileName, ShapefileType, true, retval); } // ********************************************************* // CreateNewCore() // ********************************************************* -HRESULT CShapefile::CreateNewCore(BSTR ShapefileName, ShpfileType ShapefileType, bool applyRandomOptions, VARIANT_BOOL *retval) -{ - *retval = VARIANT_FALSE; - VARIANT_BOOL vb; - - // check for supported types - if( ShapefileType != SHP_NULLSHAPE && - ShapefileType != SHP_POINT && - ShapefileType != SHP_POLYLINE && - ShapefileType != SHP_POLYGON && - ShapefileType != SHP_POINTZ && - ShapefileType != SHP_POLYLINEZ && - ShapefileType != SHP_POLYGONZ && - ShapefileType != SHP_MULTIPOINT && - ShapefileType != SHP_MULTIPOINTZ && - ShapefileType != SHP_POINTM && // MWGIS-69 - ShapefileType != SHP_POLYLINEM && - ShapefileType != SHP_POLYGONM && - ShapefileType != SHP_MULTIPOINTM ) - { - ErrorMessage(tkUNSUPPORTED_SHAPEFILE_TYPE); - return S_OK; - } - - USES_CONVERSION; - CString tmp_shpfileName = OLE2CA(ShapefileName); - - // ---------------------------------------------- - // in memory shapefile (without name) - // ---------------------------------------------- - if (tmp_shpfileName.GetLength() == 0) - { - // closing the old shapefile (error code inside the function) - Close(&vb); - - if( vb == VARIANT_TRUE ) - { - CoCreateInstance(CLSID_Table,NULL,CLSCTX_INPROC_SERVER,IID_ITable,(void**)&_table); - - _table->CreateNew(m_globalSettings.emptyBstr, &vb); - - if (!vb) - { - long error; - _table->get_LastErrorCode(&error); - ErrorMessage(error); - _table->Release(); - _table = NULL; - } - else - { - _shpfiletype = ShapefileType; - _isEditingShapes = true; - _sourceType = sstInMemory; - - if (applyRandomOptions) { - ShapeStyleHelper::ApplyRandomDrawingOptions(this); - } - - *retval = VARIANT_TRUE; - } - } - - return S_OK; - } - - if (tmp_shpfileName.GetLength() <= 3) - { - ErrorMessage(tkINVALID_FILENAME); - return S_OK; - } - - // ---------------------------------------------- - // in memory shapefile (name specified, is acceptable and available) - // ---------------------------------------------- - CString shpName, shxName, dbfName, prjName; - shpName = tmp_shpfileName; - shxName = tmp_shpfileName.Left(tmp_shpfileName.GetLength() - 3) + "shx"; - dbfName = tmp_shpfileName.Left(tmp_shpfileName.GetLength() - 3) + "dbf"; - prjName = tmp_shpfileName.Left(tmp_shpfileName.GetLength() - 3) + "prj"; - - // new file is created, so there must not be any files with this names - if( Utility::FileExists(shpName) != FALSE ) - { - ErrorMessage(tkSHP_FILE_EXISTS); - return S_OK; - } - if( Utility::FileExists(shxName) != FALSE ) - { - ErrorMessage(tkSHX_FILE_EXISTS); - return S_OK; - } - if( Utility::FileExists(dbfName) != FALSE ) - { - ErrorMessage(tkDBF_FILE_EXISTS); - return S_OK; - } - if( Utility::FileExists(prjName) != FALSE ) - { - ErrorMessage(tkPRJ_FILE_EXISTS); - return S_OK; - } - - // closing the old shapefile (error code inside the function) - this->Close(&vb); - - if( vb == VARIANT_TRUE ) - { - CoCreateInstance(CLSID_Table,NULL,CLSCTX_INPROC_SERVER,IID_ITable,(void**)&_table); - - _table->put_GlobalCallback(_globalCallback); - - CString newDbfName = tmp_shpfileName.Left(tmp_shpfileName.GetLength() - 3) + "dbf"; - CComBSTR bstrName(newDbfName); - _table->CreateNew(bstrName, &vb); - - if (!vb) - { - _table->get_LastErrorCode(&_lastErrorCode); - ErrorMessage(_lastErrorCode); - _table->Release(); - _table = NULL; - } - else - { - _shpfileName = tmp_shpfileName; - _shxfileName = tmp_shpfileName.Left(tmp_shpfileName.GetLength() - 3) + "shx"; - _dbffileName = tmp_shpfileName.Left(tmp_shpfileName.GetLength() - 3) + "dbf"; - _prjfileName = tmp_shpfileName.Left(tmp_shpfileName.GetLength() - 3) + "prj"; - - _shpfiletype = ShapefileType; - _isEditingShapes = true; - _sourceType = sstInMemory; - - if (applyRandomOptions) { - ShapeStyleHelper::ApplyRandomDrawingOptions(this); - } - - *retval = VARIANT_TRUE; - } - } - - LabelsHelper::UpdateLabelsPositioning(this); - - return S_OK; +HRESULT CShapefile::CreateNewCore(BSTR ShapefileName, ShpfileType ShapefileType, bool applyRandomOptions, + VARIANT_BOOL* retval) +{ + *retval = VARIANT_FALSE; + VARIANT_BOOL vb; + + // check for supported types + if (ShapefileType != SHP_NULLSHAPE && + ShapefileType != SHP_POINT && + ShapefileType != SHP_POLYLINE && + ShapefileType != SHP_POLYGON && + ShapefileType != SHP_POINTZ && + ShapefileType != SHP_POLYLINEZ && + ShapefileType != SHP_POLYGONZ && + ShapefileType != SHP_MULTIPOINT && + ShapefileType != SHP_MULTIPOINTZ && + ShapefileType != SHP_POINTM && // MWGIS-69 + ShapefileType != SHP_POLYLINEM && + ShapefileType != SHP_POLYGONM && + ShapefileType != SHP_MULTIPOINTM) + { + ErrorMessage(tkUNSUPPORTED_SHAPEFILE_TYPE); + return S_OK; + } + + USES_CONVERSION; + CString tmp_shpfileName = OLE2CA(ShapefileName); + + // ---------------------------------------------- + // in memory shapefile (without name) + // ---------------------------------------------- + if (tmp_shpfileName.GetLength() == 0) + { + // closing the old shapefile (error code inside the function) + Close(&vb); + + if (vb == VARIANT_TRUE) + { + CoCreateInstance(CLSID_Table, nullptr, CLSCTX_INPROC_SERVER, IID_ITable, (void**)&_table); + + _table->CreateNew(m_globalSettings.emptyBstr, &vb); + + if (!vb) + { + long error; + _table->get_LastErrorCode(&error); + ErrorMessage(error); + _table->Release(); + _table = nullptr; + } + else + { + _shpfiletype = ShapefileType; + _isEditingShapes = true; + _sourceType = sstInMemory; + + if (applyRandomOptions) + { + ShapeStyleHelper::ApplyRandomDrawingOptions(this); + } + + *retval = VARIANT_TRUE; + } + } + + return S_OK; + } + + if (tmp_shpfileName.GetLength() <= 3) + { + ErrorMessage(tkINVALID_FILENAME); + return S_OK; + } + + const CString& shpName = tmp_shpfileName; + const CString shxName = tmp_shpfileName.Left(tmp_shpfileName.GetLength() - 3) + "shx"; + const CString dbfName = tmp_shpfileName.Left(tmp_shpfileName.GetLength() - 3) + "dbf"; + const CString prjName = tmp_shpfileName.Left(tmp_shpfileName.GetLength() - 3) + "prj"; + + // new file is created, so there must not be any files with this names + if (Utility::FileExists(shpName) != FALSE) + { + ErrorMessage(tkSHP_FILE_EXISTS); + return S_OK; + } + if (Utility::FileExists(shxName) != FALSE) + { + ErrorMessage(tkSHX_FILE_EXISTS); + return S_OK; + } + if (Utility::FileExists(dbfName) != FALSE) + { + ErrorMessage(tkDBF_FILE_EXISTS); + return S_OK; + } + if (Utility::FileExists(prjName) != FALSE) + { + ErrorMessage(tkPRJ_FILE_EXISTS); + return S_OK; + } + + // closing the old shapefile (error code inside the function) + this->Close(&vb); + + if (vb == VARIANT_TRUE) + { + CoCreateInstance(CLSID_Table, nullptr, CLSCTX_INPROC_SERVER, IID_ITable, (void**)&_table); + + _table->put_GlobalCallback(_globalCallback); + + const CString newDbfName = tmp_shpfileName.Left(tmp_shpfileName.GetLength() - 3) + "dbf"; + const CComBSTR bstrName(newDbfName); + _table->CreateNew(bstrName, &vb); + + if (!vb) + { + _table->get_LastErrorCode(&_lastErrorCode); + ErrorMessage(_lastErrorCode); + _table->Release(); + _table = nullptr; + } + else + { + _shpfileName = tmp_shpfileName; + _shxfileName = tmp_shpfileName.Left(tmp_shpfileName.GetLength() - 3) + "shx"; + _dbffileName = tmp_shpfileName.Left(tmp_shpfileName.GetLength() - 3) + "dbf"; + _prjfileName = tmp_shpfileName.Left(tmp_shpfileName.GetLength() - 3) + "prj"; + + _shpfiletype = ShapefileType; + _isEditingShapes = true; + _sourceType = sstInMemory; + + if (applyRandomOptions) + { + ShapeStyleHelper::ApplyRandomDrawingOptions(this); + } + + *retval = VARIANT_TRUE; + } + } + + LabelsHelper::UpdateLabelsPositioning(this); + + return S_OK; } // ********************************************************* // CreateNewWithShapeID() // ********************************************************* -STDMETHODIMP CShapefile::CreateNewWithShapeID(BSTR ShapefileName, ShpfileType ShapefileType, VARIANT_BOOL *retval) +STDMETHODIMP CShapefile::CreateNewWithShapeID(BSTR ShapefileName, ShpfileType ShapefileType, VARIANT_BOOL* retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - USES_CONVERSION; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + USES_CONVERSION; - CreateNew(ShapefileName, ShapefileType, retval); - - if (*retval) - ShapefileHelper::InsertMwShapeIdField(this); + CreateNew(ShapefileName, ShapefileType, retval); - return S_OK; + if (*retval) + ShapefileHelper::InsertMwShapeIdField(this); + + return S_OK; } #pragma endregion @@ -859,544 +872,547 @@ STDMETHODIMP CShapefile::CreateNewWithShapeID(BSTR ShapefileName, ShpfileType Sh // ***************************************************************** // Close() // ***************************************************************** -STDMETHODIMP CShapefile::Close(VARIANT_BOOL *retval) -{ - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - - if (_appendMode) { - StopAppendMode(); - } - - ClearCachedGeometries(); - - if( _isEditingShapes ) - { - // just stop editing shapes, if the shape is in open status - this->StopEditingShapes(VARIANT_FALSE, VARIANT_TRUE, NULL, retval); - } - - // stop editing table in case only it have been edited - VARIANT_BOOL isEditingTable = VARIANT_FALSE; - if( _table ) - { - _table->get_EditingTable(&isEditingTable); - if (isEditingTable) - { - this->StopEditingTable(VARIANT_FALSE,_globalCallback,retval); - _table->get_EditingTable(&isEditingTable); - } - } - - // removing shape data - this->ReleaseMemoryShapes(); - for (unsigned int i = 0; i < _shapeData.size(); i++) - { - delete _shapeData[i]; - } - _shapeData.clear(); - - if (_spatialIndexLoaded) - IndexSearching::unloadSpatialIndex(_spatialIndexID); - - _sourceType = sstUninitialized; - _shpfiletype = SHP_NULLSHAPE; - LabelsHelper::UpdateLabelsPositioning(this); - - _shpfileName = ""; - _shxfileName = ""; - _dbffileName = ""; - - _minX = 0.0; - _minY = 0.0; - _minZ = 0.0; - _maxX = 0.0; - _maxY = 0.0; - _maxZ = 0.0; - _minM = 0.0; - _maxM = 0.0; - - if (_inputValidation != NULL) - { - _inputValidation->Release(); - _inputValidation = NULL; - } - - if (_outputValidation != NULL) - { - _outputValidation->Release(); - _outputValidation = NULL; - } - - if( _shpfile != NULL) fclose(_shpfile); - _shpfile = NULL; - - if( _shxfile != NULL) fclose(_shxfile); - _shxfile = NULL; - - if( _table != NULL ) - { - VARIANT_BOOL vbretval; - _table->Close(&vbretval); - _table->Release(); - _table = NULL; - } - if (_labels) - { - _labels->Clear(); - _labels->ClearCategories(); - } - if (_charts) - { - _charts->Clear(); - } - if (_categories) - { - _categories->Clear(); - } - *retval = VARIANT_TRUE; - - return S_OK; +STDMETHODIMP CShapefile::Close(VARIANT_BOOL* retval) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + + if (_appendMode) + { + StopAppendMode(); + } + + ClearCachedGeometries(); + + if (_isEditingShapes) + { + // just stop editing shapes, if the shape is in open status + this->StopEditingShapes(VARIANT_FALSE, VARIANT_TRUE, nullptr, retval); + } + + // stop editing table in case only it have been edited + VARIANT_BOOL isEditingTable = VARIANT_FALSE; + if (_table) + { + _table->get_EditingTable(&isEditingTable); + if (isEditingTable) + { + this->StopEditingTable(VARIANT_FALSE, _globalCallback, retval); + _table->get_EditingTable(&isEditingTable); + } + } + + // removing shape data + this->ReleaseMemoryShapes(); + for (auto& i : _shapeData) + { + delete i; + } + _shapeData.clear(); + + if (_spatialIndexLoaded) + IndexSearching::unloadSpatialIndex(_spatialIndexID); + + _sourceType = sstUninitialized; + _shpfiletype = SHP_NULLSHAPE; + LabelsHelper::UpdateLabelsPositioning(this); + + _shpfileName = ""; + _shxfileName = ""; + _dbffileName = ""; + + _minX = 0.0; + _minY = 0.0; + _minZ = 0.0; + _maxX = 0.0; + _maxY = 0.0; + _maxZ = 0.0; + _minM = 0.0; + _maxM = 0.0; + + if (_inputValidation != nullptr) + { + _inputValidation->Release(); + _inputValidation = nullptr; + } + + if (_outputValidation != nullptr) + { + _outputValidation->Release(); + _outputValidation = nullptr; + } + + if (_shpfile != nullptr) fclose(_shpfile); + _shpfile = nullptr; + + if (_shxfile != nullptr) fclose(_shxfile); + _shxfile = nullptr; + + if (_table != nullptr) + { + VARIANT_BOOL vbretval; + _table->Close(&vbretval); + _table->Release(); + _table = nullptr; + } + if (_labels) + { + _labels->Clear(); + _labels->ClearCategories(); + } + if (_charts) + { + _charts->Clear(); + } + if (_categories) + { + _categories->Clear(); + } + *retval = VARIANT_TRUE; + + return S_OK; } // ********************************************************** // Dump() // ********************************************************** //Saves shapefile without reopening it in a new location -STDMETHODIMP CShapefile::Dump(BSTR ShapefileName, ICallback *cBack, VARIANT_BOOL *retval) -{ - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *retval = VARIANT_FALSE; - - bool callbackIsNull = (_globalCallback == NULL); - if(_globalCallback == NULL && cBack != NULL) - { - _globalCallback = cBack; - _globalCallback->AddRef(); - } - - if (_table == NULL || _sourceType == sstUninitialized) - { - ErrorMessage(tkSHAPEFILE_UNINITIALIZED); - return S_OK; - } - - // in case someone else is writing, we leave - if (_writing) - { - ErrorMessage(tkSHP_WRITE_VIOLATION); - return S_OK; - } - - if (!this->ValidateOutput(this, "Dump", "Shapefile", false)) - return S_OK; - - USES_CONVERSION; - CString sa_shpfileName; - sa_shpfileName = OLE2CA(ShapefileName); - - if( sa_shpfileName.GetLength() <= 3 ) - { - ErrorMessage(tkINVALID_FILENAME); - } - else - { - CString sa_shxfileName = sa_shpfileName.Left(sa_shpfileName.GetLength() - 3) + "shx"; - CString sa_dbffileName = sa_shpfileName.Left(sa_shpfileName.GetLength() - 3) + "dbf"; - - // ----------------------------------------------- - // it's not allowed to rewrite the existing files - // ----------------------------------------------- - if( Utility::FileExists(sa_shpfileName) ) - { - ErrorMessage(tkSHP_FILE_EXISTS); - return S_OK; - } - if( Utility::FileExists(sa_shxfileName) ) - { - ErrorMessage(tkSHX_FILE_EXISTS); - return S_OK; - } - if( Utility::FileExists(sa_dbffileName) ) - { - ErrorMessage(tkDBF_FILE_EXISTS); - return S_OK; - } - - // ----------------------------------------------- - // checking in-memory shapes - // ----------------------------------------------- - if( _isEditingShapes ) - { - if( VerifyMemShapes(cBack) == FALSE ) - { - // error Code is set in function - return S_OK; - } - - // refresh extents - VARIANT_BOOL retVal; - this->RefreshExtents(&retVal); - } - - // ----------------------------------------------- - // opening files - // ----------------------------------------------- - FILE * shpfile = fopen( sa_shpfileName, "wb+" ); - if( shpfile == NULL ) - { - ErrorMessage(tkCANT_CREATE_SHP); - return S_OK; - } - - //shx - FILE * shxfile = fopen( sa_shxfileName, "wb+" ); - if( shxfile == NULL ) - { - fclose( shpfile ); - ErrorMessage(tkCANT_CREATE_SHX); - return S_OK; - } - - // ------------------------------------------------ - // writing the files - // ------------------------------------------------ - this->WriteShp(shpfile,cBack); - this->WriteShx(shxfile,cBack); - - fclose(shpfile); - fclose(shxfile); - - // ------------------------------------------------ - // saving dbf table - // ------------------------------------------------ - _table->Dump(sa_dbffileName.AllocSysString(), cBack, retval); - if( *retval == FALSE ) - { - _table->get_LastErrorCode(&_lastErrorCode); - return S_OK; - } - - // saving projection in new format - VARIANT_BOOL vbretval; - CStringW prjfileName = sa_shpfileName.Left(sa_shpfileName.GetLength() - 3) + L"prj"; - CComBSTR bstr(prjfileName); - _geoProjection->WriteToFileEx(bstr, VARIANT_TRUE, &vbretval); - - *retval = VARIANT_TRUE; - } - - // restoring callback - if (callbackIsNull) { - _globalCallback = NULL; - } - - return S_OK; +STDMETHODIMP CShapefile::Dump(BSTR ShapefileName, ICallback* cBack, VARIANT_BOOL* retval) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + *retval = VARIANT_FALSE; + + const bool callbackIsNull = _globalCallback == nullptr; + if (_globalCallback == nullptr && cBack != nullptr) + { + _globalCallback = cBack; + _globalCallback->AddRef(); + } + + if (_table == nullptr || _sourceType == sstUninitialized) + { + ErrorMessage(tkSHAPEFILE_UNINITIALIZED); + return S_OK; + } + + // in case someone else is writing, we leave + if (_writing) + { + ErrorMessage(tkSHP_WRITE_VIOLATION); + return S_OK; + } + + if (!this->ValidateOutput(this, "Dump", "Shapefile", false)) + return S_OK; + + USES_CONVERSION; + CString sa_shpfileName = OLE2CA(ShapefileName); + + if (sa_shpfileName.GetLength() <= 3) + { + ErrorMessage(tkINVALID_FILENAME); + } + else + { + const CString sa_shxfileName = sa_shpfileName.Left(sa_shpfileName.GetLength() - 3) + "shx"; + CString sa_dbffileName = sa_shpfileName.Left(sa_shpfileName.GetLength() - 3) + "dbf"; + + // ----------------------------------------------- + // it's not allowed to rewrite the existing files + // ----------------------------------------------- + if (Utility::FileExists(sa_shpfileName)) + { + ErrorMessage(tkSHP_FILE_EXISTS); + return S_OK; + } + if (Utility::FileExists(sa_shxfileName)) + { + ErrorMessage(tkSHX_FILE_EXISTS); + return S_OK; + } + if (Utility::FileExists(sa_dbffileName)) + { + ErrorMessage(tkDBF_FILE_EXISTS); + return S_OK; + } + + // ----------------------------------------------- + // checking in-memory shapes + // ----------------------------------------------- + if (_isEditingShapes) + { + if (VerifyMemShapes(cBack) == FALSE) + { + // error Code is set in function + return S_OK; + } + + // refresh extents + VARIANT_BOOL retVal; + this->RefreshExtents(&retVal); + } + + // ----------------------------------------------- + // opening files + // ----------------------------------------------- + FILE* shpfile = fopen(sa_shpfileName, "wb+"); + if (shpfile == nullptr) + { + ErrorMessage(tkCANT_CREATE_SHP); + return S_OK; + } + + //shx + FILE* shxfile = fopen(sa_shxfileName, "wb+"); + if (shxfile == nullptr) + { + fclose(shpfile); + ErrorMessage(tkCANT_CREATE_SHX); + return S_OK; + } + + // ------------------------------------------------ + // writing the files + // ------------------------------------------------ + this->WriteShp(shpfile, cBack); + this->WriteShx(shxfile, cBack); + + fclose(shpfile); + fclose(shxfile); + + // ------------------------------------------------ + // saving dbf table + // ------------------------------------------------ + _table->Dump(sa_dbffileName.AllocSysString(), cBack, retval); + if (*retval == FALSE) + { + _table->get_LastErrorCode(&_lastErrorCode); + return S_OK; + } + + // saving projection in new format + VARIANT_BOOL vbretval; + const CStringW prjfileName = sa_shpfileName.Left(sa_shpfileName.GetLength() - 3) + L"prj"; + const CComBSTR bstr(prjfileName); + _geoProjection->WriteToFileEx(bstr, VARIANT_TRUE, &vbretval); + + *retval = VARIANT_TRUE; + } + + // restoring callback + if (callbackIsNull) + { + _globalCallback = nullptr; + } + + return S_OK; } // ********************************************************** // SaveAs() // ********************************************************** // Saves shapefile to the new or the same location. Doesn't stop editing mode. -STDMETHODIMP CShapefile::SaveAs(BSTR ShapefileName, ICallback *cBack, VARIANT_BOOL *retval) -{ - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *retval = VARIANT_FALSE; - - bool callbackIsNull = (_globalCallback == NULL); - if(_globalCallback == NULL && cBack != NULL) - { - _globalCallback = cBack; - _globalCallback->AddRef(); - } - - if (_table == NULL || _sourceType == sstUninitialized) - { - ErrorMessage(tkSHAPEFILE_UNINITIALIZED); - return S_OK; - } - - // in case someone else is writing, we leave - if (_writing) - { - ErrorMessage(tkSHP_WRITE_VIOLATION); - return S_OK; - } - - if (!this->ValidateOutput(this, "SaveAs", "Shapefile", false)) - return S_OK; - - USES_CONVERSION; - CStringW newShpName; - newShpName = OLE2W(ShapefileName); - - if( newShpName.GetLength() <= 3 ) - { - ErrorMessage(tkINVALID_FILENAME); - } - else if (!Utility::EndsWith(newShpName, L".shp")) - { - ErrorMessage(tkINVALID_FILE_EXTENSION); - } - else - { - CStringW newShxName = newShpName.Left(newShpName.GetLength() - 3) + L"shx"; - CStringW newDbfName = newShpName.Left(newShpName.GetLength() - 3) + L"dbf"; - - // ----------------------------------------------- - // it's not allowed to rewrite the existing files - // ----------------------------------------------- - if( Utility::FileExistsW(newShpName) ) - { - ErrorMessage(tkSHP_FILE_EXISTS); - return S_OK; - } - if (Utility::FileExistsW(newShxName)) - { - ErrorMessage(tkSHX_FILE_EXISTS); - return S_OK; - } - if (Utility::FileExistsW(newDbfName)) - { - ErrorMessage(tkDBF_FILE_EXISTS); - return S_OK; - } - - // ----------------------------------------------- - // checking in-memory shapes - // ----------------------------------------------- - if( _isEditingShapes ) - { - if( VerifyMemShapes(cBack) == FALSE ) - { - // error Code is set in function - return S_OK; - } - - // refresh extents - VARIANT_BOOL retVal; - RefreshExtents(&retVal); - } - - // ----------------------------------------------- - // opening files - // ----------------------------------------------- - FILE * shpfile = _wfopen(newShpName, L"wb+"); - if( shpfile == NULL ) - { - ErrorMessage(tkCANT_CREATE_SHP); - return S_OK; - } - - //shx - FILE * shxfile = _wfopen(newShxName, L"wb+"); - if( shxfile == NULL ) - { - fclose( shpfile ); - ErrorMessage(tkCANT_CREATE_SHX); - return S_OK; - } - - // ------------------------------------------------ - // writing the files - // ------------------------------------------------ - WriteShp(shpfile,cBack); - WriteShx(shxfile,cBack); - - fclose(shpfile); - fclose(shxfile); - - // ------------------------------------------------ - // saving dbf table - // ------------------------------------------------ - CComBSTR bstrDbf(newDbfName); - _table->SaveAs(bstrDbf, cBack, retval); - - if( *retval != VARIANT_TRUE ) - { - _table->get_LastErrorCode(&_lastErrorCode); - _wunlink(newShpName); - _wunlink(newShxName); - return S_OK; - } - - // ------------------------------------------------- - // switching to the new files - // ------------------------------------------------- - shpfile = _wfopen(newShpName, L"rb"); - shxfile = _wfopen(newShxName, L"rb"); - - if( _shpfile != NULL ) fclose(_shpfile); - _shpfile = shpfile; - - if( _shxfile != NULL ) fclose(_shxfile); - _shxfile = shxfile; - - // update all filenames: - _shpfileName = newShpName; // saving of shp filename should be done before writing the projection; - _shxfileName = newShxName; // otherwise projection string will be written to the memory - _dbffileName = newDbfName; - _prjfileName = newShpName.Left(newShpName.GetLength() - 3) + L"prj"; - - _sourceType = sstDiskBased; - - // saving projection in new format - VARIANT_BOOL vbretval, isEmpty; - _geoProjection->get_IsEmpty(&isEmpty); - if (!isEmpty) { - CComBSTR bstr(_prjfileName); - _geoProjection->WriteToFileEx(bstr, VARIANT_TRUE, &vbretval); - } - - if (_useQTree) - GenerateQTree(); - - *retval = VARIANT_TRUE; - } - - // restoring callback - if (callbackIsNull) { - _globalCallback = NULL; - } - - return S_OK; +STDMETHODIMP CShapefile::SaveAs(BSTR ShapefileName, ICallback* cBack, VARIANT_BOOL* retval) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + *retval = VARIANT_FALSE; + + const bool callbackIsNull = _globalCallback == nullptr; + if (_globalCallback == nullptr && cBack != nullptr) + { + _globalCallback = cBack; + _globalCallback->AddRef(); + } + + if (_table == nullptr || _sourceType == sstUninitialized) + { + ErrorMessage(tkSHAPEFILE_UNINITIALIZED); + return S_OK; + } + + // in case someone else is writing, we leave + if (_writing) + { + ErrorMessage(tkSHP_WRITE_VIOLATION); + return S_OK; + } + + if (!this->ValidateOutput(this, "SaveAs", "Shapefile", false)) + return S_OK; + + USES_CONVERSION; + CStringW newShpName = OLE2W(ShapefileName); + + if (newShpName.GetLength() <= 3) + { + ErrorMessage(tkINVALID_FILENAME); + } + else if (!Utility::EndsWith(newShpName, L".shp")) + { + ErrorMessage(tkINVALID_FILE_EXTENSION); + } + else + { + const CStringW newShxName = newShpName.Left(newShpName.GetLength() - 3) + L"shx"; + const CStringW newDbfName = newShpName.Left(newShpName.GetLength() - 3) + L"dbf"; + + // ----------------------------------------------- + // it's not allowed to rewrite the existing files + // ----------------------------------------------- + if (Utility::FileExistsW(newShpName)) + { + ErrorMessage(tkSHP_FILE_EXISTS); + return S_OK; + } + if (Utility::FileExistsW(newShxName)) + { + ErrorMessage(tkSHX_FILE_EXISTS); + return S_OK; + } + if (Utility::FileExistsW(newDbfName)) + { + ErrorMessage(tkDBF_FILE_EXISTS); + return S_OK; + } + + // ----------------------------------------------- + // checking in-memory shapes + // ----------------------------------------------- + if (_isEditingShapes) + { + if (VerifyMemShapes(cBack) == FALSE) + { + // error Code is set in function + return S_OK; + } + + // refresh extents + VARIANT_BOOL retVal; + RefreshExtents(&retVal); + } + + // ----------------------------------------------- + // opening files + // ----------------------------------------------- + FILE* shpfile = _wfopen(newShpName, L"wb+"); + if (shpfile == nullptr) + { + ErrorMessage(tkCANT_CREATE_SHP); + return S_OK; + } + + //shx + FILE* shxfile = _wfopen(newShxName, L"wb+"); + if (shxfile == nullptr) + { + fclose(shpfile); + ErrorMessage(tkCANT_CREATE_SHX); + return S_OK; + } + + // ------------------------------------------------ + // writing the files + // ------------------------------------------------ + WriteShp(shpfile, cBack); + WriteShx(shxfile, cBack); + + fclose(shpfile); + fclose(shxfile); + + // ------------------------------------------------ + // saving dbf table + // ------------------------------------------------ + const CComBSTR bstrDbf(newDbfName); + _table->SaveAs(bstrDbf, cBack, retval); + + if (*retval != VARIANT_TRUE) + { + _table->get_LastErrorCode(&_lastErrorCode); + _wunlink(newShpName); + _wunlink(newShxName); + return S_OK; + } + + // ------------------------------------------------- + // switching to the new files + // ------------------------------------------------- + shpfile = _wfopen(newShpName, L"rb"); + shxfile = _wfopen(newShxName, L"rb"); + + if (_shpfile != nullptr) fclose(_shpfile); + _shpfile = shpfile; + + if (_shxfile != nullptr) fclose(_shxfile); + _shxfile = shxfile; + + // update all filenames: + _shpfileName = newShpName; // saving of shp filename should be done before writing the projection; + _shxfileName = newShxName; // otherwise projection string will be written to the memory + _dbffileName = newDbfName; + _prjfileName = newShpName.Left(newShpName.GetLength() - 3) + L"prj"; + + _sourceType = sstDiskBased; + + // saving projection in new format + VARIANT_BOOL vbretval, isEmpty; + _geoProjection->get_IsEmpty(&isEmpty); + if (!isEmpty) + { + const CComBSTR bstr(_prjfileName); + _geoProjection->WriteToFileEx(bstr, VARIANT_TRUE, &vbretval); + } + + if (_useQTree) + GenerateQTree(); + + *retval = VARIANT_TRUE; + } + + // restoring callback + if (callbackIsNull) + { + _globalCallback = nullptr; + } + + return S_OK; } // ************************************************************** // Save() // ************************************************************** // saving without exiting edit mode -STDMETHODIMP CShapefile::Save(ICallback *cBack, VARIANT_BOOL *retval) -{ - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *retval = VARIANT_FALSE; - - if (_globalCallback == NULL && cBack != NULL) - { - _globalCallback = cBack; - _globalCallback->AddRef(); - } - - // no edits were made; it doesn't make sense to save it - if( _isEditingShapes == FALSE ) - { - ErrorMessage( tkSHPFILE_NOT_IN_EDIT_MODE ); - return S_OK; - } - - if( VerifyMemShapes(_globalCallback) == FALSE ) - { - // error Code is set in function - return S_OK; - } - - if (!this->ValidateOutput(this, "Save", "Shapefile", false)) - return S_OK; - - // compute the extents - VARIANT_BOOL res; - RefreshExtents(&res); - - // ------------------------------------------------- - // Reopen the files in the write mode - // ------------------------------------------------- - if (_shpfile && _shxfile) - { - _shpfile = _wfreopen(_shpfileName,L"wb+",_shpfile); - _shxfile = _wfreopen(_shxfileName,L"wb+",_shxfile); - } - else - { - _shpfile = _wfopen(_shpfileName,L"wb+"); - _shxfile = _wfopen(_shxfileName,L"wb+"); - } - - if( _shpfile == NULL || _shxfile == NULL ) - { - if( _shxfile != NULL ) - { - fclose( _shxfile ); - _shxfile = NULL; - _lastErrorCode = tkCANT_OPEN_SHX; - } - if( _shpfile != NULL ) - { - fclose( _shpfile ); - _shpfile = NULL; - _lastErrorCode = tkCANT_OPEN_SHP; - } - *retval = FALSE; - - ErrorMessage(_lastErrorCode); - } - else - { - _writing = true; - - // ------------------------------------------------- - // Writing the files - // ------------------------------------------------- - WriteShp(_shpfile,cBack); - WriteShx(_shxfile,cBack); - - if (_useQTree) - GenerateQTree(); - - // ------------------------------------------------- - // Reopen the updated files - // ------------------------------------------------- - _shpfile = _wfreopen(_shpfileName,L"rb+",_shpfile); - _shxfile = _wfreopen(_shxfileName,L"rb+",_shxfile); - - if( _shpfile == NULL || _shxfile == NULL ) - { - if( _shxfile != NULL ) - { - fclose( _shxfile ); - _shxfile = NULL; - _lastErrorCode = tkCANT_OPEN_SHX; - } - if( _shpfile != NULL ) - { - fclose( _shpfile ); - _shpfile = NULL; - _lastErrorCode = tkCANT_OPEN_SHP; - } - *retval = FALSE; - - ErrorMessage( _lastErrorCode ); - } - else - { - //Save the table file - _table->Save(cBack,retval); - - _sourceType = sstDiskBased; - - // saving projection in new format - VARIANT_BOOL vbretval, isEmpty; - _geoProjection->get_IsEmpty(&isEmpty); - if (!isEmpty) { - CComBSTR bstr(_prjfileName); - _geoProjection->WriteToFileEx(bstr, VARIANT_TRUE, &vbretval); - } - - *retval = VARIANT_TRUE; - } - } - - _writing = false; - return S_OK; +STDMETHODIMP CShapefile::Save(ICallback* cBack, VARIANT_BOOL* retval) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + *retval = VARIANT_FALSE; + + if (_globalCallback == nullptr && cBack != nullptr) + { + _globalCallback = cBack; + _globalCallback->AddRef(); + } + + // no edits were made; it doesn't make sense to save it + if (_isEditingShapes == FALSE) + { + ErrorMessage(tkSHPFILE_NOT_IN_EDIT_MODE); + return S_OK; + } + + if (VerifyMemShapes(_globalCallback) == FALSE) + { + // error Code is set in function + return S_OK; + } + + if (!this->ValidateOutput(this, "Save", "Shapefile", false)) + return S_OK; + + // compute the extents + VARIANT_BOOL res; + RefreshExtents(&res); + + // ------------------------------------------------- + // Reopen the files in the write mode + // ------------------------------------------------- + if (_shpfile && _shxfile) + { + _shpfile = _wfreopen(_shpfileName, L"wb+", _shpfile); + _shxfile = _wfreopen(_shxfileName, L"wb+", _shxfile); + } + else + { + _shpfile = _wfopen(_shpfileName, L"wb+"); + _shxfile = _wfopen(_shxfileName, L"wb+"); + } + + if (_shpfile == nullptr || _shxfile == nullptr) + { + if (_shxfile != nullptr) + { + fclose(_shxfile); + _shxfile = nullptr; + _lastErrorCode = tkCANT_OPEN_SHX; + } + if (_shpfile != nullptr) + { + fclose(_shpfile); + _shpfile = nullptr; + _lastErrorCode = tkCANT_OPEN_SHP; + } + *retval = FALSE; + + ErrorMessage(_lastErrorCode); + } + else + { + _writing = true; + + // ------------------------------------------------- + // Writing the files + // ------------------------------------------------- + WriteShp(_shpfile, cBack); + WriteShx(_shxfile, cBack); + + if (_useQTree) + GenerateQTree(); + + // ------------------------------------------------- + // Reopen the updated files + // ------------------------------------------------- + _shpfile = _wfreopen(_shpfileName, L"rb+", _shpfile); + _shxfile = _wfreopen(_shxfileName, L"rb+", _shxfile); + + if (_shpfile == nullptr || _shxfile == nullptr) + { + if (_shxfile != nullptr) + { + fclose(_shxfile); + _shxfile = nullptr; + _lastErrorCode = tkCANT_OPEN_SHX; + } + if (_shpfile != nullptr) + { + fclose(_shpfile); + _shpfile = nullptr; + _lastErrorCode = tkCANT_OPEN_SHP; + } + *retval = FALSE; + + ErrorMessage(_lastErrorCode); + } + else + { + //Save the table file + _table->Save(cBack, retval); + + _sourceType = sstDiskBased; + + // saving projection in new format + VARIANT_BOOL vbretval, isEmpty; + _geoProjection->get_IsEmpty(&isEmpty); + if (!isEmpty) + { + const CComBSTR bstr(_prjfileName); + _geoProjection->WriteToFileEx(bstr, VARIANT_TRUE, &vbretval); + } + + *retval = VARIANT_TRUE; + } + } + + _writing = false; + return S_OK; } // ************************************************************ // Resource() // ************************************************************ -STDMETHODIMP CShapefile::Resource(BSTR newShpPath, VARIANT_BOOL *retval) +STDMETHODIMP CShapefile::Resource(BSTR newShpPath, VARIANT_BOOL* retval) { - USES_CONVERSION; - Close(retval); - Open(newShpPath, NULL, retval); - return S_OK; + USES_CONVERSION; + Close(retval); + Open(newShpPath, nullptr, retval); + return S_OK; } #pragma endregion @@ -1406,324 +1422,320 @@ STDMETHODIMP CShapefile::Resource(BSTR newShpPath, VARIANT_BOOL *retval) // Creates new shapefile with the same type and fields as existing one STDMETHODIMP CShapefile::Clone(IShapefile** retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - ShapefileHelper::CloneCore(this, retVal, _shpfiletype); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + ShapefileHelper::CloneCore(this, retVal, _shpfiletype); + return S_OK; } // ************************************************************ // get_Extents() // ************************************************************ -STDMETHODIMP CShapefile::get_Extents(IExtents **pVal) +STDMETHODIMP CShapefile::get_Extents(IExtents** pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + + IExtents* bBox = nullptr; + ComHelper::CreateExtents(&bBox); - IExtents * bBox = NULL; - ComHelper::CreateExtents(&bBox); - - // Extents could change because of the moving of points of a single shape - // It's difficult to track such changes, so we still need to recalculate them - // here to enforce proper drawing; _fastMode mode - for those who want - // to call refresh extents theirselfs - if (!_fastMode) - { - VARIANT_BOOL vbretval; - this->RefreshExtents(&vbretval); - } + // Extents could change because of the moving of points of a single shape + // It's difficult to track such changes, so we still need to recalculate them + // here to enforce proper drawing; _fastMode mode - for those who want + // to call refresh extents theirselfs + if (!_fastMode) + { + VARIANT_BOOL vbretval; + this->RefreshExtents(&vbretval); + } - bBox->SetBounds(_minX,_minY,_minZ,_maxX,_maxY,_maxZ); - bBox->SetMeasureBounds(_minM,_maxM); - *pVal = bBox; + bBox->SetBounds(_minX, _minY, _minZ, _maxX, _maxY, _maxZ); + bBox->SetMeasureBounds(_minM, _maxM); + *pVal = bBox; - return S_OK; + return S_OK; } #pragma region AttributeTable // **************************************************************** // EditInsertField() // **************************************************************** -STDMETHODIMP CShapefile::EditInsertField(IField *NewField, long *FieldIndex, ICallback *cBack, VARIANT_BOOL *retval) +STDMETHODIMP CShapefile::EditInsertField(IField* NewField, long* FieldIndex, ICallback* cBack, VARIANT_BOOL* retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) + AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if(cBack == NULL && _globalCallback!=NULL) - cBack = _globalCallback; + if (cBack == nullptr && _globalCallback != nullptr) + cBack = _globalCallback; - if( _table != NULL ) - { - _table->EditInsertField(NewField,FieldIndex,cBack,retval); - } - else - { - *retval = VARIANT_FALSE; - _lastErrorCode = tkFILE_NOT_OPEN; - ErrorMessage(_lastErrorCode, cBack); - return S_OK; - } + if (_table != nullptr) + { + _table->EditInsertField(NewField, FieldIndex, cBack, retval); + } + else + { + *retval = VARIANT_FALSE; + _lastErrorCode = tkFILE_NOT_OPEN; + ErrorMessage(_lastErrorCode, cBack); + return S_OK; + } - if( *retval == VARIANT_FALSE ) - { - _table->get_LastErrorCode(&_lastErrorCode); - *retval = VARIANT_FALSE; - } + if (*retval == VARIANT_FALSE) + { + _table->get_LastErrorCode(&_lastErrorCode); + *retval = VARIANT_FALSE; + } - return S_OK; + return S_OK; } // **************************************************************** // EditDeleteField() // **************************************************************** -STDMETHODIMP CShapefile::EditDeleteField(long FieldIndex, ICallback *cBack, VARIANT_BOOL *retval) +STDMETHODIMP CShapefile::EditDeleteField(long FieldIndex, ICallback* cBack, VARIANT_BOOL* retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) + AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if(_globalCallback == NULL && cBack != NULL) - { - _globalCallback = cBack; - _globalCallback->AddRef(); - } + if (_globalCallback == nullptr && cBack != nullptr) + { + _globalCallback = cBack; + _globalCallback->AddRef(); + } - if( _table != NULL ) - { - _table->EditDeleteField(FieldIndex,cBack,retval); - } - else - { - *retval = VARIANT_FALSE; - ErrorMessage(tkFILE_NOT_OPEN); - return S_OK; - } + if (_table != nullptr) + { + _table->EditDeleteField(FieldIndex, cBack, retval); + } + else + { + *retval = VARIANT_FALSE; + ErrorMessage(tkFILE_NOT_OPEN); + return S_OK; + } - if( *retval == VARIANT_FALSE ) - { - _table->get_LastErrorCode(&_lastErrorCode); - *retval = VARIANT_FALSE; - } + if (*retval == VARIANT_FALSE) + { + _table->get_LastErrorCode(&_lastErrorCode); + *retval = VARIANT_FALSE; + } - return S_OK; + return S_OK; } // **************************************************************** // EditCellValue() // **************************************************************** -STDMETHODIMP CShapefile::EditCellValue(long FieldIndex, long ShapeIndex, VARIANT NewVal, VARIANT_BOOL *retval) +STDMETHODIMP CShapefile::EditCellValue(long FieldIndex, long ShapeIndex, VARIANT NewVal, VARIANT_BOOL* retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) + AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if( _table != NULL ) - { - _table->EditCellValue(FieldIndex,ShapeIndex,NewVal,retval); - } - else - { - *retval = VARIANT_FALSE; - ErrorMessage(tkFILE_NOT_OPEN); - return S_OK; - } + if (_table != nullptr) + { + _table->EditCellValue(FieldIndex, ShapeIndex, NewVal, retval); + } + else + { + *retval = VARIANT_FALSE; + ErrorMessage(tkFILE_NOT_OPEN); + return S_OK; + } - if( *retval == VARIANT_FALSE ) - { - _table->get_LastErrorCode(&_lastErrorCode); - } + if (*retval == VARIANT_FALSE) + { + _table->get_LastErrorCode(&_lastErrorCode); + } - return S_OK; + return S_OK; } // **************************************************************** // StartEditingTable() // **************************************************************** -STDMETHODIMP CShapefile::StartEditingTable(ICallback *cBack, VARIANT_BOOL *retval) -{ - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - - if (_appendMode) - { - ErrorMessage(tkDBF_NO_EDIT_MODE_WHEN_APPENDING); - *retval = VARIANT_FALSE; - return S_OK; - } - - if(_globalCallback == NULL && cBack != NULL) - { - _globalCallback = cBack; - _globalCallback->AddRef(); - } - - if( _table != NULL ) - { - _table->StartEditingTable(cBack,retval); - } - else - { - *retval = VARIANT_FALSE; - ErrorMessage(tkFILE_NOT_OPEN); - return S_OK; - } - - if( *retval == VARIANT_FALSE ) - { - _table->get_LastErrorCode(&_lastErrorCode); - *retval = VARIANT_FALSE; - } - - return S_OK; +STDMETHODIMP CShapefile::StartEditingTable(ICallback* cBack, VARIANT_BOOL* retval) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + + if (_appendMode) + { + ErrorMessage(tkDBF_NO_EDIT_MODE_WHEN_APPENDING); + *retval = VARIANT_FALSE; + return S_OK; + } + + if (_globalCallback == nullptr && cBack != nullptr) + { + _globalCallback = cBack; + _globalCallback->AddRef(); + } + + if (_table != nullptr) + { + _table->StartEditingTable(cBack, retval); + } + else + { + *retval = VARIANT_FALSE; + ErrorMessage(tkFILE_NOT_OPEN); + return S_OK; + } + + if (*retval == VARIANT_FALSE) + { + _table->get_LastErrorCode(&_lastErrorCode); + *retval = VARIANT_FALSE; + } + + return S_OK; } // **************************************************************** // StopEditingTable() // **************************************************************** -STDMETHODIMP CShapefile::StopEditingTable(VARIANT_BOOL ApplyChanges, ICallback *cBack, VARIANT_BOOL *retval) -{ - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - - if(_globalCallback == NULL && cBack!=NULL) - { - _globalCallback = cBack; - _globalCallback->AddRef(); - } - - if( _table != NULL ) - { - _table->StopEditingTable(ApplyChanges,cBack,retval); - } - else - { - *retval = VARIANT_FALSE; - ErrorMessage(tkFILE_NOT_OPEN); - return S_OK; - } - - if( *retval == FALSE ) - { - _table->get_LastErrorCode(&_lastErrorCode); - *retval = VARIANT_FALSE; - } - - return S_OK; +STDMETHODIMP CShapefile::StopEditingTable(VARIANT_BOOL ApplyChanges, ICallback* cBack, VARIANT_BOOL* retval) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + + if (_globalCallback == nullptr && cBack != nullptr) + { + _globalCallback = cBack; + _globalCallback->AddRef(); + } + + if (_table != nullptr) + { + _table->StopEditingTable(ApplyChanges, cBack, retval); + } + else + { + *retval = VARIANT_FALSE; + ErrorMessage(tkFILE_NOT_OPEN); + return S_OK; + } + + if (*retval == FALSE) + { + _table->get_LastErrorCode(&_lastErrorCode); + *retval = VARIANT_FALSE; + } + + return S_OK; } // ***************************************************************** // get_Field() // ***************************************************************** -STDMETHODIMP CShapefile::get_Field(long FieldIndex, IField **pVal) -{ - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - - if( _table != NULL ) - { - _table->get_Field(FieldIndex,pVal); - if(*pVal != NULL) - { - // we need to report error from field class, and will use callback from this class for it - ICallback* cBack = NULL; - if ((*pVal)->get_GlobalCallback(&cBack) == NULL && this->_globalCallback != NULL) - (*pVal)->put_GlobalCallback(_globalCallback); - - if (cBack != NULL) - cBack->Release(); // we put a reference in field class so must release it here - } - } - else - { - ErrorMessage(tkFILE_NOT_OPEN); - return S_OK; - } - - return S_OK; +STDMETHODIMP CShapefile::get_Field(long FieldIndex, IField** pVal) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + + if (_table != nullptr) + { + _table->get_Field(FieldIndex, pVal); + if (*pVal != nullptr) + { + // we need to report error from field class, and will use callback from this class for it + ICallback* cBack = nullptr; + if ((*pVal)->get_GlobalCallback(&cBack) == NULL && this->_globalCallback != nullptr) + (*pVal)->put_GlobalCallback(_globalCallback); + + if (cBack != nullptr) + cBack->Release(); // we put a reference in field class so must release it here + } + } + else + { + ErrorMessage(tkFILE_NOT_OPEN); + return S_OK; + } + + return S_OK; } // ***************************************************************** // get_FieldByName() // ***************************************************************** -STDMETHODIMP CShapefile::get_FieldByName(BSTR Fieldname, IField **pVal) +STDMETHODIMP CShapefile::get_FieldByName(BSTR Fieldname, IField** pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - USES_CONVERSION; - + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + USES_CONVERSION; + long max; - - CString strTestname; - CString strFieldname; - IField *testVal; - - _table->get_NumFields(&max); - if( _table != NULL ) - { - if( _tcslen( OLE2CA(Fieldname) ) > 0 ) - { - strFieldname = OLE2A(Fieldname); - } - else - { - ErrorMessage(tkZERO_LENGTH_STRING); - } - - for (int fld=0; fld < max; fld++) - { - _table->get_Field(fld,&testVal); - CComBSTR Testname; - testVal->get_Name(&Testname); - strTestname = OLE2A(Testname); - if( strTestname.CompareNoCase(strFieldname) == 0) - { - *pVal = testVal; - return S_OK; - } - else - { - testVal->Release(); - } - } - } - else - { - ErrorMessage(tkFILE_NOT_OPEN); - return S_OK; - } - - // we did not have a file error, but we also didn't match the name - pVal = NULL; - return S_OK; + + CString strFieldname; + IField* testVal; + + _table->get_NumFields(&max); + if (_table != nullptr) + { + if (_tcslen(OLE2CA(Fieldname)) > 0) + { + strFieldname = OLE2A(Fieldname); + } + else + { + ErrorMessage(tkZERO_LENGTH_STRING); + } + + for (int fld = 0; fld < max; fld++) + { + _table->get_Field(fld, &testVal); + CComBSTR Testname; + testVal->get_Name(&Testname); + CString strTestname = OLE2A(Testname); + if (strTestname.CompareNoCase(strFieldname) == 0) + { + *pVal = testVal; + return S_OK; + } + testVal->Release(); + } + } + else + { + ErrorMessage(tkFILE_NOT_OPEN); + return S_OK; + } + + // we did not have a file error, but we also didn't match the name + pVal = nullptr; + return S_OK; } // ***************************************************************** // get_CellValue() // ***************************************************************** -STDMETHODIMP CShapefile::get_CellValue(long FieldIndex, long ShapeIndex, VARIANT *pVal) +STDMETHODIMP CShapefile::get_CellValue(long FieldIndex, long ShapeIndex, VARIANT* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) + AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if( _table != NULL ) - { - _table->get_CellValue(FieldIndex,ShapeIndex,pVal); - } - else - { - ErrorMessage(tkFILE_NOT_OPEN); - return S_OK; - } + if (_table != nullptr) + { + _table->get_CellValue(FieldIndex, ShapeIndex, pVal); + } + else + { + ErrorMessage(tkFILE_NOT_OPEN); + return S_OK; + } - return S_OK; + return S_OK; } // ***************************************************************** // get_EditingTable() // ***************************************************************** -STDMETHODIMP CShapefile::get_EditingTable(VARIANT_BOOL *pVal) +STDMETHODIMP CShapefile::get_EditingTable(VARIANT_BOOL* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if( _table != NULL ) - { - _table->get_EditingTable(pVal); - } - else - { - *pVal = VARIANT_FALSE; - ErrorMessage(tkFILE_NOT_OPEN); - return S_OK; - } + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + if (_table != nullptr) + { + _table->get_EditingTable(pVal); + } + else + { + *pVal = VARIANT_FALSE; + ErrorMessage(tkFILE_NOT_OPEN); + return S_OK; + } - return S_OK; + return S_OK; } // ************************************************************* @@ -1731,13 +1743,13 @@ STDMETHODIMP CShapefile::get_EditingTable(VARIANT_BOOL *pVal) // ************************************************************* STDMETHODIMP CShapefile::get_Table(ITable** retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *retVal = _table; - if ( _table ) - { - _table->AddRef(); - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + *retVal = _table; + if (_table) + { + _table->AddRef(); + } + return S_OK; } #pragma endregion @@ -1748,27 +1760,28 @@ STDMETHODIMP CShapefile::get_Table(ITable** retVal) // ************************************************************* STDMETHODIMP CShapefile::get_ShapeRotation(long ShapeIndex, double* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - if( ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) - { - *pVal = -1; - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - } - else - *pVal = _shapeData[ShapeIndex]->rotation; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + if (ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) + { + *pVal = -1; + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + } + else + *pVal = _shapeData[ShapeIndex]->rotation; + return S_OK; } + STDMETHODIMP CShapefile::put_ShapeRotation(long ShapeIndex, double newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - if( ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - } - else - _shapeData[ShapeIndex]->rotation = static_cast(newVal); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + if (ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + } + else + _shapeData[ShapeIndex]->rotation = static_cast(newVal); - return S_OK; + return S_OK; } // ************************************************************* @@ -1776,40 +1789,42 @@ STDMETHODIMP CShapefile::put_ShapeRotation(long ShapeIndex, double newVal) // ************************************************************* STDMETHODIMP CShapefile::get_ShapeVisible(long ShapeIndex, VARIANT_BOOL* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *pVal = VARIANT_FALSE; - if (ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - } - else { - // this particular shape was not hidden explicitly or via visibility expression - if (!_shapeData[ShapeIndex]->hidden() && _shapeData[ShapeIndex]->isVisible()) - { - long ctIndex = -1; - get_ShapeCategory(ShapeIndex, &ctIndex); - if (ctIndex == -1) - { - // no category, check default options - _defaultDrawOpt->get_Visible(pVal); - } - else - { - // there is category, check whether it is visible - CComPtr ct = NULL; - get_ShapeCategory3(ShapeIndex, &ct); - if (ct) - { - CComPtr options = NULL; - ct->get_DrawingOptions(&options); - if (options) { - options->get_Visible(pVal); - } - } - } - } - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *pVal = VARIANT_FALSE; + if (ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + } + else + { + // this particular shape was not hidden explicitly or via visibility expression + if (!_shapeData[ShapeIndex]->hidden() && _shapeData[ShapeIndex]->isVisible()) + { + long ctIndex = -1; + get_ShapeCategory(ShapeIndex, &ctIndex); + if (ctIndex == -1) + { + // no category, check default options + _defaultDrawOpt->get_Visible(pVal); + } + else + { + // there is category, check whether it is visible + CComPtr ct = nullptr; + get_ShapeCategory3(ShapeIndex, &ct); + if (ct) + { + CComPtr options = nullptr; + ct->get_DrawingOptions(&options); + if (options) + { + options->get_Visible(pVal); + } + } + } + } + } + return S_OK; } // ************************************************************* @@ -1817,29 +1832,30 @@ STDMETHODIMP CShapefile::get_ShapeVisible(long ShapeIndex, VARIANT_BOOL* pVal) // ************************************************************* STDMETHODIMP CShapefile::get_ShapeIsHidden(LONG shapeIndex, VARIANT_BOOL* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - if (shapeIndex < 0 || shapeIndex >= (long)_shapeData.size()) - { - *pVal = VARIANT_FALSE; - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - } - else { - *pVal = _shapeData[shapeIndex]->hidden() ? VARIANT_TRUE : VARIANT_FALSE; - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + if (shapeIndex < 0 || shapeIndex >= (long)_shapeData.size()) + { + *pVal = VARIANT_FALSE; + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + } + else + { + *pVal = _shapeData[shapeIndex]->hidden() ? VARIANT_TRUE : VARIANT_FALSE; + } + return S_OK; } STDMETHODIMP CShapefile::put_ShapeIsHidden(LONG shapeIndex, VARIANT_BOOL newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - if (shapeIndex < 0 || shapeIndex >= (long)_shapeData.size()) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - } - else - _shapeData[shapeIndex]->hidden(newVal ? true : false); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + if (shapeIndex < 0 || shapeIndex >= (long)_shapeData.size()) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + } + else + _shapeData[shapeIndex]->hidden(newVal != 0); - return S_OK; + return S_OK; } // ************************************************************* @@ -1847,31 +1863,33 @@ STDMETHODIMP CShapefile::put_ShapeIsHidden(LONG shapeIndex, VARIANT_BOOL newVal) // ************************************************************* STDMETHODIMP CShapefile::get_ShapeModified(long ShapeIndex, VARIANT_BOOL* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - if (ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) - { - *retVal = -1; - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - } - else { - *retVal = _shapeData[ShapeIndex]->modified() ? VARIANT_TRUE : VARIANT_FALSE; - } + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + if (ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) + { + *retVal = -1; + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + } + else + { + *retVal = _shapeData[ShapeIndex]->modified() ? VARIANT_TRUE : VARIANT_FALSE; + } - return S_OK; + return S_OK; } STDMETHODIMP CShapefile::put_ShapeModified(long ShapeIndex, VARIANT_BOOL newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - if (ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - } - else { - _shapeData[ShapeIndex]->modified(newVal ? true : false); - } + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + if (ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + } + else + { + _shapeData[ShapeIndex]->modified(newVal != 0); + } - return S_OK; + return S_OK; } @@ -1880,27 +1898,28 @@ STDMETHODIMP CShapefile::put_ShapeModified(long ShapeIndex, VARIANT_BOOL newVal) // ************************************************************* STDMETHODIMP CShapefile::get_ShapeCategory(long ShapeIndex, long* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - if( ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) //_numShapes) - { - *pVal = -1; - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - } - else - *pVal = _shapeData[ShapeIndex]->category; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + if (ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) //_numShapes) + { + *pVal = -1; + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + } + else + *pVal = _shapeData[ShapeIndex]->category; + return S_OK; } + STDMETHODIMP CShapefile::put_ShapeCategory(long ShapeIndex, long newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - if( ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) //_numShapes ) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - } - else - _shapeData[ShapeIndex]->category = (int)newVal; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + if (ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) //_numShapes ) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + } + else + _shapeData[ShapeIndex]->category = (int)newVal; - return S_OK; + return S_OK; } // ************************************************************* @@ -1908,54 +1927,51 @@ STDMETHODIMP CShapefile::put_ShapeCategory(long ShapeIndex, long newVal) // ************************************************************* STDMETHODIMP CShapefile::put_ShapeCategory2(long ShapeIndex, BSTR categoryName) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - if( ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - } - else - { - int index; - _categories->get_CategoryIndexByName(categoryName, &index); - if (index == -1) - { - ErrorMessage(tkCATEGORY_WASNT_FOUND); - } - else - { - _shapeData[ShapeIndex]->category = (int)index; - } - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + if (ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + } + else + { + int index; + _categories->get_CategoryIndexByName(categoryName, &index); + if (index == -1) + { + ErrorMessage(tkCATEGORY_WASNT_FOUND); + } + else + { + _shapeData[ShapeIndex]->category = (int)index; + } + } + return S_OK; } STDMETHODIMP CShapefile::get_ShapeCategory2(long ShapeIndex, BSTR* categoryName) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - if( ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - } - else - { - int index = _shapeData[ShapeIndex]->category; - long count; - _categories->get_Count(&count); - if (index >= 0 && index < count) - { - IShapefileCategory* ct; - _categories->get_Item(index, &ct); - ct->get_Name(categoryName); - ct->Release(); - return S_OK; - } - else - { - ErrorMessage(tkCATEGORY_WASNT_FOUND); - } - } - *categoryName = SysAllocString(L""); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + if (ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + } + else + { + const int index = _shapeData[ShapeIndex]->category; + long count; + _categories->get_Count(&count); + if (index >= 0 && index < count) + { + IShapefileCategory* ct; + _categories->get_Item(index, &ct); + ct->get_Name(categoryName); + ct->Release(); + return S_OK; + } + ErrorMessage(tkCATEGORY_WASNT_FOUND); + } + *categoryName = SysAllocString(L""); + return S_OK; } // ************************************************************* @@ -1963,52 +1979,52 @@ STDMETHODIMP CShapefile::get_ShapeCategory2(long ShapeIndex, BSTR* categoryName) // ************************************************************* STDMETHODIMP CShapefile::put_ShapeCategory3(long ShapeIndex, IShapefileCategory* category) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - if( ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - } - else - { - int index; - _categories->get_CategoryIndex(category, &index); - if (index == -1) - { - ErrorMessage(tkCATEGORY_WASNT_FOUND); - } - else - { - _shapeData[ShapeIndex]->category = (int)index; - } - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + if (ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + } + else + { + int index; + _categories->get_CategoryIndex(category, &index); + if (index == -1) + { + ErrorMessage(tkCATEGORY_WASNT_FOUND); + } + else + { + _shapeData[ShapeIndex]->category = (int)index; + } + } + return S_OK; } STDMETHODIMP CShapefile::get_ShapeCategory3(long ShapeIndex, IShapefileCategory** category) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *category = NULL; - if( ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - } - else - { - int index = _shapeData[ShapeIndex]->category; - long count; - _categories->get_Count(&count); - if (index >= 0 && index < count) - { - IShapefileCategory* ct; - _categories->get_Item(index, &ct); - *category = ct; // ref was added in the get_Item - } - else - { - ErrorMessage(tkCATEGORY_WASNT_FOUND); - } - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *category = nullptr; + if (ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + } + else + { + const int index = _shapeData[ShapeIndex]->category; + long count; + _categories->get_Count(&count); + if (index >= 0 && index < count) + { + IShapefileCategory* ct; + _categories->get_Item(index, &ct); + *category = ct; // ref was added in the get_Item + } + else + { + ErrorMessage(tkCATEGORY_WASNT_FOUND); + } + } + return S_OK; } // ******************************************************************* @@ -2017,24 +2033,25 @@ STDMETHODIMP CShapefile::get_ShapeCategory3(long ShapeIndex, IShapefileCategory* // Returns and sets parameters used to draw selection for the shapefile. STDMETHODIMP CShapefile::get_SelectionDrawingOptions(IShapeDrawingOptions** pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *pVal = _selectDrawOpt; - if (_selectDrawOpt) - _selectDrawOpt->AddRef(); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *pVal = _selectDrawOpt; + if (_selectDrawOpt) + _selectDrawOpt->AddRef(); + return S_OK; } + STDMETHODIMP CShapefile::put_SelectionDrawingOptions(IShapeDrawingOptions* newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - if (!newVal) - { - ErrorMessage(tkINVALID_PARAMETER_VALUE); - } - else - { - ComHelper::SetRef(newVal, (IDispatch**)&_selectDrawOpt, false); - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + if (!newVal) + { + ErrorMessage(tkINVALID_PARAMETER_VALUE); + } + else + { + ComHelper::SetRef(newVal, (IDispatch**)&_selectDrawOpt, false); + } + return S_OK; } // ******************************************************************* @@ -2043,57 +2060,59 @@ STDMETHODIMP CShapefile::put_SelectionDrawingOptions(IShapeDrawingOptions* newVa // Returns and sets parameters used to draw shapefile by default. STDMETHODIMP CShapefile::get_DefaultDrawingOptions(IShapeDrawingOptions** pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *pVal = _defaultDrawOpt; - if (_defaultDrawOpt) - _defaultDrawOpt->AddRef(); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *pVal = _defaultDrawOpt; + if (_defaultDrawOpt) + _defaultDrawOpt->AddRef(); + return S_OK; } + STDMETHODIMP CShapefile::put_DefaultDrawingOptions(IShapeDrawingOptions* newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - - if (!newVal) - { - ErrorMessage(tkINVALID_PARAMETER_VALUE); - } - else - { - ComHelper::SetRef(newVal, (IDispatch**)&_defaultDrawOpt); - } - return S_OK; -} - -/***********************************************************************/ -/* put_ReferenceToCategories -/***********************************************************************/ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + if (!newVal) + { + ErrorMessage(tkINVALID_PARAMETER_VALUE); + } + else + { + ComHelper::SetRef(newVal, (IDispatch**)&_defaultDrawOpt); + } + return S_OK; +} + +// *********************************************************************** +// put_ReferenceToCategories +// *********************************************************************** void CShapefile::put_ReferenceToCategories(bool bNullReference) { - if (_categories == NULL) return; - CShapefileCategories* coCategories = static_cast(_categories); - if (!bNullReference) - coCategories->put_ParentShapefile(this); - else - coCategories->put_ParentShapefile(NULL); + if (_categories == nullptr) return; + auto* coCategories = dynamic_cast(_categories); + if (!bNullReference) + coCategories->put_ParentShapefile(this); + else + coCategories->put_ParentShapefile(nullptr); }; -/***********************************************************************/ -/* get/put_Categories -/***********************************************************************/ +// *********************************************************************** +// get/put_Categories +// *********************************************************************** STDMETHODIMP CShapefile::get_Categories(IShapefileCategories** pVal) { - *pVal = _categories; - if (_categories != NULL) - _categories->AddRef(); - return S_OK; + *pVal = _categories; + if (_categories != nullptr) + _categories->AddRef(); + return S_OK; } + STDMETHODIMP CShapefile::put_Categories(IShapefileCategories* newVal) { - if (ComHelper::SetRef((IDispatch*)newVal, (IDispatch**) &_categories, false)) - { - ((CShapefileCategories*)_categories)->put_ParentShapefile(this); - } - return S_OK; + if (ComHelper::SetRef((IDispatch*)newVal, (IDispatch**)&_categories, false)) + { + ((CShapefileCategories*)_categories)->put_ParentShapefile(this); + } + return S_OK; } #pragma endregion @@ -2102,33 +2121,34 @@ STDMETHODIMP CShapefile::put_Categories(IShapefileCategories* newVal) // ******************************************************************** STDMETHODIMP CShapefile::get_SelectionColor(OLE_COLOR* retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retval = _selectionColor; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *retval = _selectionColor; + return S_OK; } + STDMETHODIMP CShapefile::put_SelectionColor(OLE_COLOR newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - _selectionColor = newVal; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + _selectionColor = newVal; + return S_OK; } // ******************************************************************** // get_SelectionTransparency // ******************************************************************** -STDMETHODIMP CShapefile::get_SelectionTransparency (BYTE* retval) +STDMETHODIMP CShapefile::get_SelectionTransparency(BYTE* retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retval = _selectionTransparency; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *retval = _selectionTransparency; + return S_OK; } -STDMETHODIMP CShapefile::put_SelectionTransparency (BYTE newVal) + +STDMETHODIMP CShapefile::put_SelectionTransparency(BYTE newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - if (newVal > 255) newVal = 255; - if (newVal < 0) newVal = 0; - _selectionTransparency = newVal; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + if (newVal > 255) newVal = 255; + _selectionTransparency = newVal; + return S_OK; } // ******************************************************************** @@ -2136,15 +2156,16 @@ STDMETHODIMP CShapefile::put_SelectionTransparency (BYTE newVal) // ******************************************************************** STDMETHODIMP CShapefile::get_SelectionAppearance(tkSelectionAppearance* retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retval = _selectionAppearance; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *retval = _selectionAppearance; + return S_OK; } + STDMETHODIMP CShapefile::put_SelectionAppearance(tkSelectionAppearance newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - _selectionAppearance = newVal; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + _selectionAppearance = newVal; + return S_OK; } // ******************************************************************** @@ -2152,15 +2173,16 @@ STDMETHODIMP CShapefile::put_SelectionAppearance(tkSelectionAppearance newVal) // ******************************************************************** STDMETHODIMP CShapefile::get_CollisionMode(tkCollisionMode* retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retval = _collisionMode; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *retval = _collisionMode; + return S_OK; } + STDMETHODIMP CShapefile::put_CollisionMode(tkCollisionMode newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - _collisionMode = newVal; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + _collisionMode = newVal; + return S_OK; } #pragma region "Seialization" @@ -2169,166 +2191,182 @@ STDMETHODIMP CShapefile::put_CollisionMode(tkCollisionMode newVal) // ******************************************************** STDMETHODIMP CShapefile::Serialize(VARIANT_BOOL SaveSelection, BSTR* retVal) { - return Serialize2(SaveSelection, VARIANT_FALSE, retVal); + return Serialize2(SaveSelection, VARIANT_FALSE, retVal); } STDMETHODIMP CShapefile::Serialize2(VARIANT_BOOL SaveSelection, VARIANT_BOOL SerializeCategories, BSTR* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - CPLXMLNode* psTree = this->SerializeCore(VARIANT_TRUE, "ShapefileClass", SerializeCategories ? true : false); - Utility::SerializeAndDestroyXmlTree(psTree, retVal); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + CPLXMLNode* psTree = this->SerializeCore(VARIANT_TRUE, "ShapefileClass", SerializeCategories != 0); + Utility::SerializeAndDestroyXmlTree(psTree, retVal); + return S_OK; } // ******************************************************** // SerializeCore() // ******************************************************** - CPLXMLNode* CShapefile::SerializeCore(VARIANT_BOOL SaveSelection, CString ElementName, bool serializeCategories) -{ - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - USES_CONVERSION; - - CPLXMLNode* psTree = CPLCreateXMLNode( NULL, CXT_Element, ElementName ); - - if (psTree) - { - CString s = OLE2CA(_expression); - if (s != "") - Utility::CPLCreateXMLAttributeAndValue(psTree, "VisibilityExpression", s); - - - - if (_useQTree != FALSE) - Utility::CPLCreateXMLAttributeAndValue(psTree, "UseQTree", CPLString().Printf("%d", (int)_useQTree)); - - if (_collisionMode != LocalList ) - Utility::CPLCreateXMLAttributeAndValue(psTree, "CollisionMode", CPLString().Printf("%d", (int)_collisionMode)); - - if (_selectionAppearance != saSelectionColor) - Utility::CPLCreateXMLAttributeAndValue(psTree, "SelectionAppearance", CPLString().Printf("%d", (int)_selectionAppearance)); - - if (_selectionColor != RGB(255, 255, 0)) - Utility::CPLCreateXMLAttributeAndValue(psTree, "SelectionColor", CPLString().Printf("%d", (int)_selectionColor)); - - if (_selectionTransparency != 180) - Utility::CPLCreateXMLAttributeAndValue(psTree, "SelectionTransparency", CPLString().Printf("%d", (int)_selectionTransparency)); - - if (_minDrawingSize != 1) - Utility::CPLCreateXMLAttributeAndValue(psTree, "MinDrawingSize", CPLString().Printf("%d", _minDrawingSize)); - - // for in-memory shapefiles only - if (_sourceType == sstInMemory) - Utility::CPLCreateXMLAttributeAndValue(psTree, "ShpType", CPLString().Printf("%d", (int)this->_shpfiletype)); - - s = OLE2CA(_sortField); - if (s != "") - Utility::CPLCreateXMLAttributeAndValue(psTree, "SortField", s); - - if (_sortAscending != VARIANT_FALSE) - Utility::CPLCreateXMLAttributeAndValue(psTree, "SortAscending", CPLString().Printf("%d", (int)_sortAscending)); - - // drawing options - CPLXMLNode* node = ((CShapeDrawingOptions*)_defaultDrawOpt)->SerializeCore("DefaultDrawingOptions"); - if (node) - { - CPLAddXMLChild(psTree, node); - } - - if(_selectionAppearance == saDrawingOptions) - { - CPLXMLNode* node = ((CShapeDrawingOptions*)_selectDrawOpt)->SerializeCore("SelectionDrawingOptions"); - if (node) - { - CPLAddXMLChild(psTree, node); - } - } - - // categories - node = ((CShapefileCategories*)_categories)->SerializeCore("ShapefileCategoriesClass"); - if (node) - { - CPLAddXMLChild(psTree, node); - } - - // labels - CPLXMLNode* psLabels = ((CLabels*)_labels)->SerializeCore("LabelsClass"); - if (psLabels) - { - CPLAddXMLChild(psTree, psLabels); - } - - // charts - CPLXMLNode* psCharts = ((CCharts*)_charts)->SerializeCore("ChartsClass"); - if (psCharts) - { - CPLAddXMLChild(psTree, psCharts); - } - - // ---------------------------------------------------- - // selection - // ---------------------------------------------------- - long numSelected; - this->get_NumSelected(&numSelected); - - if (numSelected > 0 && SaveSelection) - { - char* selection = new char[_shapeData.size() + 1]; - selection[_shapeData.size()] = '\0'; - for (unsigned int i = 0; i < _shapeData.size(); i++) - { - selection[i] = _shapeData[i]->selected() ? '1' : '0'; - } - - CPLXMLNode* nodeSelection = CPLCreateXMLElementAndValue(psTree, "Selection", selection); - if (nodeSelection) - { - Utility::CPLCreateXMLAttributeAndValue(nodeSelection, "TotalCount", CPLString().Printf("%d", _shapeData.size())); - Utility::CPLCreateXMLAttributeAndValue(nodeSelection, "SelectedCount", CPLString().Printf("%d", numSelected)); - } - delete[] selection; - } - - // ---------------------------------------------------- - // serialization of category indices - // ---------------------------------------------------- - bool serializeCategories = false; - - for(size_t i = 0; i < _shapeData.size(); i++) - { - if (_shapeData[i]->category != -1) { - serializeCategories = true; - } - } - - if (serializeCategories) - { - s = ""; - // doing it with CString is ugly of course, better to allocate a buffer - CString temp; - for(size_t i = 0; i < _shapeData.size(); i++) { - temp.Format("%d,", _shapeData[i]->category); - s += temp; - } - - // when there are no indices assigned, write an empty node with Count = 0; - // to signal, that categories must not be applied automatically (behavior for older versions) - CPLXMLNode* nodeCats = CPLCreateXMLElementAndValue(psTree, "CategoryIndices", s.GetBuffer()); - if (nodeCats) { - Utility::CPLCreateXMLAttributeAndValue(nodeCats, "Count", CPLString().Printf("%d", serializeCategories ? _shapeData.size() : 0)); - } - } - - // ---------------------------------------------------- - // table - // ---------------------------------------------------- - if (_table) { - CPLXMLNode* psTable = ((CTableClass*)_table)->SerializeCore("TableClass"); - if (psTable) { - CPLAddXMLChild(psTree, psTable); - } - } - } - return psTree; +CPLXMLNode* CShapefile::SerializeCore(VARIANT_BOOL SaveSelection, CString ElementName, bool serializeCategories) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + USES_CONVERSION; + + CPLXMLNode* psTree = CPLCreateXMLNode(nullptr, CXT_Element, ElementName); + + if (psTree) + { + CString s = OLE2CA(_expression); + if (s != "") + Utility::CPLCreateXMLAttributeAndValue(psTree, "VisibilityExpression", s); + + + if (_useQTree != FALSE) + Utility::CPLCreateXMLAttributeAndValue(psTree, "UseQTree", CPLString().Printf("%d", (int)_useQTree)); + + if (_collisionMode != LocalList) + Utility::CPLCreateXMLAttributeAndValue(psTree, "CollisionMode", + CPLString().Printf("%d", (int)_collisionMode)); + + if (_selectionAppearance != saSelectionColor) + Utility::CPLCreateXMLAttributeAndValue(psTree, "SelectionAppearance", + CPLString().Printf("%d", (int)_selectionAppearance)); + + if (_selectionColor != RGB(255, 255, 0)) + Utility::CPLCreateXMLAttributeAndValue(psTree, "SelectionColor", + CPLString().Printf("%d", (int)_selectionColor)); + + if (_selectionTransparency != 180) + Utility::CPLCreateXMLAttributeAndValue(psTree, "SelectionTransparency", + CPLString().Printf("%d", (int)_selectionTransparency)); + + if (_minDrawingSize != 1) + Utility::CPLCreateXMLAttributeAndValue(psTree, "MinDrawingSize", CPLString().Printf("%d", _minDrawingSize)); + + // for in-memory shapefiles only + if (_sourceType == sstInMemory) + Utility::CPLCreateXMLAttributeAndValue(psTree, "ShpType", + CPLString().Printf("%d", (int)this->_shpfiletype)); + + s = OLE2CA(_sortField); + if (s != "") + Utility::CPLCreateXMLAttributeAndValue(psTree, "SortField", s); + + if (_sortAscending != VARIANT_FALSE) + Utility::CPLCreateXMLAttributeAndValue(psTree, "SortAscending", + CPLString().Printf("%d", (int)_sortAscending)); + + // drawing options + CPLXMLNode* node = ((CShapeDrawingOptions*)_defaultDrawOpt)->SerializeCore("DefaultDrawingOptions"); + if (node) + { + CPLAddXMLChild(psTree, node); + } + + if (_selectionAppearance == saDrawingOptions) + { + node = ((CShapeDrawingOptions*)_selectDrawOpt)->SerializeCore("SelectionDrawingOptions"); + if (node) + { + CPLAddXMLChild(psTree, node); + } + } + + // categories + node = ((CShapefileCategories*)_categories)->SerializeCore("ShapefileCategoriesClass"); + if (node) + { + CPLAddXMLChild(psTree, node); + } + + // labels + CPLXMLNode* psLabels = ((CLabels*)_labels)->SerializeCore("LabelsClass"); + if (psLabels) + { + CPLAddXMLChild(psTree, psLabels); + } + + // charts + CPLXMLNode* psCharts = ((CCharts*)_charts)->SerializeCore("ChartsClass"); + if (psCharts) + { + CPLAddXMLChild(psTree, psCharts); + } + + // ---------------------------------------------------- + // selection + // ---------------------------------------------------- + long numSelected; + this->get_NumSelected(&numSelected); + + if (numSelected > 0 && SaveSelection) + { + auto* selection = new char[_shapeData.size() + 1]; + selection[_shapeData.size()] = '\0'; + for (unsigned int i = 0; i < _shapeData.size(); i++) + { + selection[i] = _shapeData[i]->selected() ? '1' : '0'; + } + + CPLXMLNode* nodeSelection = CPLCreateXMLElementAndValue(psTree, "Selection", selection); + if (nodeSelection) + { + Utility::CPLCreateXMLAttributeAndValue(nodeSelection, "TotalCount", + CPLString().Printf("%d", _shapeData.size())); + Utility::CPLCreateXMLAttributeAndValue(nodeSelection, "SelectedCount", + CPLString().Printf("%d", numSelected)); + } + delete[] selection; + } + + // ---------------------------------------------------- + // serialization of category indices + // ---------------------------------------------------- + // Paul Meems TODO: This variable comes in as a parameter as well. + // Is this correct? + bool serializeCategories = false; + + for (auto& i : _shapeData) + { + if (i->category != -1) + { + serializeCategories = true; + } + } + + if (serializeCategories) + { + s = ""; + // doing it with CString is ugly of course, better to allocate a buffer + CString temp; + for (auto& i : _shapeData) + { + temp.Format("%d,", i->category); + s += temp; + } + + // when there are no indices assigned, write an empty node with Count = 0; + // to signal, that categories must not be applied automatically (behavior for older versions) + CPLXMLNode* nodeCats = CPLCreateXMLElementAndValue(psTree, "CategoryIndices", s.GetBuffer()); + if (nodeCats) + { + Utility::CPLCreateXMLAttributeAndValue(nodeCats, "Count", + CPLString().Printf( + "%d", serializeCategories ? _shapeData.size() : 0)); + } + } + + // ---------------------------------------------------- + // table + // ---------------------------------------------------- + if (_table) + { + CPLXMLNode* psTable = ((CTableClass*)_table)->SerializeCore("TableClass"); + if (psTable) + { + CPLAddXMLChild(psTree, psTable); + } + } + } + return psTree; } // ******************************************************** @@ -2336,21 +2374,21 @@ STDMETHODIMP CShapefile::Serialize2(VARIANT_BOOL SaveSelection, VARIANT_BOOL Ser // ******************************************************** STDMETHODIMP CShapefile::Deserialize(VARIANT_BOOL LoadSelection, BSTR newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - USES_CONVERSION; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + USES_CONVERSION; - CString s = OLE2CA(newVal); - CPLXMLNode* node = CPLParseXMLString(s.GetString()); - if (node) - { - CPLXMLNode* nodeSf = CPLGetXMLNode(node, "=ShapefileClass"); - if (nodeSf) - { - this->DeserializeCore(VARIANT_TRUE, nodeSf); - } - CPLDestroyXMLNode(node); - } - return S_OK; + CString s = OLE2CA(newVal); + CPLXMLNode* node = CPLParseXMLString(s.GetString()); + if (node) + { + CPLXMLNode* nodeSf = CPLGetXMLNode(node, "=ShapefileClass"); + if (nodeSf) + { + this->DeserializeCore(VARIANT_TRUE, nodeSf); + } + CPLDestroyXMLNode(node); + } + return S_OK; } // ******************************************************** @@ -2358,173 +2396,173 @@ STDMETHODIMP CShapefile::Deserialize(VARIANT_BOOL LoadSelection, BSTR newVal) // ******************************************************** bool CShapefile::DeserializeCore(VARIANT_BOOL LoadSelection, CPLXMLNode* node) { - USES_CONVERSION; - - if (!node ) - return false; - - CString s; - s = CPLGetXMLValue( node, "VisibilityExpression", NULL ); - SysFreeString(_expression); - _expression = A2BSTR(s); - - s = CPLGetXMLValue( node, "UseQTree", NULL ); - _useQTree = (s != "") ? (BOOL)atoi(s.GetString()) : FALSE; - - s = CPLGetXMLValue( node, "CollisionMode", NULL ); - _collisionMode = (s != "") ? (tkCollisionMode)atoi(s.GetString()) : LocalList; - - s = CPLGetXMLValue( node, "SelectionAppearance", NULL ); - _selectionAppearance = (s != "") ? (tkSelectionAppearance)atoi(s.GetString()): saSelectionColor; - - s = CPLGetXMLValue( node, "SelectionColor", NULL ); - _selectionColor = (s != "") ? (OLE_COLOR)atoi(s.GetString()) : RGB(255, 255, 0); - - s = CPLGetXMLValue( node, "SelectionTransparency", NULL ); - _selectionTransparency = (s != "") ? (unsigned char)atoi(s.GetString()) : 180; - - s = CPLGetXMLValue( node, "MinDrawingSize", NULL ); - _minDrawingSize = (s != "") ? atoi(s.GetString()) : 1; - - s = CPLGetXMLValue(node, "SortField", NULL); - CComBSTR bstrSortField = A2W(s); - this->put_SortField(bstrSortField); - - s = CPLGetXMLValue(node, "SortAscending", NULL); - VARIANT_BOOL sortAsc = (s != "") ? (VARIANT_BOOL)atoi(s.GetString()) : VARIANT_FALSE; - this->put_SortAscending(sortAsc); - - if (_sourceType == sstInMemory) - { - s = CPLGetXMLValue( node, "ShpType", NULL ); - if (s != "") { - _shpfiletype = (ShpfileType)atoi(s.GetString()); - } - } - - // drawing options - CPLXMLNode* psChild = CPLGetXMLNode(node, "DefaultDrawingOptions"); - if (psChild) { - ((CShapeDrawingOptions*)_defaultDrawOpt)->DeserializeCore(psChild); - } - - if (_selectionAppearance == saDrawingOptions) - { - CPLXMLNode* psChild = CPLGetXMLNode(node, "SelectionDrawingOptions"); - if (psChild) { - ((CShapeDrawingOptions*)_selectDrawOpt)->DeserializeCore(psChild); - } - } - - // Categories - psChild = CPLGetXMLNode(node, "ShapefileCategoriesClass"); - if (psChild) - { - ((CShapefileCategories*)_categories)->DeserializeCore(psChild, false); - } - - // category indices - bool hasIndices = false; - CPLXMLNode* nodeCats = CPLGetXMLNode(node, "CategoryIndices"); - - if (nodeCats) - { - CString indices = CPLGetXMLValue(nodeCats, "=CategoryIndices", ""); - if (indices.GetLength() > 0) - { - s = CPLGetXMLValue(nodeCats, "Count", "0"); - long savedCount = atoi(s); - int foundCount = 0; - char* buffer = indices.GetBuffer(); - for (int i = 0; i < indices.GetLength(); i++) { - if (buffer[i] == ',') { - foundCount++; - } - } - - if (foundCount == savedCount && foundCount == _shapeData.size()) - { - int size = _shapeData.size(); - int pos = 0, count = 0; - CString ct; - ct = indices.Tokenize(",", pos); - while (ct.GetLength() != 0 && count < size) - { - _shapeData[count]->category = atoi(ct); - ct = indices.Tokenize(",", pos); - count++; - }; - hasIndices = true; - } - } - } - else - { - // for older versions of file without indices apply previously loaded cats - ((CShapefileCategories*)_categories)->ApplyExpressions(); - } - - // Labels - psChild = CPLGetXMLNode(node, "LabelsClass"); - if (psChild) - { - ((CLabels*)_labels)->DeserializeCore(psChild); - } - - // Charts - psChild = CPLGetXMLNode(node, "ChartsClass"); - if (psChild) - { - ((CCharts*)_charts)->DeserializeCore(psChild); - } - - // selection - CPLXMLNode* nodeSelection = CPLGetXMLNode(node, "Selection"); - if (nodeSelection && LoadSelection) - { - this->SelectNone(); - - s = CPLGetXMLValue(nodeSelection, "TotalCount", "0"); - long count = atoi(s); - s = CPLGetXMLValue(nodeSelection, "=Selection", ""); - if (s.GetLength() == count && s.GetLength() == _shapeData.size()) - { - char* selection = s.GetBuffer(); - for (unsigned int i = 0; i < _shapeData.size(); i++) - { - if (selection[i] == '1') - { - _shapeData[i]->selected(true); - } - } - } - } - - - // table - if (_table) - { - psChild = CPLGetXMLNode(node, "TableClass"); - if (psChild) - { - ((CTableClass*)_table)->DeserializeCore(psChild); - } - } - return true; + USES_CONVERSION; + + if (!node) + return false; + + CString s = CPLGetXMLValue(node, "VisibilityExpression", nullptr); + SysFreeString(_expression); + _expression = A2BSTR(s); + + s = CPLGetXMLValue(node, "UseQTree", nullptr); + _useQTree = s != "" ? (BOOL)atoi(s.GetString()) : FALSE; + + s = CPLGetXMLValue(node, "CollisionMode", nullptr); + _collisionMode = s != "" ? (tkCollisionMode)atoi(s.GetString()) : LocalList; + + s = CPLGetXMLValue(node, "SelectionAppearance", nullptr); + _selectionAppearance = s != "" ? (tkSelectionAppearance)atoi(s.GetString()) : saSelectionColor; + + s = CPLGetXMLValue(node, "SelectionColor", nullptr); + _selectionColor = s != "" ? (OLE_COLOR)atoi(s.GetString()) : RGB(255, 255, 0); + + s = CPLGetXMLValue(node, "SelectionTransparency", nullptr); + _selectionTransparency = s != "" ? (unsigned char)atoi(s.GetString()) : 180; + + s = CPLGetXMLValue(node, "MinDrawingSize", nullptr); + _minDrawingSize = s != "" ? atoi(s.GetString()) : 1; + + s = CPLGetXMLValue(node, "SortField", nullptr); + const CComBSTR bstrSortField = A2W(s); + this->put_SortField(bstrSortField); + + s = CPLGetXMLValue(node, "SortAscending", nullptr); + const VARIANT_BOOL sortAsc = s != "" ? (VARIANT_BOOL)atoi(s.GetString()) : VARIANT_FALSE; + this->put_SortAscending(sortAsc); + + if (_sourceType == sstInMemory) + { + s = CPLGetXMLValue(node, "ShpType", nullptr); + if (s != "") + { + _shpfiletype = (ShpfileType)atoi(s.GetString()); + } + } + + // drawing options + CPLXMLNode* psChild = CPLGetXMLNode(node, "DefaultDrawingOptions"); + if (psChild) + { + ((CShapeDrawingOptions*)_defaultDrawOpt)->DeserializeCore(psChild); + } + + if (_selectionAppearance == saDrawingOptions) + { + psChild = CPLGetXMLNode(node, "SelectionDrawingOptions"); + if (psChild) + { + ((CShapeDrawingOptions*)_selectDrawOpt)->DeserializeCore(psChild); + } + } + + // Categories + psChild = CPLGetXMLNode(node, "ShapefileCategoriesClass"); + if (psChild) + { + ((CShapefileCategories*)_categories)->DeserializeCore(psChild, false); + } + + CPLXMLNode* nodeCats = CPLGetXMLNode(node, "CategoryIndices"); + + if (nodeCats) + { + CString indices = CPLGetXMLValue(nodeCats, "=CategoryIndices", ""); + if (indices.GetLength() > 0) + { + s = CPLGetXMLValue(nodeCats, "Count", "0"); + const long savedCount = atoi(s); + int foundCount = 0; + char* buffer = indices.GetBuffer(); + for (int i = 0; i < indices.GetLength(); i++) + { + if (buffer[i] == ',') + { + foundCount++; + } + } + + if (foundCount == savedCount && foundCount == _shapeData.size()) + { + const int size = _shapeData.size(); + int pos = 0, count = 0; + CString ct = indices.Tokenize(",", pos); + while (ct.GetLength() != 0 && count < size) + { + _shapeData[count]->category = atoi(ct); + ct = indices.Tokenize(",", pos); + count++; + }; + // bool hasIndices = true; + } + } + } + else + { + // for older versions of file without indices apply previously loaded cats + ((CShapefileCategories*)_categories)->ApplyExpressions(); + } + + // Labels + psChild = CPLGetXMLNode(node, "LabelsClass"); + if (psChild) + { + ((CLabels*)_labels)->DeserializeCore(psChild); + } + + // Charts + psChild = CPLGetXMLNode(node, "ChartsClass"); + if (psChild) + { + ((CCharts*)_charts)->DeserializeCore(psChild); + } + + // selection + CPLXMLNode* nodeSelection = CPLGetXMLNode(node, "Selection"); + if (nodeSelection && LoadSelection) + { + this->SelectNone(); + + s = CPLGetXMLValue(nodeSelection, "TotalCount", "0"); + const long count = atoi(s); + s = CPLGetXMLValue(nodeSelection, "=Selection", ""); + if (s.GetLength() == count && s.GetLength() == _shapeData.size()) + { + char* selection = s.GetBuffer(); + for (unsigned int i = 0; i < _shapeData.size(); i++) + { + if (selection[i] == '1') + { + _shapeData[i]->selected(true); + } + } + } + } + + + // table + if (_table) + { + psChild = CPLGetXMLNode(node, "TableClass"); + if (psChild) + { + ((CTableClass*)_table)->DeserializeCore(psChild); + } + } + return true; } #pragma endregion - #pragma region Projection // ***************************************************************** // get_Projection() // ***************************************************************** -STDMETHODIMP CShapefile::get_Projection(BSTR *pVal) +STDMETHODIMP CShapefile::get_Projection(BSTR* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - _geoProjection->ExportToProj4(pVal); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + _geoProjection->ExportToProj4(pVal); + return S_OK; } // ***************************************************************** @@ -2532,17 +2570,17 @@ STDMETHODIMP CShapefile::get_Projection(BSTR *pVal) // ***************************************************************** STDMETHODIMP CShapefile::put_Projection(BSTR proj4Projection) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - USES_CONVERSION; - - VARIANT_BOOL vbretval; - _geoProjection->ImportFromProj4(proj4Projection, &vbretval); - if (vbretval) - { - CComBSTR bstrFilename(_prjfileName); - _geoProjection->WriteToFileEx(bstrFilename, VARIANT_TRUE, &vbretval); - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + USES_CONVERSION; + + VARIANT_BOOL vbretval; + _geoProjection->ImportFromProj4(proj4Projection, &vbretval); + if (vbretval) + { + const CComBSTR bstrFilename(_prjfileName); + _geoProjection->WriteToFileEx(bstrFilename, VARIANT_TRUE, &vbretval); + } + return S_OK; } // ***************************************************************** @@ -2550,12 +2588,12 @@ STDMETHODIMP CShapefile::put_Projection(BSTR proj4Projection) // ***************************************************************** STDMETHODIMP CShapefile::get_GeoProjection(IGeoProjection** retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - if (_geoProjection) - _geoProjection->AddRef(); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + if (_geoProjection) + _geoProjection->AddRef(); - *retVal = _geoProjection; - return S_OK; + *retVal = _geoProjection; + return S_OK; } // ***************************************************************** @@ -2563,15 +2601,30 @@ STDMETHODIMP CShapefile::get_GeoProjection(IGeoProjection** retVal) // ***************************************************************** STDMETHODIMP CShapefile::put_GeoProjection(IGeoProjection* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - ComHelper::SetRef((IDispatch*)pVal, (IDispatch**)&_geoProjection, false); - if (_prjfileName.GetLength() != 0) - { - VARIANT_BOOL vbretval; - CComBSTR bstr(_prjfileName); - _geoProjection->WriteToFileEx(bstr, VARIANT_TRUE, &vbretval); - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + ComHelper::SetRef((IDispatch*)pVal, (IDispatch**)&_geoProjection, false); + if (_prjfileName.GetLength() != 0) + { + VARIANT_BOOL vbretval; + const CComBSTR bstr(_prjfileName); + _geoProjection->WriteToFileEx(bstr, VARIANT_TRUE, &vbretval); + } + return S_OK; +} + +// **************************************************************** +// get_IsGeographicProjection +// **************************************************************** +STDMETHODIMP CShapefile::get_IsGeographicProjection(VARIANT_BOOL* pVal) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + *pVal = VARIANT_FALSE; + + if (_geoProjection) + _geoProjection->get_IsGeographic(pVal); + + return S_OK; } // ***************************************************************** @@ -2579,11 +2632,11 @@ STDMETHODIMP CShapefile::put_GeoProjection(IGeoProjection* pVal) // ***************************************************************** STDMETHODIMP CShapefile::Reproject(IGeoProjection* newProjection, LONG* reprojectedCount, IShapefile** retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - - if (!this->ReprojectCore(newProjection, reprojectedCount, retVal, false)) - *retVal = NULL; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + if (!this->ReprojectCore(newProjection, reprojectedCount, retVal, false)) + *retVal = nullptr; + return S_OK; } // ***************************************************************** @@ -2591,200 +2644,204 @@ STDMETHODIMP CShapefile::Reproject(IGeoProjection* newProjection, LONG* reprojec // ***************************************************************** STDMETHODIMP CShapefile::ReprojectInPlace(IGeoProjection* newProjection, LONG* reprojectedCount, VARIANT_BOOL* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - if (!_isEditingShapes) - { - ErrorMessage(tkSHPFILE_NOT_IN_EDIT_MODE); - *retVal = VARIANT_FALSE; - } - else - { - if (this->ReprojectCore(newProjection, reprojectedCount, NULL, true)) - { - // spatial index must be deleted, as it became useful all the same - VARIANT_BOOL vb; - RemoveSpatialIndex(&vb); - - // update qtree - if (_useQTree) - GenerateQTree(); - - VARIANT_BOOL vbretval; - this->RefreshExtents(&vbretval); - *retVal = VARIANT_TRUE; - return S_OK; - } - *retVal = NULL; - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + if (!_isEditingShapes) + { + ErrorMessage(tkSHPFILE_NOT_IN_EDIT_MODE); + *retVal = VARIANT_FALSE; + } + else + { + if (this->ReprojectCore(newProjection, reprojectedCount, nullptr, true)) + { + // spatial index must be deleted, as it became useful all the same + VARIANT_BOOL vb; + RemoveSpatialIndex(&vb); + + // update qtree + if (_useQTree) + GenerateQTree(); + + VARIANT_BOOL vbretval; + this->RefreshExtents(&vbretval); + *retVal = VARIANT_TRUE; + return S_OK; + } + *retVal = NULL; + } + return S_OK; } // ***************************************************************** // ReprojectCore() // ***************************************************************** -bool CShapefile::ReprojectCore(IGeoProjection* newProjection, LONG* reprojectedCount, IShapefile** retVal, bool reprojectInPlace) -{ - // ------------------------------------------------------ - // Validation - // ------------------------------------------------------ - if (!newProjection) - { - ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); - return false; - } - - VARIANT_BOOL isEmpty1, isEmpty2; - newProjection->get_IsEmpty(&isEmpty1); - _geoProjection->get_IsEmpty(&isEmpty2); - if (isEmpty1 || isEmpty2) - { - ErrorMessage(tkPROJECTION_NOT_INITIALIZED); - return false; - } - - if (!ValidateInput(this, "Reproject/ReprojectInPlace", "this", VARIANT_FALSE)) - return false; - - m_globalSettings.gdalErrorMessage = ""; - OGRSpatialReference* projSource = ((CGeoProjection*)_geoProjection)->get_SpatialReference(); - OGRSpatialReference* projTarget = ((CGeoProjection*)newProjection)->get_SpatialReference(); - - OGRCoordinateTransformation* transf = OGRCreateCoordinateTransformation( projSource, projTarget ); - if (!transf) - { - m_globalSettings.gdalErrorMessage = CPLGetLastErrorMsg(); - ErrorMessage(tkFAILED_TO_REPROJECT); - return false; - } - - // ------------------------------------------------------ - // Creating output - // ------------------------------------------------------ - if (!reprojectInPlace) - this->Clone(retVal); - - // ------------------------------------------------------ - // Processing - // ------------------------------------------------------ - CComVariant var; - long numShapes = _shapeData.size(); - long count = 0; - - long numFields, percent = 0; - this->get_NumFields(&numFields); - - VARIANT_BOOL vb = VARIANT_FALSE; - *reprojectedCount = 0; - - for (long i = 0; i < numShapes; i++) - { - CallbackHelper::Progress(_globalCallback, i, numShapes, "Reprojecting...", _key, percent); - - IShape* shp = NULL; - this->GetValidatedShape(i, &shp); - if (!shp) continue; - - if (!reprojectInPlace) - { - IShape* shpNew = NULL; - shp->Clone(&shpNew); - shp->Release(); - shp = shpNew; - } - - if (shp) - { - long numPoints; - shp->get_NumPoints(&numPoints); - - if(numPoints > 0) - { - double* x = new double[numPoints]; - double* y = new double[numPoints]; - - // extracting coordinates - for (long j = 0; j < numPoints; j++) - { - shp->get_XY(j, x + j, y + j, &vb); - } - - // will work faster after embedding to the CShape class - BOOL res = transf->Transform( numPoints, x, y); - if (!res) - { - if (m_globalSettings.gdalErrorMessage == "") - m_globalSettings.gdalErrorMessage = CPLGetLastErrorMsg(); - } - else - { - // saving updated coordinates - for (long j = 0; j < numPoints; j++) - { - shp->put_XY(j, x[j], y[j], &vb); - } - - if (!reprojectInPlace) - { - (*retVal)->get_NumShapes(&count); - (*retVal)->EditInsertShape(shp, &count, &vb); - - // copying attributes - for (long j = 0; j < numFields; j++) - { - this->get_CellValue(j, i, &var); - (*retVal)->EditCellValue(j, i, var, &vb); - } - } - (*reprojectedCount)++; - } - delete[] x; delete[] y; - } - shp->Release(); - } - } - - if (transf) - { - OGRCoordinateTransformation::DestroyCT(transf); - transf = NULL; - } - - // When creating the new shapefile was successfull: - if (vb) - { - // setting new projection - if (reprojectInPlace) - { - _geoProjection->CopyFrom(newProjection, &vb); - } - else - { - IGeoProjection* proj = NULL; - (*retVal)->get_GeoProjection(&proj); - if (proj) - { - proj->CopyFrom(newProjection, &vb); - proj->Release(); - } - } - } - - ShapefileHelper::ClearShapefileModifiedFlag((*retVal)); // inserted shapes were marked as modified, correct this - - // -------------------------------------- - // Output validation - // -------------------------------------- - CallbackHelper::ProgressCompleted(_globalCallback, _key); - - if (!reprojectInPlace) { - this->ValidateOutput(retVal, "Reproject/ReprojectInPlace", "Shapefile", false); - } - else { - this->ValidateOutput(this, "Reproject/ReprojectInPlace", "Shapefile", false); - } - - // it's critical to set correct projection, so false will be returned if it wasn't done - return vb ? true : false; +bool CShapefile::ReprojectCore(IGeoProjection* newProjection, LONG* reprojectedCount, IShapefile** retVal, + bool reprojectInPlace) +{ + // ------------------------------------------------------ + // Validation + // ------------------------------------------------------ + if (!newProjection) + { + ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); + return false; + } + + VARIANT_BOOL isEmpty1, isEmpty2; + newProjection->get_IsEmpty(&isEmpty1); + _geoProjection->get_IsEmpty(&isEmpty2); + if (isEmpty1 || isEmpty2) + { + ErrorMessage(tkPROJECTION_NOT_INITIALIZED); + return false; + } + + if (!ValidateInput(this, "Reproject/ReprojectInPlace", "this", VARIANT_FALSE)) + return false; + + m_globalSettings.gdalErrorMessage = ""; + OGRSpatialReference* projSource = ((CGeoProjection*)_geoProjection)->get_SpatialReference(); + OGRSpatialReference* projTarget = ((CGeoProjection*)newProjection)->get_SpatialReference(); + + OGRCoordinateTransformation* transf = OGRCreateCoordinateTransformation(projSource, projTarget); + if (!transf) + { + m_globalSettings.gdalErrorMessage = CPLGetLastErrorMsg(); + ErrorMessage(tkFAILED_TO_REPROJECT); + return false; + } + + // ------------------------------------------------------ + // Creating output + // ------------------------------------------------------ + if (!reprojectInPlace) + this->Clone(retVal); + + // ------------------------------------------------------ + // Processing + // ------------------------------------------------------ + CComVariant var; + const long numShapes = _shapeData.size(); + long count = 0; + + long numFields, percent = 0; + this->get_NumFields(&numFields); + + VARIANT_BOOL vb = VARIANT_FALSE; + *reprojectedCount = 0; + + for (long i = 0; i < numShapes; i++) + { + CallbackHelper::Progress(_globalCallback, i, numShapes, "Reprojecting...", _key, percent); + + IShape* shp = nullptr; + this->GetValidatedShape(i, &shp); + if (!shp) continue; + + if (!reprojectInPlace) + { + IShape* shpNew = nullptr; + shp->Clone(&shpNew); + shp->Release(); + shp = shpNew; + } + + if (shp) + { + long numPoints; + shp->get_NumPoints(&numPoints); + + if (numPoints > 0) + { + auto* x = new double[numPoints]; + auto* y = new double[numPoints]; + + // extracting coordinates + for (long j = 0; j < numPoints; j++) + { + shp->get_XY(j, x + j, y + j, &vb); + } + + // will work faster after embedding to the CShape class + const BOOL res = transf->Transform(numPoints, x, y); + if (!res) + { + if (m_globalSettings.gdalErrorMessage == "") + m_globalSettings.gdalErrorMessage = CPLGetLastErrorMsg(); + } + else + { + // saving updated coordinates + for (long j = 0; j < numPoints; j++) + { + shp->put_XY(j, x[j], y[j], &vb); + } + + if (!reprojectInPlace) + { + (*retVal)->get_NumShapes(&count); + (*retVal)->EditInsertShape(shp, &count, &vb); + + // copying attributes + for (long j = 0; j < numFields; j++) + { + this->get_CellValue(j, i, &var); + (*retVal)->EditCellValue(j, i, var, &vb); + } + } + (*reprojectedCount)++; + } + delete[] x; + delete[] y; + } + shp->Release(); + } + } + + if (transf) + { + OGRCoordinateTransformation::DestroyCT(transf); + transf = nullptr; + } + + // When creating the new shapefile was successfull: + if (vb) + { + // setting new projection + if (reprojectInPlace) + { + _geoProjection->CopyFrom(newProjection, &vb); + } + else + { + IGeoProjection* proj = nullptr; + (*retVal)->get_GeoProjection(&proj); + if (proj) + { + proj->CopyFrom(newProjection, &vb); + proj->Release(); + } + } + } + + ShapefileHelper::ClearShapefileModifiedFlag(*retVal); // inserted shapes were marked as modified, correct this + + // -------------------------------------- + // Output validation + // -------------------------------------- + CallbackHelper::ProgressCompleted(_globalCallback, _key); + + if (!reprojectInPlace) + { + this->ValidateOutput(retVal, "Reproject/ReprojectInPlace", "Shapefile", false); + } + else + { + this->ValidateOutput(this, "Reproject/ReprojectInPlace", "Shapefile", false); + } + + // it's critical to set correct projection, so false will be returned if it wasn't done + return vb != 0; } #pragma endregion @@ -2793,12 +2850,12 @@ bool CShapefile::ReprojectCore(IGeoProjection* newProjection, LONG* reprojectedC // ***************************************************************** STDMETHODIMP CShapefile::FixUpShapes(IShapefile** retVal, VARIANT_BOOL* fixed) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - - // MWGIS-90: default to all shapes: - FixUpShapes2(VARIANT_FALSE, retVal, fixed); - - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + // MWGIS-90: default to all shapes: + FixUpShapes2(VARIANT_FALSE, retVal, fixed); + + return S_OK; } // ********************************************************* @@ -2806,17 +2863,18 @@ STDMETHODIMP CShapefile::FixUpShapes(IShapefile** retVal, VARIANT_BOOL* fixed) // ********************************************************* STDMETHODIMP CShapefile::FixUpShapes2(VARIANT_BOOL SelectedOnly, IShapefile** result, VARIANT_BOOL* fixed) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *fixed = VARIANT_FALSE; + *fixed = VARIANT_FALSE; - if (*result == NULL) { - Clone(result); - } + if (*result == nullptr) + { + Clone(result); + } - *fixed = FixupShapesCore(SelectedOnly, *result); + *fixed = FixupShapesCore(SelectedOnly, *result); - return S_OK; + return S_OK; } // ********************************************************* @@ -2824,180 +2882,208 @@ STDMETHODIMP CShapefile::FixUpShapes2(VARIANT_BOOL SelectedOnly, IShapefile** re // ********************************************************* VARIANT_BOOL CShapefile::FixupShapesCore(VARIANT_BOOL selectedOnly, IShapefile* result) { - if (!result) return VARIANT_FALSE; + if (!result) return VARIANT_FALSE; - tkUnitsOfMeasure units; - _geoProjection->get_LinearUnits(&units); + tkUnitsOfMeasure units; + _geoProjection->get_LinearUnits(&units); - long numFields; - this->get_NumFields(&numFields); + long numFields; + this->get_NumFields(&numFields); - long percent = 0; - int numShapes = _shapeData.size(); - VARIANT_BOOL fixed = VARIANT_TRUE; + long percent = 0; + const int numShapes = _shapeData.size(); + // VARIANT_BOOL fixed = VARIANT_TRUE; - for (int i = 0; i < numShapes; i++) - { - CallbackHelper::Progress(_globalCallback, i, numShapes, "Fixing...", _key, percent); + for (int i = 0; i < numShapes; i++) + { + CallbackHelper::Progress(_globalCallback, i, numShapes, "Fixing...", _key, percent); - if (!ShapeAvailable(i, selectedOnly)) - continue; + if (!ShapeAvailable(i, selectedOnly)) + continue; - IShape* shp = NULL; - get_Shape(i, &shp); - if (!shp) { - continue; - } + IShape* shp = nullptr; + get_Shape(i, &shp); + if (!shp) + { + continue; + } - IShape* shpNew = NULL; - shp->FixUp2(units, &shpNew); - shp->Release(); + IShape* shpNew = nullptr; + shp->FixUp2(units, &shpNew); + shp->Release(); - // failed to fix the shape? skip it. - if (!shpNew) { - CString s; - s.Format("Failed to fix shape: %d", i); - CallbackHelper::ErrorMsg("Shapefile", NULL, "", s); - continue; - } + // failed to fix the shape? skip it. + if (!shpNew) + { + CString s; + s.Format("Failed to fix shape: %d", i); + CallbackHelper::ErrorMsg("Shapefile", nullptr, "", s); + continue; + } - long shapeIndex = 0; - result->get_NumShapes(&shapeIndex); + long shapeIndex = 0; + result->get_NumShapes(&shapeIndex); - VARIANT_BOOL vbretval = VARIANT_FALSE; - result->EditInsertShape(shpNew, &shapeIndex, &vbretval); - shpNew->Release(); + VARIANT_BOOL vbretval = VARIANT_FALSE; + result->EditInsertShape(shpNew, &shapeIndex, &vbretval); + shpNew->Release(); - if (vbretval) - { - // TODO: extract, it's definitely used in other methods as well - CComVariant var; - for (int iFld = 0; iFld < numFields; iFld++) - { - get_CellValue(iFld, i, &var); - result->EditCellValue(iFld, shapeIndex, var, &vbretval); - } - } - } + if (vbretval) + { + // TODO: extract, it's definitely used in other methods as well + CComVariant var; + for (int iFld = 0; iFld < numFields; iFld++) + { + get_CellValue(iFld, i, &var); + result->EditCellValue(iFld, shapeIndex, var, &vbretval); + } + } + } - CallbackHelper::ProgressCompleted(_globalCallback, _key); + CallbackHelper::ProgressCompleted(_globalCallback, _key); - return VARIANT_TRUE; + return VARIANT_TRUE; } // ********************************************************* // GetRelatedShapes() // ********************************************************* -STDMETHODIMP CShapefile::GetRelatedShapes(long referenceIndex, tkSpatialRelation relation, VARIANT* resultArray, VARIANT_BOOL* retval) -{ - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retval = VARIANT_FALSE; - if (referenceIndex < 0 || referenceIndex > (long)_shapeData.size()) - { - this->ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - return S_OK; - } - - IShape* shp = NULL; - this->get_Shape(referenceIndex, &shp); - if (shp) - { - this->GetRelatedShapeCore(shp, referenceIndex, relation, resultArray, retval); - shp->Release(); - } - return S_OK; +STDMETHODIMP CShapefile::GetRelatedShapes(long referenceIndex, tkSpatialRelation relation, VARIANT* resultArray, + VARIANT_BOOL* retval) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *retval = VARIANT_FALSE; + if (referenceIndex < 0 || referenceIndex > (long)_shapeData.size()) + { + this->ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + return S_OK; + } + + IShape* shp = nullptr; + this->get_Shape(referenceIndex, &shp); + if (shp) + { + this->GetRelatedShapeCore(shp, referenceIndex, relation, resultArray, retval); + shp->Release(); + } + return S_OK; } // ********************************************************* // GetRelatedShapes2() // ********************************************************* -STDMETHODIMP CShapefile::GetRelatedShapes2(IShape* referenceShape, tkSpatialRelation relation, VARIANT* resultArray, VARIANT_BOOL* retval) +STDMETHODIMP CShapefile::GetRelatedShapes2(IShape* referenceShape, tkSpatialRelation relation, VARIANT* resultArray, + VARIANT_BOOL* retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retval = VARIANT_FALSE; - if (!referenceShape) - { - this->ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); - return S_OK; - } - - this->GetRelatedShapeCore(referenceShape, -1, relation, resultArray, retval); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *retval = VARIANT_FALSE; + if (!referenceShape) + { + this->ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); + return S_OK; + } + + this->GetRelatedShapeCore(referenceShape, -1, relation, resultArray, retval); + return S_OK; } // ********************************************************* // GetRelatedShapeCore() // ********************************************************* -void CShapefile::GetRelatedShapeCore(IShape* referenceShape, long referenceIndex, tkSpatialRelation relation, VARIANT* resultArray, VARIANT_BOOL* retval) -{ - if (relation == srDisjoint) - { - // TODO: implement - ErrorMessage(tkMETHOD_NOT_IMPLEMENTED); - return; - } - - this->ReadGeosGeometries(VARIANT_TRUE); - - // turns on the quad tree - VARIANT_BOOL useQTree = VARIANT_FALSE; - this->get_UseQTree(&useQTree); - if (!useQTree) this->put_UseQTree(VARIANT_TRUE); - - double xMin, xMax, yMin, yMax; - if(((CShape*)referenceShape)->get_ExtentsXY(xMin, yMin, xMax, yMax)) - { - QTreeExtent query(xMin, xMax, yMax, yMin); - std::vector shapes = this->_qtree->GetNodes(query); - std::vector arr; - - GEOSGeom geomBase = NULL; - if (referenceIndex > 0) - { - geomBase = _shapeData[referenceIndex]->geosGeom; - } - else - { - geomBase = GeosConverter::ShapeToGeom(referenceShape); - } - - if (geomBase) - { - for (size_t i = 0; i < shapes.size(); i++) - { - if (i == referenceIndex) - continue; // it doesn't make sense to compare the shape with itself - - GEOSGeom geom = _shapeData[shapes[i]]->geosGeom; - if (geom != NULL) - { - char res = 0; - switch (relation) - { - case srContains: res = GeosHelper::Contains(geomBase, geom); break; - case srCrosses: res = GeosHelper::Crosses(geomBase, geom); break; - case srEquals: res = GeosHelper::Equals(geomBase, geom); break; - case srIntersects: res = GeosHelper::Intersects(geomBase, geom); break; - case srOverlaps: res = GeosHelper::Overlaps(geomBase, geom); break; - case srTouches: res = GeosHelper::Touches(geomBase, geom); break; - case srWithin: res = GeosHelper::Within(geomBase, geom); break; - } - if (res) - { - arr.push_back(shapes[i]); - } - } - } - - if (referenceIndex == -1) - GeosHelper::DestroyGeometry(geomBase); // the geometry was created in this function so it must be destroyed - } - - *retval = Templates::Vector2SafeArray(&arr, VT_I4, resultArray); - } - - // Don't clear the list here as function may be called in a loop - //this->ClearCachedGeometries(); +void CShapefile::GetRelatedShapeCore(IShape* referenceShape, long referenceIndex, tkSpatialRelation relation, + VARIANT* resultArray, VARIANT_BOOL* retval) +{ + if (relation == srDisjoint) + { + // TODO: implement + ErrorMessage(tkMETHOD_NOT_IMPLEMENTED); + return; + } + + // rather than generate geometries for all shapes, + // only generate for those within qtree extent (see below) + //this->ReadGeosGeometries(VARIANT_FALSE); + + // turns on the quad tree + VARIANT_BOOL useQTree = VARIANT_FALSE; + this->get_UseQTree(&useQTree); + if (!useQTree) this->put_UseQTree(VARIANT_TRUE); + + double xMin, xMax, yMin, yMax; + if (((CShape*)referenceShape)->get_ExtentsXY(xMin, yMin, xMax, yMax)) + { + const QTreeExtent query(xMin, xMax, yMax, yMin); + std::vector shapes = this->_qtree->GetNodes(query); + std::vector arr; + + // generate GEOS geometries only for shapes within qtree extent + for (size_t i = 0; i < shapes.size(); i++) + // minimize work by 'select'ing necessary shapes + this->put_ShapeSelected(shapes[i], VARIANT_TRUE); + // now generate only for 'select'ed shapes + this->ReadGeosGeometries(VARIANT_TRUE); + // don't leave shapes 'select'ed + for (size_t i = 0; i < shapes.size(); i++) + this->put_ShapeSelected(shapes[i], VARIANT_FALSE); + + GEOSGeom geomBase; + if (referenceIndex > 0) + { + geomBase = _shapeData[referenceIndex]->geosGeom; + } + else + { + geomBase = GeosConverter::ShapeToGeom(referenceShape); + } + + if (geomBase) + { + for (size_t i = 0; i < shapes.size(); i++) + { + if (i == referenceIndex) + continue; // it doesn't make sense to compare the shape with itself + + // ReSharper disable once CppLocalVariableMayBeConst + GEOSGeom geom = _shapeData[shapes[i]]->geosGeom; + if (geom != nullptr) + { + char res = 0; + switch (relation) + { + case srContains: res = GeosHelper::Contains(geomBase, geom); + break; + case srCrosses: res = GeosHelper::Crosses(geomBase, geom); + break; + case srEquals: res = GeosHelper::Equals(geomBase, geom); + break; + case srIntersects: res = GeosHelper::Intersects(geomBase, geom); + break; + case srOverlaps: res = GeosHelper::Overlaps(geomBase, geom); + break; + case srTouches: res = GeosHelper::Touches(geomBase, geom); + break; + case srWithin: res = GeosHelper::Within(geomBase, geom); + break; + case srDisjoint: break; + default: ; + } + if (res) + { + arr.push_back(shapes[i]); + } + } + } + + if (referenceIndex == -1) + GeosHelper::DestroyGeometry(geomBase); + // the geometry was created in this function so it must be destroyed + } + + *retval = Templates::Vector2SafeArray(&arr, VT_I4, resultArray); + } + + // Don't clear the list here as function may be called in a loop + //this->ClearCachedGeometries(); } // *************************************************** @@ -3005,33 +3091,33 @@ void CShapefile::GetRelatedShapeCore(IShape* referenceShape, long referenceIndex // *************************************************** STDMETHODIMP CShapefile::get_Identifiable(VARIANT_BOOL* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retVal = _hotTracking; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *retVal = _hotTracking; + return S_OK; } STDMETHODIMP CShapefile::put_Identifiable(VARIANT_BOOL newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - _hotTracking = newVal; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + _hotTracking = newVal; + return S_OK; } // ***************************************************************** // EditAddField() // ***************************************************************** -STDMETHODIMP CShapefile::EditAddField(BSTR name, FieldType type, int precision, int width, long *fieldIndex) +STDMETHODIMP CShapefile::EditAddField(BSTR name, FieldType type, int precision, int width, long* fieldIndex) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - if (!this->_table) - { - this->ErrorMessage(tkDBF_FILE_DOES_NOT_EXIST); - } - else - { - _table->EditAddField(name, type, precision, width, fieldIndex); - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + if (!this->_table) + { + this->ErrorMessage(tkDBF_FILE_DOES_NOT_EXIST); + } + else + { + _table->EditAddField(name, type, precision, width, fieldIndex); + } + return S_OK; } // ***************************************************************** @@ -3039,52 +3125,53 @@ STDMETHODIMP CShapefile::EditAddField(BSTR name, FieldType type, int precision, // ***************************************************************** STDMETHODIMP CShapefile::EditAddShape(IShape* shape, long* shapeIndex) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - VARIANT_BOOL retval; - *shapeIndex = _shapeData.size(); + VARIANT_BOOL retval; + *shapeIndex = _shapeData.size(); - EditInsertShape(shape, shapeIndex, &retval); + EditInsertShape(shape, shapeIndex, &retval); - if (retval == VARIANT_FALSE) - *shapeIndex = -1; + if (retval == VARIANT_FALSE) + *shapeIndex = -1; - return S_OK; + return S_OK; } // ***************************************************************** // GetClosestVertex() // ***************************************************************** -STDMETHODIMP CShapefile::GetClosestVertex(double x, double y, double maxDistance, - long* shapeIndex, long* pointIndex, double* distance, VARIANT_BOOL* retVal) -{ - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - - *retVal = VARIANT_FALSE; - *shapeIndex = -1; - *pointIndex = -1; - - bool result= false; - if (maxDistance <= 0.0) - { - // search through all shapefile - std::vector ids; - for (size_t i = 0; i < _shapeData.size(); i++) { - ids.push_back(i); - } - result = ShapefileHelper::GetClosestPoint(this, x, y, maxDistance, ids, shapeIndex, pointIndex, *distance); - } - else - { - std::vector ids; - Extent box(x - maxDistance, x + maxDistance, y - maxDistance, y + maxDistance); - if (this->SelectShapesCore(box, 0.0, SelectMode::INTERSECTION, ids, false)) - { - result = ShapefileHelper::GetClosestPoint(this, x, y, maxDistance, ids, shapeIndex, pointIndex, *distance); - } - } - *retVal = result ? VARIANT_TRUE: VARIANT_FALSE; - return S_OK; +STDMETHODIMP CShapefile::GetClosestVertex(double x, double y, double maxDistance, + long* shapeIndex, long* pointIndex, double* distance, VARIANT_BOOL* retVal) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + *retVal = VARIANT_FALSE; + *shapeIndex = -1; + *pointIndex = -1; + + bool result = false; + if (maxDistance <= 0.0) + { + // search through all shapefile + std::vector ids; + for (size_t i = 0; i < _shapeData.size(); i++) + { + ids.push_back(i); + } + result = ShapefileHelper::GetClosestPoint(this, x, y, maxDistance, ids, shapeIndex, pointIndex, *distance); + } + else + { + std::vector ids; + Extent box(x - maxDistance, x + maxDistance, y - maxDistance, y + maxDistance); + if (this->SelectShapesCore(box, 0.0, SelectMode::INTERSECTION, ids, false)) + { + result = ShapefileHelper::GetClosestPoint(this, x, y, maxDistance, ids, shapeIndex, pointIndex, *distance); + } + } + *retVal = result ? VARIANT_TRUE : VARIANT_FALSE; + return S_OK; } // ***************************************************************** @@ -3092,31 +3179,31 @@ STDMETHODIMP CShapefile::GetClosestVertex(double x, double y, double maxDistance // ***************************************************************** STDMETHODIMP CShapefile::HasInvalidShapes(VARIANT_BOOL* result) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *result = VARIANT_FALSE; - int numShapes = _shapeData.size(); - - for (int i = 0; i < numShapes; i++) - { - IShape* shp = NULL; - this->get_Shape(i, &shp); - - if (!shp) - { - *result = VARIANT_TRUE; - break; - } - - VARIANT_BOOL retval = VARIANT_TRUE; - shp->get_IsValid(&retval); - shp->Release(); - if (retval == VARIANT_FALSE) - { - *result = VARIANT_TRUE; - break; - } - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *result = VARIANT_FALSE; + const int numShapes = _shapeData.size(); + + for (int i = 0; i < numShapes; i++) + { + IShape* shp = nullptr; + this->get_Shape(i, &shp); + + if (!shp) + { + *result = VARIANT_TRUE; + break; + } + + VARIANT_BOOL retval = VARIANT_TRUE; + shp->get_IsValid(&retval); + shp->Release(); + if (retval == VARIANT_FALSE) + { + *result = VARIANT_TRUE; + break; + } + } + return S_OK; } // ***************************************************************** @@ -3124,11 +3211,11 @@ STDMETHODIMP CShapefile::HasInvalidShapes(VARIANT_BOOL* result) // ***************************************************************** STDMETHODIMP CShapefile::get_UndoList(IUndoList** pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - if (_undoList) - _undoList->AddRef(); - *pVal = _undoList; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + if (_undoList) + _undoList->AddRef(); + *pVal = _undoList; + return S_OK; } // ***************************************************************** @@ -3136,15 +3223,16 @@ STDMETHODIMP CShapefile::get_UndoList(IUndoList** pVal) // ***************************************************************** STDMETHODIMP CShapefile::get_Snappable(VARIANT_BOOL* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *pVal = _snappable; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *pVal = _snappable; + return S_OK; } + STDMETHODIMP CShapefile::put_Snappable(VARIANT_BOOL newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - _snappable = newVal; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + _snappable = newVal; + return S_OK; } // ***************************************************************** @@ -3152,9 +3240,9 @@ STDMETHODIMP CShapefile::put_Snappable(VARIANT_BOOL newVal) // ***************************************************************** STDMETHODIMP CShapefile::get_ShapefileType2D(ShpfileType* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *pVal = ShapeUtility::Convert2D(_shpfiletype); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *pVal = ShapeUtility::Convert2D(_shpfiletype); + return S_OK; } // ***************************************************************** @@ -3162,9 +3250,26 @@ STDMETHODIMP CShapefile::get_ShapefileType2D(ShpfileType* pVal) // ***************************************************************** STDMETHODIMP CShapefile::get_FieldIndexByName(BSTR FieldName, LONG* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - _table->get_FieldIndexByName(FieldName, pVal); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + _table->get_FieldIndexByName(FieldName, pVal); + return S_OK; +} + +// *************************************************** +// get_Selectable +// *************************************************** +STDMETHODIMP CShapefile::get_Selectable(VARIANT_BOOL* retVal) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *retVal = _selectable; + return S_OK; +} + +STDMETHODIMP CShapefile::put_Selectable(VARIANT_BOOL newVal) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + _selectable = newVal; + return S_OK; } // ***************************************************************** @@ -3172,25 +3277,26 @@ STDMETHODIMP CShapefile::get_FieldIndexByName(BSTR FieldName, LONG* pVal) // ***************************************************************** STDMETHODIMP CShapefile::Move(DOUBLE xProjOffset, DOUBLE yProjOffset, VARIANT_BOOL* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retVal = VARIANT_FALSE; - if (_sourceType != sstInMemory) - { - ErrorMessage(tkSHPFILE_NOT_IN_EDIT_MODE); - return S_OK; - } - long numShapes; - get_NumShapes(&numShapes); - for (long i = 0; i < numShapes; i++) - { - CComPtr shp = NULL; - get_Shape(i, &shp); - if (shp) { - shp->Move(xProjOffset, yProjOffset); - } - } - *retVal = VARIANT_TRUE; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *retVal = VARIANT_FALSE; + if (_sourceType != sstInMemory) + { + ErrorMessage(tkSHPFILE_NOT_IN_EDIT_MODE); + return S_OK; + } + long numShapes; + get_NumShapes(&numShapes); + for (long i = 0; i < numShapes; i++) + { + CComPtr shp = nullptr; + get_Shape(i, &shp); + if (shp) + { + shp->Move(xProjOffset, yProjOffset); + } + } + *retVal = VARIANT_TRUE; + return S_OK; } // ***************************************************************** @@ -3198,17 +3304,18 @@ STDMETHODIMP CShapefile::Move(DOUBLE xProjOffset, DOUBLE yProjOffset, VARIANT_BO // ***************************************************************** STDMETHODIMP CShapefile::get_ShapeRendered(LONG ShapeIndex, VARIANT_BOOL* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *pVal = VARIANT_FALSE; - if (ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - } - else { - *pVal = _shapeData[ShapeIndex]->wasRendered() ? VARIANT_TRUE : VARIANT_FALSE; - } - return S_OK; + *pVal = VARIANT_FALSE; + if (ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + } + else + { + *pVal = _shapeData[ShapeIndex]->wasRendered() ? VARIANT_TRUE : VARIANT_FALSE; + } + return S_OK; } // ***************************************************************** @@ -3216,9 +3323,10 @@ STDMETHODIMP CShapefile::get_ShapeRendered(LONG ShapeIndex, VARIANT_BOOL* pVal) // ***************************************************************** void CShapefile::MarkUndrawn() { - for (size_t i = 0; i < _shapeData.size(); i++) { - _shapeData[i]->wasRendered(false); - } + for (auto& i : _shapeData) + { + i->wasRendered(false); + } } // ************************************************************* @@ -3226,28 +3334,30 @@ void CShapefile::MarkUndrawn() // ************************************************************* STDMETHODIMP CShapefile::get_SortField(BSTR* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - USES_CONVERSION; - *pVal = OLE2BSTR(_sortField); + USES_CONVERSION; + *pVal = OLE2BSTR(_sortField); - return S_OK; + return S_OK; } + STDMETHODIMP CShapefile::put_SortField(BSTR newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - ::SysFreeString(_sortField); - USES_CONVERSION; - _sortField = OLE2BSTR(newVal); + SysFreeString(_sortField); + USES_CONVERSION; + _sortField = OLE2BSTR(newVal); - _sortingChanged = true; + _sortingChanged = true; - if (_labels) { - _labels->UpdateSizeField(); - } + if (_labels) + { + _labels->UpdateSizeField(); + } - return S_OK; + return S_OK; } // ************************************************************* @@ -3255,20 +3365,21 @@ STDMETHODIMP CShapefile::put_SortField(BSTR newVal) // ************************************************************* STDMETHODIMP CShapefile::get_SortAscending(VARIANT_BOOL* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *pVal = _sortAscending; + *pVal = _sortAscending; - return S_OK; + return S_OK; } + STDMETHODIMP CShapefile::put_SortAscending(VARIANT_BOOL newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - _sortAscending = newVal; - _sortingChanged = true; + _sortAscending = newVal; + _sortingChanged = true; - return S_OK; + return S_OK; } // ************************************************************* @@ -3276,12 +3387,12 @@ STDMETHODIMP CShapefile::put_SortAscending(VARIANT_BOOL newVal) // ************************************************************* STDMETHODIMP CShapefile::UpdateSortField() { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - // this will trigger rereading of the table on next redraw - _sortingChanged = true; + // this will trigger rereading of the table on next redraw + _sortingChanged = true; - return S_OK; + return S_OK; } // ************************************************************* @@ -3289,40 +3400,40 @@ STDMETHODIMP CShapefile::UpdateSortField() // ************************************************************* bool CShapefile::GetSorting(vector** indices) { - *indices = NULL; + *indices = nullptr; - if (!_sortingChanged) { - *indices = &_sorting; - return true; - } + if (!_sortingChanged) + { + *indices = &_sorting; + return true; + } - long fieldIndex; - get_FieldIndexByName(_sortField, &fieldIndex); + long fieldIndex; + get_FieldIndexByName(_sortField, &fieldIndex); - if (fieldIndex == -1) { - return false; - } + if (fieldIndex == -1) + { + return false; + } - if (!_table) { - return false; - } + if (!_table) + { + return false; + } - _sortingChanged = false; + _sortingChanged = false; - if (((CTableClass*)_table)->GetSorting(fieldIndex, _sorting)) - { - if (!_sortAscending) - { - std::reverse(_sorting.begin(), _sorting.end()); - } + if (((CTableClass*)_table)->GetSorting(fieldIndex, _sorting)) + { + if (!_sortAscending) + { + std::reverse(_sorting.begin(), _sorting.end()); + } - *indices = &_sorting; - return true; - } - else { - CallbackHelper::ErrorMsg("Failed to sort labels"); - } + *indices = &_sorting; + return true; + } + CallbackHelper::ErrorMsg("Failed to sort labels"); - return false; + return false; } - diff --git a/src/COM classes/Shapefile.h b/src/COM classes/Shapefile.h index 8e0ef02b..f57f474f 100644 --- a/src/COM classes/Shapefile.h +++ b/src/COM classes/Shapefile.h @@ -256,6 +256,9 @@ class ATL_NO_VTABLE CShapefile : STDMETHOD(StartAppendMode)(VARIANT_BOOL* retVal); STDMETHOD(StopAppendMode)(); STDMETHOD(get_AppendMode)(VARIANT_BOOL* pVal); + STDMETHOD(get_IsGeographicProjection)(VARIANT_BOOL* pVal); + STDMETHOD(get_Selectable)(VARIANT_BOOL* retVal); + STDMETHOD(put_Selectable)(VARIANT_BOOL newVal); private: // data for point in shapefile test @@ -321,7 +324,8 @@ class ATL_NO_VTABLE CShapefile : IUndoList* _undoList; VARIANT_BOOL _hotTracking; - bool _geosGeometriesRead; + VARIANT_BOOL _selectable; + bool _geosGeometriesRead; tkCollisionMode _collisionMode; // collision mode for point shapefiles tkGeometryEngine _geometryEngine; // GEOS or Clipper bool _writing; // is currently writing to the file @@ -408,7 +412,7 @@ class ATL_NO_VTABLE CShapefile : void AggregateShapesCore(VARIANT_BOOL SelectedOnly, LONG FieldIndex, IFieldStatOperations* statOperations, IShapefile** retval); void DissolveCore(long FieldIndex, VARIANT_BOOL SelectedOnly, IFieldStatOperations* statOperations, IShapefile** sf); void CalculateFieldStats(map*>& indicesMap, IFieldStatOperations* operations, IShapefile* output); - void InsertShapesVector(IShapefile* sf, vector& vShapes, IShapefile* sfSubject, long subjectId, std::map* fieldMapSubject = NULL, IShapefile* sfClip = NULL, long clipId = -1, std::map* fieldMapClip = NULL); + static void InsertShapesVector(IShapefile* sf, vector& vShapes, IShapefile* sfSubject, long subjectId, std::map* fieldMapSubject = NULL, IShapefile* sfClip = NULL, long clipId = -1, std::map* fieldMapClip = NULL); void GetRelatedShapeCore(IShape* referenceShape, long referenceIndex, tkSpatialRelation relation, VARIANT* resultArray, VARIANT_BOOL* retval); void ReleaseRenderingCache(); bool ReadShapeExtents(long ShapeIndex, Extent& result); diff --git a/src/COM classes/ShapefileCategories.cpp b/src/COM classes/ShapefileCategories.cpp index e677df49..5513a244 100644 --- a/src/COM classes/ShapefileCategories.cpp +++ b/src/COM classes/ShapefileCategories.cpp @@ -23,8 +23,9 @@ * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ // lsu 9 may 2010 - created the file + // Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper -#include "stdafx.h" +#include "StdAfx.h" #include "ShapefileCategories.h" #include "ShapefileCategory.h" #include "ShapeDrawingOptions.h" @@ -40,7 +41,7 @@ STDMETHODIMP CShapefileCategories::get_Count(long* pVal) AFX_MANAGE_STATE(AfxGetStaticModuleState()) *pVal = _categories.size(); return S_OK; -}; +} // *************************************************************** // Add() @@ -62,24 +63,24 @@ STDMETHODIMP CShapefileCategories::Insert(long Index, BSTR Name, IShapefileCateg if(Index < 0 || Index > (long)_categories.size()) { ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - *retVal = VARIANT_FALSE; + *retVal = nullptr; return NULL; } - *retVal = NULL; - IShapefileCategory* cat = NULL; - CoCreateInstance( CLSID_ShapefileCategory, NULL, CLSCTX_INPROC_SERVER, IID_IShapefileCategory, (void**)&cat); - if (cat == NULL) return S_OK; + *retVal = nullptr; + IShapefileCategory* cat = nullptr; + CoCreateInstance( CLSID_ShapefileCategory, nullptr, CLSCTX_INPROC_SERVER, IID_IShapefileCategory, (void**)&cat); + if (cat == nullptr) return S_OK; // initialization with default options if shapefile is present - if (_shapefile != NULL) + if (_shapefile != nullptr) { - IShapeDrawingOptions* defaultOpt = NULL; + IShapeDrawingOptions* defaultOpt = nullptr; _shapefile->get_DefaultDrawingOptions(&defaultOpt); CDrawingOptionsEx* newOpt =((CShapeDrawingOptions*)defaultOpt)->get_UnderlyingOptions(); defaultOpt->Release(); - IShapeDrawingOptions* opt = NULL; + IShapeDrawingOptions* opt = nullptr; cat->get_DrawingOptions(&opt); ((CShapeDrawingOptions*)opt)->put_underlyingOptions(newOpt); opt->Release(); @@ -105,22 +106,22 @@ STDMETHODIMP CShapefileCategories::Insert(long Index, BSTR Name, IShapefileCateg // *************************************************************** // Remove() // *************************************************************** -STDMETHODIMP CShapefileCategories::Remove(long Index, VARIANT_BOOL* vbretval) +STDMETHODIMP CShapefileCategories::Remove(long Index, VARIANT_BOOL* vbRetval) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *vbretval = VARIANT_FALSE; + *vbRetval = VARIANT_FALSE; if( Index < 0 || Index >= (long)_categories.size() ) { ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - *vbretval = VARIANT_FALSE; + *vbRetval = VARIANT_FALSE; } else { _categories[Index]->Release(); - _categories[Index] = NULL; + _categories[Index] = nullptr; _categories.erase(_categories.begin() + Index); - *vbretval = VARIANT_TRUE; + *vbRetval = VARIANT_TRUE; } return S_OK; } @@ -131,18 +132,18 @@ STDMETHODIMP CShapefileCategories::Remove(long Index, VARIANT_BOOL* vbretval) STDMETHODIMP CShapefileCategories::Clear() { AFX_MANAGE_STATE(AfxGetStaticModuleState()) - for (unsigned int i=0; i < _categories.size(); i++ ) + for (auto& _categorie : _categories) { - _categories[i]->Release(); + _categorie->Release(); } _categories.clear(); if (_shapefile) { std::vector* data = ((CShapefile*)_shapefile)->get_ShapeVector(); - for (unsigned int i = 0; i < data->size(); i++) + for (auto& i : *data) { - (*data)[i]->category = -1; + i->category = -1; } } return S_OK; @@ -157,7 +158,7 @@ STDMETHODIMP CShapefileCategories::get_Item (long Index, IShapefileCategory** re if( Index < 0 || Index >= (long)_categories.size() ) { ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - *retval = NULL; + *retval = nullptr; } else { @@ -175,24 +176,18 @@ STDMETHODIMP CShapefileCategories::put_Item(long Index, IShapefileCategory* newV ErrorMessage(tkINDEX_OUT_OF_BOUNDS); return S_OK; } - else - { - if (!newVal) - { - ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); - return S_OK; - } - else - { - if (_categories[Index] != newVal) - { - _categories[Index]->Release(); - _categories[Index] = newVal; - _categories[Index]->AddRef(); - } - return S_OK; - } - } + if (!newVal) + { + ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); + return S_OK; + } + if (_categories[Index] != newVal) + { + _categories[Index]->Release(); + _categories[Index] = newVal; + _categories[Index]->AddRef(); + } + return S_OK; } // *************************************************************** @@ -205,10 +200,10 @@ STDMETHODIMP CShapefileCategories::AddRange(long FieldIndex, tkClassificationTyp USES_CONVERSION; *retVal = VARIANT_FALSE; - if(_shapefile == NULL) + if(_shapefile == nullptr) return S_OK; - CComPtr tbl = NULL; + CComPtr tbl = nullptr; _shapefile->get_Table(&tbl); if (!tbl) return S_OK; @@ -220,18 +215,18 @@ STDMETHODIMP CShapefileCategories::AddRange(long FieldIndex, tkClassificationTyp _classificationField = -1; // fast processing is off - IShapefileCategory* cat = NULL; + IShapefileCategory* cat = nullptr; - for (int i = 0; i < (int)values->size(); i++ ) + for (auto& value : *values) { CString strValue; - CComBSTR bstrName((*values)[i].name); - CComBSTR bstrExpression((*values)[i].expression); + CComBSTR bstrName(value.name); + CComBSTR bstrExpression(value.expression); this->Add(bstrName, &cat); cat->put_Expression(bstrExpression); - cat->put_MinValue((*values)[i].minValue); - cat->put_MaxValue((*values)[i].maxValue); + cat->put_MinValue(value.minValue); + cat->put_MaxValue(value.maxValue); cat->Release(); } @@ -259,7 +254,7 @@ STDMETHODIMP CShapefileCategories::Generate2(BSTR fieldName, tkClassificationTyp if (!_shapefile) return S_OK; - CComPtr tbl = NULL; + CComPtr tbl = nullptr; _shapefile->get_Table(&tbl); if (!tbl) return S_OK; @@ -280,10 +275,10 @@ STDMETHODIMP CShapefileCategories::Generate(long FieldIndex, tkClassificationTyp USES_CONVERSION; *retVal = VARIANT_FALSE; - if(_shapefile == NULL) + if(_shapefile == nullptr) return S_OK; - CComPtr tbl = NULL; + CComPtr tbl = nullptr; _shapefile->get_Table(&tbl); if (!tbl) return S_OK; @@ -311,21 +306,20 @@ void CShapefileCategories::GenerateCore(std::vector* categories, this->Clear(); _classificationField = -1; // fast processing is off - IShapefileCategory* icat = NULL; - CShapefileCategory* ct = NULL; + IShapefileCategory* icat = nullptr; + // CShapefileCategory* ct = nullptr; - for (int i = 0; i < (int)categories->size(); i++) + for (auto& categorie : *categories) { - CString strValue; - CComBSTR bstrName((*categories)[i].name); - CComBSTR bstrExpression((*categories)[i].expression); + CComBSTR bstrName(categorie.name); + CComBSTR bstrExpression(categorie.expression); this->Add(bstrName, &icat); icat->put_Expression(bstrExpression); icat->put_ValueType(ClassificationType == tkClassificationType::ctUniqueValues ? cvSingleValue : cvRange); - icat->put_MinValue((*categories)[i].minValue); - icat->put_MaxValue((*categories)[i].maxValue); + icat->put_MinValue(categorie.minValue); + icat->put_MaxValue(categorie.maxValue); icat->Release(); } @@ -346,7 +340,7 @@ STDMETHODIMP CShapefileCategories::get_Key(BSTR *pVal) STDMETHODIMP CShapefileCategories::put_Key(BSTR newVal) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) - ::SysFreeString(_key); + SysFreeString(_key); USES_CONVERSION; _key = OLE2BSTR(newVal); return S_OK; @@ -365,7 +359,7 @@ STDMETHODIMP CShapefileCategories::get_Caption(BSTR *pVal) STDMETHODIMP CShapefileCategories::put_Caption(BSTR newVal) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) - ::SysFreeString(_caption); + SysFreeString(_caption); USES_CONVERSION; _caption = OLE2BSTR(newVal); return S_OK; @@ -403,7 +397,7 @@ STDMETHODIMP CShapefileCategories::get_GlobalCallback(ICallback **pVal) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) *pVal = _globalCallback; - if( _globalCallback != NULL ) _globalCallback->AddRef(); + if( _globalCallback != nullptr ) _globalCallback->AddRef(); return S_OK; } @@ -434,7 +428,7 @@ void CShapefileCategories::put_ParentShapefile(IShapefile* newVal) { _shapefile = newVal; } -IShapefile* CShapefileCategories::get_ParentShapefile() +IShapefile* CShapefileCategories::get_ParentShapefile() const { return _shapefile; } @@ -446,8 +440,7 @@ CDrawingOptionsEx* CShapefileCategories::get_UnderlyingOptions(int Index) { if (Index >=0 && Index < (int)_categories.size()) return ((CShapefileCategory*)_categories[Index])->get_UnderlyingOptions(); - else - return NULL; + return nullptr; } // ******************************************************************* @@ -469,11 +462,11 @@ STDMETHODIMP CShapefileCategories::ApplyExpression(long CategoryIndex) // switching off shapes that are currently included in the category std::vector* data = ((CShapefile*)_shapefile)->get_ShapeVector(); - for (unsigned int i = 0; i < data->size(); i++) + for (auto& i : *data) { - if ((*data)[i]->category == CategoryIndex) + if (i->category == CategoryIndex) { - (*data)[i]->category = -1; + i->category = -1; } } @@ -489,7 +482,7 @@ void CShapefileCategories::ApplyExpressionCore(long CategoryIndex) if (!_shapefile) return; - CComPtr tbl = NULL; + CComPtr tbl = nullptr; _shapefile->get_Table(&tbl); if ( !tbl ) return; @@ -501,9 +494,10 @@ void CShapefileCategories::ApplyExpressionCore(long CategoryIndex) results.resize(numShapes, -1); bool uniqueValues = true; - for (unsigned int i = 0; i < _categories.size(); i++) { + for (auto& _categorie : _categories) + { tkCategoryValue value; - _categories[i]->get_ValueType(&value); + _categorie->get_ValueType(&value); if (value != cvSingleValue) { uniqueValues = false; break; @@ -554,7 +548,7 @@ void CShapefileCategories::ApplyExpressionCore(long CategoryIndex) if (parsingIsNeeded) { // building list of expressions - std::vector expressions; + std::vector expressions; for (unsigned int i = 0; i < _categories.size(); i++) { if (i == CategoryIndex || CategoryIndex == -1 ) @@ -562,13 +556,13 @@ void CShapefileCategories::ApplyExpressionCore(long CategoryIndex) CComBSTR expr; _categories[i]->get_Expression(&expr); USES_CONVERSION; - CString str = OLE2CA(expr); + CStringW str = OLE2CW(expr); expressions.push_back(str); } else { // we don't need this categories, so dummy strings for them - CString str = ""; + CStringW str = L""; expressions.push_back(str); } } @@ -678,7 +672,7 @@ STDMETHODIMP CShapefileCategories::ApplyColorScheme3 (tkColorSchemeType Type, IC } } - IShapeDrawingOptions* options = NULL; + IShapeDrawingOptions* options = nullptr; for (int i = CategoryStartIndex; i <= CategoryEndIndex; i++) { OLE_COLOR color; @@ -704,6 +698,8 @@ STDMETHODIMP CShapefileCategories::ApplyColorScheme3 (tkColorSchemeType Type, IC case shElementFill2: options->put_FillColor2(color); break; case shElementLines: options->put_LineColor(color); break; case shElementFillBackground: options->put_FillBgColor(color); break; + case shElementDefault: break; + default: ; } options->Release(); } @@ -724,15 +720,15 @@ STDMETHODIMP CShapefileCategories::MoveUp (long Index, VARIANT_BOOL* retval) _categories[Index] = catBefore; std::vector* data = ((CShapefile*)_shapefile)->get_ShapeVector(); - for (unsigned int i = 0; i < data->size(); i++) + for (auto& i : *data) { - if ((*data)[i]->category == Index) + if (i->category == Index) { - (*data)[i]->category = Index - 1; + i->category = Index - 1; } - else if ((*data)[i]->category == Index - 1) + else if (i->category == Index - 1) { - (*data)[i]->category = Index; + i->category = Index; } } @@ -759,15 +755,15 @@ STDMETHODIMP CShapefileCategories::MoveDown (long Index, VARIANT_BOOL* retval) _categories[Index] = catAfter; std::vector* data = ((CShapefile*)_shapefile)->get_ShapeVector(); - for (unsigned int i = 0; i < data->size(); i++) + for (auto& i : *data) { - if ((*data)[i]->category == Index) + if (i->category == Index) { - (*data)[i]->category = Index + 1; + i->category = Index + 1; } - else if ((*data)[i]->category == Index + 1) + else if (i->category == Index + 1) { - (*data)[i]->category = Index; + i->category = Index; } } *retval = VARIANT_TRUE; @@ -799,19 +795,18 @@ CPLXMLNode* CShapefileCategories::SerializeCore(CString ElementName) { USES_CONVERSION; - CPLXMLNode* psTree = CPLCreateXMLNode( NULL, CXT_Element, ElementName); - CString str; + CPLXMLNode* psTree = CPLCreateXMLNode( nullptr, CXT_Element, ElementName); - // classification field + // classification field Utility::CPLCreateXMLAttributeAndValue(psTree, "ClassificationField", CPLString().Printf("%d", _classificationField)); // field type FieldType type; - CComPtr table = NULL; + CComPtr table = nullptr; _shapefile->get_Table(&table); if (table && _classificationField != -1) { - CComPtr fld = NULL; + CComPtr fld = nullptr; table->get_Field(_classificationField, &fld); if (fld) { @@ -820,24 +815,24 @@ CPLXMLNode* CShapefileCategories::SerializeCore(CString ElementName) } } - for (unsigned int i = 0; i < _categories.size(); i++) + for (auto& _categorie : _categories) { CPLXMLNode* psNode = CPLCreateXMLNode(psTree, CXT_Element, "ShapefileCategoryClass"); // name BSTR vbstr; - _categories[i]->get_Name(&vbstr); + _categorie->get_Name(&vbstr); Utility::CPLCreateXMLAttributeAndValue( psNode, "Name", OLE2CA(vbstr)); SysFreeString(vbstr); // expression - _categories[i]->get_Expression(&vbstr); - str = OLE2CA(vbstr); + _categorie->get_Expression(&vbstr); + CString str = OLE2CA(vbstr); Utility::CPLCreateXMLAttributeAndValue( psNode, "Expression", str); SysFreeString(vbstr); - CShapefileCategory* ct = (CShapefileCategory*)_categories[i]; + auto* ct = (CShapefileCategory*)_categorie; Utility::CPLCreateXMLAttributeAndValue(psNode, "ValueType", CPLString().Printf("%d", (int)ct->GetCategoryValue())); // values @@ -846,7 +841,7 @@ CPLXMLNode* CShapefileCategories::SerializeCore(CString ElementName) // options IShapeDrawingOptions* options; - _categories[i]->get_DrawingOptions(&options); + _categorie->get_DrawingOptions(&options); CPLXMLNode* psChild = ((CShapeDrawingOptions*)options)->SerializeCore("ShapeDrawingOptionsClass"); if (psChild) { @@ -872,7 +867,7 @@ bool CShapefileCategories::DeserializeCore(CPLXMLNode* node, bool applyExpressio bool loadField = false; FieldType type = INTEGER_FIELD; - CString s = CPLGetXMLValue( node, "FieldType", NULL ); + CString s = CPLGetXMLValue( node, "FieldType", nullptr ); if (s != "") { type = (FieldType)atoi(s); @@ -881,16 +876,16 @@ bool CShapefileCategories::DeserializeCore(CPLXMLNode* node, bool applyExpressio if (loadField) { - s = CPLGetXMLValue( node, "ClassificationField", NULL ); + s = CPLGetXMLValue( node, "ClassificationField", nullptr ); if (s != "") { - int fieldIndex = atoi(s); + const int fieldIndex = atoi(s); - CComPtr table = NULL; + CComPtr table = nullptr; _shapefile->get_Table(&table); if (table) { - IField* fld = NULL; + IField* fld = nullptr; table->get_Field(fieldIndex, &fld); if (fld) { @@ -916,30 +911,30 @@ bool CShapefileCategories::DeserializeCore(CPLXMLNode* node, bool applyExpressio if (strcmp(node->pszValue, "ShapefileCategoryClass") == 0) { CString str; - str = CPLGetXMLValue( node, "Name", NULL ); + str = CPLGetXMLValue( node, "Name", nullptr ); CComBSTR bstrName( str ); - IShapefileCategory* cat = NULL; + IShapefileCategory* cat = nullptr; this->Add(bstrName, &cat); - str = CPLGetXMLValue( node, "Expression", NULL ); + str = CPLGetXMLValue( node, "Expression", nullptr ); CComBSTR bstrExpression(str); cat->put_Expression(bstrExpression); - str = CPLGetXMLValue(node, "ValueType", NULL); - tkCategoryValue ctVal = (str != "") ? (tkCategoryValue)atoi(str.GetString()) : cvExpression; + str = CPLGetXMLValue(node, "ValueType", nullptr); + const tkCategoryValue ctVal = str != "" ? (tkCategoryValue)atoi(str.GetString()) : cvExpression; cat->put_ValueType(ctVal); - str = CPLGetXMLValue(node, "Value", NULL); + str = CPLGetXMLValue(node, "Value", nullptr); Utility::DeserializeVariant(str, type, ((CShapefileCategory*)cat)->GetMinValue()); - str = CPLGetXMLValue(node, "MaxValue", NULL); + str = CPLGetXMLValue(node, "MaxValue", nullptr); Utility::DeserializeVariant(str, type, ((CShapefileCategory*)cat)->GetMaxValue()); // drawing options CPLXMLNode* nodeOptions = CPLGetXMLNode( node, "ShapeDrawingOptionsClass" ); if (nodeOptions) { - IShapeDrawingOptions* options = NULL; + IShapeDrawingOptions* options = nullptr; cat->get_DrawingOptions(&options); ((CShapeDrawingOptions*)options)->DeserializeCore(nodeOptions); options->Release(); @@ -1064,19 +1059,19 @@ STDMETHODIMP CShapefileCategories::GeneratePolygonColors(IColorScheme* scheme, V Coloring::ColorGraph* graph = ((CShapefile*)_shapefile)->GeneratePolygonColors(); if (graph) { - int colorCount = graph->GetColorCount(); + const int colorCount = graph->GetColorCount(); // ------------------------------------------------- // create categories // ------------------------------------------------- - int firstCategory = _categories.size(); + const int firstCategory = _categories.size(); long numBreaks; scheme->get_NumBreaks(&numBreaks); for(int i = 0; i < colorCount; i++) { CString s; s.Format("Color %d", i + 1); - IShapefileCategory* ct = NULL; + IShapefileCategory* ct = nullptr; CComBSTR bstrName(s); this->Add(bstrName, &ct); @@ -1101,10 +1096,10 @@ STDMETHODIMP CShapefileCategories::GeneratePolygonColors(IColorScheme* scheme, V // ------------------------------------------------- // apply indices to polygons // ------------------------------------------------- - for(size_t i = 0; i < graph->nodes.size(); i++) + for (auto& node : graph->nodes) { - int shapeId = graph->nodes[i]->id; - _shapefile->put_ShapeCategory(shapeId, firstCategory + graph->nodes[i]->color); + const int shapeId = node->id; + _shapefile->put_ShapeCategory(shapeId, firstCategory + node->color); } delete graph; } @@ -1140,11 +1135,11 @@ STDMETHODIMP CShapefileCategories::Sort(LONG FieldIndex, VARIANT_BOOL Ascending, return S_OK; } - CComPtr table = NULL; + CComPtr table = nullptr; _shapefile->get_Table(&table); if (!table) return S_OK; - IField* fld = NULL; + IField* fld = nullptr; table->get_Field(FieldIndex, &fld); FieldType type; fld->get_Type(&type); @@ -1163,40 +1158,39 @@ STDMETHODIMP CShapefileCategories::Sort(LONG FieldIndex, VARIANT_BOOL Ascending, multimap map; - for (unsigned int i = 0; i < _categories.size(); i++) + for (auto& _categorie : _categories) { - VARIANT_BOOL vbretval = VARIANT_FALSE; + const VARIANT_BOOL vbretval = VARIANT_FALSE; CComVariant var = NULL; BSTR expr; - _categories[i]->get_Expression(&expr); + _categorie->get_Expression(&expr); // TODO: implement //table->CalculateStat(FieldIndex, Operation, expr, &var, &vbretval); if (vbretval) { - pair myPair(var, _categories[i]); + pair myPair(var, _categorie); map.insert(myPair); } else { - pair myPair(valDefault, _categories[i]); + pair myPair(valDefault, _categorie); map.insert(myPair); } } - - // returning result - multimap ::iterator p; - p = map.begin(); + + multimap::iterator p = map.begin(); int i = 0; ASSERT(map.size() == _categories.size()); while(p != map.end()) { - IShapefileCategory* cat = p->second; + // IShapefileCategory* cat = p->second; _categories[i] = p->second; - i++; p++; + i++; + ++p; } *retVal = VARIANT_TRUE; @@ -1226,14 +1220,14 @@ void CShapefileCategories::GetCategoryData(vector& data) { USES_CONVERSION; data.clear(); - for (size_t i = 0; i < _categories.size(); i++) + for (auto& _categorie : _categories) { - CategoriesData* ct = new CategoriesData(); - _categories[i]->get_MinValue(&ct->minValue); - _categories[i]->get_MaxValue(&ct->maxValue); - _categories[i]->get_ValueType(&ct->valueType); + auto* ct = new CategoriesData(); + _categorie->get_MinValue(&ct->minValue); + _categorie->get_MaxValue(&ct->maxValue); + _categorie->get_ValueType(&ct->valueType); CComBSTR expr; - _categories[i]->get_Expression(&expr); + _categorie->get_Expression(&expr); ct->expression = OLE2A(expr); ct->classificationField = _classificationField; data.push_back(ct); diff --git a/src/COM classes/ShapefileCategories.h b/src/COM classes/ShapefileCategories.h index 81638520..d82e04d0 100644 --- a/src/COM classes/ShapefileCategories.h +++ b/src/COM classes/ShapefileCategories.h @@ -158,7 +158,7 @@ class ATL_NO_VTABLE CShapefileCategories : CPLXMLNode* SerializeCore(CString ElementName); void put_ParentShapefile(IShapefile* newVal); - IShapefile* get_ParentShapefile(); + IShapefile* get_ParentShapefile() const; CDrawingOptionsEx* get_UnderlyingOptions(int Index); void GenerateCore(std::vector* categories, long FieldIndex, tkClassificationType ClassificationType, VARIANT_BOOL* retVal); void GetCategoryData(vector& data); diff --git a/src/COM classes/Shapefile_Geoprocessing.cpp b/src/COM classes/Shapefile_Geoprocessing.cpp index 1743d507..9739a09c 100644 --- a/src/COM classes/Shapefile_Geoprocessing.cpp +++ b/src/COM classes/Shapefile_Geoprocessing.cpp @@ -1,5 +1,5 @@ //******************************************************************************************************** -//File name: Shapefile.cpp +//File name: Shapefile_Geoprocessing.cpp //Description: Implementation of the CShapefile (see other cpp files as well) //******************************************************************************************************** //The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); @@ -18,8 +18,9 @@ //Contributor(s): (Open source contributors should list themselves and their modifications here). // ------------------------------------------------------------------------------------------------------- // lsu 3-02-2011: split the initial Shapefile.cpp file to make entities of the reasonable size +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper -#include "stdafx.h" +#include "StdAfx.h" #include "Shapefile.h" #include "OgrConverter.h" #include "Templates.h" @@ -49,23 +50,24 @@ // ******************************************************************** int CloneField(IShapefile* source, IShapefile* target, int fieldIndex, long newFieldIndex) { - IField* fld = NULL; - source->get_Field(fieldIndex, &fld); - if (fld) - { - IField* newField = NULL; - fld->Clone(&newField); - fld->Release(); - VARIANT_BOOL vb; - if (newFieldIndex == -1) { - long numShapes; - target->get_NumShapes(&numShapes); - newFieldIndex = numShapes; - } - target->EditInsertField(newField, &newFieldIndex, NULL, &vb); - newField->Release(); - } - return newFieldIndex; + IField* fld = nullptr; + source->get_Field(fieldIndex, &fld); + if (fld) + { + IField* newField = nullptr; + fld->Clone(&newField); + fld->Release(); + VARIANT_BOOL vb; + if (newFieldIndex == -1) + { + long numShapes; + target->get_NumShapes(&numShapes); + newFieldIndex = numShapes; + } + target->EditInsertField(newField, &newFieldIndex, nullptr, &vb); + newField->Release(); + } + return newFieldIndex; } // ****************************************************************** @@ -73,104 +75,98 @@ int CloneField(IShapefile* source, IShapefile* target, int fieldIndex, long newF // ****************************************************************** // Inserts shapes, resulting from intersection of one shape of subject shapefile, // and one shape of clip shapefile, attributes a copied from both shapefiles -void CShapefile::InsertShapesVector(IShapefile* sf, vector& vShapes, - IShapefile* sfSubject, long subjectId, std::map* fieldMapSubject, - IShapefile* sfClip, long clipId, std::map* fieldMapClip) +void CShapefile::InsertShapesVector(IShapefile* sf, vector& vShapes, + IShapefile* sfSubject, long subjectId, std::map* fieldMapSubject, + IShapefile* sfClip, long clipId, std::map* fieldMapClip) { - long numFieldSubject, numFieldsClip; - sfSubject->get_NumFields(&numFieldSubject); - if (sfClip) - sfClip->get_NumFields(&numFieldsClip); - - VARIANT_BOOL vbretval; - - ShpfileType fileType; - sf->get_ShapefileType(&fileType); - - CComVariant var; - - IGeoProjection* proj = NULL; - sf->get_GeoProjection(&proj); - VARIANT_BOOL isGeographic; - proj->get_IsGeographic(&isGeographic); - proj->Release(); - - for (int i = 0; i < (int)vShapes.size(); i++) - { - IShape* shp = NULL; - shp = vShapes[i]; - - ShpfileType shpType; - shp->get_ShapeType(&shpType); - - if (shpType == fileType) - { - // area checking - shpType = ShapeUtility::Convert2D(shpType); - if (shpType == SHP_POLYGON) - { - double area; - shp->get_Area(&area); - if (area < m_globalSettings.GetMinPolygonArea(isGeographic)) - { - shp->Release(); - continue; - } - - double perimeter; - shp->get_Perimeter(&perimeter); - - if (isGeographic) - area *= METERS_PER_DEGREE; // comparison is performed in meters, area will grow as square of linear size - // we multilpy area only; in reality:((area * c^2)/ (perimeter * c)) - - if (area/perimeter < m_globalSettings.minAreaToPerimeterRatio) - { - shp->Release(); - continue; - } - } - - long newId; - sf->get_NumShapes(&newId); - sf->EditInsertShape(shp, &newId, &vbretval); - - // passing the values from subject shapefile - // can be optimized for not extracting the same values many times - if (fieldMapSubject) - { - - std::map::iterator p = fieldMapSubject->begin(); - while (p != fieldMapSubject->end()) - { - sfSubject->get_CellValue(p->first, subjectId, &var); - sf->EditCellValue(p->second, newId, var, &vbretval); - ++p; - } - } - else - { - for (int iFld = 0; iFld < numFieldSubject; iFld++) - { - sfSubject->get_CellValue(iFld, subjectId, &var); - sf->EditCellValue(iFld, newId, var, &vbretval); - } - } - - // passing the values from clip shapefile - if ( sfClip && fieldMapClip) - { - std::map::iterator p = fieldMapClip->begin(); - while (p != fieldMapClip->end()) - { - sfClip->get_CellValue(p->first, clipId, &var); - sf->EditCellValue(p->second, newId, var, &vbretval); - ++p; - } - } - } - shp->Release(); - } + long numFieldSubject, numFieldsClip; + sfSubject->get_NumFields(&numFieldSubject); + if (sfClip) + sfClip->get_NumFields(&numFieldsClip); + + VARIANT_BOOL vbretval; + + ShpfileType fileType; + sf->get_ShapefileType(&fileType); + + CComVariant var; + + VARIANT_BOOL isGeographic = VARIANT_FALSE; + sf->get_IsGeographicProjection(&isGeographic); + + for (auto shp : vShapes) + { + ShpfileType shpType; + shp->get_ShapeType(&shpType); + + if (shpType == fileType) + { + // area checking + shpType = ShapeUtility::Convert2D(shpType); + if (shpType == SHP_POLYGON) + { + double area; + shp->get_Area(&area); + if (area < m_globalSettings.GetMinPolygonArea(isGeographic)) + { + shp->Release(); + continue; + } + + double perimeter; + shp->get_Perimeter(&perimeter); + + if (isGeographic) + area *= METERS_PER_DEGREE; + // comparison is performed in meters, area will grow as square of linear size + // we multilpy area only; in reality:((area * c^2)/ (perimeter * c)) + + if (area / perimeter < m_globalSettings.minAreaToPerimeterRatio) + { + shp->Release(); + continue; + } + } + + long newId; + sf->get_NumShapes(&newId); + sf->EditInsertShape(shp, &newId, &vbretval); + + // passing the values from subject shapefile + // can be optimized for not extracting the same values many times + if (fieldMapSubject) + { + auto p = fieldMapSubject->begin(); + while (p != fieldMapSubject->end()) + { + sfSubject->get_CellValue(p->first, subjectId, &var); + sf->EditCellValue(p->second, newId, var, &vbretval); + ++p; + } + } + else + { + for (int iFld = 0; iFld < numFieldSubject; iFld++) + { + sfSubject->get_CellValue(iFld, subjectId, &var); + sf->EditCellValue(iFld, newId, var, &vbretval); + } + } + + // passing the values from clip shapefile + if (sfClip && fieldMapClip) + { + auto p = fieldMapClip->begin(); + while (p != fieldMapClip->end()) + { + sfClip->get_CellValue(p->first, clipId, &var); + sf->EditCellValue(p->second, newId, var, &vbretval); + ++p; + } + } + } + shp->Release(); + } } // ********************************************************************** @@ -179,41 +175,41 @@ void CShapefile::InsertShapesVector(IShapefile* sf, vector& vShapes, // A utility function to add geometry produced by simplification routine to the sfTarget shapefile, // with copying of attributes from source shapefile. // initShapeIndex - the index of shape to copy the attribute from -bool InsertGeosGeometry(IShapefile* sfTarget, GEOSGeometry* gsNew, IShapefile* sfSouce, int initShapeIndex ) +bool InsertGeosGeometry(IShapefile* sfTarget, GEOSGeometry* gsNew, IShapefile* sfSouce, int initShapeIndex) { - if (gsNew) - { - ShpfileType shpType; - sfTarget->get_ShapefileType(&shpType); - bool isM = ShapeUtility::IsM(shpType); - - std::vector shapes; - if (GeosConverter::GeomToShapes(gsNew, &shapes, isM)) - { - long index, numFields; - VARIANT_BOOL vbretval; - sfTarget->get_NumFields(&numFields); - - for (unsigned int j = 0; j < shapes.size(); j++) - { - sfTarget->get_NumShapes(&index); - sfTarget->EditInsertShape(shapes[j], &index, &vbretval); - - if (vbretval) - { - CComVariant val; - for (int f = 0; f < numFields; f++) - { - sfSouce->get_CellValue(f, initShapeIndex, &val); - sfTarget->EditCellValue(f, index, val, &vbretval); - } - } - shapes[j]->Release(); - } - return true; - } - } - return false; + if (gsNew) + { + ShpfileType shpType; + sfTarget->get_ShapefileType(&shpType); + const bool isM = ShapeUtility::IsM(shpType); + + std::vector shapes; + if (GeosConverter::GeomToShapes(gsNew, &shapes, isM)) + { + long index, numFields; + VARIANT_BOOL vbretval; + sfTarget->get_NumFields(&numFields); + + for (auto& shape : shapes) + { + sfTarget->get_NumShapes(&index); + sfTarget->EditInsertShape(shape, &index, &vbretval); + + if (vbretval) + { + CComVariant val; + for (int f = 0; f < numFields; f++) + { + sfSouce->get_CellValue(f, initShapeIndex, &val); + sfTarget->EditCellValue(f, index, val, &vbretval); + } + } + shape->Release(); + } + return true; + } + } + return false; } // ******************************************************************** @@ -223,49 +219,49 @@ bool InsertGeosGeometry(IShapefile* sfTarget, GEOSGeometry* gsNew, IShapefile* s // The row index of attribute table are taken from the key property of the shape void CopyShape(IShapefile* sfSource, IShape* shp, IShapefile* sfResult) { - if (shp) - { - USES_CONVERSION; - CComBSTR key; - shp->get_Key(&key); - CString str = OLE2CA(key); - int initIndex = atoi(str); - - VARIANT_BOOL isEditingShapes, vbretval; - sfSource->get_EditingShapes(&isEditingShapes); - - LONG numShapes; - sfResult->get_NumShapes(&numShapes); - - if (isEditingShapes) - { - // in the editing mode we share a shape with the parent shapefile - // a copy is needed to avoid conflicts - IShape* shpCopy = NULL; - shp->Clone(&shpCopy); - sfResult->EditInsertShape(shpCopy, &numShapes, &vbretval); - shpCopy->Release(); - } - else - { - sfResult->EditInsertShape(shp, &numShapes, &vbretval); - } - - if (vbretval) - { - LONG numFields; - sfSource->get_NumFields(&numFields); - - // copy attributes - CComVariant val; - for (int iFld = 0; iFld < numFields; iFld++) - { - sfSource->get_CellValue(iFld, initIndex, &val); - sfResult->EditCellValue(iFld, numShapes, val, &vbretval); - } - } - shp->Release(); - } + if (shp) + { + USES_CONVERSION; + CComBSTR key; + shp->get_Key(&key); + const CString str = OLE2CA(key); + const int initIndex = atoi(str); + + VARIANT_BOOL isEditingShapes, vbretval; + sfSource->get_EditingShapes(&isEditingShapes); + + LONG numShapes; + sfResult->get_NumShapes(&numShapes); + + if (isEditingShapes) + { + // in the editing mode we share a shape with the parent shapefile + // a copy is needed to avoid conflicts + IShape* shpCopy = nullptr; + shp->Clone(&shpCopy); + sfResult->EditInsertShape(shpCopy, &numShapes, &vbretval); + shpCopy->Release(); + } + else + { + sfResult->EditInsertShape(shp, &numShapes, &vbretval); + } + + if (vbretval) + { + LONG numFields; + sfSource->get_NumFields(&numFields); + + // copy attributes + CComVariant val; + for (int iFld = 0; iFld < numFields; iFld++) + { + sfSource->get_CellValue(iFld, initIndex, &val); + sfResult->EditCellValue(iFld, numShapes, val, &vbretval); + } + } + shp->Release(); + } } #pragma endregion @@ -277,159 +273,154 @@ void CopyShape(IShapefile* sfSource, IShape* shp, IShapefile* sfResult) // Returns numbers(ids) of shapes of this shapefile which are situated // in certain spatial relation to the shapes of the input shapefile. -STDMETHODIMP CShapefile::SelectByShapefile(IShapefile* sf, tkSpatialRelation Relation, - VARIANT_BOOL SelectedOnly, VARIANT* arr, ICallback* cBack, VARIANT_BOOL *retval) +STDMETHODIMP CShapefile::SelectByShapefile(IShapefile* sf, tkSpatialRelation Relation, + VARIANT_BOOL SelectedOnly, VARIANT* arr, ICallback* cBack, + VARIANT_BOOL* retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - USES_CONVERSION; - *retval = VARIANT_FALSE; - - if(_globalCallback == NULL && cBack != NULL) - { - _globalCallback = cBack; - _globalCallback->AddRef(); - } - - if( sf == NULL) - { - ErrorMessage( tkUNEXPECTED_NULL_PARAMETER ); - return S_OK; - } - - long _numShapes1, _numShapes2; - _numShapes1 = _shapeData.size(); - sf->get_NumShapes(&_numShapes2); - if (_numShapes1 == 0)return NULL; - if (_numShapes2 == 0) return NULL; - - QTree* qTree = this->GenerateQTreeCore(false); - if (!qTree) - { - ErrorMessage(tkFAILED_TO_BUILD_SPATIAL_INDEX); - return NULL; - } - - // ids of selected shapes - set result; - - // to avoid converting same shapes to ogr geometry multiple times - vector vGeometries; - vGeometries.assign(_numShapes1, NULL); - - vector* data = ((CShapefile*)sf)->get_ShapeVector(); - - long percent = 0; - for(long shapeid2 = 0; shapeid2 < _numShapes2; shapeid2++) - { - CallbackHelper::Progress(_globalCallback, shapeid2, _numShapes2, "Calculating...", _key, percent); - - if (SelectedOnly && !(*data)[shapeid2]->selected()) - continue; - - vector shapeIds; - double xMin, xMax, yMin, yMax; - ((CShapefile*)sf)->QuickExtentsCore(shapeid2, &xMin, &yMin, &xMax, &yMax); - shapeIds = qTree->GetNodes(QTreeExtent(xMin,xMax,yMax,yMin)); - - if (shapeIds.size() > 0) - { - IShape* shp2 = NULL; - OGRGeometry* oGeom2 = NULL; - sf->get_Shape(shapeid2, &shp2); - oGeom2 = OgrConverter::ShapeToGeometry(shp2); - shp2->Release(); - - if (oGeom2 != NULL) - { - for (int j=0; j < (int)shapeIds.size(); j++) - { - long shapeId1 = shapeIds[j]; - - // shape wasn't selected so far - if (result.find(shapeId1) == result.end()) - { - OGRGeometry* oGeom1 = NULL; - - if (vGeometries[shapeId1] == NULL) - { - IShape* shp1 = NULL; - this->get_Shape(shapeId1, &shp1); - oGeom1 = OgrConverter::ShapeToGeometry(shp1); - shp1->Release(); - vGeometries[shapeId1] = oGeom1; - } - else - { - oGeom1 = vGeometries[shapeId1]; - } - - OGRBoolean res; - if (Relation == srContains) res = oGeom1->Contains(oGeom2); - else if (Relation == srCrosses) res = oGeom1->Crosses(oGeom2); - else if (Relation == srEquals) res = oGeom1->Equal(oGeom2); - else if (Relation == srIntersects) res = oGeom1->Intersect(oGeom2); - else if (Relation == srDisjoint) res = oGeom1->Intersect(oGeom2); - else if (Relation == srOverlaps) res = oGeom1->Overlaps(oGeom2); - else if (Relation == srTouches) res = oGeom1->Touches(oGeom2); - else if (Relation == srWithin) res = oGeom1->Within(oGeom2); - - if (res) - { - result.insert(shapeId1); - } - } - } - OGRGeometryFactory::destroyGeometry(oGeom2); - } - } - } - - // disjoint is opposite to intersects; so to get it we need to find shapes that do intersect with input - // and then invert the selection - if (Relation == srDisjoint) - { - vector v; - set::iterator p = result.begin(); - bool include; - - for (int i = 0; i < _numShapes1; i++) - { - include = true; - while(p != result.end()) - { - if (*p == i) - include = false; // this shape actually intersects, so it can't be disjointed - - if (*p > i) - break; - - ++p; - } - if (include) - { - v.push_back(i); - } - } - *retval = Templates::Vector2SafeArray(&v, VT_I4, arr); - } - else - { - *retval = Templates::Set2SafeArray(&result, VT_I4, arr); - } - - // cleaning - CallbackHelper::ProgressCompleted(_globalCallback, _key); - - for(int i = 0; i < (int)vGeometries.size(); i++) - { - if (vGeometries[i] !=NULL) - OGRGeometryFactory::destroyGeometry(vGeometries[i]); - } - - if (qTree !=NULL) - delete qTree; - - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + USES_CONVERSION; + *retval = VARIANT_FALSE; + + if (_globalCallback == nullptr && cBack != nullptr) + { + _globalCallback = cBack; + _globalCallback->AddRef(); + } + + if (sf == nullptr) + { + ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); + return S_OK; + } + + long _numShapes2; + const long _numShapes1 = _shapeData.size(); + sf->get_NumShapes(&_numShapes2); + if (_numShapes1 == 0)return NULL; + if (_numShapes2 == 0) return NULL; + + QTree* qTree = this->GenerateQTreeCore(false); + if (!qTree) + { + ErrorMessage(tkFAILED_TO_BUILD_SPATIAL_INDEX); + return NULL; + } + + // ids of selected shapes + set result; + + // to avoid converting same shapes to ogr geometry multiple times + vector vGeometries; + vGeometries.assign(_numShapes1, nullptr); + + vector* data = ((CShapefile*)sf)->get_ShapeVector(); + + long percent = 0; + for (long shapeid2 = 0; shapeid2 < _numShapes2; shapeid2++) + { + CallbackHelper::Progress(_globalCallback, shapeid2, _numShapes2, "Calculating...", _key, percent); + + if (SelectedOnly && !(*data)[shapeid2]->selected()) + continue; + + double xMin, xMax, yMin, yMax; + ((CShapefile*)sf)->QuickExtentsCore(shapeid2, &xMin, &yMin, &xMax, &yMax); + vector shapeIds = qTree->GetNodes(QTreeExtent(xMin, xMax, yMax, yMin)); + + if (!shapeIds.empty()) + { + IShape* shp2 = nullptr; + sf->get_Shape(shapeid2, &shp2); + OGRGeometry* oGeom2 = OgrConverter::ShapeToGeometry(shp2); + shp2->Release(); + + if (oGeom2 != nullptr) + { + for (long shapeId1 : shapeIds) + { + // shape wasn't selected so far + if (result.find(shapeId1) == result.end()) + { + OGRGeometry* oGeom1; + + if (vGeometries[shapeId1] == nullptr) + { + IShape* shp1 = nullptr; + this->get_Shape(shapeId1, &shp1); + oGeom1 = OgrConverter::ShapeToGeometry(shp1); + shp1->Release(); + vGeometries[shapeId1] = oGeom1; + } + else + { + oGeom1 = vGeometries[shapeId1]; + } + + OGRBoolean res = 0; + if (Relation == srContains) res = oGeom1->Contains(oGeom2); + else if (Relation == srCrosses) res = oGeom1->Crosses(oGeom2); + else if (Relation == srEquals) res = oGeom1->Equals(oGeom2); + else if (Relation == srIntersects) res = oGeom1->Intersects(oGeom2); + else if (Relation == srDisjoint) res = oGeom1->Intersects(oGeom2); + else if (Relation == srOverlaps) res = oGeom1->Overlaps(oGeom2); + else if (Relation == srTouches) res = oGeom1->Touches(oGeom2); + else if (Relation == srWithin) res = oGeom1->Within(oGeom2); + + if (res) + { + result.insert(shapeId1); + } + } + } + OGRGeometryFactory::destroyGeometry(oGeom2); + } + } + } + + // disjoint is opposite to intersects; so to get it we need to find shapes that do intersect with input + // and then invert the selection + if (Relation == srDisjoint) + { + vector v; + auto p = result.begin(); + + for (int i = 0; i < _numShapes1; i++) + { + bool include = true; + while (p != result.end()) + { + if (*p == i) + include = false; // this shape actually intersects, so it can't be disjointed + + if (*p > i) + break; + + ++p; + } + if (include) + { + v.push_back(i); + } + } + *retval = Templates::Vector2SafeArray(&v, VT_I4, arr); + } + else + { + *retval = Templates::Set2SafeArray(&result, VT_I4, arr); + } + + // cleaning + CallbackHelper::ProgressCompleted(_globalCallback, _key); + + for (auto& vGeometrie : vGeometries) + { + if (vGeometrie != nullptr) + OGRGeometryFactory::destroyGeometry(vGeometrie); + } + + delete qTree; + + return S_OK; } // *********************************************************** @@ -440,71 +431,67 @@ STDMETHODIMP CShapefile::SelectByShapefile(IShapefile* sf, tkSpatialRelation Rel // a substitute for SelectShapes function after some improvements. // returns VARIANT_TRUE when shapes were selected, and VARIANT_FALSE if // no shapes fell into selection -VARIANT_BOOL CShapefile::SelectShapesAlt(IExtents *BoundBox, double Tolerance, SelectMode SelectMode, VARIANT* arr) +VARIANT_BOOL CShapefile::SelectShapesAlt(IExtents* BoundBox, double Tolerance, SelectMode SelectMode, VARIANT* arr) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - VARIANT_BOOL retval = VARIANT_FALSE; - - double xMin, xMax, yMin, yMax, zMin, zMax; - BoundBox->GetBounds(&xMin,&yMin,&zMin,&xMax,&yMax,&zMax); - - if( xMin == xMax && yMin == yMax ) - { - xMax +=1; - yMax +=1; - } - - QTree* qTree = this->GenerateQTreeCore(false); - if (!qTree) - { - ErrorMessage(tkFAILED_TO_BUILD_SPATIAL_INDEX); - return NULL; - } - - if( Tolerance > 0.0 ) - { xMin = xMin - Tolerance/2; - yMin = yMin - Tolerance/2; - xMax = xMax + Tolerance/2; - yMax = yMax + Tolerance/2; - } - - BoundBox->SetBounds(xMin, yMin, zMin, xMax, yMax, zMax); - - set results; - vector shapeIds; - shapeIds = qTree->GetNodes(QTreeExtent(xMin,xMax,yMax,yMin)); - - IShape* temp = NULL; - OGRGeometry* oBox; - ((CExtents*)BoundBox)->ToShape(&temp); - oBox = OgrConverter::ShapeToGeometry(temp); - temp->Release(); - - for(int i=0; i < (int)shapeIds.size(); i++) - { - IShape* shp = NULL; - OGRGeometry* oGeom; - OGRBoolean res = 0; - this->get_Shape((long)shapeIds[i], &shp); - oGeom = OgrConverter::ShapeToGeometry(shp); - shp->Release(); - - res = oBox->Contains(oGeom); - - if (!res) - res = oGeom->Contains(oBox); - - if( SelectMode == INTERSECTION && !res) - res = oGeom->Intersect (oBox); - - if (res) results.insert(shapeIds[i]); - OGRGeometryFactory::destroyGeometry(oGeom); - } - - if (qTree!=NULL) delete qTree; - if (oBox!=NULL) OGRGeometryFactory::destroyGeometry(oBox); - retval = Templates::Set2SafeArray(&results, VT_I4, arr); - return retval; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + double xMin, xMax, yMin, yMax, zMin, zMax; + BoundBox->GetBounds(&xMin, &yMin, &zMin, &xMax, &yMax, &zMax); + + if (xMin == xMax && yMin == yMax) + { + xMax += 1; + yMax += 1; + } + + QTree* qTree = this->GenerateQTreeCore(false); + if (!qTree) + { + ErrorMessage(tkFAILED_TO_BUILD_SPATIAL_INDEX); + return NULL; + } + + if (Tolerance > 0.0) + { + xMin = xMin - Tolerance / 2; + yMin = yMin - Tolerance / 2; + xMax = xMax + Tolerance / 2; + yMax = yMax + Tolerance / 2; + } + + BoundBox->SetBounds(xMin, yMin, zMin, xMax, yMax, zMax); + + set results; + vector shapeIds = qTree->GetNodes(QTreeExtent(xMin, xMax, yMax, yMin)); + + IShape* temp = nullptr; + ((CExtents*)BoundBox)->ToShape(&temp); + OGRGeometry* oBox = OgrConverter::ShapeToGeometry(temp); + temp->Release(); + + for (int shapeId : shapeIds) + { + IShape* shp = nullptr; + this->get_Shape((long)shapeId, &shp); + OGRGeometry* oGeom = OgrConverter::ShapeToGeometry(shp); + shp->Release(); + + OGRBoolean res = oBox->Contains(oGeom); + + if (!res) + res = oGeom->Contains(oBox); + + if (SelectMode == INTERSECTION && !res) + res = oGeom->Intersects(oBox); + + if (res) results.insert(shapeId); + OGRGeometryFactory::destroyGeometry(oGeom); + } + + delete qTree; + if (oBox != nullptr) OGRGeometryFactory::destroyGeometry(oBox); + const VARIANT_BOOL retval = Templates::Set2SafeArray(&results, VT_I4, arr); + return retval; } #pragma endregion @@ -517,19 +504,20 @@ VARIANT_BOOL CShapefile::SelectShapesAlt(IExtents *BoundBox, double Tolerance, S // field. Shapes with the same attribute are merged into one. STDMETHODIMP CShapefile::Dissolve(long FieldIndex, VARIANT_BOOL SelectedOnly, IShapefile** sf) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - DissolveCore(FieldIndex, SelectedOnly, NULL, sf); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + DissolveCore(FieldIndex, SelectedOnly, nullptr, sf); + return S_OK; } // ************************************************************* // DissolveWithStats() // ************************************************************* -STDMETHODIMP CShapefile::DissolveWithStats(long FieldIndex, VARIANT_BOOL SelectedOnly, IFieldStatOperations* statOperations, IShapefile** sf) +STDMETHODIMP CShapefile::DissolveWithStats(long FieldIndex, VARIANT_BOOL SelectedOnly, + IFieldStatOperations* statOperations, IShapefile** sf) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - DissolveCore(FieldIndex, SelectedOnly, statOperations, sf); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + DissolveCore(FieldIndex, SelectedOnly, statOperations, sf); + return S_OK; } // ************************************************************* @@ -537,66 +525,67 @@ STDMETHODIMP CShapefile::DissolveWithStats(long FieldIndex, VARIANT_BOOL Selecte // ************************************************************* // Merges shapes of the shapefile based on the attribute of the given // field. Shapes with the same attribute are merged into one. -void CShapefile::DissolveCore(long FieldIndex, VARIANT_BOOL SelectedOnly, IFieldStatOperations* operations, IShapefile** sf) +void CShapefile::DissolveCore(long FieldIndex, VARIANT_BOOL SelectedOnly, IFieldStatOperations* operations, + IShapefile** sf) { - // ---------------------------------------------- - // Validation - // ---------------------------------------------- - long numFields; - this->get_NumFields(&numFields); - - if( FieldIndex < 0 || FieldIndex >= numFields) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - return; - } - - if (!ValidateInput(this, "Dissolve", "this", SelectedOnly)) - return; - - // ---------------------------------------------- - // Creating output - // ---------------------------------------------- - ShpfileType type = _shpfiletype; - - // for points change to multi-point type, as there is no other way to group them - if (type == SHP_POINT) type = SHP_MULTIPOINT; - if (type == SHP_POINTZ) type = SHP_MULTIPOINTZ; - if (type == SHP_POINTM) type = SHP_MULTIPOINTM; - - // ShapefileHelper::CloneNoFields(this, sf, type); - // ----------------------------------------------- - // Creating output - // ----------------------------------------------- - if (!ShapefileHelper::CloneNoFields(this, sf, type)) - { - // Get errorcode and pass the source: - long errorCode; - (*sf)->get_LastErrorCode(&errorCode); - *sf = NULL; - ErrorMessage(errorCode); - return; - } - CloneField(this, *sf, FieldIndex, -1); - - // ------------------------------------------- - // processing - // ------------------------------------------- - if (_geometryEngine == engineGeos) - { - this->DissolveGEOS(FieldIndex, SelectedOnly, operations, *sf); - } - else - { - this->DissolveClipper(FieldIndex, SelectedOnly, operations, *sf); - } - - // ------------------------------------------- - // output validation - // ------------------------------------------- - CallbackHelper::ProgressCompleted(_globalCallback, _key); - ValidateOutput(sf, "Dissolve"); - return; + // ---------------------------------------------- + // Validation + // ---------------------------------------------- + long numFields; + this->get_NumFields(&numFields); + + if (FieldIndex < 0 || FieldIndex >= numFields) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + return; + } + + if (!ValidateInput(this, "Dissolve", "this", SelectedOnly)) + return; + + // ---------------------------------------------- + // Creating output + // ---------------------------------------------- + ShpfileType type = _shpfiletype; + + // for points change to multi-point type, as there is no other way to group them + if (type == SHP_POINT) type = SHP_MULTIPOINT; + if (type == SHP_POINTZ) type = SHP_MULTIPOINTZ; + if (type == SHP_POINTM) type = SHP_MULTIPOINTM; + + // ShapefileHelper::CloneNoFields(this, sf, type); + // ----------------------------------------------- + // Creating output + // ----------------------------------------------- + if (!ShapefileHelper::CloneNoFields(this, sf, type)) + { + // Get errorcode and pass the source: + long errorCode; + (*sf)->get_LastErrorCode(&errorCode); + *sf = nullptr; + ErrorMessage(errorCode); + return; + } + CloneField(this, *sf, FieldIndex, -1); + + // ------------------------------------------- + // processing + // ------------------------------------------- + if (_geometryEngine == engineGeos) + { + this->DissolveGEOS(FieldIndex, SelectedOnly, operations, *sf); + } + else + { + this->DissolveClipper(FieldIndex, SelectedOnly, operations, *sf); + } + + // ------------------------------------------- + // output validation + // ------------------------------------------- + CallbackHelper::ProgressCompleted(_globalCallback, _key); + ValidateOutput(sf, "Dissolve"); + return; } // ************************************************************* @@ -604,519 +593,526 @@ void CShapefile::DissolveCore(long FieldIndex, VARIANT_BOOL SelectedOnly, IField // ************************************************************* char* GetStatOperationName(tkFieldStatOperation op) { - switch(op) - { - case fsoSum: - return "SUM"; - case fsoAvg: - return "AVG"; - case fsoMin: - return "MIN"; - case fsoMax: - return "MAX"; - case fsoMode: - return "MOD"; - case fsoWeightedAvg: - return "WAVG"; - default: - return ""; - } + switch (op) + { + case fsoSum: + return "SUM"; + case fsoAvg: + return "AVG"; + case fsoMin: + return "MIN"; + case fsoMax: + return "MAX"; + case fsoMode: + return "MOD"; + case fsoWeightedAvg: + return "WAVG"; + default: + return ""; + } } // ************************************************************* // CalculateFieldStats() // ************************************************************* -void CShapefile::CalculateFieldStats(map*>& fieldMap, IFieldStatOperations* ioperations, IShapefile* sf) +void CShapefile::CalculateFieldStats(map*>& fieldMap, IFieldStatOperations* ioperations, + IShapefile* sf) { - // -------------------------------------------- - // validating operations - // -------------------------------------------- - USES_CONVERSION; - long numFields; - this->get_NumFields(&numFields); - std::vector* operations = &((CFieldStatOperations*)ioperations)->_operations; - - VARIANT_BOOL vb; - ioperations->Validate(this, &vb); - - for(size_t i = 0; i < operations->size(); i++) - { - FieldOperation* op = (*operations)[i]; - - // creating output field for operation - if (op->valid) - { - IField* field = NULL; - this->get_Field(op->fieldIndex, &field); - if (field) - { - CComBSTR bstr; - field->get_Name(&bstr); - CStringW name = OLE2W(bstr); - name = name + "_" + A2W(GetStatOperationName(op->operation)); - - FieldType type; - field->get_Type(&type); - if (type == INTEGER_FIELD && op->operation == fsoAvg) - type = DOUBLE_FIELD; - - long precision; - field->get_Precision(&precision); - long width; - field->get_Width(&width); - - long fieldIndex; - CComBSTR bstrName(name); - sf->EditAddField(bstrName, type, precision, width, &fieldIndex); - - op->targetIndex = fieldIndex; - op->targetFieldType = type; - field->Release(); - } - } - } - - // make sure that output field names are unique - FieldHelper::UniqueFieldNames(sf); - - // -------------------------------------------- - // calculating - // -------------------------------------------- - double areaSum; - double area; - double val; - double sum = 0.0; - CString sVal; - CString sSum; - CComVariant var; - int index = 0; - int size = fieldMap.size(); - long percent = 0; - map frequencies; - - ShpfileType type; - sf->get_ShapefileType(&type); - type = ShapeUtility::Convert2D(type); - - map *>::iterator p = fieldMap.begin(); // row in the output, rows in the input - while(p != fieldMap.end()) - { - CallbackHelper::Progress(_globalCallback, index, size, "Calculating stats", _key, percent); - - for(size_t i = 0; i < operations->size(); i++) - { - FieldOperation* op = (*operations)[i]; - if (!op->valid) continue; - - if (op->targetFieldType == STRING_FIELD) - { - sVal = ""; - frequencies.clear(); - for(size_t j = 0; j < p->second->size(); j++) - { - this->get_CellValue(op->fieldIndex, (*p->second)[j], &var); - stringVal(var, sVal); - - if (op->operation == fsoMode) - { - frequencies[sVal] = frequencies.find(sVal) == frequencies.end() ? 1 : frequencies[sVal] + 1; - } - else - { - // take the first one, as output despite the operation - if (sSum.GetLength() == 0) - { - sSum = sVal; - } - else - { - int res = sSum.CompareNoCase(sVal); - if ((res < 0 && op->operation == fsoMax) || - (res > 0 && op->operation == fsoMin)) - { - sSum = sVal; - } - } - } - } - - if (op->operation == fsoMode) - { - int max = INT_MIN; - map ::iterator p = frequencies.begin(); - while(p != frequencies.end()) - { - if (max < p->second) { - max = p->second; - sSum = p->first; - } - ++p; - } - } - - CComVariant result(sSum); - sf->EditCellValue(op->targetIndex, p->first, result, &vb); - } - else - { - switch(op->operation) - { - case fsoSum: - case fsoAvg: - case fsoWeightedAvg: - sum = 0.0; - areaSum = 0.0; - break; - case fsoMin: - sum = DBL_MAX; - break; - case fsoMax: - sum = DBL_MIN; - break; - } - - // going through rows of original shapefile, which fell into the same group - for(size_t j = 0; j < p->second->size(); j++) - { - this->get_CellValue(op->fieldIndex, (*p->second)[j], &var); - dVal(var, val); - - switch(op->operation) - { - case fsoSum: - case fsoAvg: - sum += val; - break; - case fsoMin: - if (val < sum) sum = val; - case fsoMax: - if (val > sum) sum = val; - break; - case fsoWeightedAvg: - { - if (type == SHP_POLYGON || type == SHP_POLYLINE) - { - IShape* shp = NULL; - this->GetValidatedShape((*p->second)[j], &shp); - if (shp) - { - if (type == SHP_POLYGON) shp->get_Area(&area); - else shp->get_Length(&area); // weighting by length of polylines - shp->Release(); - areaSum += area; - sum += val * area; - } - } - else - { - sum += val; // regular sum for points - } - } - break; - } - } - - if (op->operation == fsoAvg) - { - sum /= p->second->size(); - } - if (op->operation == fsoWeightedAvg) - { - sum /= areaSum; - } - - if (op->targetFieldType == INTEGER_FIELD) - { - CComVariant result(Utility::Rint(sum)); - sf->EditCellValue(op->targetIndex, p->first, result, &vb); - } - else - { - CComVariant result(sum); - sf->EditCellValue(op->targetIndex, p->first, result, &vb); - } - } - } - index++; - ++p; - } - - CallbackHelper::ProgressCompleted(_globalCallback, _key); + // -------------------------------------------- + // validating operations + // -------------------------------------------- + USES_CONVERSION; + long numFields; + this->get_NumFields(&numFields); + std::vector* operations = &((CFieldStatOperations*)ioperations)->_operations; + + VARIANT_BOOL vb; + ioperations->Validate(this, &vb); + + for (auto op : *operations) + { + // creating output field for operation + if (op->valid) + { + IField* field = nullptr; + this->get_Field(op->fieldIndex, &field); + if (field) + { + CComBSTR bstr; + field->get_Name(&bstr); + CStringW name = OLE2W(bstr); + name = name + "_" + A2W(GetStatOperationName(op->operation)); + + FieldType type; + field->get_Type(&type); + if (type == INTEGER_FIELD && op->operation == fsoAvg) + type = DOUBLE_FIELD; + + long precision; + field->get_Precision(&precision); + long width; + field->get_Width(&width); + + long fieldIndex; + const CComBSTR bstrName(name); + sf->EditAddField(bstrName, type, precision, width, &fieldIndex); + + op->targetIndex = fieldIndex; + op->targetFieldType = type; + field->Release(); + } + } + } + + // make sure that output field names are unique + FieldHelper::UniqueFieldNames(sf); + + // -------------------------------------------- + // calculating + // -------------------------------------------- + double areaSum = 0; + double area; + double val; + double sum = 0.0; + CString sSum; + CComVariant var; + int index = 0; + const int size = fieldMap.size(); + long percent = 0; + map frequencies; + + ShpfileType type; + sf->get_ShapefileType(&type); + type = ShapeUtility::Convert2D(type); + + auto p = fieldMap.begin(); // row in the output, rows in the input + while (p != fieldMap.end()) + { + CallbackHelper::Progress(_globalCallback, index, size, "Calculating stats", _key, percent); + + for (auto op : *operations) + { + if (!op->valid) continue; + + if (op->targetFieldType == STRING_FIELD) + { + CString sVal = ""; + frequencies.clear(); + for (int j : *p->second) + { + this->get_CellValue(op->fieldIndex, j, &var); + stringVal(var, sVal); + + if (op->operation == fsoMode) + { + frequencies[sVal] = frequencies.find(sVal) == frequencies.end() ? 1 : frequencies[sVal] + 1; + } + else + { + // take the first one, as output despite the operation + if (sSum.GetLength() == 0) + { + sSum = sVal; + } + else + { + const int res = sSum.CompareNoCase(sVal); + if (res < 0 && op->operation == fsoMax || + res > 0 && op->operation == fsoMin) + { + sSum = sVal; + } + } + } + } + + if (op->operation == fsoMode) + { + int max = INT_MIN; + auto frequency = frequencies.begin(); + while (frequency != frequencies.end()) + { + if (max < frequency->second) + { + max = frequency->second; + sSum = frequency->first; + } + ++frequency; + } + } + + const CComVariant result(sSum); + sf->EditCellValue(op->targetIndex, p->first, result, &vb); + } + else + { + switch (op->operation) + { + case fsoSum: + case fsoAvg: + case fsoWeightedAvg: + sum = 0.0; + areaSum = 0.0; + break; + case fsoMin: + sum = DBL_MAX; + break; + case fsoMax: + sum = DBL_MIN; + break; + case fsoMode: break; + default: ; + } + + // going through rows of original shapefile, which fell into the same group + for (int j : *p->second) + { + this->get_CellValue(op->fieldIndex, j, &var); + dVal(var, val); + + switch (op->operation) + { + case fsoSum: + case fsoAvg: + sum += val; + break; + case fsoMin: + if (val < sum) sum = val; + case fsoMax: + if (val > sum) sum = val; + break; + case fsoWeightedAvg: + { + if (type == SHP_POLYGON || type == SHP_POLYLINE) + { + IShape* shp = nullptr; + this->GetValidatedShape(j, &shp); + if (shp) + { + if (type == SHP_POLYGON) shp->get_Area(&area); + else shp->get_Length(&area); // weighting by length of polylines + shp->Release(); + areaSum += area; + sum += val * area; + } + } + else + { + sum += val; // regular sum for points + } + } + break; + case fsoMode: break; + default: ; + } + } + + if (op->operation == fsoAvg) + { + sum /= p->second->size(); + } + if (op->operation == fsoWeightedAvg) + { + sum /= areaSum; + } + + if (op->targetFieldType == INTEGER_FIELD) + { + const CComVariant result(Utility::Rint(sum)); + sf->EditCellValue(op->targetIndex, p->first, result, &vb); + } + else + { + const CComVariant result(sum); + sf->EditCellValue(op->targetIndex, p->first, result, &vb); + } + } + } + index++; + ++p; + } + + CallbackHelper::ProgressCompleted(_globalCallback, _key); } // ************************************************************* // DissolveGEOS() // ************************************************************* -void CShapefile::DissolveGEOS(long FieldIndex, VARIANT_BOOL SelectedOnly, IFieldStatOperations* operations, IShapefile* sf) +void CShapefile::DissolveGEOS(long FieldIndex, VARIANT_BOOL SelectedOnly, IFieldStatOperations* operations, + IShapefile* sf) { - map *> fieldMap; // index in output, indices in input - map *> indicesMap; // value in input, indices in input - map *> shapeMap; - - CComVariant val; // VARIANT hasn't got comparison operators and therefore - // can't be used with associative containers - - bool calcStats = false; - if (operations) - { - int count; - operations->get_Count(&count); - calcStats = count > 0; - } - - ReadGeosGeometries(SelectedOnly); - - long percent = 0; - int size = (int)_shapeData.size(); - for(long i = 0; i < size; i++) - { - CallbackHelper::Progress(_globalCallback, i, size, "Grouping shapes...", _key, percent); - - if (!ShapeAvailable(i, SelectedOnly)) - continue; - - GEOSGeometry* gsGeom = this->GetGeosGeometry(i); - if (gsGeom != NULL) - { - this->get_CellValue(FieldIndex, i, &val); - if(shapeMap.find(val) != shapeMap.end()) - { - shapeMap[val]->push_back(gsGeom); - if (calcStats) { - indicesMap[val]->push_back(i); - } - } - else - { - vector* v = new vector; - v->push_back(gsGeom); - shapeMap[val] = v; - - if (calcStats) { - vector* v2 = new vector; - v2->push_back(i); - indicesMap[val] = v2; - } - } - } - } - - bool isM = ShapeUtility::IsM(_shpfiletype); - - // saving results - long count = 0; // number of shapes inserted - int shapeProcessed = 0; // for progress bar - percent = 0; - size = shapeMap.size(); - - VARIANT_BOOL vbretval; - map *>::iterator p = shapeMap.begin(); - - ShpfileType targetType; - sf->get_ShapefileType(&targetType); - - while(p != shapeMap.end()) - { - CallbackHelper::Progress(_globalCallback, shapeProcessed, size, "Merging shapes...", _key, percent); - - GEOSGeometry* gsGeom = GeosConverter::MergeGeometries(*(p->second), NULL, false, false); - delete p->second; // deleting the vector - - if (gsGeom != NULL) - { - std::vector vShapes; - if (GeosConverter::GeomToShapes(gsGeom, &vShapes, isM)) - { - for (unsigned int i = 0; i < vShapes.size(); i++) - { - IShape* shp = vShapes[i]; - if (shp != NULL) - { - ShapeHelper::ForceProperShapeType(shp, targetType); - - sf->EditInsertShape(shp, &count, &vbretval); - if (vbretval) - sf->EditCellValue(0, count, (VARIANT)p->first, &vbretval); - - shp->Release(); - - if (calcStats) - { - std::vector* indices = indicesMap[p->first]; - fieldMap[count] = indices; - } - - count++; - } - } - } - GeosHelper::DestroyGeometry(gsGeom); - } - ++p; - shapeProcessed++; - } - - if (calcStats) - { - CalculateFieldStats(fieldMap, operations, sf); - - // delete indices map - map *>::iterator p2 = indicesMap.begin(); - while(p2 != indicesMap.end()) - { - delete p2->second; - ++p2; - } - } - - this->ClearCachedGeometries(); - CallbackHelper::ProgressCompleted(_globalCallback, _key); + map*> fieldMap; // index in output, indices in input + map*> indicesMap; // value in input, indices in input + map*> shapeMap; + + CComVariant val; // VARIANT hasn't got comparison operators and therefore + // can't be used with associative containers + + bool calcStats = false; + if (operations) + { + int count; + operations->get_Count(&count); + calcStats = count > 0; + } + + ReadGeosGeometries(SelectedOnly); + + long percent = 0; + auto size = (int)_shapeData.size(); + for (long i = 0; i < size; i++) + { + CallbackHelper::Progress(_globalCallback, i, size, "Grouping shapes...", _key, percent); + + if (!ShapeAvailable(i, SelectedOnly)) + continue; + + GEOSGeometry* gsGeom = this->GetGeosGeometry(i); + if (gsGeom != nullptr) + { + this->get_CellValue(FieldIndex, i, &val); + if (shapeMap.find(val) != shapeMap.end()) + { + shapeMap[val]->push_back(gsGeom); + if (calcStats) + { + indicesMap[val]->push_back(i); + } + } + else + { + auto* v = new vector; + v->push_back(gsGeom); + shapeMap[val] = v; + + if (calcStats) + { + auto* v2 = new vector; + v2->push_back(i); + indicesMap[val] = v2; + } + } + } + } + + const bool isM = ShapeUtility::IsM(_shpfiletype); + + // saving results + long count = 0; // number of shapes inserted + int shapeProcessed = 0; // for progress bar + percent = 0; + size = shapeMap.size(); + + VARIANT_BOOL vbretval; + auto p = shapeMap.begin(); + + ShpfileType targetType; + sf->get_ShapefileType(&targetType); + + while (p != shapeMap.end()) + { + CallbackHelper::Progress(_globalCallback, shapeProcessed, size, "Merging shapes...", _key, percent); + + GEOSGeometry* gsGeom = GeosConverter::MergeGeometries(*p->second, nullptr, false, false); + delete p->second; // deleting the vector + + if (gsGeom != nullptr) + { + std::vector vShapes; + if (GeosConverter::GeomToShapes(gsGeom, &vShapes, isM)) + { + for (auto shp : vShapes) + { + if (shp != nullptr) + { + ShapeHelper::ForceProperShapeType(shp, targetType); + + sf->EditInsertShape(shp, &count, &vbretval); + if (vbretval) + sf->EditCellValue(0, count, (VARIANT)p->first, &vbretval); + + shp->Release(); + + if (calcStats) + { + std::vector* indices = indicesMap[p->first]; + fieldMap[count] = indices; + } + + count++; + } + } + } + GeosHelper::DestroyGeometry(gsGeom); + } + ++p; + shapeProcessed++; + } + + if (calcStats) + { + CalculateFieldStats(fieldMap, operations, sf); + + // delete indices map + auto p2 = indicesMap.begin(); + while (p2 != indicesMap.end()) + { + delete p2->second; + ++p2; + } + } + + this->ClearCachedGeometries(); + CallbackHelper::ProgressCompleted(_globalCallback, _key); } // ************************************************************* // DissolveClipper() // ************************************************************* -void CShapefile::DissolveClipper(long FieldIndex, VARIANT_BOOL SelectedOnly, IFieldStatOperations* operations, IShapefile* sf) +void CShapefile::DissolveClipper(long FieldIndex, VARIANT_BOOL SelectedOnly, IFieldStatOperations* operations, + IShapefile* sf) { - map *> fieldMap; // index in output, indices in input - map *> indicesMap; // value in input, indices in input - map shapeMap; - - CComVariant val; // VARIANT hasn't got comparison operators and therefore - // can't be used with associative containers - long percent = 0; - int size = (int)_shapeData.size(); - // std::vector polygons; - std::vector polygons; - polygons.resize(size, NULL); - - ClipperConverter ogr(this); - - bool calcStats = false; - if (operations) - { - int count; - operations->get_Count(&count); - calcStats = count > 0; - } - - for(long i = 0; i < size; i++) - { - CallbackHelper::Progress(_globalCallback, i, size, "Merging shapes...", _key, percent); - - if (!ShapeAvailable(i, SelectedOnly)) - continue; - - IShape* shp = NULL; - this->GetValidatedShape(i, &shp); - if (!shp) continue; - - // ClipperLib::Polygons* poly = ogr.Shape2ClipperPolygon(shp); - ClipperLib::Paths* poly = ogr.Shape2ClipperPolygon(shp); - shp->Release(); - - if (poly == NULL) continue; - - this->get_CellValue(FieldIndex, i, &val); - - if(shapeMap.find(val) != shapeMap.end()) - { - // shapeMap[val]->AddPolygons(*poly, ClipperLib::ptClip); - shapeMap[val]->AddPaths(*poly, ClipperLib::ptClip, true); - polygons[i] = poly; - if (calcStats) { - indicesMap[val]->push_back(i); - } - } - else - { - shapeMap[val] = new ClipperLib::Clipper(); - // shapeMap[val]->AddPolygons(*poly, ClipperLib::ptClip); - shapeMap[val]->AddPaths(*poly, ClipperLib::ptClip, true); - polygons[i] = poly; - - if (calcStats) { - vector* v2 = new vector; - v2->push_back(i); - indicesMap[val] = v2; - } - } - } - - // perform clipping and saving the results - VARIANT_BOOL vbretval; - long count = 0; - map ::iterator p = shapeMap.begin(); - while(p != shapeMap.end()) - { - IShape* shp = NULL; - // ClipperLib::Polygons result; - ClipperLib::Paths result; - ClipperLib::Clipper* clip = p->second; - if (clip) - { - clip->Execute(ClipperLib::ctUnion, result); - shp = ogr.ClipperPolygon2Shape(&result); - - if (shp) - { - long numPoints; - shp->get_NumPoints(&numPoints); - if (numPoints > 0) - { - sf->EditInsertShape(shp, &count, &vbretval); - if (vbretval) - sf->EditCellValue(0, count, (VARIANT)p->first, &vbretval); - - if (calcStats) - { - std::vector* indices = indicesMap[p->first]; - fieldMap[count] = indices; - } - - count++; - } - shp->Release(); - } - delete p->second; - } - ++p; - } - - // deleting the polygons - for (int i = 0; i < size; i++) - { - if (polygons[i]) - { - delete polygons[i]; - } - } - - CallbackHelper::ProgressCompleted(_globalCallback, _key); - shapeMap.clear(); - - if (calcStats) - { - CalculateFieldStats(fieldMap, operations, sf); - - // delete indices map - map *>::iterator p = indicesMap.begin(); - while(p != indicesMap.end()) - { - delete p->second; - ++p; - } - } + map*> fieldMap; // index in output, indices in input + map*> indicesMap; // value in input, indices in input + map shapeMap; + + CComVariant val; // VARIANT hasn't got comparison operators and therefore + // can't be used with associative containers + long percent = 0; + const auto size = (int)_shapeData.size(); + // std::vector polygons; + std::vector polygons; + polygons.resize(size, nullptr); + + ClipperConverter ogr(this); + + bool calcStats = false; + if (operations) + { + int count; + operations->get_Count(&count); + calcStats = count > 0; + } + + for (long i = 0; i < size; i++) + { + CallbackHelper::Progress(_globalCallback, i, size, "Merging shapes...", _key, percent); + + if (!ShapeAvailable(i, SelectedOnly)) + continue; + + IShape* shp = nullptr; + this->GetValidatedShape(i, &shp); + if (!shp) continue; + + // ClipperLib::Polygons* poly = ogr.Shape2ClipperPolygon(shp); + ClipperLib::Paths* poly = ogr.Shape2ClipperPolygon(shp); + shp->Release(); + + if (poly == nullptr) continue; + + this->get_CellValue(FieldIndex, i, &val); + + if (shapeMap.find(val) != shapeMap.end()) + { + // shapeMap[val]->AddPolygons(*poly, ClipperLib::ptClip); + shapeMap[val]->AddPaths(*poly, ClipperLib::ptClip, true); + polygons[i] = poly; + if (calcStats) + { + indicesMap[val]->push_back(i); + } + } + else + { + shapeMap[val] = new ClipperLib::Clipper(); + // shapeMap[val]->AddPolygons(*poly, ClipperLib::ptClip); + shapeMap[val]->AddPaths(*poly, ClipperLib::ptClip, true); + polygons[i] = poly; + + if (calcStats) + { + auto* v2 = new vector; + v2->push_back(i); + indicesMap[val] = v2; + } + } + } + + // perform clipping and saving the results + VARIANT_BOOL vbretval; + long count = 0; + auto p = shapeMap.begin(); + while (p != shapeMap.end()) + { + // ClipperLib::Polygons result; + ClipperLib::Paths result; + ClipperLib::Clipper* clip = p->second; + if (clip) + { + clip->Execute(ClipperLib::ctUnion, result); + IShape* shp = ogr.ClipperPolygon2Shape(&result); + + if (shp) + { + long numPoints; + shp->get_NumPoints(&numPoints); + if (numPoints > 0) + { + sf->EditInsertShape(shp, &count, &vbretval); + if (vbretval) + sf->EditCellValue(0, count, (VARIANT)p->first, &vbretval); + + if (calcStats) + { + std::vector* indices = indicesMap[p->first]; + fieldMap[count] = indices; + } + + count++; + } + shp->Release(); + } + delete p->second; + } + ++p; + } + + // deleting the polygons + for (int i = 0; i < size; i++) + { + if (polygons[i]) + { + delete polygons[i]; + } + } + + CallbackHelper::ProgressCompleted(_globalCallback, _key); + shapeMap.clear(); + + if (calcStats) + { + CalculateFieldStats(fieldMap, operations, sf); + + // delete indices map + auto index = indicesMap.begin(); + while (index != indicesMap.end()) + { + delete index->second; + ++index; + } + } } // ******************************************************************** // AggregateShapesWithStats() // ******************************************************************** -STDMETHODIMP CShapefile::AggregateShapesWithStats(VARIANT_BOOL SelectedOnly, LONG FieldIndex, IFieldStatOperations* statOperations, IShapefile** retval) +STDMETHODIMP CShapefile::AggregateShapesWithStats(VARIANT_BOOL SelectedOnly, LONG FieldIndex, + IFieldStatOperations* statOperations, IShapefile** retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - AggregateShapesCore(SelectedOnly, FieldIndex, statOperations, retval); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AggregateShapesCore(SelectedOnly, FieldIndex, statOperations, retval); + return S_OK; } // ******************************************************************** @@ -1124,464 +1120,479 @@ STDMETHODIMP CShapefile::AggregateShapesWithStats(VARIANT_BOOL SelectedOnly, LON // ******************************************************************** STDMETHODIMP CShapefile::AggregateShapes(VARIANT_BOOL SelectedOnly, LONG FieldIndex, IShapefile** retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - AggregateShapesCore(SelectedOnly, FieldIndex, NULL, retval); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AggregateShapesCore(SelectedOnly, FieldIndex, nullptr, retval); + return S_OK; } // ******************************************************************** // AggregateShapesCore() // ******************************************************************** // Returns new shapefile instance. -void CShapefile::AggregateShapesCore(VARIANT_BOOL SelectedOnly, LONG FieldIndex, IFieldStatOperations* operations, IShapefile** retval) +void CShapefile::AggregateShapesCore(VARIANT_BOOL SelectedOnly, LONG FieldIndex, IFieldStatOperations* operations, + IShapefile** retval) { - long numFields; - this->get_NumFields(&numFields); - - if( FieldIndex != -1 && !(FieldIndex >= 0 && FieldIndex < numFields)) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - return; - } - - if(!ValidateInput(this, "AggregateShapes", "this", SelectedOnly)) - return; - - // ---------------------------------------------- - // Creating output - // ---------------------------------------------- - ShpfileType targetType = _shpfiletype; - - // for points change to multi-point type, as there is no other way to group them - if (targetType == SHP_POINT) targetType = SHP_MULTIPOINT; - if (targetType == SHP_POINTZ) targetType = SHP_MULTIPOINTZ; - if (targetType == SHP_POINTM) targetType = SHP_MULTIPOINTM; - - //ShapefileHelper::CloneNoFields(this, retval, targetType); - // ----------------------------------------------- - // Creating output - // ----------------------------------------------- - if (!ShapefileHelper::CloneNoFields(this, retval, targetType)) - { - // Get errorcode and pass the source: - long errorCode; - (*retval)->get_LastErrorCode(&errorCode); - *retval = NULL; - ErrorMessage(errorCode); - return; - } - long newFieldIndex = 0; - CloneField(this, *retval, FieldIndex, newFieldIndex); - - // ---------------------------------------------- - // Building groups by field id - // ---------------------------------------------- - VARIANT_BOOL vbretval; - map *> fieldMap; // index in output, indices in input - map *> indicesMap; // value in input, indices in input - map *> shapeMap; - - CComVariant val; - - bool calcStats = false; - if (operations) - { - int count; - operations->get_Count(&count); - calcStats = count > 0; - } + long numFields; + this->get_NumFields(&numFields); + + if (FieldIndex != -1 && !(FieldIndex >= 0 && FieldIndex < numFields)) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + return; + } + + if (!ValidateInput(this, "AggregateShapes", "this", SelectedOnly)) + return; + + // ---------------------------------------------- + // Creating output + // ---------------------------------------------- + ShpfileType targetType = _shpfiletype; + + // for points change to multi-point type, as there is no other way to group them + if (targetType == SHP_POINT) targetType = SHP_MULTIPOINT; + if (targetType == SHP_POINTZ) targetType = SHP_MULTIPOINTZ; + if (targetType == SHP_POINTM) targetType = SHP_MULTIPOINTM; + + //ShapefileHelper::CloneNoFields(this, retval, targetType); + // ----------------------------------------------- + // Creating output + // ----------------------------------------------- + if (!ShapefileHelper::CloneNoFields(this, retval, targetType)) + { + // Get errorcode and pass the source: + long errorCode; + (*retval)->get_LastErrorCode(&errorCode); + *retval = nullptr; + ErrorMessage(errorCode); + return; + } + const long newFieldIndex = 0; + CloneField(this, *retval, FieldIndex, newFieldIndex); + + // ---------------------------------------------- + // Building groups by field id + // ---------------------------------------------- + VARIANT_BOOL vbretval; + map*> fieldMap; // index in output, indices in input + map*> indicesMap; // value in input, indices in input + map*> shapeMap; + + CComVariant val; + + bool calcStats = false; + if (operations) + { + int count; + operations->get_Count(&count); + calcStats = count > 0; + } + + long percent = 0; + auto size = (int)_shapeData.size(); + + for (long i = 0; i < size; i++) + { + CallbackHelper::Progress(_globalCallback, i, size, "Grouping shapes...", _key, percent); + + if (!ShapeAvailable(i, SelectedOnly)) + continue; + + IShape* shp = nullptr; + this->GetValidatedShape(i, &shp); + if (!shp) continue; + + if (FieldIndex != -1) + this->get_CellValue(FieldIndex, i, &val); + + if (shapeMap.find(val) != shapeMap.end()) + { + shapeMap[val]->push_back(shp); + if (calcStats) + { + indicesMap[val]->push_back(i); + } + } + else + { + auto* v = new vector; + v->push_back(shp); + shapeMap[val] = v; + + if (calcStats) + { + auto* v2 = new vector; + v2->push_back(i); + indicesMap[val] = v2; + } + } + } + + // ---------------------------------------------- + // Merging groups + // ---------------------------------------------- + long count = 0; // number of shapes inserted + int i = 0; // for progress bar + percent = 0; + size = shapeMap.size(); + auto p = shapeMap.begin(); + + while (p != shapeMap.end()) + { + CallbackHelper::Progress(_globalCallback, i, size, "Merging shapes...", _key, percent); + + // merging shapes + vector* shapes = p->second; + long pntIndex = 0; + long partIndex = 0; + + IShape* shpBase = nullptr; + for (unsigned int j = 0; j < shapes->size(); j++) + { + if (j == 0) + { + if (_isEditingShapes) + { + // in editing mode we share the shape with parent shapefile + // so a copy is needed to avoid conflicts + (*shapes)[0]->Clone(&shpBase); + (*shapes)[0]->Release(); + } + else + { + // in the regular mode we are the sole owners of shape + shpBase = (*shapes)[0]; + } + shpBase->get_NumPoints(&pntIndex); + shpBase->get_NumParts(&partIndex); + + ShapeHelper::ForceProperShapeType(shpBase, targetType); + } + else + { + IShape* shp = (*shapes)[j]; + + if (ShapeUtility::Convert2D(targetType) == SHP_MULTIPOINT) + { + // in case of multi-point target type, simply copy all the points to base shape + // no need to deal with parts, multi-points don't have those + long numPoints = 0; + shp->get_NumPoints(&numPoints); + for (long n = 0; n < numPoints; n++) + { + CComPtr pnt = nullptr; + shp->get_Point(n, &pnt); + long pointCount; + shpBase->get_NumPoints(&pointCount); + shpBase->InsertPoint(pnt, &pointCount, &vbretval); + } + } + else + { + long numParts; + shp->get_NumParts(&numParts); + + for (long part = 0; part < numParts; part++) + { + shpBase->InsertPart(pntIndex, &partIndex, &vbretval); + + long start, end; + shp->get_Part(part, &start); + shp->get_EndOfPart(part, &end); + + for (long point = start; point <= end; point++) + { + IPoint* pnt = nullptr; + shp->get_Point(point, &pnt); + if (pnt) + { + IPoint* pntNew = nullptr; + pnt->Clone(&pntNew); + shpBase->InsertPoint(pntNew, &pntIndex, &vbretval); + pntIndex++; + pntNew->Release(); + pnt->Release(); + } + } + + partIndex++; + } + } + shp->Release(); + } + } + + shpBase->get_NumPoints(&pntIndex); + shpBase->get_NumParts(&partIndex); + + if (partIndex >= 0 && pntIndex > 0) + { + (*retval)->EditInsertShape(shpBase, &count, &vbretval); + (*retval)->EditCellValue(newFieldIndex, count, p->first, &vbretval); + + if (calcStats) + { + std::vector* indices = indicesMap[p->first]; + fieldMap[count] = indices; + } + } + + if (_isEditingShapes) // it was cloned in edit mode only + shpBase->Release(); + + delete p->second; // deleting the vector + count++; + ++p; + i++; + } + + // ---------------------------------------------- + // Calculation of stats + // ---------------------------------------------- + if (calcStats) + { + CalculateFieldStats(fieldMap, operations, *retval); + + // delete indices map + auto index = indicesMap.begin(); + while (index != indicesMap.end()) + { + delete index->second; + ++index; + } + } + + // ---------------------------------------------- + // Validating output + // ---------------------------------------------- + CallbackHelper::ProgressCompleted(_globalCallback, _key); + ValidateOutput(retval, "AggregateShapes"); + return; +} +#pragma endregion - long percent = 0; - int size = (int)_shapeData.size(); +#pragma region Buffer - for(long i = 0; i < size; i++) - { - CallbackHelper::Progress(_globalCallback, i, size, "Grouping shapes...", _key, percent); +// ******************************************************************** +// BufferByDistance() +// ******************************************************************** +STDMETHODIMP CShapefile::BufferByDistance(double Distance, LONG nSegments, VARIANT_BOOL SelectedOnly, + VARIANT_BOOL MergeResults, IShapefile** sf) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + if (MergeResults) + { + // ShapefileHelper::CloneNoFields(this, sf, SHP_POLYGON, true); + // ----------------------------------------------- + // Creating output + // ----------------------------------------------- + if (!ShapefileHelper::CloneNoFields(this, sf, SHP_POLYGON, true)) + { + // Get errorcode and pass the source: + long errorCode; + (*sf)->get_LastErrorCode(&errorCode); + *sf = nullptr; + ErrorMessage(errorCode); + return S_OK; + } + } + else + { + // if not merging shapes, copy fields + ShapefileHelper::CloneCore(this, sf, SHP_POLYGON, false); + } + + if (!BufferByDistanceCore(Distance, nSegments, SelectedOnly, MergeResults, *sf)) + { + (*sf)->Release(); + *sf = nullptr; + } + + return S_OK; +} - if (!ShapeAvailable(i, SelectedOnly)) - continue; +// ******************************************************************** +// BufferByDistance() +// ******************************************************************** +VARIANT_BOOL CShapefile::BufferByDistanceCore(double Distance, LONG nSegments, VARIANT_BOOL SelectedOnly, + VARIANT_BOOL MergeResults, IShapefile* sf) +{ + // ------------------------------------------- + // validating + // ------------------------------------------- + if (!ValidateInput(this, "BufferByDistance", "this", SelectedOnly)) + return VARIANT_FALSE; + + // ------------------------------------------- + // processing + // ------------------------------------------- + VARIANT_BOOL vb; + const int size = _shapeData.size(); + long count = 0; + long percent = 0; + + std::vector results; + results.reserve(size); + + ReadGeosGeometries(SelectedOnly); + + const bool isM = ShapeUtility::IsM(_shpfiletype); + + for (long i = 0; i < size; i++) + { + CallbackHelper::Progress(_globalCallback, i, size, "Buffering shapes...", _key, percent); + + if (!ShapeAvailable(i, SelectedOnly)) + continue; + + GEOSGeometry* oGeom1 = this->GetGeosGeometry(i); + if (oGeom1) + { + GEOSGeometry* oGeom2 = GeosHelper::Buffer(oGeom1, Distance, (int)nSegments); + + if (oGeom2 == nullptr) continue; + + if (MergeResults) + { + results.push_back(oGeom2); + } + else + { + vector vShapes; + + if (GeosConverter::GeomToShapes(oGeom2, &vShapes, isM)) + { + this->InsertShapesVector(sf, vShapes, this, i, nullptr); + count += vShapes.size(); + } + GeosHelper::DestroyGeometry(oGeom2); + } + } + } + + // ------------------------------------------- + // merging the results + // ------------------------------------------- + if (MergeResults) + { + GEOSGeometry* gsGeom = GeosConverter::MergeGeometries(results, _globalCallback); + // geometries will be released in the process + + if (gsGeom != nullptr) // the result should always be in g1 + { + OGRGeometry* oGeom = GeosHelper::CreateFromGEOS(gsGeom); + GeosHelper::DestroyGeometry(gsGeom); + + if (oGeom) + { + // bool isM = ShapeUtility::IsM(this->_shpfiletype); + + const OGRwkbGeometryType type = oGeom->getGeometryType(); + if (type == wkbMultiPolygon || type == wkbMultiPolygon25D) + { + std::vector polygons; + + if (OgrConverter::MultiPolygon2Polygons(oGeom, &polygons)) + { + for (auto& polygon : polygons) + { + IShape* shp = OgrConverter::GeometryToShape(polygon, isM); + if (shp) + { + sf->EditInsertShape(shp, &count, &vb); + shp->Release(); + count++; + } + } + } + } + else + { + // Doesn't use any GEOS functions: + IShape* shp = OgrConverter::GeometryToShape(oGeom, isM); + if (shp) + { + sf->EditInsertShape(shp, &count, &vb); + shp->Release(); + count++; + } + } + OGRGeometryFactory::destroyGeometry(oGeom); + } + } + } + + this->ClearCachedGeometries(); + + // ------------------------------------------- + // output validation + // ------------------------------------------- + CallbackHelper::ProgressCompleted(_globalCallback, _key); + ValidateOutput(sf, "BufferByDistance"); + + return VARIANT_TRUE; +} - IShape* shp = NULL; - this->GetValidatedShape(i, &shp); - if (!shp) continue; - - if (FieldIndex != -1) - this->get_CellValue(FieldIndex, i, &val); +#pragma endregion - if(shapeMap.find(val) != shapeMap.end()) - { - shapeMap[val]->push_back(shp); - if (calcStats) { - indicesMap[val]->push_back(i); - } - } - else - { - vector* v = new vector; - v->push_back(shp); - shapeMap[val] = v; - - if (calcStats) { - vector* v2 = new vector; - v2->push_back(i); - indicesMap[val] = v2; - } - } - } - - // ---------------------------------------------- - // Merging groups - // ---------------------------------------------- - long count = 0; // number of shapes inserted - int i = 0; // for progress bar - percent = 0; - size = shapeMap.size(); - map *>::iterator p = shapeMap.begin(); - - while(p != shapeMap.end()) - { - CallbackHelper::Progress(_globalCallback, i, size, "Merging shapes...", _key, percent); - - // merging shapes - vector* shapes = p->second; - long pntIndex = 0; - long partIndex = 0; - - IShape* shpBase = NULL; - for (unsigned int j = 0; j < shapes->size(); j++) - { - if (j == 0) - { - if (_isEditingShapes) - { - // in editing mode we share the shape with parent shapefile - // so a copy is needed to avoid conflicts - (*shapes)[0]->Clone(&shpBase); - (*shapes)[0]->Release(); - } - else - { - // in the regular mode we are the sole owners of shape - shpBase = (*shapes)[0]; - } - shpBase->get_NumPoints(&pntIndex); - shpBase->get_NumParts(&partIndex); - - ShapeHelper::ForceProperShapeType(shpBase, targetType); - } - else - { - IShape* shp = (*shapes)[j]; - - if (ShapeUtility::Convert2D(targetType) == SHP_MULTIPOINT) - { - // in case of multi-point target type, simply copy all the points to base shape - // no need to deal with parts, multi-points don't have those - long numPoints = 0; - shp->get_NumPoints(&numPoints); - for (long n = 0; n < numPoints; n++) - { - CComPtr pnt = NULL; - shp->get_Point(n, &pnt); - long pointCount; - shpBase->get_NumPoints(&pointCount); - shpBase->InsertPoint(pnt, &pointCount, &vbretval); - } - } - else - { - long numParts; - shp->get_NumParts(&numParts); - - for (long part = 0; part < numParts; part++) - { - shpBase->InsertPart(pntIndex, &partIndex, &vbretval); - - long start, end; - shp->get_Part(part, &start); - shp->get_EndOfPart(part, &end); - - for (long point = start; point <= end; point++) - { - IPoint* pnt = NULL; - shp->get_Point(point, &pnt); - if (pnt) - { - IPoint* pntNew = NULL; - pnt->Clone(&pntNew); - shpBase->InsertPoint(pntNew, &pntIndex, &vbretval); - pntIndex++; - pntNew->Release(); - pnt->Release(); - } - } - - partIndex++; - } - } - shp->Release(); - } - } - - shpBase->get_NumPoints(&pntIndex); - shpBase->get_NumParts(&partIndex); - - if (partIndex >= 0 && pntIndex > 0) - { - (*retval)->EditInsertShape(shpBase, &count, &vbretval); - (*retval)->EditCellValue(newFieldIndex, count, p->first, &vbretval); - - if (calcStats) - { - std::vector* indices = indicesMap[p->first]; - fieldMap[count] = indices; - } - } - - if (_isEditingShapes) // it was cloned in edit mode only - shpBase->Release(); - - delete p->second; // deleting the vector - count++; - ++p; - i++; - } - - // ---------------------------------------------- - // Calculation of stats - // ---------------------------------------------- - if (calcStats) - { - CalculateFieldStats(fieldMap, operations, *retval); - - // delete indices map - map *>::iterator p = indicesMap.begin(); - while(p != indicesMap.end()) - { - delete p->second; - ++p; - } - } - - // ---------------------------------------------- - // Validating output - // ---------------------------------------------- - CallbackHelper::ProgressCompleted(_globalCallback, _key); - ValidateOutput(retval, "AggregateShapes"); - return; -} -#pragma endregion - -#pragma region Buffer - -// ******************************************************************** -// BufferByDistance() -// ******************************************************************** -STDMETHODIMP CShapefile::BufferByDistance(double Distance, LONG nSegments, VARIANT_BOOL SelectedOnly, VARIANT_BOOL MergeResults, IShapefile** sf) -{ - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - - if (MergeResults) { - // ShapefileHelper::CloneNoFields(this, sf, SHP_POLYGON, true); - // ----------------------------------------------- - // Creating output - // ----------------------------------------------- - if (!ShapefileHelper::CloneNoFields(this, sf, SHP_POLYGON, true)) - { - // Get errorcode and pass the source: - long errorCode; - (*sf)->get_LastErrorCode(&errorCode); - *sf = NULL; - ErrorMessage(errorCode); - return S_OK; - } - } - else { - // if not merging shapes, copy fields - ShapefileHelper::CloneCore(this, sf, SHP_POLYGON, false); - } - - if (!BufferByDistanceCore(Distance, nSegments, SelectedOnly, MergeResults, *sf)) - { - (*sf)->Release(); - *sf = NULL; - } - - return S_OK; -} - -// ******************************************************************** -// BufferByDistance() -// ******************************************************************** -VARIANT_BOOL CShapefile::BufferByDistanceCore(double Distance, LONG nSegments, VARIANT_BOOL SelectedOnly, VARIANT_BOOL MergeResults, IShapefile* sf) -{ - // ------------------------------------------- - // validating - // ------------------------------------------- - if (!ValidateInput(this, "BufferByDistance", "this", SelectedOnly)) - return VARIANT_FALSE; - - // ------------------------------------------- - // processing - // ------------------------------------------- - VARIANT_BOOL vb; - int size = _shapeData.size(); - long count = 0; - long percent = 0; - - std::vector results; - results.reserve(size); - - ReadGeosGeometries(SelectedOnly); - - bool isM = ShapeUtility::IsM(_shpfiletype); - - for (long i = 0; i < size; i++) - { - CallbackHelper::Progress(_globalCallback, i, size, "Buffering shapes...", _key, percent); - - if (!ShapeAvailable(i, SelectedOnly)) - continue; - - GEOSGeometry* oGeom1 = this->GetGeosGeometry(i); - if (oGeom1) - { - GEOSGeometry* oGeom2 = GeosHelper::Buffer(oGeom1, Distance, (int)nSegments); - - if (oGeom2 == NULL) continue; - - if (MergeResults) - { - results.push_back(oGeom2); - } - else - { - vector vShapes; - - if (GeosConverter::GeomToShapes(oGeom2, &vShapes, isM)) - { - this->InsertShapesVector(sf, vShapes, this, i, NULL); - count += vShapes.size(); - } - GeosHelper::DestroyGeometry(oGeom2); - } - } - } - - // ------------------------------------------- - // merging the results - // ------------------------------------------- - if (MergeResults) - { - GEOSGeometry* gsGeom = GeosConverter::MergeGeometries(results, _globalCallback); // geometries will be released in the process - - if (gsGeom != NULL) // the result should always be in g1 - { - OGRGeometry* oGeom = GeosHelper::CreateFromGEOS(gsGeom); - GeosHelper::DestroyGeometry(gsGeom); - - if (oGeom) - { - bool isM = ShapeUtility::IsM(this->_shpfiletype); - - OGRwkbGeometryType type = oGeom->getGeometryType(); - if (type == wkbMultiPolygon || type == wkbMultiPolygon25D) - { - std::vector polygons; - - if (OgrConverter::MultiPolygon2Polygons(oGeom, &polygons)) - { - for (unsigned int i = 0; i < polygons.size(); i++) - { - IShape* shp = OgrConverter::GeometryToShape(polygons[i], isM); - if (shp) - { - sf->EditInsertShape(shp, &count, &vb); - shp->Release(); - count++; - } - } - } - } - else - { - // Doesn't use any GEOS functions: - IShape* shp = OgrConverter::GeometryToShape(oGeom, isM); - if (shp) - { - sf->EditInsertShape(shp, &count, &vb); - shp->Release(); - count++; - } - } - OGRGeometryFactory::destroyGeometry(oGeom); - } - } - } - - this->ClearCachedGeometries(); - - // ------------------------------------------- - // output validation - // ------------------------------------------- - CallbackHelper::ProgressCompleted(_globalCallback, _key); - ValidateOutput(sf, "BufferByDistance"); - - return VARIANT_TRUE; -} - -#pragma endregion - -#pragma region Clipping -// ******************************************************************** -// GetDifference() -// ******************************************************************** -STDMETHODIMP CShapefile::Difference(VARIANT_BOOL SelectedOnlySubject, IShapefile* sfOverlay, VARIANT_BOOL SelectedOnlyOverlay, IShapefile** retval) -{ - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - DoClipOperation(SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, retval, clDifference); - return S_OK; -} +#pragma region Clipping +// ******************************************************************** +// GetDifference() +// ******************************************************************** +STDMETHODIMP CShapefile::Difference(VARIANT_BOOL SelectedOnlySubject, IShapefile* sfOverlay, + VARIANT_BOOL SelectedOnlyOverlay, IShapefile** retval) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + DoClipOperation(SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, retval, clDifference); + return S_OK; +} // ******************************************************************** // Clip() // ******************************************************************** -STDMETHODIMP CShapefile::Clip(VARIANT_BOOL SelectedOnlySubject, IShapefile* sfOverlay, VARIANT_BOOL SelectedOnlyOverlay, IShapefile** retval) +STDMETHODIMP CShapefile::Clip(VARIANT_BOOL SelectedOnlySubject, IShapefile* sfOverlay, VARIANT_BOOL SelectedOnlyOverlay, + IShapefile** retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - DoClipOperation(SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, retval, clClip); // enumeration should be repaired - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + DoClipOperation(SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, retval, clClip); + // enumeration should be repaired + return S_OK; } // ********************************************************************** // * GetIntersection() // ********************************************************************** STDMETHODIMP CShapefile::GetIntersection(VARIANT_BOOL SelectedOnlyOfThis, IShapefile* sf, - VARIANT_BOOL SelectedOnly, ShpfileType fileType, ICallback* cBack, IShapefile** retval) + VARIANT_BOOL SelectedOnly, ShpfileType fileType, ICallback* cBack, + IShapefile** retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - DoClipOperation(SelectedOnlyOfThis, sf, SelectedOnly, retval, clIntersection, fileType); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + DoClipOperation(SelectedOnlyOfThis, sf, SelectedOnly, retval, clIntersection, fileType); + return S_OK; } // ******************************************************************** // GetSymmDifference() // ******************************************************************** -STDMETHODIMP CShapefile::SymmDifference(VARIANT_BOOL SelectedOnlySubject, IShapefile* sfOverlay, VARIANT_BOOL SelectedOnlyOverlay, IShapefile** retval) +STDMETHODIMP CShapefile::SymmDifference(VARIANT_BOOL SelectedOnlySubject, IShapefile* sfOverlay, + VARIANT_BOOL SelectedOnlyOverlay, IShapefile** retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - DoClipOperation(SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, retval, clSymDifference); // enumeration should be repaired - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + DoClipOperation(SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, retval, clSymDifference); + // enumeration should be repaired + return S_OK; } // ******************************************************************** // GetUnion() // ******************************************************************** -STDMETHODIMP CShapefile::Union(VARIANT_BOOL SelectedOnlySubject, IShapefile* sfOverlay, VARIANT_BOOL SelectedOnlyOverlay, IShapefile** retval) +STDMETHODIMP CShapefile::Union(VARIANT_BOOL SelectedOnlySubject, IShapefile* sfOverlay, + VARIANT_BOOL SelectedOnlyOverlay, IShapefile** retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - DoClipOperation(SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, retval, clUnion); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + DoClipOperation(SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, retval, clUnion); + return S_OK; } @@ -1591,50 +1602,46 @@ STDMETHODIMP CShapefile::Union(VARIANT_BOOL SelectedOnlySubject, IShapefile* sfO // auto choosing the resulting type for intersection ShpfileType GetClipOperationReturnType(ShpfileType type1, ShpfileType type2, tkClipOperation operation) { - switch (operation) - { - case clClip: - case clIntersection: - { - bool isZ = ShapeUtility::IsZ(type1) || ShapeUtility::IsZ(type2); - bool isM = ShapeUtility::IsM(type1) || ShapeUtility::IsM(type2); - - ShpfileType type2D = ShapeUtility::Convert2D(type1); - ShpfileType type2D2 = ShapeUtility::Convert2D(type2); - - // return type is always has the lower dimension of two - if (type2D == SHP_POINT || type2D2 == SHP_POINT) - { - return ShapeUtility::Get25DShapeType(SHP_POINT, isZ, isM); - } - else if (type2D == SHP_MULTIPOINT || type2D2 == SHP_MULTIPOINT) - { - return ShapeUtility::Get25DShapeType(SHP_MULTIPOINT, isZ, isM); - } - else if (type2D == SHP_POLYLINE && type2D2 == SHP_POLYLINE) - { - return ShapeUtility::Get25DShapeType(SHP_POINT, isZ, isM); - } - else if (type2D == SHP_POLYLINE || type2D2 == SHP_POLYLINE) - { - return ShapeUtility::Get25DShapeType(SHP_POLYLINE, isZ, isM); - } - else - { - return ShapeUtility::Get25DShapeType(SHP_POLYGON, isZ, isM); - } - } - break; - case clDifference: - // the subject type remains intact - return type1; - case clSymDifference: - case clUnion: - // both types should be the same - return type1; - } - - return type1; + switch (operation) + { + case clClip: + case clIntersection: + { + const bool isZ = ShapeUtility::IsZ(type1) || ShapeUtility::IsZ(type2); + const bool isM = ShapeUtility::IsM(type1) || ShapeUtility::IsM(type2); + + const ShpfileType type2D = ShapeUtility::Convert2D(type1); + const ShpfileType type2D2 = ShapeUtility::Convert2D(type2); + + // return type is always has the lower dimension of two + if (type2D == SHP_POINT || type2D2 == SHP_POINT) + { + return ShapeUtility::Get25DShapeType(SHP_POINT, isZ, isM); + } + if (type2D == SHP_MULTIPOINT || type2D2 == SHP_MULTIPOINT) + { + return ShapeUtility::Get25DShapeType(SHP_MULTIPOINT, isZ, isM); + } + if (type2D == SHP_POLYLINE && type2D2 == SHP_POLYLINE) + { + return ShapeUtility::Get25DShapeType(SHP_POINT, isZ, isM); + } + if (type2D == SHP_POLYLINE || type2D2 == SHP_POLYLINE) + { + return ShapeUtility::Get25DShapeType(SHP_POLYLINE, isZ, isM); + } + return ShapeUtility::Get25DShapeType(SHP_POLYGON, isZ, isM); + } + case clDifference: + // the subject type remains intact + return type1; + case clSymDifference: + case clUnion: + // both types should be the same + return type1; + } + + return type1; } // ******************************************************************** @@ -1642,262 +1649,285 @@ ShpfileType GetClipOperationReturnType(ShpfileType type1, ShpfileType type2, tkC // ******************************************************************** CString GetClipOperationName(tkClipOperation operation) { - switch(operation) - { - case clDifference: - return "Difference"; - case clIntersection: - return "GetIntersection"; - case clSymDifference: - return "SymmDifference"; - case clUnion: - return "Union"; - case clClip: - default: - return "Clip"; - } + switch (operation) + { + case clDifference: + return "Difference"; + case clIntersection: + return "GetIntersection"; + case clSymDifference: + return "SymmDifference"; + case clUnion: + return "Union"; + case clClip: + default: + return "Clip"; + } } // ******************************************************************** // DoClipOperarion() // ******************************************************************** -bool CShapefile::ValidateClippingOutputType(ShpfileType type1, ShpfileType type2, ShpfileType returnType, tkClipOperation operation) +bool CShapefile::ValidateClippingOutputType(ShpfileType type1, ShpfileType type2, ShpfileType returnType, + tkClipOperation operation) { - switch (operation) - { - case clSymDifference: - case clUnion: - if (type1 != type2) { - CallbackHelper::ErrorMsg("Types of both input shapefiles for symmetrical difference and union operations must be the same."); - return false; - } - break; - case clDifference: - if (type1 != returnType) - { - CallbackHelper::ErrorMsg("The type of output shapefile must be the same as in put for difference operation."); - return false; - } - break; - case clIntersection: - case clClip: - bool isM1 = ShapeUtility::IsM(type1); - bool isM2 = ShapeUtility::IsM(type2); - bool isM = ShapeUtility::IsM(returnType); - - bool isZ1 = ShapeUtility::IsZ(type1); - bool isZ2 = ShapeUtility::IsZ(type2); - bool isZ = ShapeUtility::IsZ(returnType); - - if ((isM1 != isM && isM2 != isM) || - isZ1 != isZ && isZ2 != isZ) - { - // there is no complete certainty how our conversion routines and GEOS will - // handle Z, M values, so we let it pass with a warning to see what happens - CallbackHelper::ErrorMsg("Suspicious output type for clipping operation (Z, M values must be preserved)."); - } - - ShpfileType type2D = ShapeUtility::Convert2D(type1); - ShpfileType type2D2 = ShapeUtility::Convert2D(type2); - - switch (returnType) - { - case SHP_POLYGON: - if (type2D != SHP_POLYGON || type2D2 != SHP_POLYGON) - { - ErrorMessage(tkINCOMPATIBLE_SHAPEFILE_TYPE); - return false; - } - break; - case SHP_POLYLINE: - if ((type2D != SHP_POLYLINE && type2D != SHP_POLYGON) || - (type2D2 != SHP_POLYLINE && type2D2 != SHP_POLYGON)) - { - ErrorMessage(tkINCOMPATIBLE_SHAPEFILE_TYPE); - return false; - } - break; - case SHP_POINT: - case SHP_MULTIPOINT: - // point can be received from any combination of types (even poly vs poly) - // not sure how multipoints are handled - so put no limitations for the either - break; - } - - break; - } - - return true; + switch (operation) + { + case clSymDifference: + case clUnion: + if (type1 != type2) + { + CallbackHelper::ErrorMsg( + "Types of both input shapefiles for symmetrical difference and union operations must be the same."); + return false; + } + break; + case clDifference: + if (type1 != returnType) + { + CallbackHelper::ErrorMsg( + "The type of output shapefile must be the same as in put for difference operation."); + return false; + } + break; + case clIntersection: + case clClip: + const bool isM1 = ShapeUtility::IsM(type1); + const bool isM2 = ShapeUtility::IsM(type2); + const bool isM = ShapeUtility::IsM(returnType); + + const bool isZ1 = ShapeUtility::IsZ(type1); + const bool isZ2 = ShapeUtility::IsZ(type2); + const bool isZ = ShapeUtility::IsZ(returnType); + + if (isM1 != isM && isM2 != isM || + isZ1 != isZ && isZ2 != isZ) + { + // there is no complete certainty how our conversion routines and GEOS will + // handle Z, M values, so we let it pass with a warning to see what happens + CallbackHelper::ErrorMsg("Suspicious output type for clipping operation (Z, M values must be preserved)."); + } + + const ShpfileType type2D = ShapeUtility::Convert2D(type1); + const ShpfileType type2D2 = ShapeUtility::Convert2D(type2); + + switch (returnType) + { + case SHP_POLYGON: + if (type2D != SHP_POLYGON || type2D2 != SHP_POLYGON) + { + ErrorMessage(tkINCOMPATIBLE_SHAPEFILE_TYPE); + return false; + } + break; + case SHP_POLYLINE: + if (type2D != SHP_POLYLINE && type2D != SHP_POLYGON || + type2D2 != SHP_POLYLINE && type2D2 != SHP_POLYGON) + { + ErrorMessage(tkINCOMPATIBLE_SHAPEFILE_TYPE); + return false; + } + break; + case SHP_POINT: + case SHP_MULTIPOINT: + // point can be received from any combination of types (even poly vs poly) + // not sure how multipoints are handled - so put no limitations for the either + break; + case SHP_NULLSHAPE: break; + case SHP_POINTZ: break; + case SHP_POLYLINEZ: break; + case SHP_POLYGONZ: break; + case SHP_MULTIPOINTZ: break; + case SHP_POINTM: break; + case SHP_POLYLINEM: break; + case SHP_POLYGONM: break; + case SHP_MULTIPOINTM: break; + case SHP_MULTIPATCH: break; + default: ; + } + + break; + } + + return true; } // ******************************************************************** // DoClipOperarion() // ******************************************************************** -void CShapefile::DoClipOperation(VARIANT_BOOL SelectedOnlySubject, IShapefile* sfOverlay, - VARIANT_BOOL SelectedOnlyOverlay, IShapefile** retval, - tkClipOperation operation, ShpfileType returnType) +void CShapefile::DoClipOperation(VARIANT_BOOL SelectedOnlySubject, IShapefile* sfOverlay, + VARIANT_BOOL SelectedOnlyOverlay, IShapefile** retval, + tkClipOperation operation, ShpfileType returnType) { - // ---------------------------------------------- - // Validation - // ---------------------------------------------- - if (!sfOverlay) - { - ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); - return; - } - - ShpfileType type1, type2; - type1 = _shpfiletype; - - sfOverlay->get_ShapefileType(&type2); - bool canUseClipper = type1 == SHP_POLYGON && type2 == SHP_POLYGON; - - if (returnType == SHP_NULLSHAPE) - { - returnType = GetClipOperationReturnType(type1, type2, operation); - } - - if (!ValidateClippingOutputType(type1, type2, returnType, operation)) { - return; - } - - if (!ValidateInput(this, GetClipOperationName(operation), "this", SelectedOnlySubject)) - return; - - if (!ValidateInput(sfOverlay, GetClipOperationName(operation), "sfOverlay", SelectedOnlyOverlay)) - { - return; - } - - // ---------------------------------------------- - // Creating output - // ---------------------------------------------- - // creation of resulting shapefile - // ShapefileHelper::CloneNoFields(this, retval, returnType); - if (!ShapefileHelper::CloneNoFields(this, retval, returnType)) - { - // Get errorcode and pass the source: - long errorCode; - (*retval)->get_LastErrorCode(&errorCode); - *retval = NULL; - ErrorMessage(errorCode); - return; - } - - // do field mapping - std::map fieldMap; - - // fields from the overlay shapefile will be copied for the limited number of operation only - IShapefile* sfCopy = (operation == clIntersection || operation == clSymDifference || operation == clUnion)? sfOverlay : NULL; - GeoProcessing::CopyFields(this, sfCopy, *retval, fieldMap); - - // ------------------------------------------- - // processing - // ------------------------------------------- - long numShapes1, numShapes2; - numShapes1 = _shapeData.size(); - sfOverlay->get_NumShapes(&numShapes2); - - bool useClipper = (_geometryEngine == engineClipper && canUseClipper); - - if (_globalCallback) - { - _globalCallback->QueryInterface(IID_IStopExecution,(void**)&_stopExecution); - } - - // building spatial index for the operation - if(! ((CShapefile*)sfOverlay)->GenerateTempQTree(SelectedOnlyOverlay ? true: false) ) - { - ErrorMessage(tkFAILED_TO_BUILD_SPATIAL_INDEX); - goto cleaning; - } - - if (operation == clSymDifference || operation == clUnion) - { - if (! this->GenerateTempQTree(SelectedOnlySubject ? true : false) ) - { - ErrorMessage(tkFAILED_TO_BUILD_SPATIAL_INDEX); - goto cleaning; - } - } - - if (useClipper) - { - // do calculation by Clipper - switch (operation) - { - case clDifference: - this->DifferenceClipper(this, SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, *retval); - break; - case clIntersection: - this->IntersectionClipper(SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, *retval, &fieldMap); - break; - case clClip: - this->ClipClipper(SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, *retval); - break; - case clSymDifference: - this->DifferenceClipper(this, SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, *retval); - this->DifferenceClipper(sfOverlay, SelectedOnlyOverlay, this, SelectedOnlySubject, *retval, &fieldMap); - break; - case clUnion: - std::set shapesToSkipSubject; - std::set shapesToSkipClipping; - this->IntersectionClipper(SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, *retval, &fieldMap, &shapesToSkipSubject, &shapesToSkipClipping); - this->DifferenceClipper(this, SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, *retval, NULL, &shapesToSkipSubject); - this->DifferenceClipper(sfOverlay, SelectedOnlyOverlay, this, SelectedOnlySubject, *retval, &fieldMap, &shapesToSkipClipping); - } - } - else { - this->ReadGeosGeometries(SelectedOnlySubject); - ((CShapefile*)sfOverlay)->ReadGeosGeometries(SelectedOnlyOverlay); - - // do calculation by GEOS - switch (operation) - { - case clDifference: - this->DifferenceGEOS(this, SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, *retval); - break; - case clIntersection: - this->IntersectionGEOS(SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, *retval, &fieldMap); - break; - case clClip: - this->ClipGEOS(SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, *retval); - break; - case clSymDifference: - // 2 differences - this->DifferenceGEOS(this, SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, *retval); - this->DifferenceGEOS(sfOverlay, SelectedOnlyOverlay, this, SelectedOnlySubject, *retval, &fieldMap); - break; - case clUnion: - // intersection + symmetrical difference - std::set shapesToSkipSubject; - std::set shapesToSkipClipping; - this->IntersectionGEOS(SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, *retval, &fieldMap, &shapesToSkipSubject, &shapesToSkipClipping ); - this->DifferenceGEOS(this, SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, *retval, NULL, &shapesToSkipSubject); - this->DifferenceGEOS(sfOverlay, SelectedOnlyOverlay, this, SelectedOnlySubject, *retval, &fieldMap, &shapesToSkipClipping); - break; - } - - this->ClearCachedGeometries(); - sfOverlay->ClearCachedGeometries(); - } - - // ------------------------------------------- - // cleaning - // ------------------------------------------- -cleaning: - CallbackHelper::ProgressCompleted(_globalCallback, _key); - - // clearing spatial index for the operation - ((CShapefile*)sfOverlay)->ClearTempQTree(); - if (operation == clSymDifference || operation == clUnion) { - this->ClearTempQTree(); - } - - // ------------------------------------------- - // output validation - // ------------------------------------------- - ValidateOutput(retval, GetClipOperationName(operation)); + // ---------------------------------------------- + // Validation + // ---------------------------------------------- + if (!sfOverlay) + { + ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); + return; + } + + ShpfileType type2; + const ShpfileType type1 = _shpfiletype; + + sfOverlay->get_ShapefileType(&type2); + const bool canUseClipper = type1 == SHP_POLYGON && type2 == SHP_POLYGON; + + if (returnType == SHP_NULLSHAPE) + { + returnType = GetClipOperationReturnType(type1, type2, operation); + } + + if (!ValidateClippingOutputType(type1, type2, returnType, operation)) + { + return; + } + + if (!ValidateInput(this, GetClipOperationName(operation), "this", SelectedOnlySubject)) + return; + + if (!ValidateInput(sfOverlay, GetClipOperationName(operation), "sfOverlay", SelectedOnlyOverlay)) + { + return; + } + + // ---------------------------------------------- + // Creating output + // ---------------------------------------------- + // creation of resulting shapefile + // ShapefileHelper::CloneNoFields(this, retval, returnType); + if (!ShapefileHelper::CloneNoFields(this, retval, returnType)) + { + // Get errorcode and pass the source: + long errorCode; + (*retval)->get_LastErrorCode(&errorCode); + *retval = nullptr; + ErrorMessage(errorCode); + return; + } + + // do field mapping + std::map fieldMap; + + // fields from the overlay shapefile will be copied for the limited number of operation only + IShapefile* sfCopy = operation == clIntersection || operation == clSymDifference || operation == clUnion + ? sfOverlay + : nullptr; + GeoProcessing::CopyFields(this, sfCopy, *retval, fieldMap); + + // long numShapes1 = _shapeData.size(); + long numShapes2; + sfOverlay->get_NumShapes(&numShapes2); + + const bool useClipper = _geometryEngine == engineClipper && canUseClipper; + + if (_globalCallback) + { + _globalCallback->QueryInterface(IID_IStopExecution, (void**)&_stopExecution); + } + + // building spatial index for the operation + if (! ((CShapefile*)sfOverlay)->GenerateTempQTree(SelectedOnlyOverlay != 0)) + { + ErrorMessage(tkFAILED_TO_BUILD_SPATIAL_INDEX); + goto cleaning; + } + + if (operation == clSymDifference || operation == clUnion) + { + if (! this->GenerateTempQTree(SelectedOnlySubject != 0)) + { + ErrorMessage(tkFAILED_TO_BUILD_SPATIAL_INDEX); + goto cleaning; + } + } + + if (useClipper) + { + // do calculation by Clipper + switch (operation) + { + case clDifference: + this->DifferenceClipper(this, SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, *retval); + break; + case clIntersection: + this->IntersectionClipper(SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, *retval, &fieldMap); + break; + case clClip: + this->ClipClipper(SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, *retval); + break; + case clSymDifference: + this->DifferenceClipper(this, SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, *retval); + this->DifferenceClipper(sfOverlay, SelectedOnlyOverlay, this, SelectedOnlySubject, *retval, &fieldMap); + break; + case clUnion: + std::set shapesToSkipSubject; + std::set shapesToSkipClipping; + this->IntersectionClipper(SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, *retval, &fieldMap, + &shapesToSkipSubject, &shapesToSkipClipping); + this->DifferenceClipper(this, SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, *retval, nullptr, + &shapesToSkipSubject); + this->DifferenceClipper(sfOverlay, SelectedOnlyOverlay, this, SelectedOnlySubject, *retval, &fieldMap, + &shapesToSkipClipping); + } + } + else + { + this->ReadGeosGeometries(SelectedOnlySubject); + ((CShapefile*)sfOverlay)->ReadGeosGeometries(SelectedOnlyOverlay); + + // do calculation by GEOS + switch (operation) + { + case clDifference: + this->DifferenceGEOS(this, SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, *retval); + break; + case clIntersection: + this->IntersectionGEOS(SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, *retval, &fieldMap); + break; + case clClip: + this->ClipGEOS(SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, *retval); + break; + case clSymDifference: + // 2 differences + this->DifferenceGEOS(this, SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, *retval); + this->DifferenceGEOS(sfOverlay, SelectedOnlyOverlay, this, SelectedOnlySubject, *retval, &fieldMap); + break; + case clUnion: + // intersection + symmetrical difference + std::set shapesToSkipSubject; + std::set shapesToSkipClipping; + this->IntersectionGEOS(SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, *retval, &fieldMap, + &shapesToSkipSubject, &shapesToSkipClipping); + this->DifferenceGEOS(this, SelectedOnlySubject, sfOverlay, SelectedOnlyOverlay, *retval, nullptr, + &shapesToSkipSubject); + this->DifferenceGEOS(sfOverlay, SelectedOnlyOverlay, this, SelectedOnlySubject, *retval, &fieldMap, + &shapesToSkipClipping); + break; + } + + this->ClearCachedGeometries(); + sfOverlay->ClearCachedGeometries(); + } + + // ------------------------------------------- + // cleaning + // ------------------------------------------- +cleaning: + CallbackHelper::ProgressCompleted(_globalCallback, _key); + + // clearing spatial index for the operation + ((CShapefile*)sfOverlay)->ClearTempQTree(); + if (operation == clSymDifference || operation == clUnion) + { + this->ClearTempQTree(); + } + + // ------------------------------------------- + // output validation + // ------------------------------------------- + ValidateOutput(retval, GetClipOperationName(operation)); } #pragma endregion @@ -1905,241 +1935,234 @@ void CShapefile::DoClipOperation(VARIANT_BOOL SelectedOnlySubject, IShapefile* s // ******************************************************************** // ClipGEOS() // ******************************************************************** -void CShapefile::ClipGEOS(VARIANT_BOOL SelectedOnlySubject, IShapefile* sfOverlay, VARIANT_BOOL SelectedOnlyOverlay, IShapefile* sfResult) +void CShapefile::ClipGEOS(VARIANT_BOOL SelectedOnlySubject, IShapefile* sfOverlay, VARIANT_BOOL SelectedOnlyOverlay, + IShapefile* sfResult) { - QTree* qTree = ((CShapefile*)sfOverlay)->GetTempQTree(); - - long numShapesSubject, numShapesClip; - this->get_NumShapes(&numShapesSubject); - sfOverlay->get_NumShapes(&numShapesClip); - bool isM = ShapeUtility::IsM(_shpfiletype); - - long percent = 0; - for(long subjectId = 0; subjectId < numShapesSubject; subjectId++) - { - CallbackHelper::Progress(_globalCallback, subjectId, numShapesSubject, "Clipping shapes...", _key, percent); - - if (!ShapeAvailable(subjectId, SelectedOnlySubject)) - continue; - - vector shapeIds; - double xMin, xMax, yMin, yMax; - this->QuickExtentsCore(subjectId, &xMin, &yMin, &xMax, &yMax); - shapeIds = qTree->GetNodes(QTreeExtent(xMin,xMax,yMax,yMin)); - - if (shapeIds.size() > 0) - { - GEOSGeometry* gsGeom1 = this->GetGeosGeometry(subjectId); - if (gsGeom1) - { - // iterating through clip geometries preparing their union - vector vUnion; - GEOSGeometry* gsGeom2 = NULL; - - for (int j = 0; j < (int)shapeIds.size(); j++) - { - // user can abort the operation in any time - if (_stopExecution) - { - VARIANT_BOOL stop; - _stopExecution->StopFunction(&stop); - if (stop) - { - sfResult->EditClear(&stop); - //GeosHelper::DestroyGeometry(gsGeom1); - goto cleaning; - } - } - - // extracting clip geometry - long clipId = shapeIds[j]; - - if (!((CShapefile*)sfOverlay)->ShapeAvailable(clipId, SelectedOnlyOverlay)) - continue; - - GEOSGeometry* gsGeom2 = ((CShapefile*)sfOverlay)->GetGeosGeometry(clipId); - - if (GeosHelper::Intersects(gsGeom1, gsGeom2)) - { - vUnion.push_back(gsGeom2); - } - } - - // merging (input geometries won't be destroyed) - gsGeom2 = NULL; - bool deleteNeeded = false; - - if ((int)vUnion.size() > 1) - { - gsGeom2 = GeosConverter::MergeGeometries(vUnion, NULL, false, false); - deleteNeeded = true; - } - else if ((int)vUnion.size() == 1) - { - gsGeom2 = vUnion[0]; - } - - if (gsGeom2) - { - GEOSGeometry* gsResult = GeosHelper::Intersection(gsGeom1, gsGeom2); - if (gsResult != NULL) - { - vector vShapes; - bool result = GeosConverter::GeomToShapes(gsResult, &vShapes, isM); - GeosHelper::DestroyGeometry(gsResult); - this->InsertShapesVector(sfResult, vShapes, this, subjectId, NULL); - } - - // clipping geometry should be deleted only in case it was build up from several parts - if (deleteNeeded) - GeosHelper::DestroyGeometry(gsGeom2); - } - - // subject geometry should always be deleted - //GeosHelper::DestroyGeometry(gsGeom1); - } - } - } -cleaning: - CallbackHelper::ProgressCompleted(_globalCallback, _key); + QTree* qTree = ((CShapefile*)sfOverlay)->GetTempQTree(); + + long numShapesSubject, numShapesClip; + this->get_NumShapes(&numShapesSubject); + sfOverlay->get_NumShapes(&numShapesClip); + const bool isM = ShapeUtility::IsM(_shpfiletype); + + long percent = 0; + for (long subjectId = 0; subjectId < numShapesSubject; subjectId++) + { + CallbackHelper::Progress(_globalCallback, subjectId, numShapesSubject, "Clipping shapes...", _key, percent); + + if (!ShapeAvailable(subjectId, SelectedOnlySubject)) + continue; + + double xMin, xMax, yMin, yMax; + this->QuickExtentsCore(subjectId, &xMin, &yMin, &xMax, &yMax); + vector shapeIds = qTree->GetNodes(QTreeExtent(xMin, xMax, yMax, yMin)); + + if (!shapeIds.empty()) + { + GEOSGeometry* gsGeom1 = this->GetGeosGeometry(subjectId); + if (gsGeom1) + { + // iterating through clip geometries preparing their union + vector vUnion; + + for (long clipId : shapeIds) + { + // user can abort the operation in any time + if (_stopExecution) + { + VARIANT_BOOL stop; + _stopExecution->StopFunction(&stop); + if (stop) + { + sfResult->EditClear(&stop); + //GeosHelper::DestroyGeometry(gsGeom1); + goto cleaning; + } + } + + // extracting clip geometry + if (!((CShapefile*)sfOverlay)->ShapeAvailable(clipId, SelectedOnlyOverlay)) + continue; + + GEOSGeometry* gsGeom2 = ((CShapefile*)sfOverlay)->GetGeosGeometry(clipId); + + if (GeosHelper::Intersects(gsGeom1, gsGeom2)) + { + vUnion.push_back(gsGeom2); + } + } + + // merging (input geometries won't be destroyed) + GEOSGeometry* gsGeom2 = nullptr; + bool deleteNeeded = false; + + if ((int)vUnion.size() > 1) + { + gsGeom2 = GeosConverter::MergeGeometries(vUnion, nullptr, false, false); + deleteNeeded = true; + } + else if ((int)vUnion.size() == 1) + { + gsGeom2 = vUnion[0]; + } + + if (gsGeom2) + { + GEOSGeometry* gsResult = GeosHelper::Intersection(gsGeom1, gsGeom2); + if (gsResult != nullptr) + { + vector vShapes; + GeosConverter::GeomToShapes(gsResult, &vShapes, isM); + GeosHelper::DestroyGeometry(gsResult); + this->InsertShapesVector(sfResult, vShapes, this, subjectId, nullptr); + } + + // clipping geometry should be deleted only in case it was build up from several parts + if (deleteNeeded) + GeosHelper::DestroyGeometry(gsGeom2); + } + + // subject geometry should always be deleted + //GeosHelper::DestroyGeometry(gsGeom1); + } + } + } +cleaning: + CallbackHelper::ProgressCompleted(_globalCallback, _key); } // ******************************************************************** // ClipClipper() // ******************************************************************** -void CShapefile::ClipClipper(VARIANT_BOOL SelectedOnlySubject, IShapefile* sfOverlay, VARIANT_BOOL SelectedOnlyOverlay, IShapefile* sfResult) +void CShapefile::ClipClipper(VARIANT_BOOL SelectedOnlySubject, IShapefile* sfOverlay, VARIANT_BOOL SelectedOnlyOverlay, + IShapefile* sfResult) { - QTree* qTree = ((CShapefile*)sfOverlay)->GetTempQTree(); - - long numShapesSubject, numShapesClip; - this->get_NumShapes(&numShapesSubject); - sfOverlay->get_NumShapes(&numShapesClip); - - // vector vPolygons; // we shall create vectors for both clipper and GEOS - vector vPolygons; // we shall create vectors for both clipper and GEOS - vPolygons.assign(numShapesClip, NULL); // this won't take much RAM or time - - ClipperLib::Clipper clp; - ClipperConverter ogr(this); - - long percent = 0; - for(long subjectId =0; subjectId < numShapesSubject; subjectId++) - { - CallbackHelper::Progress(_globalCallback, subjectId, numShapesSubject, "Clipping shapes...", _key, percent); - - if (!ShapeAvailable(subjectId, SelectedOnlySubject)) - continue; - - vector shapeIds; - double xMin, xMax, yMin, yMax; - this->QuickExtentsCore(subjectId, &xMin, &yMin, &xMax, &yMax); - shapeIds = qTree->GetNodes(QTreeExtent(xMin,xMax,yMax,yMin)); - - if (shapeIds.size() > 0) - { - // extracting subject polygon - IShape* shp1 = NULL; - this->GetValidatedShape(subjectId, &shp1); - if (!shp1) continue; - // ClipperLib::Polygons* poly1 = ogr.Shape2ClipperPolygon(shp1); - ClipperLib::Paths* poly1 = ogr.Shape2ClipperPolygon(shp1); - shp1->Release(); - - if (poly1) - { - // extracting clipping polygons - for (int j = 0; j < (int)shapeIds.size(); j++) - { - // user can abort the operation in any time - if (_stopExecution) - { - VARIANT_BOOL stop; - _stopExecution->StopFunction(&stop); - if (stop) - { - sfResult->EditClear(&stop); - delete poly1; - goto cleaning; - } - } - - long clipId = shapeIds[j]; - - if (!((CShapefile*)sfOverlay)->ShapeAvailable(clipId, SelectedOnlyOverlay)) - continue; - - vector vShapes; - - // ClipperLib::Polygons* poly2 = NULL; - ClipperLib::Paths* poly2 = NULL; - if (vPolygons[clipId] == NULL) - { - IShape* shp2 = NULL; - ((CShapefile*)sfOverlay)->GetValidatedShape(clipId, &shp2); - if (!shp2) continue; - poly2 = ogr.Shape2ClipperPolygon(shp2); - vPolygons[clipId] = poly2; - shp2->Release(); - } - else - { - poly2 = vPolygons[clipId]; - } - - if (poly2) - { - // clp.AddPolygons(*poly2, ClipperLib::ptClip); - clp.AddPaths(*poly2, ClipperLib::ptClip, true); - } - } - - // in case there are several input polygons, they should be merged before clipping - if (shapeIds.size() > 1) - { - // ClipperLib::Polygons polyUnion; - ClipperLib::Paths polyUnion; - clp.Execute(ClipperLib::ctUnion, polyUnion); - clp.Clear(); - // clp.AddPolygons(polyUnion, ClipperLib::ptClip); - clp.AddPaths(polyUnion, ClipperLib::ptClip, true); - } - - // adding subject polygon - if (poly1){ - // clp.AddPolygons(*poly1, ClipperLib::ptSubject); - clp.AddPaths(*poly1, ClipperLib::ptSubject, true); - } - - // do clipping - // ClipperLib::Polygons polyResult; - ClipperLib::Paths polyResult; - if (clp.Execute(ClipperLib::ctIntersection, polyResult)) - { - IShape* shp = ogr.ClipperPolygon2Shape(&polyResult); - if (shp) - { - vector vShapes; - vShapes.push_back(shp); - this->InsertShapesVector(sfResult, vShapes, this, subjectId, NULL); - } - } - - delete poly1; - clp.Clear(); - } - } - } + QTree* qTree = ((CShapefile*)sfOverlay)->GetTempQTree(); + + long numShapesSubject, numShapesClip; + this->get_NumShapes(&numShapesSubject); + sfOverlay->get_NumShapes(&numShapesClip); + + // vector vPolygons; // we shall create vectors for both clipper and GEOS + vector vPolygons; // we shall create vectors for both clipper and GEOS + vPolygons.assign(numShapesClip, nullptr); // this won't take much RAM or time + + ClipperLib::Clipper clp; + ClipperConverter ogr(this); + + long percent = 0; + for (long subjectId = 0; subjectId < numShapesSubject; subjectId++) + { + CallbackHelper::Progress(_globalCallback, subjectId, numShapesSubject, "Clipping shapes...", _key, percent); + + if (!ShapeAvailable(subjectId, SelectedOnlySubject)) + continue; + + double xMin, xMax, yMin, yMax; + this->QuickExtentsCore(subjectId, &xMin, &yMin, &xMax, &yMax); + vector shapeIds = qTree->GetNodes(QTreeExtent(xMin, xMax, yMax, yMin)); + + if (!shapeIds.empty()) + { + // extracting subject polygon + IShape* shp1 = nullptr; + this->GetValidatedShape(subjectId, &shp1); + if (!shp1) continue; + // ClipperLib::Polygons* poly1 = ogr.Shape2ClipperPolygon(shp1); + ClipperLib::Paths* poly1 = ogr.Shape2ClipperPolygon(shp1); + shp1->Release(); + + if (poly1) + { + // extracting clipping polygons + for (long clipId : shapeIds) + { + // user can abort the operation in any time + if (_stopExecution) + { + VARIANT_BOOL stop; + _stopExecution->StopFunction(&stop); + if (stop) + { + sfResult->EditClear(&stop); + delete poly1; + goto cleaning; + } + } + + if (!((CShapefile*)sfOverlay)->ShapeAvailable(clipId, SelectedOnlyOverlay)) + continue; + + vector vShapes; + + // ClipperLib::Polygons* poly2 = nullptr; + ClipperLib::Paths* poly2; + if (vPolygons[clipId] == nullptr) + { + IShape* shp2 = nullptr; + ((CShapefile*)sfOverlay)->GetValidatedShape(clipId, &shp2); + if (!shp2) continue; + poly2 = ogr.Shape2ClipperPolygon(shp2); + vPolygons[clipId] = poly2; + shp2->Release(); + } + else + { + poly2 = vPolygons[clipId]; + } + + if (poly2) + { + // clp.AddPolygons(*poly2, ClipperLib::ptClip); + clp.AddPaths(*poly2, ClipperLib::ptClip, true); + } + } + + // in case there are several input polygons, they should be merged before clipping + if (shapeIds.size() > 1) + { + // ClipperLib::Polygons polyUnion; + ClipperLib::Paths polyUnion; + clp.Execute(ClipperLib::ctUnion, polyUnion); + clp.Clear(); + // clp.AddPolygons(polyUnion, ClipperLib::ptClip); + clp.AddPaths(polyUnion, ClipperLib::ptClip, true); + } + + // adding subject polygon + if (poly1) + { + // clp.AddPolygons(*poly1, ClipperLib::ptSubject); + clp.AddPaths(*poly1, ClipperLib::ptSubject, true); + } + + // do clipping + // ClipperLib::Polygons polyResult; + ClipperLib::Paths polyResult; + if (clp.Execute(ClipperLib::ctIntersection, polyResult)) + { + IShape* shp = ogr.ClipperPolygon2Shape(&polyResult); + if (shp) + { + vector vShapes; + vShapes.push_back(shp); + this->InsertShapesVector(sfResult, vShapes, this, subjectId, nullptr); + } + } + + delete poly1; + clp.Clear(); + } + } + } cleaning: - CallbackHelper::ProgressCompleted(_globalCallback, _key); + CallbackHelper::ProgressCompleted(_globalCallback, _key); - for(int i = 0; i < (int)vPolygons.size(); i++) - { - if (vPolygons[i] !=NULL) - { - delete vPolygons[i]; - } - } + for (auto& vPolygon : vPolygons) + { + delete vPolygon; + } } #pragma endregion @@ -2150,318 +2173,310 @@ void CShapefile::ClipClipper(VARIANT_BOOL SelectedOnlySubject, IShapefile* sfOve // ****************************************************************** // shapesToExclude - vector to return shapes sum of intersected results for those is equals to the initial area // such shapes can be excluded from further calculation in case of union; -void CShapefile::IntersectionGEOS(VARIANT_BOOL SelectedOnlySubject, IShapefile* sfClip, VARIANT_BOOL SelectedOnlyClip, - IShapefile* sfResult, map* fieldMap, - std::set* subjectShapesToSkip, - std::set* clippingShapesToSkip) +void CShapefile::IntersectionGEOS(VARIANT_BOOL SelectedOnlySubject, IShapefile* sfClip, VARIANT_BOOL SelectedOnlyClip, + IShapefile* sfResult, map* fieldMap, + std::set* subjectShapesToSkip, + std::set* clippingShapesToSkip) { - QTree* qTree = ((CShapefile*)sfClip)->GetTempQTree(); - - long numShapesSubject, numShapesClip; - this->get_NumShapes(&numShapesSubject); - sfClip->get_NumShapes(&numShapesClip); - - bool isM = ShapeUtility::IsM(_shpfiletype); - - long percent = 0; - for(long subjectId = 0; subjectId < numShapesSubject; subjectId++) - { - CallbackHelper::Progress(_globalCallback, subjectId, numShapesSubject, "Intersecting shapes...", _key, percent); - - if(!ShapeAvailable(subjectId, SelectedOnlySubject)) - continue; - - vector shapeIds; - double xMin, xMax, yMin, yMax; - this->QuickExtentsCore(subjectId, &xMin, &yMin, &xMax, &yMax); - shapeIds = qTree->GetNodes(QTreeExtent(xMin,xMax,yMax,yMin)); - - if (shapeIds.size() > 0) - { - GEOSGeometry* geom1 = GetGeosGeometry(subjectId); - - if (geom1) - { - double sumArea = 0.0; - - // iterating through clip geometries - for (int j = 0; j < (int)shapeIds.size(); j++) - { - // user can abort the operation at any time - if (_stopExecution) - { - VARIANT_BOOL stop; - _stopExecution->StopFunction(&stop); - if (stop) - { - sfResult->EditClear(&stop); - //GeosHelper::DestroyGeometry(geom1); - goto cleaning; - } - } - - // extracting clip geometry - long clipId = shapeIds[j]; - - if (!((CShapefile*)sfClip)->ShapeAvailable(clipId, SelectedOnlyClip)) - continue; - - GEOSGeometry* geom2 = ((CShapefile*)sfClip)->GetGeosGeometry(clipId); - - // calculating intersection - GEOSGeometry* geom = GeosHelper::Intersection(geom1, geom2); // don't delete oGeom1 as it will be used on the next loops - if (geom == NULL) continue; - - // saving the results - vector vShapes; - bool result = GeosConverter::GeomToShapes(geom, &vShapes, isM); - GeosHelper::DestroyGeometry(geom); - - this->InsertShapesVector(sfResult, vShapes, this, subjectId, NULL, sfClip, clipId, fieldMap); // shapes are released here - } - //GeosHelper::DestroyGeometry(geom1); - } - } - } + QTree* qTree = ((CShapefile*)sfClip)->GetTempQTree(); + + long numShapesSubject, numShapesClip; + this->get_NumShapes(&numShapesSubject); + sfClip->get_NumShapes(&numShapesClip); + + const bool isM = ShapeUtility::IsM(_shpfiletype); + + long percent = 0; + for (long subjectId = 0; subjectId < numShapesSubject; subjectId++) + { + CallbackHelper::Progress(_globalCallback, subjectId, numShapesSubject, "Intersecting shapes...", _key, percent); + + if (!ShapeAvailable(subjectId, SelectedOnlySubject)) + continue; + + double xMin, xMax, yMin, yMax; + this->QuickExtentsCore(subjectId, &xMin, &yMin, &xMax, &yMax); + vector shapeIds = qTree->GetNodes(QTreeExtent(xMin, xMax, yMax, yMin)); + + if (!shapeIds.empty()) + { + GEOSGeometry* geom1 = GetGeosGeometry(subjectId); + + if (geom1) + { + // double sumArea = 0.0; + + // iterating through clip geometries + for (long clipId : shapeIds) + { + // user can abort the operation at any time + if (_stopExecution) + { + VARIANT_BOOL stop; + _stopExecution->StopFunction(&stop); + if (stop) + { + sfResult->EditClear(&stop); + //GeosHelper::DestroyGeometry(geom1); + goto cleaning; + } + } + + // extracting clip geometry + if (!((CShapefile*)sfClip)->ShapeAvailable(clipId, SelectedOnlyClip)) + continue; + + GEOSGeometry* geom2 = ((CShapefile*)sfClip)->GetGeosGeometry(clipId); + + // calculating intersection + GEOSGeometry* geom = GeosHelper::Intersection(geom1, geom2); + // don't delete oGeom1 as it will be used on the next loops + if (geom == nullptr) continue; + + // saving the results + vector vShapes; + GeosConverter::GeomToShapes(geom, &vShapes, isM); + GeosHelper::DestroyGeometry(geom); + + this->InsertShapesVector(sfResult, vShapes, this, subjectId, nullptr, sfClip, clipId, fieldMap); + // shapes are released here + } + //GeosHelper::DestroyGeometry(geom1); + } + } + } cleaning: - CallbackHelper::ProgressCompleted(_globalCallback, _key); + CallbackHelper::ProgressCompleted(_globalCallback, _key); } // ****************************************************************** // IntersectionClipperNoAttributes() // ****************************************************************** -IShapefile* CShapefile::IntersectionClipperNoAttributes(VARIANT_BOOL SelectedOnlySubject, IShapefile* sfClip, VARIANT_BOOL SelectedOnlyClip ) +IShapefile* CShapefile::IntersectionClipperNoAttributes(VARIANT_BOOL SelectedOnlySubject, IShapefile* sfClip, + VARIANT_BOOL SelectedOnlyClip) { - if (!sfClip) - return NULL; - - IShapefile* sfResult = NULL; - this->Clone(&sfResult); - - ClipperLib::Clipper clp; - ClipperConverter::AddPolygons(this, clp, ClipperLib::PolyType::ptSubject, SelectedOnlySubject == VARIANT_TRUE); - ClipperConverter::AddPolygons(sfClip, clp, ClipperLib::PolyType::ptClip, SelectedOnlyClip == VARIANT_TRUE); - - // ClipperLib::Polygons polyResult; - ClipperLib::Paths polyResult; - if (clp.Execute(ClipperLib::ClipType::ctIntersection, polyResult)) { - - ClipperConverter converter(sfResult); - IShape* shp = converter.ClipperPolygon2Shape(&polyResult); - - VARIANT_BOOL vb; - if (shp) { - long index = 0; - sfResult->EditInsertShape(shp, &index, &vb); - shp->Release(); - } - } - return sfResult; + if (!sfClip) + return nullptr; + + IShapefile* sfResult = nullptr; + this->Clone(&sfResult); + + ClipperLib::Clipper clp; + ClipperConverter::AddPolygons(this, clp, ClipperLib::PolyType::ptSubject, SelectedOnlySubject == VARIANT_TRUE); + ClipperConverter::AddPolygons(sfClip, clp, ClipperLib::PolyType::ptClip, SelectedOnlyClip == VARIANT_TRUE); + + // ClipperLib::Polygons polyResult; + ClipperLib::Paths polyResult; + if (clp.Execute(ClipperLib::ClipType::ctIntersection, polyResult)) + { + ClipperConverter converter(sfResult); + IShape* shp = converter.ClipperPolygon2Shape(&polyResult); + + VARIANT_BOOL vb; + if (shp) + { + long index = 0; + sfResult->EditInsertShape(shp, &index, &vb); + shp->Release(); + } + } + return sfResult; } // ****************************************************************** // IntersectionClipper() // ****************************************************************** -void CShapefile::IntersectionClipper( VARIANT_BOOL SelectedOnlySubject, IShapefile* sfClip, VARIANT_BOOL SelectedOnlyClip, - IShapefile* sfResult, map* fieldMap, - std::set* subjectShapesToSkip, - std::set* clippingShapesToSkip) +void CShapefile::IntersectionClipper(VARIANT_BOOL SelectedOnlySubject, IShapefile* sfClip, + VARIANT_BOOL SelectedOnlyClip, + IShapefile* sfResult, map* fieldMap, + std::set* subjectShapesToSkip, + std::set* clippingShapesToSkip) { - QTree* qTree = ((CShapefile*)sfClip)->GetTempQTree(); - - long numShapesSubject, numShapesClip; - this->get_NumShapes(&numShapesSubject); - sfClip->get_NumShapes(&numShapesClip); - - // vector vPolygons; // we shall create vectors for both clipper and GEOS - vector vPolygons; // we shall create vectors for both clipper and GEOS - vPolygons.assign(numShapesClip, NULL); // this won't take much RAM or time - - ClipperLib::Clipper clp; - ClipperConverter converter(this); - - IGeoProjection* projection = NULL; - sfClip->get_GeoProjection(&projection); - VARIANT_BOOL isGeographic = VARIANT_FALSE; - if (projection) - { - projection->get_IsGeographic(&isGeographic); - projection->Release(); - projection = NULL; - } - double AREA_TOLERANCE = m_globalSettings.GetMinPolygonArea(isGeographic) * 0.001; - - // initial areas areas of the clipping shapes - std::vector initClipAreas; - // areas of the clipping shapes that were passed to the result: (int)index of shapes -> (double)area - std::vector resultClipAreas; - - ShpfileType shpType; - sfClip->get_ShapefileType(&shpType); - - bool buildSkipLists = (subjectShapesToSkip != NULL && clippingShapesToSkip != NULL && m_globalSettings.shapefileFastUnion && - ShapeUtility::Convert2D(_shpfiletype) == SHP_POLYGON && ShapeUtility::Convert2D(shpType) == SHP_POLYGON); - - if (buildSkipLists) - { - initClipAreas.resize(numShapesClip, 0.0); - resultClipAreas.resize(numShapesClip, 0.0); - } - - long percent = 0; - for(long subjectId =0; subjectId < numShapesSubject; subjectId++) - { - CallbackHelper::Progress(_globalCallback, subjectId, numShapesSubject, "Intersecting shapes...", _key, percent); + QTree* qTree = ((CShapefile*)sfClip)->GetTempQTree(); + + long numShapesSubject, numShapesClip; + this->get_NumShapes(&numShapesSubject); + sfClip->get_NumShapes(&numShapesClip); + + // vector vPolygons; // we shall create vectors for both clipper and GEOS + vector vPolygons; // we shall create vectors for both clipper and GEOS + vPolygons.assign(numShapesClip, nullptr); // this won't take much RAM or time + + ClipperLib::Clipper clp; + ClipperConverter converter(this); + + VARIANT_BOOL isGeographic = VARIANT_FALSE; + sfResult->get_IsGeographicProjection(&isGeographic); + + const double AREA_TOLERANCE = m_globalSettings.GetMinPolygonArea(isGeographic) * 0.001; + + // initial areas areas of the clipping shapes + std::vector initClipAreas; + // areas of the clipping shapes that were passed to the result: (int)index of shapes -> (double)area + std::vector resultClipAreas; + + ShpfileType shpType; + sfClip->get_ShapefileType(&shpType); + + const bool buildSkipLists = subjectShapesToSkip != nullptr && clippingShapesToSkip != nullptr && m_globalSettings. + shapefileFastUnion && + ShapeUtility::Convert2D(_shpfiletype) == SHP_POLYGON && ShapeUtility::Convert2D(shpType) == SHP_POLYGON; + + if (buildSkipLists) + { + initClipAreas.resize(numShapesClip, 0.0); + resultClipAreas.resize(numShapesClip, 0.0); + } + + long percent = 0; + for (long subjectId = 0; subjectId < numShapesSubject; subjectId++) + { + CallbackHelper::Progress(_globalCallback, subjectId, numShapesSubject, "Intersecting shapes...", _key, percent); + + if (!this->ShapeAvailable(subjectId, SelectedOnlySubject)) + continue; + + double xMin, xMax, yMin, yMax; + this->QuickExtentsCore(subjectId, &xMin, &yMin, &xMax, &yMax); + vector shapeIds = qTree->GetNodes(QTreeExtent(xMin, xMax, yMax, yMin)); + + if (!shapeIds.empty()) + { + // extracting subject polygon + IShape* shp1 = nullptr; + this->GetValidatedShape(subjectId, &shp1); + if (!shp1) continue; + // ClipperLib::Polygons* poly1 = converter.Shape2ClipperPolygon(shp1); + ClipperLib::Paths* poly1 = converter.Shape2ClipperPolygon(shp1); + + double initArea = 0.0; + shp1->get_Area(&initArea); + shp1->Release(); + + if (poly1) + { + double sumArea = 0.0; + + // extracting clipping polygons + for (long clipId : shapeIds) + { + // user can abort the operation in any time + if (_stopExecution) + { + VARIANT_BOOL stop; + _stopExecution->StopFunction(&stop); + if (stop) + { + sfResult->EditClear(&stop); + delete poly1; + goto cleaning; + } + } + + if (!((CShapefile*)sfClip)->ShapeAvailable(clipId, SelectedOnlyClip)) + continue; + + // vector vShapes; + + // Processing with Clipper + // ClipperLib::Polygons* poly2 = nullptr; + ClipperLib::Paths* poly2; + + if (vPolygons[clipId] == nullptr) + { + IShape* shp2 = nullptr; + ((CShapefile*)sfClip)->GetValidatedShape(clipId, &shp2); + if (!shp2) continue; + poly2 = converter.Shape2ClipperPolygon(shp2); + vPolygons[clipId] = poly2; + + if (buildSkipLists) + { + double tempArea = 0.0; + shp2->get_Area(&tempArea); + initClipAreas[clipId] = tempArea; + } + + shp2->Release(); + } + else + { + poly2 = vPolygons[clipId]; + } + + if (poly2) + { + if (poly1) + { + // clp.AddPolygons(*poly1, ClipperLib::ptSubject); + clp.AddPaths(*poly1, ClipperLib::ptSubject, true); + } + if (poly2) + { + // clp.AddPolygons(*poly2, ClipperLib::ptClip); + clp.AddPaths(*poly2, ClipperLib::ptClip, true); + } + + // do clipping + // ClipperLib::Polygons polyResult; + ClipperLib::Paths polyResult; + if (clp.Execute(ClipperLib::ctIntersection, polyResult)) + { + IShape* shp = converter.ClipperPolygon2Shape(&polyResult); + if (shp) + { + // sum of area to exclude shapes from difference + if (buildSkipLists) + { + double areaTemp; + shp->get_Area(&areaTemp); + + // subject shape + sumArea += areaTemp; + resultClipAreas[clipId] += areaTemp; + } + + vector vShapes; + vShapes.push_back(shp); + this->InsertShapesVector(sfResult, vShapes, this, subjectId, nullptr, sfClip, clipId, + fieldMap); + } + } + clp.Clear(); + } + } + delete poly1; + + if (buildSkipLists) + { + if (fabs(sumArea - initArea) < AREA_TOLERANCE) + { + subjectShapesToSkip->insert(subjectId); + } + } + } + } + } + + // building the list of clipping shapes to skip + if (buildSkipLists) + { + for (int i = 0; i < numShapesClip; i++) + { + if (fabs(initClipAreas[i] - resultClipAreas[i]) < AREA_TOLERANCE && initClipAreas[i] != 0.0) + { + clippingShapesToSkip->insert(i); + } + } + } - if(!this->ShapeAvailable(subjectId, SelectedOnlySubject)) - continue; - - vector shapeIds; - double xMin, xMax, yMin, yMax; - this->QuickExtentsCore(subjectId, &xMin, &yMin, &xMax, &yMax); - shapeIds = qTree->GetNodes(QTreeExtent(xMin,xMax,yMax,yMin)); - - if (shapeIds.size() > 0) - { - // extracting subject polygon - IShape* shp1 = NULL; - this->GetValidatedShape(subjectId, &shp1); - if (!shp1) continue; - // ClipperLib::Polygons* poly1 = converter.Shape2ClipperPolygon(shp1); - ClipperLib::Paths* poly1 = converter.Shape2ClipperPolygon(shp1); - - double initArea = 0.0; - shp1->get_Area(&initArea); - shp1->Release(); - - if (poly1) - { - double sumArea = 0.0; - - // extracting clipping polygons - for (int j = 0; j < (int)shapeIds.size(); j++) - { - // user can abort the operation in any time - if (_stopExecution) - { - VARIANT_BOOL stop; - _stopExecution->StopFunction(&stop); - if (stop) - { - sfResult->EditClear(&stop); - delete poly1; - goto cleaning; - } - } - - long clipId = shapeIds[j]; - - if (!((CShapefile*)sfClip)->ShapeAvailable(clipId, SelectedOnlyClip)) - continue; - - vector vShapes; - - // Processing with Clipper - // ClipperLib::Polygons* poly2 = NULL; - ClipperLib::Paths* poly2 = NULL; - - if (vPolygons[clipId] == NULL) - { - IShape* shp2 = NULL; - ((CShapefile*)sfClip)->GetValidatedShape(clipId, &shp2); - if (!shp2) continue; - poly2 = converter.Shape2ClipperPolygon(shp2); - vPolygons[clipId] = poly2; - - if (buildSkipLists) - { - double tempArea = 0.0; - shp2->get_Area(&tempArea); - initClipAreas[clipId] = tempArea; - } - - shp2->Release(); - } - else - { - poly2 = vPolygons[clipId]; - } - - if (poly2) - { - if (poly1) - { - // clp.AddPolygons(*poly1, ClipperLib::ptSubject); - clp.AddPaths(*poly1, ClipperLib::ptSubject, true); - } - if (poly2) - { - // clp.AddPolygons(*poly2, ClipperLib::ptClip); - clp.AddPaths(*poly2, ClipperLib::ptClip, true); - } - - // do clipping - // ClipperLib::Polygons polyResult; - ClipperLib::Paths polyResult; - if (clp.Execute(ClipperLib::ctIntersection, polyResult)) - { - IShape* shp = converter.ClipperPolygon2Shape(&polyResult); - if (shp) - { - // sum of area to exclude shapes from difference - if (buildSkipLists) - { - double areaTemp; - shp->get_Area(&areaTemp); - - // subject shape - sumArea += areaTemp; - resultClipAreas[clipId] += areaTemp; - } - - vector vShapes; - vShapes.push_back(shp); - this->InsertShapesVector(sfResult, vShapes, this, subjectId, NULL, sfClip, clipId, fieldMap); - } - } - clp.Clear(); - } - } - delete poly1; - - if (buildSkipLists) - { - if (fabs(sumArea - initArea) < AREA_TOLERANCE) - { - subjectShapesToSkip->insert(subjectId); - } - } - } - } - } - - // building the list of clipping shapes to skip - if (buildSkipLists) - { - for (int i = 0; i < numShapesClip; i++) - { - if ( fabs(initClipAreas[i] - resultClipAreas[i]) < AREA_TOLERANCE && initClipAreas[i] != 0.0) - { - clippingShapesToSkip->insert(i); - } - } - } - -cleaning: - CallbackHelper::ProgressCompleted(_globalCallback, _key); +cleaning: + CallbackHelper::ProgressCompleted(_globalCallback, _key); - for(int i = 0; i < (int)vPolygons.size(); i++) - { - if (vPolygons[i] !=NULL) - { - delete vPolygons[i]; - } - } + for (auto& vPolygon : vPolygons) + { + delete vPolygon; + } } #pragma endregion @@ -2469,127 +2484,124 @@ void CShapefile::IntersectionClipper( VARIANT_BOOL SelectedOnlySubject, IShapefi // ******************************************************************** // DifferenceGEOS() // ******************************************************************** -void CShapefile::DifferenceGEOS(IShapefile* sfSubject, VARIANT_BOOL SelectedOnlySubject, IShapefile* sfOverlay, VARIANT_BOOL SelectedOnlyOverlay, - IShapefile* sfResult, map* fieldMap, set* shapesToSkip) +void CShapefile::DifferenceGEOS(IShapefile* sfSubject, VARIANT_BOOL SelectedOnlySubject, IShapefile* sfOverlay, + VARIANT_BOOL SelectedOnlyOverlay, + IShapefile* sfResult, map* fieldMap, set* shapesToSkip) { - QTree* qTree = ((CShapefile*)sfOverlay)->GetTempQTree(); + QTree* qTree = ((CShapefile*)sfOverlay)->GetTempQTree(); + + long numShapesSubject, numShapesClip; + sfSubject->get_NumShapes(&numShapesSubject); + sfOverlay->get_NumShapes(&numShapesClip); + + const bool isM = ShapeUtility::IsM(_shpfiletype); + + long percent = 0; + for (long subjectId = 0; subjectId < numShapesSubject; subjectId++) + { + CallbackHelper::Progress(_globalCallback, subjectId, numShapesSubject, "Calculating difference...", _key, + percent); + + if (!((CShapefile*)sfSubject)->ShapeAvailable(subjectId, SelectedOnlySubject)) + continue; + + // those shapes are marked to skip in the course of intersection + if (shapesToSkip != nullptr) + { + if (shapesToSkip->find(subjectId) != shapesToSkip->end()) + continue; + } + + double xMin, xMax, yMin, yMax; + ((CShapefile*)sfSubject)->QuickExtentsCore(subjectId, &xMin, &yMin, &xMax, &yMax); + vector shapeIds = qTree->GetNodes(QTreeExtent(xMin, xMax, yMax, yMin)); + + if (!shapeIds.empty()) + { + GEOSGeometry* gsGeom1 = ((CShapefile*)sfSubject)->GetGeosGeometry(subjectId); + if (!gsGeom1) continue; + + vector vClip; + + // iterating through clip geometries, if the subject will stand this, we add it to the result + for (long clipId : shapeIds) + { + // user can abort the operation in any time + if (_stopExecution) + { + VARIANT_BOOL stop; + _stopExecution->StopFunction(&stop); + if (stop) + { + sfResult->EditClear(&stop); + //GeosHelper::DestroyGeometry(gsGeom1); + goto cleaning; + } + } + + // extracting clip geometry + if (!((CShapefile*)sfOverlay)->ShapeAvailable(clipId, SelectedOnlyOverlay)) + continue; + + GEOSGeometry* gsGeom2 = ((CShapefile*)sfOverlay)->GetGeosGeometry(clipId); + + if (gsGeom2 && GeosHelper::Intersects(gsGeom1, gsGeom2)) + { + vClip.push_back(gsGeom2); + } + } + + GEOSGeometry* gsClip = nullptr; + if (vClip.size() == 1) + { + gsClip = vClip[0]; + } + else if (vClip.size() > 1) + { + // union of the clipping shapes + gsClip = GeosConverter::MergeGeometries(vClip, nullptr, false, false); + } + + bool deleteNeeded = false; + if (gsClip) + { + gsGeom1 = GeosHelper::Difference(gsGeom1, gsClip); + deleteNeeded = true; + + // if clip geometry was merged, we should delete it + if (vClip.size() > 1) + { + GeosHelper::DestroyGeometry(gsClip); + } + } + + // saving what was left from the subject + if (gsGeom1 != nullptr) + { + vector vShapes; + GeosConverter::GeomToShapes(gsGeom1, &vShapes, isM); + if (deleteNeeded) + GeosHelper::DestroyGeometry(gsGeom1); + this->InsertShapesVector(sfResult, vShapes, sfSubject, subjectId, fieldMap); // shapes are released here + } + } + else + { + // insert the shape directly no other shapes intersects it + // TODO: it makes sense to rewrite it in more efficient way + IShape* shp1 = nullptr; + vector vShapes; + ((CShapefile*)sfSubject)->GetValidatedShape(subjectId, &shp1); + if (shp1) + { + vShapes.push_back(shp1); + this->InsertShapesVector(sfResult, vShapes, sfSubject, subjectId, fieldMap); // shapes are released here + } + } + } - long numShapesSubject, numShapesClip; - sfSubject->get_NumShapes(&numShapesSubject); - sfOverlay->get_NumShapes(&numShapesClip); - - bool isM = ShapeUtility::IsM(_shpfiletype); - - long percent = 0; - for(long subjectId = 0; subjectId < numShapesSubject; subjectId++) - { - CallbackHelper::Progress(_globalCallback, subjectId, numShapesSubject, "Calculating difference...", _key, percent); - - if(!((CShapefile*)sfSubject)->ShapeAvailable(subjectId, SelectedOnlySubject)) - continue; - - // those shapes are marked to skip in the course of intersection - if (shapesToSkip != NULL) - { - if (shapesToSkip->find(subjectId) != shapesToSkip->end()) - continue; - } - - vector shapeIds; - double xMin, xMax, yMin, yMax; - ((CShapefile*)sfSubject)->QuickExtentsCore(subjectId, &xMin, &yMin, &xMax, &yMax); - shapeIds = qTree->GetNodes(QTreeExtent(xMin,xMax,yMax,yMin)); - - if (shapeIds.size() > 0) - { - GEOSGeometry* gsGeom1 = ((CShapefile*)sfSubject)->GetGeosGeometry(subjectId); - if (!gsGeom1) continue; - - vector vClip; - - // iterating through clip geometries, if the subject will stand this, we add it to the result - for (int j = 0; j < (int)shapeIds.size(); j++) - { - // user can abort the operation in any time - if (_stopExecution) - { - VARIANT_BOOL stop; - _stopExecution->StopFunction(&stop); - if (stop) - { - sfResult->EditClear(&stop); - //GeosHelper::DestroyGeometry(gsGeom1); - goto cleaning; - } - } - - // extracting clip geometry - long clipId = shapeIds[j]; - - if (!((CShapefile*)sfOverlay)->ShapeAvailable(clipId, SelectedOnlyOverlay)) - continue; - - GEOSGeometry* gsGeom2 = ((CShapefile*)sfOverlay)->GetGeosGeometry(clipId); - - if (gsGeom2 && GeosHelper::Intersects(gsGeom1, gsGeom2)) - { - vClip.push_back(gsGeom2); - } - } - - GEOSGeometry* gsClip = NULL; - if (vClip.size() == 1) - { - gsClip = vClip[0]; - } - else if (vClip.size() > 1) - { - // union of the clipping shapes - gsClip = GeosConverter::MergeGeometries(vClip, NULL, false, false); - } - - bool deleteNeeded = false; - if (gsClip) - { - GEOSGeometry* gsTemp = gsGeom1; - gsGeom1 = GeosHelper::Difference(gsGeom1, gsClip); - deleteNeeded = true; - //GeosHelper::DestroyGeometry(gsTemp); // initial subject geometry isn't needed any more - - // if clip geometry was merged, we should delete it - if (vClip.size() > 1) - { - GeosHelper::DestroyGeometry(gsClip); - } - } - - // saving what was left from the subject - if (gsGeom1 != NULL) - { - vector vShapes; - bool result = GeosConverter::GeomToShapes(gsGeom1, &vShapes, isM); - if (deleteNeeded) - GeosHelper::DestroyGeometry(gsGeom1); - this->InsertShapesVector(sfResult, vShapes, sfSubject, subjectId, fieldMap); // shapes are released here - } - } - else - { - // insert the shape directly no other shapes intersects it - // TODO: it makes sense to rewrite it in more efficient way - IShape* shp1 = NULL; - vector vShapes; - ((CShapefile*)sfSubject)->GetValidatedShape(subjectId, &shp1); - if (shp1) - { - vShapes.push_back(shp1); - this->InsertShapesVector(sfResult, vShapes, sfSubject, subjectId, fieldMap); // shapes are released here - } - } - } - -cleaning: - CallbackHelper::ProgressCompleted(_globalCallback, _key); +cleaning: + CallbackHelper::ProgressCompleted(_globalCallback, _key); } #ifdef SERIALIZE_POLYGONS @@ -2625,413 +2637,438 @@ void SerializePolygon(ofstream& out, ClipperLib::Polygons* poly) // ****************************************************************** // DifferenceClipper() // ****************************************************************** -void CShapefile::DifferenceClipper(IShapefile* sfSubject, VARIANT_BOOL SelectedOnlySubject, IShapefile* sfClip, VARIANT_BOOL SelectedOnlyClip, - IShapefile* sfResult, map* fieldMap, set* shapesToSkip) +void CShapefile::DifferenceClipper(IShapefile* sfSubject, VARIANT_BOOL SelectedOnlySubject, IShapefile* sfClip, + VARIANT_BOOL SelectedOnlyClip, + IShapefile* sfResult, map* fieldMap, set* shapesToSkip) { - QTree* qTree = ((CShapefile*)sfClip)->GetTempQTree(); - - long numShapesSubject, numShapesClip; - sfSubject->get_NumShapes(&numShapesSubject); - sfClip->get_NumShapes(&numShapesClip); - - // vector vPolygons; // we shall create vectors for both clipper and GEOS - vector vPolygons; // we shall create vectors for both clipper and GEOS - vPolygons.assign(numShapesClip, NULL); // this won't take much RAM or time - - ClipperConverter ogr(sfSubject); - - long percent = 0; - for(long subjectId =0; subjectId < numShapesSubject; subjectId++) - { - CallbackHelper::Progress(_globalCallback, subjectId, numShapesSubject, "Calculating difference...", _key, percent); + QTree* qTree = ((CShapefile*)sfClip)->GetTempQTree(); + + long numShapesSubject, numShapesClip; + sfSubject->get_NumShapes(&numShapesSubject); + sfClip->get_NumShapes(&numShapesClip); + + // vector vPolygons; // we shall create vectors for both clipper and GEOS + vector vPolygons; // we shall create vectors for both clipper and GEOS + vPolygons.assign(numShapesClip, nullptr); // this won't take much RAM or time + + ClipperConverter ogr(sfSubject); + + long percent = 0; + for (long subjectId = 0; subjectId < numShapesSubject; subjectId++) + { + CallbackHelper::Progress(_globalCallback, subjectId, numShapesSubject, "Calculating difference...", _key, + percent); + + if (!((CShapefile*)sfSubject)->ShapeAvailable(subjectId, SelectedOnlySubject)) + continue; + + if (shapesToSkip != nullptr) + { + if (shapesToSkip->find(subjectId) != shapesToSkip->end()) + continue; + } + + double xMin, xMax, yMin, yMax; + ((CShapefile*)sfSubject)->QuickExtentsCore(subjectId, &xMin, &yMin, &xMax, &yMax); + vector shapeIds = qTree->GetNodes(QTreeExtent(xMin, xMax, yMax, yMin)); + + if (!shapeIds.empty()) + { + // extracting subject polygon + IShape* shp1 = nullptr; + ((CShapefile*)sfSubject)->GetValidatedShape(subjectId, &shp1); + if (!shp1) continue; + // ClipperLib::Polygons* poly1 = ogr.Shape2ClipperPolygon(shp1); + ClipperLib::Paths* poly1 = ogr.Shape2ClipperPolygon(shp1); + shp1->Release(); + + if (poly1) + { + auto* clp = new ClipperLib::Clipper; + + // extracting clipping polygons + // ClipperLib::Polygons* poly2 = nullptr; + ClipperLib::Paths* poly2; + for (long clipId : shapeIds) + { + // user can abort the operation in any time + if (_stopExecution) + { + VARIANT_BOOL stop; + _stopExecution->StopFunction(&stop); + if (stop) + { + sfResult->EditClear(&stop); + delete clp; + goto cleaning; + } + } + + if (!((CShapefile*)sfClip)->ShapeAvailable(clipId, SelectedOnlyClip)) + continue; + + vector vShapes; + + // processng with Clipper + if (vPolygons[clipId] == nullptr) + { + IShape* shp2 = nullptr; + ((CShapefile*)sfClip)->GetValidatedShape(clipId, &shp2); + if (!shp2) continue; + poly2 = ogr.Shape2ClipperPolygon(shp2); + vPolygons[clipId] = poly2; + shp2->Release(); + } + else + { + poly2 = vPolygons[clipId]; + } + + if (poly2) + { + // clp->AddPolygons(*poly2, ClipperLib::ptClip); + clp->AddPaths(*poly2, ClipperLib::ptClip, true); + } - if(!((CShapefile*)sfSubject)->ShapeAvailable(subjectId, SelectedOnlySubject)) - continue; - - if (shapesToSkip != NULL) - { - if (shapesToSkip->find(subjectId) != shapesToSkip->end()) - continue; - } - - vector shapeIds; - double xMin, xMax, yMin, yMax; - ((CShapefile*)sfSubject)->QuickExtentsCore(subjectId, &xMin, &yMin, &xMax, &yMax); - shapeIds = qTree->GetNodes(QTreeExtent(xMin,xMax,yMax,yMin)); - - if (shapeIds.size() > 0) - { - // extracting subject polygon - IShape* shp1 = NULL; - ((CShapefile*)sfSubject)->GetValidatedShape(subjectId, &shp1); - if (!shp1) continue; - // ClipperLib::Polygons* poly1 = ogr.Shape2ClipperPolygon(shp1); - ClipperLib::Paths* poly1 = ogr.Shape2ClipperPolygon(shp1); - shp1->Release(); - - if (poly1) - { - ClipperLib::Clipper* clp = new ClipperLib::Clipper; - - // extracting clipping polygons - // ClipperLib::Polygons* poly2 = NULL; - ClipperLib::Paths* poly2 = NULL; - for (int j = 0; j < (int)shapeIds.size(); j++) - { - // user can abort the operation in any time - if (_stopExecution) - { - VARIANT_BOOL stop; - _stopExecution->StopFunction(&stop); - if (stop) - { - sfResult->EditClear(&stop); - delete clp; - goto cleaning; - } - } - - long clipId = shapeIds[j]; - - if (!((CShapefile*)sfClip)->ShapeAvailable(clipId, SelectedOnlyClip)) - continue; - - vector vShapes; - - // processng with Clipper - if (vPolygons[clipId] == NULL) - { - IShape* shp2 = NULL; - ((CShapefile*)sfClip)->GetValidatedShape(clipId, &shp2); - if (!shp2) continue; - poly2 = ogr.Shape2ClipperPolygon(shp2); - vPolygons[clipId] = poly2; - shp2->Release(); - } - else - { - poly2 = vPolygons[clipId]; - } - - if (poly2) - { - // clp->AddPolygons(*poly2, ClipperLib::ptClip); - clp->AddPaths(*poly2, ClipperLib::ptClip, true); - } - - #ifdef SERIALIZE_POLYGONS +#ifdef SERIALIZE_POLYGONS SerializePolygon(out, poly2); - #endif - } - - if (poly1) - { - // clp->AddPolygons(*poly1, ClipperLib::ptSubject); - clp->AddPaths(*poly1, ClipperLib::ptSubject, true); - } - - #ifdef SERIALIZE_POLYGONS - SerializePolygon(out, poly1); - #endif - - // do clipping - // ClipperLib::Polygons polyResult; - ClipperLib::Paths polyResult; - if (clp->Execute(ClipperLib::ctDifference, polyResult)) - { - IShape* shp = ogr.ClipperPolygon2Shape(&polyResult); - if (shp) - { - vector vShapes; - vShapes.push_back(shp); - this->InsertShapesVector(sfResult, vShapes, sfSubject, subjectId, fieldMap); - } - } - - delete poly1; - clp->Clear(); - delete clp; - } - } - else - { - // insert the shape directly no other shapes intersects it - // TODO: it makes sense to rewrite it in more efficient way - IShape* shp1 = NULL; - vector vShapes; - ((CShapefile*)sfSubject)->GetValidatedShape(subjectId, &shp1); - if (shp1) - { - VARIANT_BOOL editingShapes; - sfSubject->get_EditingShapes(&editingShapes); - IShape* shpCopy = NULL; - if (editingShapes) - { - shp1->Clone(&shpCopy); - shp1->Release(); - } - else - { - shpCopy = shp1; - } - vShapes.push_back(shpCopy); - this->InsertShapesVector(sfResult, vShapes, sfSubject, subjectId, fieldMap); // shapes are released here - } - } - } - -cleaning: - CallbackHelper::ProgressCompleted(_globalCallback, _key); - - for(int i = 0; i < (int)vPolygons.size(); i++) - { - if (vPolygons[i] !=NULL) - { - delete vPolygons[i]; - } - } -} -#pragma endregion - -#pragma region Properties -// ******************************************************************** -// GeometryEngine() -// ******************************************************************** -STDMETHODIMP CShapefile::get_GeometryEngine(tkGeometryEngine* pVal) -{ - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *pVal = _geometryEngine; - return S_OK; -} - -STDMETHODIMP CShapefile::put_GeometryEngine(tkGeometryEngine newVal) -{ - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - _geometryEngine = newVal; - return S_OK; -} - -#pragma endregion - -#pragma region PointInPolygon - - - -// ******************************************************************** -// PointInShape() -// ******************************************************************** -STDMETHODIMP CShapefile::PointInShape(LONG ShapeIndex, DOUBLE x, DOUBLE y, VARIANT_BOOL* retval) -{ - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - - if( ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) - { - *retval = VARIANT_FALSE; - ErrorMessage( tkINDEX_OUT_OF_BOUNDS ); - return S_OK; - } - - long numParts, numPoints; - long * Parts; - Point2D * Points; - - if (_isEditingShapes) - { - if(_shpfiletype != SHP_POLYGON && _shpfiletype != SHP_POLYGONM && _shpfiletype != SHP_POLYGONZ) - { - *retval = VARIANT_FALSE; - return S_OK; - } - - double xMin, yMin, xMax, yMax; - this->QuickExtentsCore(ShapeIndex, &xMin, &yMin, &xMax, &yMax); - - if(x < xMin || y < yMin || x > xMax || y > yMax) - { - *retval = VARIANT_FALSE; - return S_OK; - } - - // retrieving shapes from memory - IShape * shp = _shapeData[ShapeIndex]->shape; - shp->get_NumParts(&numParts); - shp->get_NumPoints(&numPoints); - Parts = new long[numParts + 1]; - Points = new Point2D[numPoints]; - - for(int nPart = numParts - 1; nPart >= 0; nPart--) - shp->get_Part(nPart, &Parts[nPart]); - - VARIANT_BOOL vbretval; - for(int nPoint = numPoints - 1; nPoint >= 0; nPoint--) - { - shp->get_XY(nPoint, &Points[nPoint].x, &Points[nPoint].y, &vbretval); - } - Parts[numParts] = numPoints; - } - else - { - CSingleLock lock(&_readLock, TRUE); - - int shpType; - fseek(_shpfile, _shpOffsets[ShapeIndex] + sizeof(int)*2, SEEK_SET); - fread(&shpType, sizeof(int), 1, _shpfile); - - shpType = ShapeUtility::Convert2D((ShpfileType)shpType); - if(shpType != SHP_POLYGON) - { - *retval = VARIANT_FALSE; - return S_OK; - } - - ShapeHeader shpHeader; - fread(&shpHeader, sizeof(ShapeHeader), 1, _shpfile); - - // check the bounds - if(x < shpHeader.MinX || y < shpHeader.MinY || x > shpHeader.MaxX || y > shpHeader.MaxY) - { - *retval = VARIANT_FALSE; - return S_OK; - } - - Parts = new long[shpHeader.NumParts + 1]; - Points = new Point2D[shpHeader.NumPoints]; - - fread(Parts, sizeof(int), shpHeader.NumParts, _shpfile); - fread(Points, sizeof(Point2D), shpHeader.NumPoints, _shpfile); - Parts[shpHeader.NumParts] = shpHeader.NumPoints; - numParts = shpHeader.NumParts; - } - - int CrossCount = 0; - for(int nPart = numParts - 1; nPart >= 0; nPart--) - { - int nPointMax = Parts[nPart+1] - 1; - for(int nPoint = Parts[nPart]; nPoint < nPointMax; nPoint++) - { - double x1 = Points[nPoint].x - x; - double y1 = Points[nPoint].y - y; - double x2 = Points[nPoint+1].x - x; - double y2 = Points[nPoint+1].y - y; +#endif + } - double y1y2 = y1*y2; - if(y1y2 > 0.0) // If the signs are the same - { - // Then it does not cross - continue; - } - else if(y1y2 == 0.0) // Then it has intesected a vertex - { - if(y1 == 0.0) - { - if( y2 > 0.0 ) - continue; - } - else if( y1 > 0.0 ) - continue; - } + if (poly1) + { + // clp->AddPolygons(*poly1, ClipperLib::ptSubject); + clp->AddPaths(*poly1, ClipperLib::ptSubject, true); + } - if( x1 > 0.0 && x2 > 0.0 ) - { - CrossCount++; - continue; - } +#ifdef SERIALIZE_POLYGONS + SerializePolygon(out, poly1); +#endif - // Calculate Intersection - double dy = y2 - y1; - double dx = x2 - x1; + // do clipping + // ClipperLib::Polygons polyResult; + ClipperLib::Paths polyResult; + if (clp->Execute(ClipperLib::ctDifference, polyResult)) + { + IShape* shp = ogr.ClipperPolygon2Shape(&polyResult); + if (shp) + { + vector vShapes; + vShapes.push_back(shp); + this->InsertShapesVector(sfResult, vShapes, sfSubject, subjectId, fieldMap); + } + } + + delete poly1; + clp->Clear(); + delete clp; + } + } + else + { + // insert the shape directly no other shapes intersects it + // TODO: it makes sense to rewrite it in more efficient way + IShape* shp1 = nullptr; + vector vShapes; + ((CShapefile*)sfSubject)->GetValidatedShape(subjectId, &shp1); + if (shp1) + { + VARIANT_BOOL editingShapes; + sfSubject->get_EditingShapes(&editingShapes); + IShape* shpCopy = nullptr; + if (editingShapes) + { + shp1->Clone(&shpCopy); + shp1->Release(); + } + else + { + shpCopy = shp1; + } + vShapes.push_back(shpCopy); + this->InsertShapesVector(sfResult, vShapes, sfSubject, subjectId, fieldMap); // shapes are released here + } + } + } - // CDM March 2008 - if dy is zero (horiz line), this will be a bad idea... - if (dy != 0) - { - if (x1 - y1*(dx/dy) > 0.0) - CrossCount++; - } - } - } - delete [] Points; - delete [] Parts; +cleaning: + CallbackHelper::ProgressCompleted(_globalCallback, _key); - *retval = CrossCount&1 ? VARIANT_TRUE : VARIANT_FALSE; - return S_OK; + for (auto& vPolygon : vPolygons) + { + delete vPolygon; + } } #pragma endregion -#pragma region PointInShapefile +#pragma region Properties // ******************************************************************** -// PointInShapefile() +// GeometryEngine() // ******************************************************************** -STDMETHODIMP CShapefile::PointInShapefile(DOUBLE x, DOUBLE y, LONG* ShapeIndex) +STDMETHODIMP CShapefile::get_GeometryEngine(tkGeometryEngine* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - - int nShapeCount = _polySf.size(); - for(int nShape = nShapeCount - 1; nShape >= 0; nShape--) - // for(int nShape = 0; nShape < ShapeCount; nShape++) see http://www.mapwindow.org/phorum/read.php?3,9745,9950#msg-9950 - { - PolygonShapefile * sf = &_polySf[nShape]; - - if(x < sf->shpHeader.MinX || y < sf->shpHeader.MinY || x > sf->shpHeader.MaxX || y > sf->shpHeader.MaxY) - { - continue; - } - - int CrossCount = 0; - - for(int nPart = sf->shpHeader.NumParts - 1; nPart >= 0; nPart--) - { - int nPointMax = sf->Parts[nPart+1] - 1; - for(int nPoint = sf->Parts[nPart]; nPoint < nPointMax; nPoint++) - { - double x1 = sf->Points[nPoint].x - x; - double y1 = sf->Points[nPoint].y - y; - double x2 = sf->Points[nPoint+1].x - x; - double y2 = sf->Points[nPoint+1].y - y; - - double y1y2 = y1*y2; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *pVal = _geometryEngine; + return S_OK; +} - if(y1y2 > 0.0) // If the signs are the same - { - // Then it does not cross - continue; - } - else if(y1y2 == 0.0) // Then it has intesected a vertex - { - if(y1 == 0.0) - { - if( y2 > 0.0 ) - continue; - } - else if( y1 > 0.0 ) - continue; - } +STDMETHODIMP CShapefile::put_GeometryEngine(tkGeometryEngine newVal) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + _geometryEngine = newVal; + return S_OK; +} - if( x1 > 0.0 && x2 > 0.0 ) - { - CrossCount++; - continue; - } +#pragma endregion - // Calculate Intersection - double dy = y2 - y1; - double dx = x2 - x1; - double xint = x1 - y1*(dx/dy); +#pragma region PointInPolygon - if(xint > 0.0) - CrossCount++; - } - } - if(CrossCount&1) - { - *ShapeIndex = nShape; - return S_OK; - } - - } +// ******************************************************************** +// PointInShape() +// ******************************************************************** +STDMETHODIMP CShapefile::PointInShape(LONG ShapeIndex, DOUBLE x, DOUBLE y, VARIANT_BOOL* retval) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + if (ShapeIndex < 0 || ShapeIndex >= (long)_shapeData.size()) + { + *retval = VARIANT_FALSE; + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + return S_OK; + } + + long numParts, numPoints; + long* Parts; + Point2D* Points; + + if (_isEditingShapes) + { + if (_shpfiletype != SHP_POLYGON && _shpfiletype != SHP_POLYGONM && _shpfiletype != SHP_POLYGONZ) + { + *retval = VARIANT_FALSE; + return S_OK; + } + + double xMin, yMin, xMax, yMax; + this->QuickExtentsCore(ShapeIndex, &xMin, &yMin, &xMax, &yMax); + + if (x < xMin || y < yMin || x > xMax || y > yMax) + { + *retval = VARIANT_FALSE; + return S_OK; + } + + // retrieving shapes from memory + IShape* shp = _shapeData[ShapeIndex]->shape; + shp->get_NumParts(&numParts); + shp->get_NumPoints(&numPoints); + Parts = new long[numParts + 1]; + Points = new Point2D[numPoints]; + + for (int nPart = numParts - 1; nPart >= 0; nPart--) + shp->get_Part(nPart, &Parts[nPart]); + + VARIANT_BOOL vbretval; + for (int nPoint = numPoints - 1; nPoint >= 0; nPoint--) + { + shp->get_XY(nPoint, &Points[nPoint].x, &Points[nPoint].y, &vbretval); + } + Parts[numParts] = numPoints; + } + else + { + CSingleLock lock(&_readLock, TRUE); + + int shpType; + fseek(_shpfile, _shpOffsets[ShapeIndex] + sizeof(int) * 2, SEEK_SET); + fread(&shpType, sizeof(int), 1, _shpfile); + + shpType = ShapeUtility::Convert2D((ShpfileType)shpType); + if (shpType != SHP_POLYGON) + { + *retval = VARIANT_FALSE; + return S_OK; + } + + ShapeHeader shpHeader{}; + fread(&shpHeader, sizeof(ShapeHeader), 1, _shpfile); + + // check the bounds + if (x < shpHeader.MinX || y < shpHeader.MinY || x > shpHeader.MaxX || y > shpHeader.MaxY) + { + *retval = VARIANT_FALSE; + return S_OK; + } + + Parts = new long[shpHeader.NumParts + 1]; + Points = new Point2D[shpHeader.NumPoints]; + + fread(Parts, sizeof(int), shpHeader.NumParts, _shpfile); + fread(Points, sizeof(Point2D), shpHeader.NumPoints, _shpfile); + Parts[shpHeader.NumParts] = shpHeader.NumPoints; + numParts = shpHeader.NumParts; + } + + int CrossCount = 0; + // work backwards through Parts since we only know the first point of each Part + for (int nPart = numParts - 1; nPart >= 0; nPart--) + { + const int nPointMin = Parts[nPart]; + const int nPointMax = Parts[nPart + 1] - 1; + int i, j; + // algorithm by W. Randolph Franklin; for a detailed explanation, see + // https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html + // note that i starts at first point, while j starts at last point, then follows behind i + for (i = nPointMin, j = nPointMax - 1; i < nPointMax; j = i++) + { + if ( ((Points[i].y > y) != (Points[j].y > y)) && + (x < (Points[j].x - Points[i].x) * (y - Points[i].y) / (Points[j].y - Points[i].y) + Points[i].x) ) + // we have a crossing + CrossCount++; + } + + //for (int nPoint = Parts[nPart]; nPoint < nPointMax; nPoint++) + //{ + // const double x1 = Points[nPoint].x - x; + // const double y1 = Points[nPoint].y - y; + // const double x2 = Points[nPoint + 1].x - x; + // const double y2 = Points[nPoint + 1].y - y; + + // const double y1y2 = y1 * y2; + // if (y1y2 > 0.0) // If the signs are the same + // { + // // Then it does not cross + // continue; + // } + // if (y1y2 == 0.0) // Then it has intesected a vertex + // { + // if (y1 == 0.0) + // { + // if (y2 > 0.0) + // continue; + // } + // else if (y1 > 0.0) + // continue; + // } + + // if (x1 > 0.0 && x2 > 0.0) + // { + // CrossCount++; + // continue; + // } + + // // Calculate Intersection + // const double dy = y2 - y1; + // const double dx = x2 - x1; + + // // CDM March 2008 - if dy is zero (horiz line), this will be a bad idea... + // if (dy != 0) + // { + // if (x1 - y1 * (dx / dy) > 0.0) + // CrossCount++; + // } + //} + } + delete [] Points; + delete [] Parts; + + *retval = CrossCount & 1 ? VARIANT_TRUE : VARIANT_FALSE; + return S_OK; +} +#pragma endregion - *ShapeIndex = -1; - return S_OK; +#pragma region PointInShapefile +// ******************************************************************** +// PointInShapefile() +// ******************************************************************** +STDMETHODIMP CShapefile::PointInShapefile(DOUBLE x, DOUBLE y, LONG* ShapeIndex) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + const int nShapeCount = _polySf.size(); + for (int nShape = nShapeCount - 1; nShape >= 0; nShape--) + // for(int nShape = 0; nShape < ShapeCount; nShape++) see http://www.mapwindow.org/phorum/read.php?3,9745,9950#msg-9950 + { + PolygonShapefile* sf = &_polySf[nShape]; + + if (x < sf->shpHeader.MinX || y < sf->shpHeader.MinY || x > sf->shpHeader.MaxX || y > sf->shpHeader.MaxY) + { + continue; + } + + int CrossCount = 0; + + int i, j; + // work backwards through Parts since we only know the first point of each Part + for (int nPart = sf->shpHeader.NumParts - 1; nPart >= 0; nPart--) + { + const int nPointMin = sf->Parts[nPart]; + const int nPointMax = sf->Parts[nPart + 1] - 1; + // algorithm by W. Randolph Franklin; for a detailed explanation, see + // https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html + // note that i starts at first point, while j starts at last point, then follows behind i + for (i = nPointMin, j = nPointMax - 1; i < nPointMax; j = i++) + { + if (((sf->Points[i].y > y) != (sf->Points[j].y > y)) && + (x < (sf->Points[j].x - sf->Points[i].x) * (y - sf->Points[i].y) / (sf->Points[j].y - sf->Points[i].y) + sf->Points[i].x)) + // we have a crossing + CrossCount++; + } + } + //for (int nPart = sf->shpHeader.NumParts - 1; nPart >= 0; nPart--) + //{ + // const int nPointMax = sf->Parts[nPart + 1] - 1; + // for (int nPoint = sf->Parts[nPart]; nPoint < nPointMax; nPoint++) + // { + // const double x1 = sf->Points[nPoint].x - x; + // const double y1 = sf->Points[nPoint].y - y; + // const double x2 = sf->Points[nPoint + 1].x - x; + // const double y2 = sf->Points[nPoint + 1].y - y; + + // const double y1y2 = y1 * y2; + + // if (y1y2 > 0.0) // If the signs are the same + // { + // // Then it does not cross + // continue; + // } + // if (y1y2 == 0.0) // Then it has intesected a vertex + // { + // if (y1 == 0.0) + // { + // if (y2 > 0.0) + // continue; + // } + // else if (y1 > 0.0) + // continue; + // } + + // if (x1 > 0.0 && x2 > 0.0) + // { + // CrossCount++; + // continue; + // } + + // // Calculate Intersection + // const double dy = y2 - y1; + // const double dx = x2 - x1; + // const double xint = x1 - y1 * (dx / dy); + + // if (xint > 0.0) + // CrossCount++; + // } + //} + + if (CrossCount & 1) + { + *ShapeIndex = nShape; + return S_OK; + } + } + + *ShapeIndex = -1; + return S_OK; } // ******************************************************************** @@ -3039,70 +3076,71 @@ STDMETHODIMP CShapefile::PointInShapefile(DOUBLE x, DOUBLE y, LONG* ShapeIndex) // ******************************************************************** STDMETHODIMP CShapefile::BeginPointInShapefile(VARIANT_BOOL* retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - if (_writing) - { - //AfxMessageBox("Can't read"); - *retval = VARIANT_FALSE; - return S_OK; - } - - if( _shpfiletype != SHP_POLYGON ) - { - *retval = VARIANT_FALSE; - ErrorMessage(tkUNEXPECTED_SHAPE_TYPE); - return S_OK; - } - - CSingleLock lock(&_readLock, TRUE); - - int size = _shapeData.size(); - _polySf.resize(size); - - for(int nShape = 0; nShape < size; nShape++) - { - fseek(_shpfile, _shpOffsets[nShape]+sizeof(int)*2, SEEK_SET); - int shpType; - fread(&shpType, sizeof(int), 1, _shpfile); - if(shpType != SHP_POLYGON) - { - *retval = VARIANT_FALSE; - ErrorMessage(tkUNEXPECTED_SHAPE_TYPE); - return S_OK; - } - - PolygonShapefile & sf = _polySf[nShape]; - fread(&sf.shpHeader, sizeof(ShapeHeader), 1, _shpfile); - - if (sf.shpHeader.NumPoints > 0 && sf.shpHeader.NumParts > 0) - { - sf.Points.resize(sf.shpHeader.NumPoints); - sf.Parts.resize(sf.shpHeader.NumParts + 1); - fread(&sf.Parts[0], sizeof(int), sf.shpHeader.NumParts, _shpfile); - fread(&sf.Points[0], sizeof(Point2D), sf.shpHeader.NumPoints, _shpfile); - sf.Parts[sf.shpHeader.NumParts] = sf.shpHeader.NumPoints; - *retval = VARIANT_TRUE; - } - else - { - // this isn't a valid shape, we simply won't process it - ErrorMessage(tkINVALID_SHAPE); - *retval = VARIANT_FALSE; - } - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + if (_writing) + { + //AfxMessageBox("Can't read"); + *retval = VARIANT_FALSE; + return S_OK; + } + + // allow all polygon variations + if (_shpfiletype != SHP_POLYGON && _shpfiletype != SHP_POLYGONM && _shpfiletype != SHP_POLYGONZ) + { + *retval = VARIANT_FALSE; + ErrorMessage(tkUNEXPECTED_SHAPE_TYPE); + return S_OK; + } + + CSingleLock lock(&_readLock, TRUE); + + const int size = _shapeData.size(); + _polySf.resize(size); + + for (int nShape = 0; nShape < size; nShape++) + { + fseek(_shpfile, _shpOffsets[nShape] + sizeof(int) * 2, SEEK_SET); + int shpType; + fread(&shpType, sizeof(int), 1, _shpfile); + if (shpType != SHP_POLYGON && shpType != SHP_POLYGONM && shpType != SHP_POLYGONZ) + { + *retval = VARIANT_FALSE; + ErrorMessage(tkUNEXPECTED_SHAPE_TYPE); + return S_OK; + } + + PolygonShapefile& sf = _polySf[nShape]; + fread(&sf.shpHeader, sizeof(ShapeHeader), 1, _shpfile); + + if (sf.shpHeader.NumPoints > 0 && sf.shpHeader.NumParts > 0) + { + sf.Points.resize(sf.shpHeader.NumPoints); + sf.Parts.resize(sf.shpHeader.NumParts + 1); + fread(&sf.Parts[0], sizeof(int), sf.shpHeader.NumParts, _shpfile); + fread(&sf.Points[0], sizeof(Point2D), sf.shpHeader.NumPoints, _shpfile); + sf.Parts[sf.shpHeader.NumParts] = sf.shpHeader.NumPoints; + *retval = VARIANT_TRUE; + } + else + { + // this isn't a valid shape, we simply won't process it + ErrorMessage(tkINVALID_SHAPE); + *retval = VARIANT_FALSE; + } + } + return S_OK; } // ******************************************************************** // EndPointInShapefile() // ******************************************************************** -STDMETHODIMP CShapefile::EndPointInShapefile(void) +STDMETHODIMP CShapefile::EndPointInShapefile() { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - _polySf.clear(); + _polySf.clear(); - return S_OK; + return S_OK; } #pragma endregion @@ -3113,70 +3151,71 @@ STDMETHODIMP CShapefile::EndPointInShapefile(void) // ******************************************************************** VARIANT_BOOL CShapefile::ExplodeShapesCore(VARIANT_BOOL SelectedOnly, IShapefile* result) { - // ---------------------------------------------- - // Validation - // ---------------------------------------------- - if (!ValidateInput(this, "ExplodeShapes", "this", SelectedOnly)) { - return VARIANT_FALSE; - } - - // ---------------------------------------------- - // Processing - // ---------------------------------------------- - VARIANT_BOOL vb; - long count; - CComVariant var; - std::vector vShapes; - long percent = 0; - - LONG numShapes; - this->get_NumShapes(&numShapes); - - LONG numFields; - this->get_NumFields(&numFields); - - for (long i = 0; i < numShapes; i++) - { - CallbackHelper::Progress(_globalCallback, i, numShapes, "Exploding...", _key, percent); - - if (!ShapeAvailable(i, SelectedOnly)) - continue; - - IShape* shp = NULL; - GetValidatedShape(i, &shp); - if (!shp) continue; - - if (((CShape*)shp)->ExplodeCore(vShapes)) - { - for (long j = 0; j < (long)vShapes.size(); j++) - { - // all the shapes are copies of the initial ones, so no further cloning is needed - result->get_NumShapes(&count); - result->EditInsertShape(vShapes[j], &count, &vb); - - if (vb) - { - // copy attributes - for (int iFld = 0; iFld < numFields; iFld++) - { - this->get_CellValue(iFld, i, &var); - result->EditCellValue(iFld, count, var, &vb); - } - } - vShapes[j]->Release(); // reference was added in EditInsertShape - } - } - - shp->Release(); - } - - // ---------------------------------------------- - // Output validation - // ---------------------------------------------- - CallbackHelper::ProgressCompleted(_globalCallback, _key); - ValidateOutput(result, "ExplodeShapes"); - - return VARIANT_TRUE; + // ---------------------------------------------- + // Validation + // ---------------------------------------------- + if (!ValidateInput(this, "ExplodeShapes", "this", SelectedOnly)) + { + return VARIANT_FALSE; + } + + // ---------------------------------------------- + // Processing + // ---------------------------------------------- + VARIANT_BOOL vb; + long count; + CComVariant var; + std::vector vShapes; + long percent = 0; + + LONG numShapes; + this->get_NumShapes(&numShapes); + + LONG numFields; + this->get_NumFields(&numFields); + + for (long i = 0; i < numShapes; i++) + { + CallbackHelper::Progress(_globalCallback, i, numShapes, "Exploding...", _key, percent); + + if (!ShapeAvailable(i, SelectedOnly)) + continue; + + IShape* shp = nullptr; + GetValidatedShape(i, &shp); + if (!shp) continue; + + if (((CShape*)shp)->ExplodeCore(vShapes)) + { + for (auto& vShape : vShapes) + { + // all the shapes are copies of the initial ones, so no further cloning is needed + result->get_NumShapes(&count); + result->EditInsertShape(vShape, &count, &vb); + + if (vb) + { + // copy attributes + for (int iFld = 0; iFld < numFields; iFld++) + { + this->get_CellValue(iFld, i, &var); + result->EditCellValue(iFld, count, var, &vb); + } + } + vShape->Release(); // reference was added in EditInsertShape + } + } + + shp->Release(); + } + + // ---------------------------------------------- + // Output validation + // ---------------------------------------------- + CallbackHelper::ProgressCompleted(_globalCallback, _key); + ValidateOutput(result, "ExplodeShapes"); + + return VARIANT_TRUE; } // ******************************************************************** @@ -3184,17 +3223,17 @@ VARIANT_BOOL CShapefile::ExplodeShapesCore(VARIANT_BOOL SelectedOnly, IShapefile // ******************************************************************** STDMETHODIMP CShapefile::ExplodeShapes(VARIANT_BOOL SelectedOnly, IShapefile** retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - Clone(retval); + Clone(retval); - if (!ExplodeShapesCore(SelectedOnly, *retval)) - { - (*retval)->Release(); - (*retval) = NULL; - } - - return S_OK; + if (!ExplodeShapesCore(SelectedOnly, *retval)) + { + (*retval)->Release(); + *retval = nullptr; + } + + return S_OK; } // ******************************************************************** @@ -3202,73 +3241,74 @@ STDMETHODIMP CShapefile::ExplodeShapes(VARIANT_BOOL SelectedOnly, IShapefile** r // ******************************************************************** VARIANT_BOOL CShapefile::ExportSelectionCore(IShapefile* result) { - // ---------------------------------------------- - // Validation - // ---------------------------------------------- - if (!ValidateInput(this, "Sort", "this", false)) { - return VARIANT_FALSE; - } - - // ---------------------------------------------- - // Processing - // ---------------------------------------------- - LONG numFields; - this->get_NumFields(&numFields); - - VARIANT_BOOL vbretval; - - long numShapes = _shapeData.size(); - - long count = 0; - CComVariant var; - - long percent = 0; - - for (long i = 0; i < numShapes; i++) - { - CallbackHelper::Progress(_globalCallback, i, numShapes, "Exporting...", _key, percent); - - if (!_shapeData[i]->selected()) - continue; - - IShape* shp = NULL; - this->GetValidatedShape(i, &shp); - if (!shp) continue; - - if (_isEditingShapes) - { - // in the editing mode we share a shape with the parent shapefile - // a copy is needed to avoid conflicts - IShape* shpCopy = NULL; - shp->Clone(&shpCopy); - result->EditInsertShape(shpCopy, &count, &vbretval); - shpCopy->Release(); - } - else - { - result->EditInsertShape(shp, &count, &vbretval); - } - - if (vbretval) - { - // copy attributes - for (int iFld = 0; iFld < numFields; iFld++) - { - this->get_CellValue(iFld, i, &var); - result->EditCellValue(iFld, count, var, &vbretval); - } - count++; - } - shp->Release(); - } - - // ---------------------------------------------- - // Validating output - // ---------------------------------------------- - CallbackHelper::ProgressCompleted(_globalCallback, _key); - ValidateOutput(result, "ExportSelection"); - - return VARIANT_TRUE; + // ---------------------------------------------- + // Validation + // ---------------------------------------------- + if (!ValidateInput(this, "Sort", "this", false)) + { + return VARIANT_FALSE; + } + + // ---------------------------------------------- + // Processing + // ---------------------------------------------- + LONG numFields; + this->get_NumFields(&numFields); + + VARIANT_BOOL vbretval; + + const long numShapes = _shapeData.size(); + + long count = 0; + CComVariant var; + + long percent = 0; + + for (long i = 0; i < numShapes; i++) + { + CallbackHelper::Progress(_globalCallback, i, numShapes, "Exporting...", _key, percent); + + if (!_shapeData[i]->selected()) + continue; + + IShape* shp = nullptr; + this->GetValidatedShape(i, &shp); + if (!shp) continue; + + if (_isEditingShapes) + { + // in the editing mode we share a shape with the parent shapefile + // a copy is needed to avoid conflicts + IShape* shpCopy = nullptr; + shp->Clone(&shpCopy); + result->EditInsertShape(shpCopy, &count, &vbretval); + shpCopy->Release(); + } + else + { + result->EditInsertShape(shp, &count, &vbretval); + } + + if (vbretval) + { + // copy attributes + for (int iFld = 0; iFld < numFields; iFld++) + { + this->get_CellValue(iFld, i, &var); + result->EditCellValue(iFld, count, var, &vbretval); + } + count++; + } + shp->Release(); + } + + // ---------------------------------------------- + // Validating output + // ---------------------------------------------- + CallbackHelper::ProgressCompleted(_globalCallback, _key); + ValidateOutput(result, "ExportSelection"); + + return VARIANT_TRUE; } // ******************************************************************** @@ -3276,17 +3316,17 @@ VARIANT_BOOL CShapefile::ExportSelectionCore(IShapefile* result) // ******************************************************************** STDMETHODIMP CShapefile::ExportSelection(IShapefile** retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - Clone(retval); - - if (!ExportSelectionCore(*retval)) - { - (*retval)->Release(); - (*retval) = NULL; - } - - return S_OK; + Clone(retval); + + if (!ExportSelectionCore(*retval)) + { + (*retval)->Release(); + *retval = nullptr; + } + + return S_OK; } // ******************************************************************** @@ -3294,252 +3334,249 @@ STDMETHODIMP CShapefile::ExportSelection(IShapefile** retval) // ******************************************************************** STDMETHODIMP CShapefile::Sort(LONG FieldIndex, VARIANT_BOOL Ascending, IShapefile** retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - USES_CONVERSION; - - // ---------------------------------------------- - // Validation - // ---------------------------------------------- - if(!ValidateInput(this, "Sort", "this", false)) - return S_OK; - - // ---------------------------------------------- - // Creating output - // ---------------------------------------------- - this->Clone(retval); - - // ---------------------------------------------- - // Processing - // ---------------------------------------------- - LONG numFields; - this->get_NumFields(&numFields); - - long numShapes = _shapeData.size(); - multimap shapeMap; - CComVariant val; - - // processing shapes - long percent = 0; - for (long i = 0; i < numShapes; i++) - { - CallbackHelper::Progress(_globalCallback, i, numShapes, "Sorting...", _key, percent); - - IShape* shp = NULL; - this->GetValidatedShape(i, &shp); - if (shp) - { - // marking the index of shape - CString str; - str.Format("%dl", i); - CComBSTR bstr(str); - shp->put_Key(bstr); - - this->get_CellValue(FieldIndex, i, &val); - pair myPair(val, shp); - shapeMap.insert(myPair); - } - } - - long count = 0; - - // ------------------------------------------- - // writing the results - // ------------------------------------------- - if (Ascending) - { - multimap ::iterator p; - p = shapeMap.begin(); - - while(p != shapeMap.end()) - { - CallbackHelper::Progress(_globalCallback, count, numShapes, "Writing...", _key, percent); - - IShape* shp = p->second; - CopyShape(this, shp, *retval); - ++p; - } - } - else - { - std::multimap ::reverse_iterator p = shapeMap.rbegin(); - while(p != shapeMap.rend()) - { - CallbackHelper::Progress(_globalCallback, count, numShapes, "Writing...", _key, percent); - - IShape* shp = p->second; - CopyShape(this, shp, *retval); - ++p; - } - } - - // ------------------------------------------- - // output validation - // ------------------------------------------- - CallbackHelper::ProgressCompleted(_globalCallback, _key); - ValidateOutput(retval, "Sort"); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + USES_CONVERSION; + + // ---------------------------------------------- + // Validation + // ---------------------------------------------- + if (!ValidateInput(this, "Sort", "this", false)) + return S_OK; + + // ---------------------------------------------- + // Creating output + // ---------------------------------------------- + this->Clone(retval); + + // ---------------------------------------------- + // Processing + // ---------------------------------------------- + LONG numFields; + this->get_NumFields(&numFields); + + const long numShapes = _shapeData.size(); + multimap shapeMap; + CComVariant val; + + // processing shapes + long percent = 0; + for (long i = 0; i < numShapes; i++) + { + CallbackHelper::Progress(_globalCallback, i, numShapes, "Sorting...", _key, percent); + + IShape* shp = nullptr; + this->GetValidatedShape(i, &shp); + if (shp) + { + // marking the index of shape + CString str; + str.Format("%dl", i); + const CComBSTR bstr(str); + shp->put_Key(bstr); + + this->get_CellValue(FieldIndex, i, &val); + pair myPair(val, shp); + shapeMap.insert(myPair); + } + } + + const long count = 0; + + // ------------------------------------------- + // writing the results + // ------------------------------------------- + if (Ascending) + { + auto p = shapeMap.begin(); + + while (p != shapeMap.end()) + { + CallbackHelper::Progress(_globalCallback, count, numShapes, "Writing...", _key, percent); + + IShape* shp = p->second; + CopyShape(this, shp, *retval); + ++p; + } + } + else + { + auto p = shapeMap.rbegin(); + while (p != shapeMap.rend()) + { + CallbackHelper::Progress(_globalCallback, count, numShapes, "Writing...", _key, percent); + + IShape* shp = p->second; + CopyShape(this, shp, *retval); + ++p; + } + } + + // ------------------------------------------- + // output validation + // ------------------------------------------- + CallbackHelper::ProgressCompleted(_globalCallback, _key); + ValidateOutput(retval, "Sort"); + return S_OK; } // ******************************************************************** // Merge() // ******************************************************************** -STDMETHODIMP CShapefile::Merge(VARIANT_BOOL SelectedOnlyThis, IShapefile* sf, VARIANT_BOOL SelectedOnly, IShapefile** retval) +STDMETHODIMP CShapefile::Merge(VARIANT_BOOL SelectedOnlyThis, IShapefile* sf, VARIANT_BOOL SelectedOnly, + IShapefile** retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - USES_CONVERSION; - - if( sf == NULL) - { - ErrorMessage( tkUNEXPECTED_NULL_PARAMETER ); - return S_OK; - } - - // do we have enough shapes, are they selected? - long numShapes1, numShapes2; - numShapes1 = _shapeData.size(); - sf->get_NumShapes(&numShapes2); - - // we can use clipper for polygons only - ShpfileType type1, type2; - type1 = _shpfiletype; - sf->get_ShapefileType(&type2); - if (type1 != type2 ) - { - ErrorMessage(tkINCOMPATIBLE_SHAPE_TYPE); - return S_OK; - } - - if (!ValidateInput(this, "Merge", "this", SelectedOnlyThis)) - return S_OK; - - if (!ValidateInput(sf, "Merge", "sf", SelectedOnly)) - { - return S_OK; - } - - // ----------------------------------------------- - // Creating output - // ----------------------------------------------- - if (!ShapefileHelper::CloneNoFields(this, retval)) - { - // Get errorcode and pass the source: - long errorCode; - (*retval)->get_LastErrorCode(&errorCode); - *retval = NULL; - ErrorMessage(errorCode); - return S_OK; - } - - VARIANT_BOOL vbretval; - - // copying fields from both shapefiles - std::map fieldMap; - GeoProcessing::CopyFields(this, sf, *retval, fieldMap, true); - - long numFields; - this->get_NumFields(&numFields); - - // ----------------------------------------------- - // Processing - // ----------------------------------------------- - long percent = 0; - long count = 0; // index of shape in new shapefile - CComVariant val; - - for (int i = 0; i < numShapes1; i++) - { - CallbackHelper::Progress(_globalCallback, count, numShapes1, "Writing...", _key, percent); - - if (!ShapeAvailable(i, SelectedOnlyThis)) - continue; - - IShape* shp = NULL; - this->GetValidatedShape(i, &shp); - if (shp) - { - vbretval = VARIANT_FALSE; - if (_isEditingShapes) - { - IShape* shpCopy = NULL; - shp->Clone(&shpCopy); - if (shpCopy) - { - (*retval)->EditInsertShape(shpCopy, &count, &vbretval); - shpCopy->Release(); - } - } - else - { - (*retval)->EditInsertShape(shp, &count, &vbretval); - } - - // copying fields - if (vbretval) - { - - for (int iFld = 0; iFld < numFields; iFld++) - { - this->get_CellValue(iFld, i, &val); - (*retval)->EditCellValue(iFld, count, val, &vbretval); - } - count++; - } - shp->Release(); - } - } - - // working with second shapefile - percent = 0; - - for (int i = 0; i < numShapes2; i++) - { - CallbackHelper::Progress(_globalCallback, i, numShapes2, "Writing...", _key, percent); - - if (!((CShapefile*)sf)->ShapeAvailable(i, SelectedOnly)) - continue; - - IShape* shp = NULL; - ((CShapefile*)sf)->GetValidatedShape(i, &shp); - if (shp) - { - vbretval = VARIANT_FALSE; - if (_isEditingShapes) - { - IShape* shpCopy = NULL; - shp->Clone(&shpCopy); - if (shpCopy) - { - (*retval)->EditInsertShape(shpCopy, &count, &vbretval); - shpCopy->Release(); - } - } - else - { - (*retval)->EditInsertShape(shp, &count, &vbretval); - } - - // copying fields - if (vbretval) - { - std::map::iterator p = fieldMap.begin(); - while (p != fieldMap.end()) - { - sf->get_CellValue(p->first, i, &val); - (*retval)->EditCellValue(p->second, count, val, &vbretval); - ++p; - } - count++; - } - shp->Release(); - } - } - - // ------------------------------------------- - // output validation - // ------------------------------------------- - CallbackHelper::ProgressCompleted(_globalCallback, _key); - ValidateOutput(retval, "Merge"); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + USES_CONVERSION; + + if (sf == nullptr) + { + ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); + return S_OK; + } + + const long numShapes1 = _shapeData.size(); + long numShapes2; + sf->get_NumShapes(&numShapes2); + + const ShpfileType type1 = _shpfiletype; + ShpfileType type2; + sf->get_ShapefileType(&type2); + if (type1 != type2) + { + ErrorMessage(tkINCOMPATIBLE_SHAPE_TYPE); + return S_OK; + } + + if (!ValidateInput(this, "Merge", "this", SelectedOnlyThis)) + return S_OK; + + if (!ValidateInput(sf, "Merge", "sf", SelectedOnly)) + { + return S_OK; + } + + // ----------------------------------------------- + // Creating output + // ----------------------------------------------- + if (!ShapefileHelper::CloneNoFields(this, retval)) + { + // Get errorcode and pass the source: + long errorCode; + (*retval)->get_LastErrorCode(&errorCode); + *retval = nullptr; + ErrorMessage(errorCode); + return S_OK; + } + + VARIANT_BOOL vbretval; + + // copying fields from both shapefiles + std::map fieldMap; + GeoProcessing::CopyFields(this, sf, *retval, fieldMap, true); + + long numFields; + this->get_NumFields(&numFields); + + // ----------------------------------------------- + // Processing + // ----------------------------------------------- + long percent = 0; + long count = 0; // index of shape in new shapefile + CComVariant val; + + for (int i = 0; i < numShapes1; i++) + { + CallbackHelper::Progress(_globalCallback, count, numShapes1, "Writing...", _key, percent); + + if (!ShapeAvailable(i, SelectedOnlyThis)) + continue; + + IShape* shp = nullptr; + this->GetValidatedShape(i, &shp); + if (shp) + { + vbretval = VARIANT_FALSE; + if (_isEditingShapes) + { + IShape* shpCopy = nullptr; + shp->Clone(&shpCopy); + if (shpCopy) + { + (*retval)->EditInsertShape(shpCopy, &count, &vbretval); + shpCopy->Release(); + } + } + else + { + (*retval)->EditInsertShape(shp, &count, &vbretval); + } + + // copying fields + if (vbretval) + { + for (int iFld = 0; iFld < numFields; iFld++) + { + this->get_CellValue(iFld, i, &val); + (*retval)->EditCellValue(iFld, count, val, &vbretval); + } + count++; + } + shp->Release(); + } + } + + // working with second shapefile + percent = 0; + + for (int i = 0; i < numShapes2; i++) + { + CallbackHelper::Progress(_globalCallback, i, numShapes2, "Writing...", _key, percent); + + if (!((CShapefile*)sf)->ShapeAvailable(i, SelectedOnly)) + continue; + + IShape* shp = nullptr; + ((CShapefile*)sf)->GetValidatedShape(i, &shp); + if (shp) + { + vbretval = VARIANT_FALSE; + if (_isEditingShapes) + { + IShape* shpCopy = nullptr; + shp->Clone(&shpCopy); + if (shpCopy) + { + (*retval)->EditInsertShape(shpCopy, &count, &vbretval); + shpCopy->Release(); + } + } + else + { + (*retval)->EditInsertShape(shp, &count, &vbretval); + } + + // copying fields + if (vbretval) + { + auto p = fieldMap.begin(); + while (p != fieldMap.end()) + { + sf->get_CellValue(p->first, i, &val); + (*retval)->EditCellValue(p->second, count, val, &vbretval); + ++p; + } + count++; + } + shp->Release(); + } + } + + // ------------------------------------------- + // output validation + // ------------------------------------------- + CallbackHelper::ProgressCompleted(_globalCallback, _key); + ValidateOutput(retval, "Merge"); + return S_OK; } #pragma endregion @@ -3551,100 +3588,104 @@ STDMETHODIMP CShapefile::Merge(VARIANT_BOOL SelectedOnlyThis, IShapefile* sf, VA // ********************************************************************** STDMETHODIMP CShapefile::SimplifyLines(DOUBLE Tolerance, VARIANT_BOOL SelectedOnly, IShapefile** retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - USES_CONVERSION; - - // ---------------------------------------------- - // Validation - // ---------------------------------------------- - ShpfileType shpType = ShapeUtility::Convert2D(_shpfiletype); - if (shpType != SHP_POLYLINE && shpType != SHP_POLYGON) - { - ErrorMessage(tkINCOMPATIBLE_SHAPEFILE_TYPE); - return S_OK; - } - - if (!ValidateInput(this, "SimplifyLines", "this", SelectedOnly)) - return S_OK; - - // ---------------------------------------------- - // Creating output - // ---------------------------------------------- - IShapefile* sfNew = NULL; - this->Clone(&sfNew); - - LONG numFields; - this->get_NumFields(&numFields); - - // ---------------------------------------------- - // Processing - // ---------------------------------------------- - ReadGeosGeometries(SelectedOnly); - long index = 0, percent = 0; - - int numShapes = (int )_shapeData.size(); - for (int i = 0; i < numShapes; i++) - { - CallbackHelper::Progress(_globalCallback, i, numShapes, "Calculating...", _key, percent); - - if (!ShapeAvailable(i, SelectedOnly)) - continue; - - GEOSGeometry* gsGeom = GetGeosGeometry(i); - if (gsGeom == NULL) continue; - - int numGeom = GeosHelper::GetNumGeometries(gsGeom); - - if (shpType == SHP_POLYLINE) - { - GEOSGeom gsNew = GeosHelper::Simplify(gsGeom, Tolerance); - if (gsNew) - { - InsertGeosGeometry(sfNew, gsNew, this, i); - GeosHelper::DestroyGeometry(gsNew); - } - } - else - { - char* val = GeosHelper::GetGeometryType(gsGeom); - CString type = val; - GeosHelper::Free(val); - - if (type != "MultiPolygon") - { - GEOSGeom gsNew = GeosConverter::SimplifyPolygon(gsGeom, Tolerance); - if (gsNew) - { - InsertGeosGeometry(sfNew, gsNew, this, i); - GeosHelper::DestroyGeometry(gsNew); - } - } - else - { - for (int n = 0; n < GeosHelper::GetNumGeometries(gsGeom); n++) - { - const GEOSGeometry* gsPart = GeosHelper::GetGeometryN(gsGeom, n); - GEOSGeom gsNew = GeosConverter::SimplifyPolygon(gsPart, Tolerance); - if (gsPart) - { - InsertGeosGeometry(sfNew, gsNew, this, i); - GeosHelper::DestroyGeometry(gsNew); - } - } - } - } - //GeosHelper::DestroyGeometry(gsGeom); - } - - this->ClearCachedGeometries(); - *retVal = sfNew; - - // ------------------------------------------- - // output validation - // ------------------------------------------- - CallbackHelper::ProgressCompleted(_globalCallback, _key); - ValidateOutput(retVal, "SimplifyLines"); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + USES_CONVERSION; + + // ---------------------------------------------- + // Validation + // ---------------------------------------------- + const ShpfileType shpType = ShapeUtility::Convert2D(_shpfiletype); + if (shpType != SHP_POLYLINE && shpType != SHP_POLYGON) + { + ErrorMessage(tkINCOMPATIBLE_SHAPEFILE_TYPE); + return S_OK; + } + + if (!ValidateInput(this, "SimplifyLines", "this", SelectedOnly)) + return S_OK; + + // ---------------------------------------------- + // Creating output + // ---------------------------------------------- + IShapefile* sfNew = nullptr; + this->Clone(&sfNew); + + LONG numFields; + this->get_NumFields(&numFields); + + // ---------------------------------------------- + // Processing + // ---------------------------------------------- + ReadGeosGeometries(SelectedOnly); + // long index = 0; + long percent = 0; + + const auto numShapes = (int)_shapeData.size(); + for (int i = 0; i < numShapes; i++) + { + CallbackHelper::Progress(_globalCallback, i, numShapes, "Calculating...", _key, percent); + + if (!ShapeAvailable(i, SelectedOnly)) + continue; + + GEOSGeometry* gsGeom = GetGeosGeometry(i); + if (gsGeom == nullptr) continue; + + // int numGeom = GeosHelper::GetNumGeometries(gsGeom); + + if (shpType == SHP_POLYLINE) + { + // ReSharper disable once CppLocalVariableMayBeConst + GEOSGeom gsNew = GeosHelper::Simplify(gsGeom, Tolerance); + if (gsNew) + { + InsertGeosGeometry(sfNew, gsNew, this, i); + GeosHelper::DestroyGeometry(gsNew); + } + } + else + { + char* val = GeosHelper::GetGeometryType(gsGeom); + const CString type = val; + GeosHelper::Free(val); + + if (type != "MultiPolygon") + { + // ReSharper disable once CppLocalVariableMayBeConst + GEOSGeom gsNew = GeosConverter::SimplifyPolygon(gsGeom, Tolerance); + if (gsNew) + { + InsertGeosGeometry(sfNew, gsNew, this, i); + GeosHelper::DestroyGeometry(gsNew); + } + } + else + { + for (int n = 0; n < GeosHelper::GetNumGeometries(gsGeom); n++) + { + const GEOSGeometry* gsPart = GeosHelper::GetGeometryN(gsGeom, n); + // ReSharper disable once CppLocalVariableMayBeConst + GEOSGeom gsNew = GeosConverter::SimplifyPolygon(gsPart, Tolerance); + if (gsPart) + { + InsertGeosGeometry(sfNew, gsNew, this, i); + GeosHelper::DestroyGeometry(gsNew); + } + } + } + } + //GeosHelper::DestroyGeometry(gsGeom); + } + + this->ClearCachedGeometries(); + *retVal = sfNew; + + // ------------------------------------------- + // output validation + // ------------------------------------------- + CallbackHelper::ProgressCompleted(_globalCallback, _key); + ValidateOutput(retVal, "SimplifyLines"); + return S_OK; } // ********************************************************************** @@ -3652,103 +3693,107 @@ STDMETHODIMP CShapefile::SimplifyLines(DOUBLE Tolerance, VARIANT_BOOL SelectedOn // ********************************************************************** STDMETHODIMP CShapefile::Segmentize(IShapefile** retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - - // ---------------------------------------------- - // Validating - // ---------------------------------------------- - ShpfileType shpType = ShapeUtility::Convert2D(this->_shpfiletype); - if (shpType != SHP_POLYLINE) - { - this->ErrorMessage(tkINCOMPATIBLE_SHAPEFILE_TYPE); - return S_OK; - } - - if (!ValidateInput(this, "Segmentize", "this", VARIANT_FALSE)) - return S_OK; - - // ----------------------------------------------- - // Creating output - // ----------------------------------------------- - IShapefile* sfOut = NULL; - this->Clone(&sfOut); - - // ----------------------------------------------- - // Processing - // ----------------------------------------------- - // caching geos geometries - this->ReadGeosGeometries(VARIANT_FALSE); - - // turns on the quad tree - this->GenerateTempQTree(false); - - long shapeCount = _shapeData.size(); - long percent = 0; - - for (long i = 0; i < shapeCount; i++) - { - CallbackHelper::Progress(_globalCallback, i, shapeCount, "Segmentizing...", _key, percent); - - GEOSGeometry *geom1 = GetGeosGeometry(i); - - double xMin, xMax, yMin, yMax; - if(this->QuickExtentsCore(i, &xMin, &yMin, &xMax, &yMax)) - { - QTreeExtent query(xMin, xMax, yMax, yMin); - std::vector shapes = this->_qtree->GetNodes(query); - - // calculation union of all geometries - if (shapes.size() > 0) - { - GEOSGeometry *gsUnion = NULL; - for (size_t j = 0; j < shapes.size(); j++) - { - if (i != shapes[j]) - { - GEOSGeometry *gs = _shapeData[shapes[j]]->geosGeom; - if (!gsUnion) - { - gsUnion = GeosHelper::CloneGeometry(gs); - } - else - { - GEOSGeometry *gsNew = GeosHelper::Union(gsUnion, gs); - if (gsNew) - { - GeosHelper::DestroyGeometry(gsUnion); - gsUnion = gsNew; - } - } - } - } - - GEOSGeometry* gsOut = GeosHelper::Difference(geom1, gsUnion); - GeosHelper::DestroyGeometry(gsUnion); - - if (gsOut) - { - bool res = InsertGeosGeometry(sfOut, gsOut, this, i); - GeosHelper::DestroyGeometry(gsOut); - if (res) - continue; - } - } - } - InsertGeosGeometry(sfOut, geom1, this, i); - } - - *retVal = sfOut; - - // ------------------------------------------- - // output validation - // ------------------------------------------- - CallbackHelper::ProgressCompleted(_globalCallback, _key); - ClearTempQTree(); - ClearCachedGeometries(); - ValidateOutput(retVal, "Segmentize"); - return S_OK; - - GeneratePolygonColors(); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + // ---------------------------------------------- + // Validating + // ---------------------------------------------- + const ShpfileType shpType = ShapeUtility::Convert2D(this->_shpfiletype); + if (shpType != SHP_POLYLINE) + { + this->ErrorMessage(tkINCOMPATIBLE_SHAPEFILE_TYPE); + return S_OK; + } + + if (!ValidateInput(this, "Segmentize", "this", VARIANT_FALSE)) + return S_OK; + + // ----------------------------------------------- + // Creating output + // ----------------------------------------------- + IShapefile* sfOut = nullptr; + this->Clone(&sfOut); + + // ----------------------------------------------- + // Processing + // ----------------------------------------------- + // caching geos geometries + this->ReadGeosGeometries(VARIANT_FALSE); + + // turns on the quad tree + this->GenerateTempQTree(false); + + const long shapeCount = _shapeData.size(); + long percent = 0; + + for (long i = 0; i < shapeCount; i++) + { + CallbackHelper::Progress(_globalCallback, i, shapeCount, "Segmentizing...", _key, percent); + + GEOSGeometry* geom1 = GetGeosGeometry(i); + + double xMin, xMax, yMin, yMax; + if (this->QuickExtentsCore(i, &xMin, &yMin, &xMax, &yMax)) + { + const QTreeExtent query(xMin, xMax, yMax, yMin); + std::vector shapes = this->_tempTree->GetNodes(query); + + // calculation union of all geometries + if (!shapes.empty()) + { + GEOSGeometry* gsUnion = nullptr; + for (int shape : shapes) + { + if (i != shape) + { + GEOSGeometry* gs = _shapeData[shape]->geosGeom; + if (!gsUnion) + { + gsUnion = GeosHelper::CloneGeometry(gs); + } + else + { + GEOSGeometry* gsNew = GeosHelper::Union(gsUnion, gs); + if (gsNew) + { + GeosHelper::DestroyGeometry(gsUnion); + gsUnion = gsNew; + } + } + } + } + + // may have had no shapes + if (gsUnion) + { + GEOSGeometry* gsOut = GeosHelper::Difference(geom1, gsUnion); + GeosHelper::DestroyGeometry(gsUnion); + if (gsOut) + { + const bool res = InsertGeosGeometry(sfOut, gsOut, this, i); + GeosHelper::DestroyGeometry(gsOut); + if (res) + continue; + } + } + + } + } + InsertGeosGeometry(sfOut, geom1, this, i); + } + + *retVal = sfOut; + + // ------------------------------------------- + // output validation + // ------------------------------------------- + CallbackHelper::ProgressCompleted(_globalCallback, _key); + ClearTempQTree(); + ClearCachedGeometries(); + ValidateOutput(retVal, "Segmentize"); + return S_OK; + + // GeneratePolygonColors(); } #pragma endregion @@ -3756,25 +3801,25 @@ STDMETHODIMP CShapefile::Segmentize(IShapefile** retVal) double CalcMinAngle(GEOSGeometry* geom, double& xCent, double& yCent) { - double minAngle = DBL_MAX; - GEOSCoordSequence* cs = const_cast(GeosHelper::GetCoordinatesSeq(geom)); - if(cs) - { - unsigned int size = GeosHelper::CoordinateSequenceSize(cs); - double x, y; - for(unsigned int i = 0; i < size; i++) - { - if (GeosHelper::CoordinateSequenceGetXY(cs, i, x, y)) - { - x -= xCent; - y -= yCent; - double angle = GeometryHelper::GetPointAngle(x, y); - if (angle < minAngle) - minAngle = angle; - } - } - } - return minAngle; + double minAngle = DBL_MAX; + auto* cs = const_cast(GeosHelper::GetCoordinatesSeq(geom)); + if (cs) + { + const unsigned int size = GeosHelper::CoordinateSequenceSize(cs); + double x, y; + for (unsigned int i = 0; i < size; i++) + { + if (GeosHelper::CoordinateSequenceGetXY(cs, i, x, y)) + { + x -= xCent; + y -= yCent; + const double angle = GeometryHelper::GetPointAngle(x, y); + if (angle < minAngle) + minAngle = angle; + } + } + } + return minAngle; } // ********************************************************************** @@ -3782,85 +3827,85 @@ double CalcMinAngle(GEOSGeometry* geom, double& xCent, double& yCent) // ********************************************************************** Coloring::ColorGraph* CShapefile::GeneratePolygonColors() { - GenerateTempQTree(false); - QTree* tree = GetTempQTree(); - ReadGeosGeometries(VARIANT_FALSE); - - long numShapes = _shapeData.size(); - long percent = 0; - - Coloring::ColorGraph* graph = new Coloring::ColorGraph(); - - // --------------------------------------- - // spatial relations - // --------------------------------------- - for(size_t i = 0; i < _shapeData.size(); i++) - { - CallbackHelper::Progress(_globalCallback, i, numShapes, "Calculating spatial relations...", _key, percent); - - vector shapeIds; - double xMin, xMax, yMin, yMax; - this->QuickExtentsCore(i, &xMin, &yMin, &xMax, &yMax); - shapeIds = tree->GetNodes(QTreeExtent(xMin,xMax,yMax,yMin)); - - graph->InsertNode(i); - - if (shapeIds.size() > 0) - { - GEOSGeometry* geom = GetGeosGeometry(i); - if (geom) - { - for (size_t j = 0; j < shapeIds.size(); j++) - { - if (shapeIds[j] <= (int)i) // it's a single node, not a pair; or the pair was already analyzed in reverse order - continue; - - GEOSGeometry* geom2 = GetGeosGeometry(shapeIds[j]); - if (GeosHelper::Touches(geom, geom2)) - { - double angle = DBL_MAX; - bool commonEdge = false; - GEOSGeometry* g = GeosHelper::Intersection(geom, geom2); - if (g) - { - // ------------------------------------------------ - // Let's calculate clockwise direction for shape - // ------------------------------------------------ - int geomType = GeosHelper::GetGeometryTypeId(g); - switch(geomType) - { - case GEOS_POINT: - break; // we need at least common edge - case GEOS_LINESTRING: - case GEOS_LINEARRING: - case GEOS_MULTILINESTRING: - commonEdge = true; - break; - default: - CallbackHelper::AssertionFailed(Debug::Format("Unexpected geometry type: %d.", geomType)); - break; - } - GeosHelper::DestroyGeometry(g); - } - if (commonEdge) - { - graph->InsertEdge(i, shapeIds[j], angle); - - } - } - } - } - } - } - - // --------------------------------------- - // actual coloring - // --------------------------------------- - graph->DoColoring(); - - ClearCachedGeometries(); - ClearTempQTree(); - - return graph; + GenerateTempQTree(false); + QTree* tree = GetTempQTree(); + ReadGeosGeometries(VARIANT_FALSE); + + const long numShapes = _shapeData.size(); + long percent = 0; + + auto* graph = new Coloring::ColorGraph(); + + // --------------------------------------- + // spatial relations + // --------------------------------------- + for (size_t i = 0; i < _shapeData.size(); i++) + { + CallbackHelper::Progress(_globalCallback, i, numShapes, "Calculating spatial relations...", _key, percent); + + double xMin, xMax, yMin, yMax; + this->QuickExtentsCore(i, &xMin, &yMin, &xMax, &yMax); + vector shapeIds = tree->GetNodes(QTreeExtent(xMin, xMax, yMax, yMin)); + + graph->InsertNode(i); + + if (!shapeIds.empty()) + { + GEOSGeometry* geom = GetGeosGeometry(i); + if (geom) + { + for (int shapeId : shapeIds) + { + if (shapeId <= (int)i) + // it's a single node, not a pair; or the pair was already analyzed in reverse order + continue; + + GEOSGeometry* geom2 = GetGeosGeometry(shapeId); + if (GeosHelper::Touches(geom, geom2)) + { + const double angle = DBL_MAX; + bool commonEdge = false; + GEOSGeometry* g = GeosHelper::Intersection(geom, geom2); + if (g) + { + // ------------------------------------------------ + // Let's calculate clockwise direction for shape + // ------------------------------------------------ + const int geomType = GeosHelper::GetGeometryTypeId(g); + switch (geomType) + { + case GEOS_POINT: + break; // we need at least common edge + case GEOS_LINESTRING: + case GEOS_LINEARRING: + case GEOS_MULTILINESTRING: + commonEdge = true; + break; + default: + CallbackHelper::AssertionFailed( + Debug::Format("Unexpected geometry type: %d.", geomType)); + break; + } + GeosHelper::DestroyGeometry(g); + } + if (commonEdge) + { + graph->InsertEdge(i, shapeId, angle); + } + } + } + } + } + } + + // --------------------------------------- + // actual coloring + // --------------------------------------- + graph->DoColoring(); + + ClearCachedGeometries(); + ClearTempQTree(); + + return graph; } #pragma endregion diff --git a/src/COM classes/Shapefile_Selection.cpp b/src/COM classes/Shapefile_Selection.cpp index 3d0e2d36..7fa347b3 100644 --- a/src/COM classes/Shapefile_Selection.cpp +++ b/src/COM classes/Shapefile_Selection.cpp @@ -27,6 +27,8 @@ #include "SelectionHelper.h" #include "ExtentsHelper.h" #include "ShapefileHelper.h" +#include "GeosConverter.h" +#include "GeosHelper.h" #pragma region SelectShapes // ****************************************************************** @@ -79,6 +81,10 @@ bool CShapefile::SelectShapesCore(Extent& extents, double Tolerance, SelectMode b_maxY += halfTolerance; } + // build GEOSGeom for comparison + IShape* shpExt = NULL; + ComHelper::CreateShape(&shpExt); + bool bPtSelection = b_minX == b_maxX && b_minY == b_maxY; int local_numShapes = _shapeData.size(); @@ -145,7 +151,14 @@ bool CShapefile::SelectShapesCore(Extent& extents, double Tolerance, SelectMode // -------------------------------------------------- if( b_minX == b_maxX && b_minY == b_maxY ) { - if( shpType2D == SHP_POLYGON ) + VARIANT_BOOL ret; + shpExt->Create(ShpfileType::SHP_POINT, &ret); + long idx; + shpExt->AddPoint(b_minX, b_minY, &idx); + // convert input point to GEOS + GEOSGeom geosPoint = GeosConverter::ShapeToGeom(shpExt); + + if( shpType2D == SHP_POLYGON ) { for(i=0;ishape; + // convert querying shape to GEOS + GEOSGeom geosShape = GeosConverter::ShapeToGeom(shape); + // check for containment + if (GeosHelper::Contains(geosShape, geosPoint)) + { + selectResult.push_back(shapeVal); + continue; + } + } } } // Rectangle selection else { - for(i=0;iCreate(ShpfileType::SHP_POLYGON, &ret); + long idx; + shpExt->AddPoint(b_minX, b_minY, &idx); + shpExt->AddPoint(b_minX, b_maxY, &idx); + shpExt->AddPoint(b_maxX, b_maxY, &idx); + shpExt->AddPoint(b_maxX, b_minY, &idx); + shpExt->AddPoint(b_minX, b_minY, &idx); + // convert extent to GEOS + GEOSGeom geosExtent = GeosConverter::ShapeToGeom(shpExt); + + for(i=0;iwasRendered()) // continue; + // NOTE: may be resolved now as a result of MWGIS-137, but should still evaluate // *********************************************************************************** - + // bounds if (this->QuickExtentsCore(shapeVal, &s_minX, &s_minY, &s_maxX, &s_maxY)) { @@ -224,24 +255,32 @@ bool CShapefile::SelectShapesCore(Extent& extents, double Tolerance, SelectMode if( shpType2D == SHP_POLYLINE && SelectMode == INTERSECTION) { - if( DefineShapePoints( shapeVal, ShapeType, parts, xPts, yPts ) != FALSE ) - { - if (SelectionHelper::PolylineIntersection(xPts, yPts, parts, b_minX, b_maxX, b_minY, b_maxY, Tolerance)) - selectResult.push_back( shapeVal ); - } + // get current shape + IShape* shape = _shapeData[shapeVal]->shape; + // convert shape to GEOS + GEOSGeom geos = GeosConverter::ShapeToGeom(shape); + // see if shape intersects polygon extent + if (GeosHelper::Intersects(geosExtent, geos)) + { + selectResult.push_back(shapeVal); + } + // if( DefineShapePoints( shapeVal, ShapeType, parts, xPts, yPts ) != FALSE ) + //{ + // if (SelectionHelper::PolylineIntersection(xPts, yPts, parts, b_minX, b_maxX, b_minY, b_maxY, Tolerance)) + // selectResult.push_back( shapeVal ); + //} } else if( shpType2D == SHP_POLYGON && SelectMode == INTERSECTION) { - - if( DefineShapePoints( shapeVal, ShapeType, parts, xPts, yPts) ) - { - if (SelectionHelper::PolygonIntersection(xPts, yPts, parts, b_minX, b_maxX, b_minY, b_maxY, Tolerance)) { - selectResult.push_back( shapeVal ); - } - else if (ShapefileHelper::BoundsWithinPolygon(this, shapeVal, b_minX, b_maxX, b_minY, b_maxY)) { - selectResult.push_back(shapeVal); - } - } + // get current shape + IShape* shape = _shapeData[shapeVal]->shape; + // convert shape to GEOS + GEOSGeom geos = GeosConverter::ShapeToGeom(shape); + // see if shape intersects polygon extent + if (GeosHelper::Intersects(geosExtent, geos)) + { + selectResult.push_back(shapeVal); + } } else if( shpType2D == SHP_MULTIPOINT && SelectMode == INTERSECTION) { diff --git a/src/COM classes/Shapefile_Validation.cpp b/src/COM classes/Shapefile_Validation.cpp index d76b5231..33c488f0 100644 --- a/src/COM classes/Shapefile_Validation.cpp +++ b/src/COM classes/Shapefile_Validation.cpp @@ -1,4 +1,26 @@ -#include "stdafx.h" +//******************************************************************************************************** +//File name: Shapefile_Validation.cpp +//Description: Implementation of the CShapefile (see other cpp files as well) +//******************************************************************************************************** +//The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); +//you may not use this file except in compliance with the License. You may obtain a copy of the License at +//http://www.mozilla.org/MPL/ +//Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF +//ANY KIND, either express or implied. See the License for the specific language governing rights and +//limitations under the License. +// +//The Original Code is MapWindow Open Source. +// +//The Initial Developer of this version of the Original Code is Daniel P. Ames using portions created by +//Utah State University and the Idaho National Engineering and Environmental Lab that were released as +//public domain in March 2004. +// +//Contributor(s): (Open source contributors should list themselves and their modifications here). +// ------------------------------------------------------------------------------------------------------- +// lsu 3-02-2011: split the initial Shapefile.cpp file to make entities of the reasonable size +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper + +#include "StdAfx.h" #include "Shapefile.h" #include "ShapeValidator.h" #include "OgrConverter.h" @@ -10,133 +32,152 @@ // ***************************************************************** // Validate() // ***************************************************************** -STDMETHODIMP CShapefile::Validate(tkShapeValidationMode validationMode, VARIANT_BOOL selectedOnly, IShapeValidationInfo** results) +STDMETHODIMP CShapefile::Validate(tkShapeValidationMode validationMode, VARIANT_BOOL selectedOnly, + IShapeValidationInfo** results) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *results = NULL; - - if (validationMode == NoValidation) - return S_OK; - - VARIANT_BOOL editing; - get_EditingShapes(&editing); - if (!editing) - { - ErrorMessage(tkNO_FIXING_IN_DISK_MODE); - *results = ValidateInputCore(this, "Validate", "this", selectedOnly, validationMode, "Shapefile", true); - } - else - { - *results = ValidateInputCore(this, "Validate", "this", selectedOnly, validationMode); - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *results = nullptr; + + if (validationMode == NoValidation) + return S_OK; + + VARIANT_BOOL editing; + get_EditingShapes(&editing); + if (!editing) + { + ErrorMessage(tkNO_FIXING_IN_DISK_MODE); + *results = ValidateInputCore(this, "Validate", "this", selectedOnly, validationMode, "Shapefile", true); + } + else + { + *results = ValidateInputCore(this, "Validate", "this", selectedOnly, validationMode); + } + return S_OK; } // ******************************************************************** // ValidateInput() // ******************************************************************** -bool CShapefile::ValidateInput(IShapefile* isf, CString methodName, - CString parameterName, VARIANT_BOOL selectedOnly, CString className /*= "Shapefile"*/) +bool CShapefile::ValidateInput(IShapefile* isf, CString methodName, + CString parameterName, VARIANT_BOOL selectedOnly, CString className /*= "Shapefile"*/) { - if (m_globalSettings.inputValidation != AbortOnErrors) { - // this the only mode where shall be doing it before hand - // in all other cases validation & fixing will be done in get_ValidateShape call directly - return true; - } - - IShapeValidationInfo* info = ValidateInputCore(isf, methodName, parameterName, selectedOnly, m_globalSettings.inputValidation, className); - bool result = info != NULL; - if (info) info->Release(); - return result; + // MWGIS-132; this code, suggested by CLang for MWGIS-104 on 24 Aug 2018, + // is being removed because it prevents any in-memory Shapefile from passing + // validation (since in-memory shapefiles have no FILE * (_shpfile == NULL). + // This also affected all OGR layers, and was observed when attempting to + // load an OGR layer that required reprojection on-the-fly. + //// Always check if the shapefile is open: + //if (_shpfile == nullptr) + //{ + // ErrorMessage(tkFILE_NOT_OPEN); + // return false; + //} + + if (m_globalSettings.inputValidation != AbortOnErrors) + { + // this the only mode where shall be doing it before hand + // in all other cases validation & fixing will be done in get_ValidateShape call directly + return true; + } + + auto info = ValidateInputCore(isf, methodName, parameterName, selectedOnly, m_globalSettings.inputValidation, + className); + const auto result = info != nullptr; + if (info) info->Release(); + return result; } // ******************************************************************** // ValidateInputCore() // ******************************************************************** -IShapeValidationInfo* CShapefile::ValidateInputCore(IShapefile* isf, CString methodName, - CString parameterName, VARIANT_BOOL selectedOnly, tkShapeValidationMode validationMode, CString className, bool reportOnly) +IShapeValidationInfo* CShapefile::ValidateInputCore(IShapefile* isf, CString methodName, + CString parameterName, VARIANT_BOOL selectedOnly, + tkShapeValidationMode validationMode, CString className, + bool reportOnly) { - tkShapefileSourceType sourceType; - if (isf->get_SourceType(&sourceType)) - { - ErrorMessage(tkSHAPEFILE_UNINITIALIZED); - return NULL; - } - - long numShapes; - isf->get_NumShapes(&numShapes); - if (numShapes == 0) - { - ErrorMessage(tkSHAPEFILE_IS_EMPTY); - return NULL; - } - - long numSelected; - isf->get_NumSelected(&numSelected); - if (selectedOnly && numSelected == 0) - { - ErrorMessage(tkSELECTION_EMPTY); - return NULL; - } - - IShapeValidationInfo* iinfo = ShapeValidator::Validate(isf, validationMode, svtInput, - className, methodName, parameterName, _globalCallback, _key, selectedOnly ? true: false, reportOnly); - - CShapefile* sf = (CShapefile*)isf; - sf->SetValidationInfo(iinfo, svtInput); - - CShapeValidationInfo* info = (CShapeValidationInfo*)iinfo; - if (info->validationStatus == OperationAborted) - { - ErrorMessage(tkABORTED_ON_INPUT_VALIDATION); - return NULL; - } - return info; + tkShapefileSourceType sourceType; + if (isf->get_SourceType(&sourceType)) + { + ErrorMessage(tkSHAPEFILE_UNINITIALIZED); + return nullptr; + } + + long numShapes; + isf->get_NumShapes(&numShapes); + if (numShapes == 0) + { + ErrorMessage(tkSHAPEFILE_IS_EMPTY); + return nullptr; + } + + long numSelected; + isf->get_NumSelected(&numSelected); + if (selectedOnly && numSelected == 0) + { + ErrorMessage(tkSELECTION_EMPTY); + return nullptr; + } + + IShapeValidationInfo* iinfo = ShapeValidator::Validate(isf, validationMode, svtInput, + className, methodName, parameterName, _globalCallback, _key, + selectedOnly != 0, reportOnly); + + auto* sf = (CShapefile*)isf; + sf->SetValidationInfo(iinfo, svtInput); + + auto* info = (CShapeValidationInfo*)iinfo; + if (info->validationStatus == OperationAborted) + { + ErrorMessage(tkABORTED_ON_INPUT_VALIDATION); + return nullptr; + } + return info; } // ******************************************************************** // ValidateOutput() // ******************************************************************** -IShapeValidationInfo* CShapefile::ValidateOutput(IShapefile** isf, CString methodName, CString className, bool abortIfEmpty) +IShapeValidationInfo* CShapefile::ValidateOutput(IShapefile** isf, CString methodName, CString className, + bool abortIfEmpty) { - if (!(*isf)) return NULL; - - long numShapes; - (*isf)->get_NumShapes(&numShapes); - if (numShapes == 0 && abortIfEmpty) - { - ErrorMessage(tkRESULTINGSHPFILE_EMPTY); - -clear_result: - // TODO: actually I don't see much sense in it; GlobalCallback, if there is any, must have been passed - // to output shapefile as well; so in case there was an error it was already reported - long errorCode = 0; - (*isf)->get_LastErrorCode(&errorCode); - if (errorCode != 0) - ErrorMessage(errorCode); - - VARIANT_BOOL vb; - (*isf)->Close(&vb); - (*isf)->Release(); - (*isf) = NULL; - return NULL; - } - else - { - IShapeValidationInfo* iinfo = ShapeValidator::Validate(*isf, m_globalSettings.outputValidation, svtOutput, className, methodName, "", _globalCallback, _key, false); - - CShapefile* sf = (CShapefile*)this; // writing validation into this shapefile - sf->SetValidationInfo(iinfo, svtOutput); - iinfo->Release(); - - CShapeValidationInfo* info = (CShapeValidationInfo*)iinfo; - - if (info->validationStatus == tkShapeValidationStatus::OperationAborted) { - goto clear_result; - } - - return info; - } + if (!*isf) return nullptr; + + long numShapes; + (*isf)->get_NumShapes(&numShapes); + if (numShapes == 0 && abortIfEmpty) + { + ErrorMessage(tkRESULTINGSHPFILE_EMPTY); + + clear_result: + // TODO: actually I don't see much sense in it; GlobalCallback, if there is any, must have been passed + // to output shapefile as well; so in case there was an error it was already reported + long errorCode = 0; + (*isf)->get_LastErrorCode(&errorCode); + if (errorCode != 0) + ErrorMessage(errorCode); + + VARIANT_BOOL vb; + (*isf)->Close(&vb); + (*isf)->Release(); + *isf = nullptr; + return nullptr; + } + + IShapeValidationInfo* iinfo = ShapeValidator::Validate(*isf, m_globalSettings.outputValidation, svtOutput, + className, methodName, "", _globalCallback, _key, false); + + auto* sf = (CShapefile*)this; // writing validation into this shapefile + sf->SetValidationInfo(iinfo, svtOutput); + iinfo->Release(); + + auto* info = (CShapeValidationInfo*)iinfo; + + if (info->validationStatus == OperationAborted) + { + goto clear_result; + } + + return info; } // ************************************************************** @@ -144,11 +185,13 @@ IShapeValidationInfo* CShapefile::ValidateOutput(IShapefile** isf, CString metho // ************************************************************** bool CShapefile::ValidateOutput(IShapefile* sf, CString methodName, CString className, bool abortIfEmpty) { - if (!_isEditingShapes) { - return true; - } + if (!_isEditingShapes) + { + return true; + } - return ValidateOutput(&sf, methodName, className, abortIfEmpty) != NULL; // validationInfo instance is referenced by shapefile + return ValidateOutput(&sf, methodName, className, abortIfEmpty) != nullptr; + // validationInfo instance is referenced by shapefile } // ********************************************************* @@ -156,55 +199,60 @@ bool CShapefile::ValidateOutput(IShapefile* sf, CString methodName, CString clas // ********************************************************* HRESULT CShapefile::GetValidatedShape(int shapeIndex, IShape** retVal) { - IShape* shp = NULL; - get_Shape(shapeIndex, &shp); - - if (!shp) { - return S_OK; - } - - switch (m_globalSettings.inputValidation) - { - case NoValidation: - case AbortOnErrors: - // for abort on errors validation must be run before the execution of the method, - // the fact that we are processing further means that it has been passed - *retVal = shp; - return S_OK; - case TryFixProceedOnFailure: - case TryFixSkipOnFailure: - VARIANT_BOOL vb; - (*retVal)->get_IsValid(&vb); - - if (vb) { - // everything is good - *retVal = shp; - return S_OK; - } - - IShape* shpNew = NULL; - (*retVal)->FixUp(&shpNew); - - if (shpNew) { - // fixed, no problems - shp->Release(); - *retVal = shpNew; - return S_OK; - } - - - if (m_globalSettings.inputValidation == TryFixProceedOnFailure) { - // can't be fixed? we don't care :) - *retVal = shp; - } - else { - // TryFixSkipOnFailure - // well, we should cope somehow without it :( - *retVal = NULL; - } - } - - return S_OK; + IShape* shp = nullptr; + get_Shape(shapeIndex, &shp); + + if (!shp) + { + return S_OK; + } + + switch (m_globalSettings.inputValidation) + { + case NoValidation: + case AbortOnErrors: + // for abort on errors validation must be run before the execution of the method, + // the fact that we are processing further means that it has been passed + *retVal = shp; + return S_OK; + case TryFixProceedOnFailure: + case TryFixSkipOnFailure: + VARIANT_BOOL vb; + (*retVal)->get_IsValid(&vb); + + if (vb) + { + // everything is good + *retVal = shp; + return S_OK; + } + + IShape* shpNew = nullptr; + (*retVal)->FixUp(&shpNew); + + if (shpNew) + { + // fixed, no problems + shp->Release(); + *retVal = shpNew; + return S_OK; + } + + + if (m_globalSettings.inputValidation == TryFixProceedOnFailure) + { + // can't be fixed? we don't care :) + *retVal = shp; + } + else + { + // TryFixSkipOnFailure + // well, we should cope somehow without it :( + *retVal = nullptr; + } + } + + return S_OK; } // ********************************************************* @@ -212,18 +260,20 @@ HRESULT CShapefile::GetValidatedShape(int shapeIndex, IShape** retVal) // ********************************************************* bool CShapefile::ShapeAvailable(int shapeIndex, VARIANT_BOOL selectedOnly) { - if (shapeIndex < 0 || shapeIndex >= (int)_shapeData.size()) { - return false; - } + if (shapeIndex < 0 || shapeIndex >= (int)_shapeData.size()) + { + return false; + } - if (!_shapeData[shapeIndex]->selected() && selectedOnly) { - return false; - } + if (!_shapeData[shapeIndex]->selected() && selectedOnly) + { + return false; + } - return true; + return true; } -#pragma endregion +#pragma endregion #pragma region Caching GEOS geometries @@ -232,7 +282,7 @@ bool CShapefile::ShapeAvailable(int shapeIndex, VARIANT_BOOL selectedOnly) // ********************************************************* GEOSGeometry* CShapefile::GetGeosGeometry(int shapeIndex) { - return _shapeData[shapeIndex]->geosGeom; + return _shapeData[shapeIndex]->geosGeom; } // ********************************************************* @@ -240,20 +290,20 @@ GEOSGeometry* CShapefile::GetGeosGeometry(int shapeIndex) // ********************************************************* STDMETHODIMP CShapefile::ClearCachedGeometries() { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if (_geosGeometriesRead) - { - for (size_t i = 0; i < _shapeData.size(); i++) - { - if (_shapeData[i]->geosGeom) - { - GEOSGeom_destroy(_shapeData[i]->geosGeom); - _shapeData[i]->geosGeom = NULL; - } - } - _geosGeometriesRead = false; - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + if (_geosGeometriesRead) + { + for (auto& i : _shapeData) + { + if (i->geosGeom) + { + GEOSGeom_destroy(i->geosGeom); + i->geosGeom = nullptr; + } + } + _geosGeometriesRead = false; + } + return S_OK; } // ********************************************************* @@ -261,37 +311,38 @@ STDMETHODIMP CShapefile::ClearCachedGeometries() // ********************************************************* void CShapefile::ReadGeosGeometries(VARIANT_BOOL selectedOnly) { - if (_geosGeometriesRead) - { - CallbackHelper::AssertionFailed("Attempt to reread GEOS geometries while they are in memory."); - ClearCachedGeometries(); - } - - long percent = 0; - int size = (int)_shapeData.size(); - for (int i = 0; i < size; i++) - { - CallbackHelper::Progress(_globalCallback, i, size, "Converting to geometries", _key, percent); - - if (!ShapeAvailable(i, selectedOnly)) - continue; - - if(_shapeData[i]->geosGeom) - CallbackHelper::AssertionFailed("GEOS Geometry during the reading was expected to be empty."); - - IShape* shp = NULL; - this->GetValidatedShape(i, &shp); - if (shp) - { - GEOSGeom geom = GeosConverter::ShapeToGeom(shp); - if (geom) - { - _shapeData[i]->geosGeom = geom; - } - shp->Release(); - } - } - _geosGeometriesRead = true; + if (_geosGeometriesRead) + { + CallbackHelper::AssertionFailed("Attempt to reread GEOS geometries while they are in memory."); + ClearCachedGeometries(); + } + + long percent = 0; + const auto size = (int)_shapeData.size(); + for (int i = 0; i < size; i++) + { + CallbackHelper::Progress(_globalCallback, i, size, "Converting to geometries", _key, percent); + + if (!ShapeAvailable(i, selectedOnly)) + continue; + + if (_shapeData[i]->geosGeom) + CallbackHelper::AssertionFailed("GEOS Geometry during the reading was expected to be empty."); + + IShape* shp = nullptr; + this->GetValidatedShape(i, &shp); + if (shp) + { + // ReSharper disable once CppLocalVariableMayBeConst + GEOSGeom geom = GeosConverter::ShapeToGeom(shp); + if (geom) + { + _shapeData[i]->geosGeom = geom; + } + shp->Release(); + } + } + _geosGeometriesRead = true; } -#pragma endregion \ No newline at end of file +#pragma endregion diff --git a/src/COM classes/TableClass.cpp b/src/COM classes/TableClass.cpp index fdb7b1b1..8410f366 100644 --- a/src/COM classes/TableClass.cpp +++ b/src/COM classes/TableClass.cpp @@ -40,12 +40,12 @@ static char THIS_FILE[] = __FILE__; // ***************************************************** // ParseExpressionCore() // ***************************************************** -void CTableClass::ParseExpressionCore(BSTR Expression, tkValueType returnType, CString& errorString, VARIANT_BOOL* retVal) +void CTableClass::ParseExpressionCore(BSTR Expression, tkValueType returnType, CStringW& errorString, VARIANT_BOOL* retVal) { *retVal = VARIANT_FALSE; USES_CONVERSION; - CString str = OLE2A(Expression); + CStringW str = OLE2CW(Expression); CustomExpression expr; if (!expr.ReadFieldNames(this)) @@ -97,20 +97,20 @@ STDMETHODIMP CTableClass::ParseExpression(BSTR Expression, BSTR* ErrorString, VA *retVal = VARIANT_FALSE; USES_CONVERSION; - CString str = OLE2CA(Expression); + CStringW str = OLE2CW(Expression); CustomExpression expr; if (expr.ReadFieldNames(this)) { - CString err; + CStringW err; if (expr.Parse(str, true, err)) { *retVal = VARIANT_TRUE; } else { - *ErrorString = A2BSTR(err); + *ErrorString = W2BSTR(err); } } else @@ -129,10 +129,10 @@ STDMETHODIMP CTableClass::TestExpression(BSTR Expression, tkValueType ReturnType { AFX_MANAGE_STATE(AfxGetStaticModuleState()); - CString err; + CStringW err; ParseExpressionCore(Expression, ReturnType, err, retVal); - *ErrorString = A2BSTR(err); + *ErrorString = W2BSTR(err); return S_OK; } @@ -144,10 +144,10 @@ STDMETHODIMP CTableClass::Query(BSTR Expression, VARIANT* Result, BSTR* ErrorStr *retVal = VARIANT_FALSE; SysFreeString(*ErrorString); // do we need it here? USES_CONVERSION; - CString str = OLE2CA(Expression); + CStringW str = OLE2CW(Expression); std::vector indices; - CString err; + CStringW err; if (QueryCore(str, indices, err)) { *ErrorString = SysAllocString(L""); @@ -164,7 +164,7 @@ STDMETHODIMP CTableClass::Query(BSTR Expression, VARIANT* Result, BSTR* ErrorStr } else { - *ErrorString = A2BSTR(err); + *ErrorString = W2BSTR(err); } return S_OK; } @@ -186,12 +186,12 @@ STDMETHODIMP CTableClass::Calculate(BSTR Expression, LONG RowIndex, VARIANT* Res } USES_CONVERSION; - CString str = OLE2A(Expression); + CStringW str = OLE2CW(Expression); CustomExpression expr; if (expr.ReadFieldNames(this)) { - CString err; + CStringW err; if (expr.Parse(Expression, true, err)) { TableHelper::SetFieldValues(this, RowIndex, expr); @@ -220,7 +220,7 @@ STDMETHODIMP CTableClass::Calculate(BSTR Expression, LONG RowIndex, VARIANT* Res } else { - *ErrorString = A2BSTR(err); + *ErrorString = W2BSTR(err); } } else @@ -233,14 +233,14 @@ STDMETHODIMP CTableClass::Calculate(BSTR Expression, LONG RowIndex, VARIANT* Res // ******************************************************************** // Query_() // ******************************************************************** -bool CTableClass::QueryCore(CString Expression, std::vector& indices, CString& ErrorString) +bool CTableClass::QueryCore(CStringW Expression, std::vector& indices, CStringW& ErrorString) { indices.clear(); CustomExpression expr; if (expr.ReadFieldNames(this)) { - CString err; + CStringW err; if (expr.Parse(Expression, true, err)) { bool error = false; @@ -289,7 +289,7 @@ bool CTableClass::QueryCore(CString Expression, std::vector& indices, CStr // ******************************************************************** // CalculateCore() // ******************************************************************** -bool CTableClass::CalculateCore(CString Expression, std::vector& results, CString& ErrorString, +bool CTableClass::CalculateCore(CStringW Expression, std::vector& results, CStringW& ErrorString, CString floatFormat, int rowIndex /*= -1*/) { USES_CONVERSION; @@ -303,7 +303,7 @@ bool CTableClass::CalculateCore(CString Expression, std::vector& resul return false; } - CString err; + CStringW err; if (!expr.Parse(Expression, true, err)) { ErrorString = err; @@ -357,7 +357,7 @@ bool CTableClass::CalculateCore(CString Expression, std::vector& resul // for each shape index a category is specified. If the given shape didn't fall into // any category -1 is used. The first categories in the list have higher priority // Results vector with certain categories can be provided by caller; those categories won't be changed -void CTableClass::AnalyzeExpressions(std::vector& expressions, std::vector& results) +void CTableClass::AnalyzeExpressions(std::vector& expressions, std::vector& results) { // TODO: optimize, if all expressions have the same fields in the same positions // don't read the values multiple times. @@ -370,7 +370,7 @@ void CTableClass::AnalyzeExpressions(std::vector& expressions, std::vec { if (expressions[categoryId] != "") { - CString err; + CStringW err; if (expr.Parse(expressions[categoryId], true, err)) { for (unsigned int i = 0; i < _rows.size(); i++) @@ -463,10 +463,10 @@ STDMETHODIMP CTableClass::get_CellValue(long FieldIndex, long RowIndex, VARIANT } else { - VARIANT var; - VariantInit(&var); - var.vt = VT_EMPTY; - pVal = &var; + // MWGIS-128 + // no value, send back EMPTY variant + VariantInit(pVal); + pVal->vt = VT_EMPTY; } return S_OK; } @@ -575,7 +575,7 @@ STDMETHODIMP CTableClass::Open(BSTR dbfFilename, ICallback *cBack, VARIANT_BOOL if( *retval) { - CStringW name = OLE2W(dbfFilename); + CStringW name = OLE2CW(dbfFilename); if (!Utility::FileExistsW(name)) { ErrorMessage(tkDBF_FILE_DOES_NOT_EXIST); @@ -653,7 +653,7 @@ STDMETHODIMP CTableClass::CreateNew(BSTR dbfFilename, VARIANT_BOOL *retval) // ******************************************************** void CTableClass::CloseUnderlyingFile() { - _filename = ""; + _filename = L""; if( _dbfHandle != NULL ) { DBFClose(_dbfHandle); @@ -839,7 +839,7 @@ bool CTableClass::SaveToFile(const CStringW& dbfFilename, bool updateFileInPlace STDMETHODIMP CTableClass::Dump(BSTR dbfFilename, ICallback *cBack, VARIANT_BOOL *retval) { USES_CONVERSION; - *retval = SaveToFile( OLE2W(dbfFilename), false, cBack) ? VARIANT_TRUE: VARIANT_FALSE; + *retval = SaveToFile(OLE2W(dbfFilename), false, cBack) ? VARIANT_TRUE: VARIANT_FALSE; return S_OK; } @@ -1363,8 +1363,20 @@ bool CTableClass::ReadRecord(long RowIndex) //WCHAR *buffer = Utility::StringToWideChar(v); //val->bstrVal = W2BSTR(buffer); //delete[] buffer; - val->bstrVal = W2BSTR(Utility::ConvertFromUtf8(v)); - } + + // jf, 12-22-2018 + // if code page was not provided (via .CPG file), OR if code page is specified UTF-8, + // then we will assume we have to unpack it as UTF-8, else we assume the string is + // already encoded properly, or is in the Windows multi-byte charset, and take it as is. + // Further reading: https://support.esri.com/en/technical-article/000013192 + // NOTE that this code differs from shapefiles being read from OGR, which are always UTF-8. + if(DBFGetCodePage(_dbfHandle) == nullptr || strcmp(DBFGetCodePage(_dbfHandle), "UTF-8") == 0) + // assume UTF-8 + val->bstrVal = W2BSTR(Utility::ConvertFromUtf8(v)); + else + // else assume it's already interpreted by associated code page + val->bstrVal = A2BSTR(v); + } } else if( type == INTEGER_FIELD ) { @@ -2232,7 +2244,7 @@ vector* CTableClass::GenerateCategories(long FieldIndex, tkClass CComBSTR str; fld->get_Name(&str); USES_CONVERSION; - CString fieldName = OLE2CA(str); + CStringW fieldName = OLE2CW(str); fld->Release(); fld = NULL; /* we won't define intervals for string values */ @@ -2490,24 +2502,24 @@ vector* CTableClass::GenerateCategories(long FieldIndex, tkClass for (int i = 0; i < (int)result->size(); i++ ) { //CString strExpression; - CString strValue; + CStringW strValue; CComVariant* val = &(*result)[i].minValue; // TODO: MWGIS-72: Support Russian encoding // https://stackoverflow.com/questions/45484130/how-to-convert-ccomvariant-bstr-to-cstring switch (val->vt) { case VT_BSTR: - strValue = OLE2CA(val->bstrVal); + strValue = OLE2W(val->bstrVal); (*result)[i].name = strValue; (*result)[i].expression = "[" + fieldName + "] = \"" + strValue + "\""; break; case VT_R8: - strValue.Format("%g", val->dblVal); + strValue.Format(L"%g", val->dblVal); (*result)[i].name = strValue; (*result)[i].expression = "[" + fieldName + "] = " + strValue; break; case VT_I4: - strValue.Format("%i", val->lVal); + strValue.Format(L"%i", val->lVal); (*result)[i].name = strValue; (*result)[i].expression = "[" + fieldName + "] = " + strValue; break; @@ -2517,7 +2529,7 @@ vector* CTableClass::GenerateCategories(long FieldIndex, tkClass else //if (ClassificationType == ctEqualIntervals || ClassificationType == ctEqualCount) { // in case % is present, we need to put to double it for proper formatting - fieldName.Replace("%", "%%"); + fieldName.Replace(L"%", L"%%"); for (int i = 0; i < (int)result->size(); i++ ) { @@ -2534,7 +2546,7 @@ vector* CTableClass::GenerateCategories(long FieldIndex, tkClass data->maxValue.dblVal = ceil(data->maxValue.dblVal); } - CString upperBound = (i == result->size() - 1) ? "<=" : "<"; + CStringW upperBound = (i == result->size() - 1) ? "<=" : "<"; switch (data->minValue.vt) { @@ -2566,15 +2578,15 @@ vector* CTableClass::GenerateCategories(long FieldIndex, tkClass // ***************************************************************** // FieldNames() // ***************************************************************** -std::vector* CTableClass::get_FieldNames() +std::vector* CTableClass::get_FieldNames() { - std::vector* names = new std::vector; + std::vector* names = new std::vector; for (unsigned int i = 0; i < _fields.size(); i++ ) { CComBSTR bstr; _fields[i]->field->get_Name(&bstr); USES_CONVERSION; - CString str = OLE2CA(bstr); + CStringW str = OLE2CW(bstr); names->push_back(str); } return names; @@ -2906,7 +2918,7 @@ bool CTableClass::MakeUniqueFieldNames() // ************************************************************* // JoinFields() // ************************************************************* -bool CTableClass::JoinFields(ITable* table2, std::vector& mapping, set& fieldList) +bool CTableClass::JoinFields(ITable* table2, std::vector& mapping, set& fieldList) { USES_CONVERSION; @@ -2920,7 +2932,7 @@ bool CTableClass::JoinFields(ITable* table2, std::vector& mapping fld->get_Name(&name); // either take all or take those that are in the list provided by user; comparison is case sensitive - if (fieldList.size() == 0 || fieldList.find(OLE2A(name)) != fieldList.end()) + if (fieldList.size() == 0 || fieldList.find(OLE2W(name)) != fieldList.end()) { IField* fldNew; fld->Clone(&fldNew); @@ -2953,8 +2965,8 @@ STDMETHODIMP CTableClass::Join(ITable* table2, BSTR fieldTo, BSTR fieldFrom, VAR { AFX_MANAGE_STATE(AfxGetStaticModuleState()); USES_CONVERSION; - set fields; - bool res = this->JoinInternal(table2, OLE2A(fieldTo), OLE2A(fieldFrom), "", "", fields); + set fields; + bool res = this->JoinInternal(table2, OLE2W(fieldTo), OLE2W(fieldFrom), "", "", fields); *retVal = res ? VARIANT_TRUE: VARIANT_FALSE; return S_OK; } @@ -2966,8 +2978,8 @@ STDMETHODIMP CTableClass::Join2(ITable* table2, BSTR fieldTo, BSTR fieldFrom, BS { AFX_MANAGE_STATE(AfxGetStaticModuleState()); USES_CONVERSION; - set fields; - bool res = this->JoinInternal(table2, OLE2A(fieldTo), OLE2A(fieldFrom), OLE2W(filenameToReopen), OLE2A(joinOptions), fields); + set fields; + bool res = this->JoinInternal(table2, OLE2W(fieldTo), OLE2W(fieldFrom), OLE2W(filenameToReopen), OLE2A(joinOptions), fields); *retVal = res ? VARIANT_TRUE: VARIANT_FALSE; return S_OK; } @@ -2981,7 +2993,7 @@ STDMETHODIMP CTableClass::Join3(ITable* table2, BSTR fieldTo, BSTR fieldFrom, BS USES_CONVERSION; // Check dimensions of the array. - set fields; + set fields; if (SafeArrayGetDim(filedList) == 1) { LONG lLBound, lUBound; @@ -2993,12 +3005,12 @@ STDMETHODIMP CTableClass::Join3(ITable* table2, BSTR fieldTo, BSTR fieldFrom, BS { LONG count = lUBound-lLBound + 1; for (int i = 0; i < count; i++){ - fields.insert(OLE2A(pbstr[i])); + fields.insert(OLE2W(pbstr[i])); } } } - bool res = this->JoinInternal(table2, OLE2A(fieldTo), OLE2A(fieldFrom), OLE2A(filenameToReopen), OLE2A(joinOptions), fields); + bool res = this->JoinInternal(table2, OLE2W(fieldTo), OLE2W(fieldFrom), OLE2W(filenameToReopen), OLE2A(joinOptions), fields); *retVal = res ? VARIANT_TRUE: VARIANT_FALSE; return S_OK; } @@ -3016,7 +3028,7 @@ STDMETHODIMP CTableClass::TryJoin(ITable* table2, BSTR fieldTo, BSTR fieldFrom, long index1, index2; USES_CONVERSION; - if (this->CheckJoinInput(table2, OLE2A(fieldTo), OLE2A(fieldFrom), index1, index2)) + if (this->CheckJoinInput(table2, OLE2W(fieldTo), OLE2W(fieldFrom), index1, index2)) { // building a maps for field of target table std::map vals; @@ -3057,7 +3069,7 @@ STDMETHODIMP CTableClass::TryJoin(ITable* table2, BSTR fieldTo, BSTR fieldFrom, // ***************************************************** // CheckJoinInput() // ***************************************************** -bool CTableClass::CheckJoinInput(ITable* table2, CString fieldTo, CString fieldFrom, long& index1, long& index2) +bool CTableClass::CheckJoinInput(ITable* table2, CStringW fieldTo, CStringW fieldFrom, long& index1, long& index2) { if (!table2) { @@ -3097,7 +3109,7 @@ bool CTableClass::CheckJoinInput(ITable* table2, CString fieldTo, CString fieldF // ***************************************************** // JoinInternal() // ***************************************************** -bool CTableClass::JoinInternal(ITable* table2, CString fieldTo, CString fieldFrom, CStringW filenameToReopen, CString options, set& fieldList) +bool CTableClass::JoinInternal(ITable* table2, CStringW fieldTo, CStringW fieldFrom, CStringW filenameToReopen, CString options, set& fieldList) { long index1, index2; if (!this->CheckJoinInput(table2, fieldTo, fieldFrom, index1, index2)) @@ -3112,7 +3124,7 @@ bool CTableClass::JoinInternal(ITable* table2, CString fieldTo, CString fieldFro // reading list of fields specified by user CString csvFields; - set::iterator it = fieldList.begin(); + set::iterator it = fieldList.begin(); while(it != fieldList.end()) { csvFields += *it + ","; @@ -3341,7 +3353,7 @@ STDMETHODIMP CTableClass::get_JoinFilename(int joinIndex, BSTR* retVal) if (joinIndex < 0 || joinIndex >= (int)_joins.size()) { ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - *retVal = A2BSTR(""); + *retVal = W2BSTR(L""); } else { @@ -3359,11 +3371,11 @@ STDMETHODIMP CTableClass::get_JoinFromField(int joinIndex, BSTR* retVal) if (joinIndex < 0 || joinIndex >= (int)_joins.size()) { ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - *retVal = A2BSTR(""); + *retVal = W2BSTR(L""); } else { - *retVal = A2BSTR(_joins[joinIndex]->fieldFrom); + *retVal = W2BSTR(_joins[joinIndex]->fieldFrom); } return S_OK; } @@ -3378,11 +3390,11 @@ STDMETHODIMP CTableClass::get_JoinToField(int joinIndex, BSTR* retVal) if (joinIndex < 0 || joinIndex >= (int)_joins.size()) { ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - *retVal = A2BSTR(""); + *retVal = W2BSTR(L""); } else { - *retVal = A2BSTR(_joins[joinIndex]->fieldTo); + *retVal = W2BSTR(_joins[joinIndex]->fieldTo); } return S_OK; } @@ -3397,11 +3409,11 @@ STDMETHODIMP CTableClass::get_JoinFields(LONG joinIndex, BSTR* pVal) if (joinIndex < 0 || joinIndex >= (int)_joins.size()) { ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - *pVal = A2BSTR(""); + *pVal = W2BSTR(L""); } else { - *pVal = A2BSTR(_joins[joinIndex]->fields); + *pVal = W2BSTR(_joins[joinIndex]->fields); } return S_OK; @@ -3495,9 +3507,9 @@ CPLXMLNode* CTableClass::SerializeCore(CString ElementName) CPLXMLNode* psNode = CPLCreateXMLNode(psJoins, CXT_Element, "Join"); Utility::CPLCreateXMLAttributeAndValue(psNode, "Filename", CPLString().Printf(Utility::ConvertToUtf8(name))); - Utility::CPLCreateXMLAttributeAndValue(psNode, "FieldTo", CPLString().Printf(_joins[i]->fieldTo)); - Utility::CPLCreateXMLAttributeAndValue(psNode, "FieldFrom", CPLString().Printf(_joins[i]->fieldFrom)); - Utility::CPLCreateXMLAttributeAndValue(psNode, "Fields", CPLString().Printf(_joins[i]->fields)); + Utility::CPLCreateXMLAttributeAndValue(psNode, "FieldTo", CPLString().Printf(Utility::ConvertToUtf8(_joins[i]->fieldTo))); + Utility::CPLCreateXMLAttributeAndValue(psNode, "FieldFrom", CPLString().Printf(Utility::ConvertToUtf8(_joins[i]->fieldFrom))); + Utility::CPLCreateXMLAttributeAndValue(psNode, "Fields", CPLString().Printf(Utility::ConvertToUtf8(_joins[i]->fields))); Utility::CPLCreateXMLAttributeAndValue(psNode, "Options", CPLString().Printf(_joins[i]->options)); } } @@ -3613,9 +3625,9 @@ void CTableClass::RestoreFields(CPLXMLNode* node) // ******************************************************** void CTableClass::RestoreJoins(CPLXMLNode* node) { - CStringW folderName = ""; + CStringW folderName = L""; wchar_t* cwd = NULL; - if (this->_filename != "") + if (this->_filename != L"") { cwd = new wchar_t[4096]; _wgetcwd(cwd, 4096); @@ -3663,9 +3675,9 @@ void CTableClass::RestoreJoins(CPLXMLNode* node) if (numRows > 0 && numCols > 0) { - set fieldList; + set fieldList; int pos = 0; - CString field = fields.Tokenize(",", pos); + CStringW field = fields.Tokenize(",", pos); while (field.GetLength() != 0) { fieldList.insert(field); diff --git a/src/COM classes/TableClass.h b/src/COM classes/TableClass.h index 3d909a54..dc03a118 100644 --- a/src/COM classes/TableClass.h +++ b/src/COM classes/TableClass.h @@ -164,11 +164,11 @@ class ATL_NO_VTABLE CTableClass : struct JoinInfo { CStringW filename; - CString fieldFrom; - CString fieldTo; + CStringW fieldFrom; + CStringW fieldTo; CString options; int joinId; - CString fields; // comma separated list + CStringW fields; // comma separated list }; struct FieldMapping @@ -210,7 +210,7 @@ class ATL_NO_VTABLE CTableClass : FieldType GetFieldType(long fieldIndex); long GetFieldPrecision(long fieldIndex); inline void ErrorMessage(long ErrorCode); - std::vector* get_FieldNames(); + std::vector* get_FieldNames(); void RestoreJoins(CPLXMLNode* node); void RestoreFields(CPLXMLNode* node); void ClearFieldCustomizations(); @@ -231,14 +231,14 @@ class ATL_NO_VTABLE CTableClass : bool GetUids(long fieldIndex, map& resutls); bool UpdateTableRow(TableRow* newRow, long rowIndex); - void ParseExpressionCore(BSTR Expression, tkValueType returnType, CString& ErrorString, VARIANT_BOOL* retVal); + void ParseExpressionCore(BSTR Expression, tkValueType returnType, CStringW& ErrorString, VARIANT_BOOL* retVal); std::vector* GenerateCategories(long FieldIndex, tkClassificationType ClassificationType, long numClasses); std::vector* GenerateCategories(long FieldIndex, tkClassificationType ClassificationType, long numClasses, CComVariant minValue, CComVariant maxValue); - void AnalyzeExpressions(std::vector& expressions, std::vector& results); - bool QueryCore(CString Expression, std::vector& indices, CString& ErrorString); - bool CalculateCore(CString Expression, std::vector& results, CString& ErrorString, CString floatFormat, int rowIndex = -1); + void AnalyzeExpressions(std::vector& expressions, std::vector& results); + bool QueryCore(CStringW Expression, std::vector& indices, CStringW& ErrorString); + bool CalculateCore(CStringW Expression, std::vector& results, CStringW& ErrorString, CString floatFormat, int rowIndex = -1); bool get_FieldValuesDouble(int FieldIndex, std::vector& values); bool get_FieldValuesInteger(int FieldIndex, std::vector& values); @@ -246,9 +246,9 @@ class ATL_NO_VTABLE CTableClass : bool set_IndexValue(int rowIndex); bool MakeUniqueFieldNames(); - bool CheckJoinInput(ITable* table2, CString fieldTo, CString fieldFrom, long& index1, long& index2); - bool JoinFields(ITable* table2, std::vector& mapping, set& fieldList); - bool JoinInternal(ITable* table2, CString fieldTo, CString fieldFrom, CStringW filenameToReopen, CString options, set& fieldList); + bool CheckJoinInput(ITable* table2, CStringW fieldTo, CStringW fieldFrom, long& index1, long& index2); + bool JoinFields(ITable* table2, std::vector& mapping, set& fieldList); + bool JoinInternal(ITable* table2, CStringW fieldTo, CStringW fieldFrom, CStringW filenameToReopen, CString options, set& fieldList); void RemoveJoinedFields(); bool HasFieldChanges(); void MarkFieldsAsUnchanged(); diff --git a/src/COM classes/TileProviders.cpp b/src/COM classes/TileProviders.cpp index 5763e0a5..f6a0a2b0 100644 --- a/src/COM classes/TileProviders.cpp +++ b/src/COM classes/TileProviders.cpp @@ -17,8 +17,9 @@ ************************************************************************************** * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ - -#include "stdafx.h" +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper + +#include "StdAfx.h" #include "TileProviders.h" #include "BingMapProvider.h" #include "OpenStreetMapProvider.h" @@ -33,59 +34,59 @@ // ************************************************************ BaseProvider* CTileProviders::get_Provider(int providerId) { - for (size_t i = 0; i < _providers.size(); i++) - { - if (_providers[i]->Id == providerId) - { - return _providers[i]; - } - } - return NULL; + for (size_t i = 0; i < _providers.size(); i++) + { + if (_providers[i]->Id == providerId) + { + return _providers[i]; + } + } + return nullptr; } #pragma region "ErrorHandling" // ************************************************************ // get_GlobalCallback() // ************************************************************ -STDMETHODIMP CTileProviders::get_GlobalCallback(ICallback **pVal) +STDMETHODIMP CTileProviders::get_GlobalCallback(ICallback** pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *pVal = _globalCallback; - if( _globalCallback != NULL ) - _globalCallback->AddRef(); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + *pVal = _globalCallback; + if (_globalCallback != nullptr) + _globalCallback->AddRef(); + return S_OK; } // ************************************************************ // put_GlobalCallback() // ************************************************************ -STDMETHODIMP CTileProviders::put_GlobalCallback(ICallback *newVal) +STDMETHODIMP CTileProviders::put_GlobalCallback(ICallback* newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - ComHelper::SetRef(newVal, (IDispatch**)&_globalCallback); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + ComHelper::SetRef(newVal, (IDispatch**)&_globalCallback); + return S_OK; } // ***************************************************************** // get_ErrorMsg() // ***************************************************************** -STDMETHODIMP CTileProviders::get_ErrorMsg(long ErrorCode, BSTR *pVal) +STDMETHODIMP CTileProviders::get_ErrorMsg(long ErrorCode, BSTR* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - USES_CONVERSION; - *pVal = A2BSTR(ErrorMsg(ErrorCode)); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + USES_CONVERSION; + *pVal = A2BSTR(ErrorMsg(ErrorCode)); + return S_OK; } // ************************************************************ // get_LastErrorCode() // ************************************************************ -STDMETHODIMP CTileProviders::get_LastErrorCode(long *pVal) +STDMETHODIMP CTileProviders::get_LastErrorCode(long* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *pVal = _lastErrorCode; - _lastErrorCode = tkNO_ERROR; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + *pVal = _lastErrorCode; + _lastErrorCode = tkNO_ERROR; + return S_OK; } // ************************************************************** @@ -93,26 +94,27 @@ STDMETHODIMP CTileProviders::get_LastErrorCode(long *pVal) // ************************************************************** void CTileProviders::ErrorMessage(long ErrorCode) { - _lastErrorCode = ErrorCode; - CallbackHelper::ErrorMsg("TileProviders", _globalCallback, _key, ErrorMsg(_lastErrorCode)); + _lastErrorCode = ErrorCode; + CallbackHelper::ErrorMsg("TileProviders", _globalCallback, _key, ErrorMsg(_lastErrorCode)); } // ************************************************************ // get/put_Key() // ************************************************************ -STDMETHODIMP CTileProviders::get_Key(BSTR *pVal) +STDMETHODIMP CTileProviders::get_Key(BSTR* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - USES_CONVERSION; - *pVal = OLE2BSTR(_key); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + USES_CONVERSION; + *pVal = OLE2BSTR(_key); + return S_OK; } + STDMETHODIMP CTileProviders::put_Key(BSTR newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - ::SysFreeString(_key); - _key = OLE2BSTR(newVal); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + SysFreeString(_key); + _key = OLE2BSTR(newVal); + return S_OK; } #pragma endregion @@ -122,83 +124,86 @@ STDMETHODIMP CTileProviders::put_Key(BSTR newVal) // getProvider() // ***************************************************** // Instantiate default providers -BaseProvider* CTileProviders::getProviderCore(tkTileProvider provider ) -{ - for (size_t i = 0; i < _providers.size(); i++) - { - if (_providers[i]->Id == provider) - return _providers[i]; - } - - BaseProvider* p = NULL; - - switch (provider) - { - case BingMaps: - p = (BaseProvider*)new BingMapProvider(); - break; - case BingSatellite: - p = (BaseProvider*)new BingSatelliteProvider(); - break; - case BingHybrid: - p = (BaseProvider*)new BingHybridProvider(); - break; - case OpenStreetMap: - p = (BaseProvider*)new OpenStreetMapProvider(); - break; - case HereMaps: - p = (BaseProvider*)new OviMapProvider(); - break; - case HereSatellite: - p = (BaseProvider*)new OviSatelliteProvider(); - break; - case HereHybrid: - p = (BaseProvider*)new OviHybridProvider(); - break; - case HereTerrain: - p = (BaseProvider*)new OviTerrainProvider(); - break; - case GoogleMaps: - p = (BaseProvider*)new GoogleMapProvider(); - break; - case GoogleSatellite: - p = (BaseProvider*)new GoogleSatelliteProvider(); - break; - case GoogleHybrid: - p = (BaseProvider*)new GoogleHybridProvider(this); - break; - case GoogleTerrain: - p = (BaseProvider*)new GoogleTerrainProvider(); - break; - case OpenTransportMap: - p = (BaseProvider*)new OpenTransportMapProvider(); - break; - case OpenCycleMap: - p = (BaseProvider*)new OpenCycleMapProvider(); - break; - case Rosreestr: - { - RosreestrProvider* pr1 = new RosreestrProvider(false); - pr1->get_SubProviders()->push_back(pr1); - pr1->AddDynamicOverlay(new RosreestrProvider(true)); - pr1->AddDynamicOverlay(new RosreestrBordersProvider()); - p = (BaseProvider*)pr1; - } - break; - case OpenHumanitarianMap: - p = (BaseProvider*)new OpenHumanitarianMapProvider(); - break; - case MapQuestAerial: - p = (BaseProvider*)new MapQuestProvider(); - break; - } - - if (p) - { - _providers.push_back(p); - } - - return p; +BaseProvider* CTileProviders::getProviderCore(tkTileProvider provider) +{ + for (size_t i = 0; i < _providers.size(); i++) + { + if (_providers[i]->Id == provider) + return _providers[i]; + } + + BaseProvider* p = nullptr; + + switch (provider) + { + case BingMaps: + p = (BaseProvider*)new BingMapProvider(); + break; + case BingSatellite: + p = (BaseProvider*)new BingSatelliteProvider(); + break; + case BingHybrid: + p = (BaseProvider*)new BingHybridProvider(); + break; + case OpenStreetMap: + p = (BaseProvider*)new OpenStreetMapProvider(); + break; + case HereMaps: + p = (BaseProvider*)new OviMapProvider(); + break; + case HereSatellite: + p = (BaseProvider*)new OviSatelliteProvider(); + break; + case HereHybrid: + p = (BaseProvider*)new OviHybridProvider(); + break; + case HereTerrain: + p = (BaseProvider*)new OviTerrainProvider(); + break; + case GoogleMaps: + p = (BaseProvider*)new GoogleMapProvider(); + break; + case GoogleSatellite: + p = (BaseProvider*)new GoogleSatelliteProvider(); + break; + case GoogleHybrid: + p = (BaseProvider*)new GoogleHybridProvider(this); + break; + case GoogleTerrain: + p = (BaseProvider*)new GoogleTerrainProvider(); + break; + case OpenTransportMap: + p = (BaseProvider*)new OpenTransportMapProvider(); + break; + case OpenCycleMap: + p = (BaseProvider*)new OpenCycleMapProvider(); + break; + case Rosreestr: + { + auto* pr1 = new RosreestrProvider(false); + pr1->get_SubProviders()->push_back(pr1); + pr1->AddDynamicOverlay(new RosreestrProvider(true)); + pr1->AddDynamicOverlay(new RosreestrBordersProvider()); + p = (BaseProvider*)pr1; + } + break; + case OpenHumanitarianMap: + p = (BaseProvider*)new OpenHumanitarianMapProvider(); + break; + case MapQuestAerial: + p = (BaseProvider*)new MapQuestProvider(); + break; + case ProviderNone: break; + case ProviderCustom: break; + default: ; + } + + if (p) + { + _providers.push_back(p); + } + + return p; } #pragma endregion @@ -207,51 +212,48 @@ BaseProvider* CTileProviders::getProviderCore(tkTileProvider provider ) // ****************************************************** STDMETHODIMP CTileProviders::Remove(LONG providerId, VARIANT_BOOL clearCache, VARIANT_BOOL* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - BaseProvider* p = this->get_Provider(providerId); - if (!p) - { - ErrorMessage(tkINVALID_PROVIDER_ID); - return S_OK; - } - else - { - CustomTileProvider* custom = dynamic_cast(p); - if (!custom) - { - ErrorMessage(tkCANT_DELETE_DEFAULT_PROVIDER); - return S_OK; - } - else - { - if (_tiles != NULL) - { - // check, probably the provider is currently in use - int id = -1; - _tiles->get_ProviderId(&id); - if (p->Id == id) { - _tiles->put_Provider(tkTileProvider::OpenStreetMap); - } - } - - for (size_t i = 0; i < _providers.size(); i++) - { - if (_providers[i]->Id == providerId) - { - delete _providers[i]; - _providers.erase(_providers.begin() + i); - break; - } - } - - if (clearCache) - { - _tiles->ClearCache2(Disk, providerId); - _tiles->ClearCache2(RAM, providerId); - } - return S_OK; - } - } + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + BaseProvider* p = this->get_Provider(providerId); + if (!p) + { + ErrorMessage(tkINVALID_PROVIDER_ID); + return S_OK; + } + + auto* custom = dynamic_cast(p); + if (!custom) + { + ErrorMessage(tkCANT_DELETE_DEFAULT_PROVIDER); + return S_OK; + } + + if (_tiles != nullptr) + { + // check, probably the provider is currently in use + int id = -1; + _tiles->get_ProviderId(&id); + if (p->Id == id) + { + _tiles->put_Provider(tkTileProvider::OpenStreetMap); + } + } + + for (size_t i = 0; i < _providers.size(); i++) + { + if (_providers[i]->Id == providerId) + { + delete _providers[i]; + _providers.erase(_providers.begin() + i); + break; + } + } + + if (clearCache) + { + _tiles->ClearCache2(Disk, providerId); + _tiles->ClearCache2(RAM, providerId); + } + return S_OK; } // ******************************************************* @@ -259,41 +261,42 @@ STDMETHODIMP CTileProviders::Remove(LONG providerId, VARIANT_BOOL clearCache, VA // ******************************************************* STDMETHODIMP CTileProviders::Clear(VARIANT_BOOL clearCache) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - - // make sure no provider is currently in use - if (_tiles != NULL) - { - tkTileProvider provider; - _tiles->get_Provider(&provider); - - if (provider == tkTileProvider::ProviderCustom) { - _tiles->put_Provider(tkTileProvider::OpenStreetMap); - } - } - - // default providers should remain untouched - std::vector::iterator it = _providers.begin(); - while (it < _providers.end()) - { - CustomTileProvider* p = dynamic_cast(*it); - if (p) - { - if (clearCache) - { - _tiles->ClearCache2(Disk, p->Id); - _tiles->ClearCache2(RAM, p->Id); - } - - delete *it; - it = _providers.erase(it); - } - else - { - ++it; - } - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + + // make sure no provider is currently in use + if (_tiles != nullptr) + { + tkTileProvider provider; + _tiles->get_Provider(&provider); + + if (provider == tkTileProvider::ProviderCustom) + { + _tiles->put_Provider(tkTileProvider::OpenStreetMap); + } + } + + // default providers should remain untouched + std::vector::iterator it = _providers.begin(); + while (it < _providers.end()) + { + CustomTileProvider* p = dynamic_cast(*it); + if (p) + { + if (clearCache) + { + _tiles->ClearCache2(Disk, p->Id); + _tiles->ClearCache2(RAM, p->Id); + } + + delete *it; + it = _providers.erase(it); + } + else + { + ++it; + } + } + return S_OK; } // ******************************************************* @@ -301,52 +304,57 @@ STDMETHODIMP CTileProviders::Clear(VARIANT_BOOL clearCache) // ******************************************************* STDMETHODIMP CTileProviders::get_Count(LONG* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *pVal = _providers.size(); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + *pVal = _providers.size(); + return S_OK; } // ******************************************************* // Add() // ******************************************************* -STDMETHODIMP CTileProviders::Add(int Id, BSTR name, BSTR urlPattern, tkTileProjection projection, int minZoom, int maxZoom, VARIANT_BOOL* retVal) -{ - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *retVal = VARIANT_FALSE; - - if (minZoom < 1 ) - minZoom = 1; - - if (Id < (int)tkTileProvider::ProviderCustom) { - this->ErrorMessage(tkPROVIDER_ID_RESERVED); - return S_OK; - } - - BaseProvider* p = this->get_Provider(Id); - if (p) { - this->ErrorMessage(tkPROVIDER_ID_IN_USE); - return S_OK; - } - - try - { - CustomTileProvider* provider = new CustomTileProvider(Id, name, urlPattern, projection, minZoom, maxZoom); - _providers.push_back(provider); - *retVal = VARIANT_TRUE; - } - catch(int val) - { - switch (val) - { - case 1: - this->ErrorMessage(tkINVALID_PROJECTION); - break; - case 2: - this->ErrorMessage(tkINVALID_URL); - break; - } - } - return S_OK; +STDMETHODIMP CTileProviders::Add(int Id, BSTR name, BSTR urlPattern, tkTileProjection projection, int minZoom, + int maxZoom, BSTR copyright, VARIANT_BOOL* retVal) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + *retVal = VARIANT_FALSE; + + if (minZoom < 1) + minZoom = 1; + + if (Id < (int)tkTileProvider::ProviderCustom) + { + this->ErrorMessage(tkPROVIDER_ID_RESERVED); + return S_OK; + } + + BaseProvider* p = this->get_Provider(Id); + if (p) + { + this->ErrorMessage(tkPROVIDER_ID_IN_USE); + return S_OK; + } + + try + { + CustomTileProvider* provider = new CustomTileProvider(Id, name, urlPattern, projection, minZoom, maxZoom, + copyright); + _providers.push_back(provider); + *retVal = VARIANT_TRUE; + } + catch (int val) + { + switch (val) + { + case 1: + this->ErrorMessage(tkINVALID_PROJECTION); + break; + case 2: + this->ErrorMessage(tkINVALID_URL); + break; + default: ; + } + } + return S_OK; } #pragma region Provider properties @@ -355,15 +363,17 @@ STDMETHODIMP CTileProviders::Add(int Id, BSTR name, BSTR urlPattern, tkTileProje // ******************************************************* STDMETHODIMP CTileProviders::get_Id(int Index, LONG* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if (Index < 0 || Index >= (int)_providers.size()){ - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - *retVal = -1; - } - else { - *retVal = (LONG)_providers[Index]->Id; - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + if (Index < 0 || Index >= (int)_providers.size()) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + *retVal = -1; + } + else + { + *retVal = (LONG)_providers[Index]->Id; + } + return S_OK; } // ******************************************************* @@ -371,33 +381,32 @@ STDMETHODIMP CTileProviders::get_Id(int Index, LONG* retVal) // ******************************************************* STDMETHODIMP CTileProviders::get_Name(int Index, BSTR* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - USES_CONVERSION; - if (Index < 0 || Index >= (int)_providers.size()) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - *retVal = A2BSTR(""); - } - else { - *retVal = A2BSTR(_providers[Index]->Name); - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + USES_CONVERSION; + if (Index < 0 || Index >= (int)_providers.size()) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + *retVal = A2BSTR(""); + } + else + { + *retVal = A2BSTR(_providers[Index]->Name); + } + return S_OK; } STDMETHODIMP CTileProviders::put_Name(int Index, BSTR pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if (Index < 0 || Index >= (int)_providers.size()) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - return S_OK; - } - else - { - USES_CONVERSION; - _providers[Index]->Name = OLE2A(pVal); - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + if (Index < 0 || Index >= (int)_providers.size()) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + return S_OK; + } + + USES_CONVERSION; + _providers[Index]->Name = OLE2A(pVal); + return S_OK; } // ******************************************************* @@ -405,32 +414,33 @@ STDMETHODIMP CTileProviders::put_Name(int Index, BSTR pVal) // ******************************************************* STDMETHODIMP CTileProviders::get_Language(int Index, BSTR* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - USES_CONVERSION; - if (Index < 0 || Index >= (int)_providers.size()) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - *retVal = A2BSTR(""); - } - else { - *retVal = A2BSTR(_providers[Index]->LanguageStr); - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + USES_CONVERSION; + if (Index < 0 || Index >= (int)_providers.size()) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + *retVal = A2BSTR(""); + } + else + { + *retVal = A2BSTR(_providers[Index]->LanguageStr); + } + return S_OK; } STDMETHODIMP CTileProviders::put_Language(int Index, BSTR pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if (Index < 0 || Index >= (int)_providers.size()) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - } - else - { - USES_CONVERSION; - _providers[Index]->LanguageStr = OLE2A(pVal); - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + if (Index < 0 || Index >= (int)_providers.size()) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + } + else + { + USES_CONVERSION; + _providers[Index]->LanguageStr = OLE2A(pVal); + } + return S_OK; } // ****************************************************** @@ -438,8 +448,8 @@ STDMETHODIMP CTileProviders::put_Language(int Index, BSTR pVal) // ****************************************************** STDMETHODIMP CTileProviders::get_IsCustom(int Index, VARIANT_BOOL* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + return S_OK; } // ******************************************************* @@ -447,17 +457,18 @@ STDMETHODIMP CTileProviders::get_IsCustom(int Index, VARIANT_BOOL* pVal) // ******************************************************* STDMETHODIMP CTileProviders::get_Version(int Index, BSTR* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - USES_CONVERSION; - if (Index < 0 || Index >= (int)_providers.size()) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - *retVal = A2BSTR(""); - } - else { - *retVal = A2BSTR(_providers[Index]->Version); - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + USES_CONVERSION; + if (Index < 0 || Index >= (int)_providers.size()) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + *retVal = A2BSTR(""); + } + else + { + *retVal = A2BSTR(_providers[Index]->Version); + } + return S_OK; } // ******************************************************* @@ -465,17 +476,17 @@ STDMETHODIMP CTileProviders::get_Version(int Index, BSTR* retVal) // ******************************************************* STDMETHODIMP CTileProviders::put_Version(int Index, BSTR pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if (Index < 0 || Index >= (int)_providers.size()) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - } - else - { - USES_CONVERSION; - _providers[Index]->Version = OLE2A(pVal); - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + if (Index < 0 || Index >= (int)_providers.size()) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + } + else + { + USES_CONVERSION; + _providers[Index]->Version = OLE2A(pVal); + } + return S_OK; } // ******************************************************* @@ -483,16 +494,17 @@ STDMETHODIMP CTileProviders::put_Version(int Index, BSTR pVal) // ******************************************************* STDMETHODIMP CTileProviders::get_UrlPattern(int Index, BSTR* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if (Index < 0 || Index >= (int)_providers.size()) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - *retVal = A2BSTR(""); - } - else { - *retVal = A2BSTR(_providers[Index]->get_UrlFormat()); - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + if (Index < 0 || Index >= (int)_providers.size()) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + *retVal = A2BSTR(""); + } + else + { + *retVal = A2BSTR(_providers[Index]->get_UrlFormat()); + } + return S_OK; } // ******************************************************* @@ -500,17 +512,18 @@ STDMETHODIMP CTileProviders::get_UrlPattern(int Index, BSTR* retVal) // ******************************************************* STDMETHODIMP CTileProviders::get_Projection(int Index, tkTileProjection* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if (Index < 0 || Index >= (int)_providers.size()) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - *retVal = (tkTileProjection)-1; - } - else { - CustomTileProvider* p = dynamic_cast(_providers[Index]); - *retVal = p ? p->get_Projection() : (tkTileProjection)-1; - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + if (Index < 0 || Index >= (int)_providers.size()) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + *retVal = (tkTileProjection)-1; + } + else + { + CustomTileProvider* p = dynamic_cast(_providers[Index]); + *retVal = p ? p->get_Projection() : (tkTileProjection)-1; + } + return S_OK; } // ******************************************************* @@ -518,16 +531,17 @@ STDMETHODIMP CTileProviders::get_Projection(int Index, tkTileProjection* retVal) // ******************************************************* STDMETHODIMP CTileProviders::get_MinZoom(int Index, int* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if (Index < 0 || Index >= (int)_providers.size()) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - *retVal = -1; - } - else { - *retVal = _providers[Index]->get_MinZoom(); - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + if (Index < 0 || Index >= (int)_providers.size()) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + *retVal = -1; + } + else + { + *retVal = _providers[Index]->get_MinZoom(); + } + return S_OK; } // ******************************************************* @@ -535,16 +549,17 @@ STDMETHODIMP CTileProviders::get_MinZoom(int Index, int* retVal) // ******************************************************* STDMETHODIMP CTileProviders::get_MaxZoom(int Index, int* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if (Index < 0 || Index >= (int)_providers.size()) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - *retVal = -1; - } - else { - *retVal = _providers[Index]->get_MaxZoom(); - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + if (Index < 0 || Index >= (int)_providers.size()) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + *retVal = -1; + } + else + { + *retVal = _providers[Index]->get_MaxZoom(); + } + return S_OK; } #pragma endregion @@ -553,24 +568,24 @@ STDMETHODIMP CTileProviders::get_MaxZoom(int Index, int* retVal) // ******************************************************* STDMETHODIMP CTileProviders::get_IndexByProvider(tkTileProvider provider, int* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - this->get_IndexByProviderId((int)provider, retVal); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + this->get_IndexByProviderId((int)provider, retVal); + return S_OK; } STDMETHODIMP CTileProviders::get_IndexByProviderId(int providerId, int* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *retVal = -1; - for (size_t i = 0; i < _providers.size(); i++) - { - if (providerId == _providers[i]->Id) - { - *retVal = i; - break; - } - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + *retVal = -1; + for (size_t i = 0; i < _providers.size(); i++) + { + if (providerId == _providers[i]->Id) + { + *retVal = i; + break; + } + } + return S_OK; } // ******************************************************* @@ -578,12 +593,13 @@ STDMETHODIMP CTileProviders::get_IndexByProviderId(int providerId, int* retVal) // ******************************************************* CStringW CTileProviders::get_CopyrightNotice(tkTileProvider provider) { - int index = -1; - get_IndexByProviderId((int)provider, &index); - if (index >= 0 && index < (int)_providers.size()) { - return _providers[index]->get_Copyright(); - } - return L""; + int index = -1; + get_IndexByProviderId((int)provider, &index); + if (index >= 0 && index < (int)_providers.size()) + { + return _providers[index]->get_Copyright(); + } + return L""; } // ******************************************************* @@ -591,14 +607,15 @@ CStringW CTileProviders::get_CopyrightNotice(tkTileProvider provider) // ******************************************************* CString CTileProviders::get_LicenseUrl(tkTileProvider provider) { - int index = -1; + int index = -1; - get_IndexByProviderId((int)provider, &index); + get_IndexByProviderId((int)provider, &index); - if (index >= 0 && index < (int)_providers.size()) { - return _providers[index]->get_LicenseUrl(); - } - return ""; + if (index >= 0 && index < (int)_providers.size()) + { + return _providers[index]->get_LicenseUrl(); + } + return ""; } // ******************************************************* @@ -606,30 +623,30 @@ CString CTileProviders::get_LicenseUrl(tkTileProvider provider) // ******************************************************* STDMETHODIMP CTileProviders::get_GeographicBounds(int Index, IExtents** pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *pVal = NULL; + *pVal = nullptr; - if (!ValidateProviderIndex(Index)) return S_OK; + if (!ValidateProviderIndex(Index)) return S_OK; - Extent box = _providers[Index]->get_Projection()->GetClipBounds(); + Extent box = _providers[Index]->get_Projection()->GetClipBounds(); - *pVal = ExtentsHelper::Populate(box); + *pVal = ExtentsHelper::Populate(box); - return S_OK; + return S_OK; } STDMETHODIMP CTileProviders::put_GeographicBounds(int Index, IExtents* newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + if (!ValidateProviderIndex(Index)) return S_OK; - if (!ValidateProviderIndex(Index)) return S_OK; - - Extent box(newVal); + Extent box(newVal); - _providers[Index]->get_Projection()->SetClipBounds(box); + _providers[Index]->get_Projection()->SetClipBounds(box); - return S_OK; + return S_OK; } // ******************************************************* @@ -637,11 +654,11 @@ STDMETHODIMP CTileProviders::put_GeographicBounds(int Index, IExtents* newVal) // ******************************************************* bool CTileProviders::ValidateProviderIndex(int index) { - if (index < 0 || index >= (int)_providers.size()) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - return false; - } + if (index < 0 || index >= (int)_providers.size()) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + return false; + } - return true; + return true; } diff --git a/src/COM classes/TileProviders.h b/src/COM classes/TileProviders.h index 093ef29e..067171bd 100644 --- a/src/COM classes/TileProviders.h +++ b/src/COM classes/TileProviders.h @@ -100,7 +100,7 @@ class ATL_NO_VTABLE CTileProviders : STDMETHOD(Remove)(LONG provider, VARIANT_BOOL clearCache, VARIANT_BOOL* retVal); STDMETHOD(Clear)(VARIANT_BOOL clearCache); STDMETHOD(get_Count)(LONG* pVal); - STDMETHOD(Add)(int Id, BSTR name, BSTR urlPattern, tkTileProjection projection, int minZoom, int maxZoom, VARIANT_BOOL* retVal); + STDMETHOD(Add)(int Id, BSTR name, BSTR urlPattern, tkTileProjection projection, int minZoom, int maxZoom, BSTR copyright, VARIANT_BOOL* retVal); STDMETHOD(get_Id)(int Index, LONG* retVal); STDMETHOD(get_Name)(int Index, BSTR* retVal); diff --git a/src/COM classes/Tiles.cpp b/src/COM classes/Tiles.cpp index 1f66d482..569c93ec 100644 --- a/src/COM classes/Tiles.cpp +++ b/src/COM classes/Tiles.cpp @@ -1,5 +1,5 @@ /************************************************************************************** - * File name: Tiles.h + * File name: Tiles.cpp * * Project: MapWindow Open Source (MapWinGis ActiveX control) * Description: implementation of CTiles @@ -21,8 +21,9 @@ ************************************************************************************** * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ - -#include "stdafx.h" +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper + +#include "StdAfx.h" #include "Tiles.h" #include "DiskCache.h" #include "TileHelper.h" @@ -34,9 +35,9 @@ // ************************************************************ // get_Provider() // ************************************************************ -BaseProvider* CTiles::get_Provider(int providerId) +BaseProvider* CTiles::get_Provider(int providerId) const { - return ((CTileProviders*)_providers)->get_Provider(providerId); + return ((CTileProviders*)_providers)->get_Provider(providerId); } // ************************************************************ @@ -44,70 +45,70 @@ BaseProvider* CTiles::get_Provider(int providerId) // ************************************************************ bool CTiles::get_ReloadNeeded() { - bool val = _reloadNeeded; - _reloadNeeded = false; - return val; + const bool val = _reloadNeeded; + _reloadNeeded = false; + return val; } // ************************************************************ // Stop() // ************************************************************ -void CTiles::Stop() +void CTiles::Stop() { - _manager.get_Loader()->Stop(); + _manager.get_Loader()->Stop(); - put_Visible(VARIANT_FALSE); // will prevent reloading tiles after remove all layers in map destructor + put_Visible(VARIANT_FALSE); // will prevent reloading tiles after remove all layers in map destructor } // ************************************************************ // get_GlobalCallback() // ************************************************************ -STDMETHODIMP CTiles::get_GlobalCallback(ICallback **pVal) +STDMETHODIMP CTiles::get_GlobalCallback(ICallback** pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) + AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *pVal = _globalCallback; + *pVal = _globalCallback; - if( _globalCallback != NULL ) - _globalCallback->AddRef(); + if (_globalCallback != nullptr) + _globalCallback->AddRef(); - return S_OK; + return S_OK; } // ************************************************************ // put_GlobalCallback() // ************************************************************ -STDMETHODIMP CTiles::put_GlobalCallback(ICallback *newVal) +STDMETHODIMP CTiles::put_GlobalCallback(ICallback* newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - ComHelper::SetRef(newVal, (IDispatch**)&_globalCallback); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + ComHelper::SetRef(newVal, (IDispatch**)&_globalCallback); + return S_OK; } // ***************************************************************** // get_ErrorMsg() // ***************************************************************** -STDMETHODIMP CTiles::get_ErrorMsg(long ErrorCode, BSTR *pVal) +STDMETHODIMP CTiles::get_ErrorMsg(long ErrorCode, BSTR* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) + AFX_MANAGE_STATE(AfxGetStaticModuleState()) - USES_CONVERSION; - *pVal = A2BSTR(ErrorMsg(ErrorCode)); + USES_CONVERSION; + *pVal = A2BSTR(ErrorMsg(ErrorCode)); - return S_OK; + return S_OK; } // ************************************************************ // get_LastErrorCode() // ************************************************************ -STDMETHODIMP CTiles::get_LastErrorCode(long *pVal) +STDMETHODIMP CTiles::get_LastErrorCode(long* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) + AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *pVal = _lastErrorCode; - _lastErrorCode = tkNO_ERROR; + *pVal = _lastErrorCode; + _lastErrorCode = tkNO_ERROR; - return S_OK; + return S_OK; } // ************************************************************** @@ -115,78 +116,80 @@ STDMETHODIMP CTiles::get_LastErrorCode(long *pVal) // ************************************************************** void CTiles::ErrorMessage(long ErrorCode) { - _lastErrorCode = ErrorCode; - CallbackHelper::ErrorMsg("Tiles", _globalCallback, _key, ErrorMsg(_lastErrorCode)); + _lastErrorCode = ErrorCode; + CallbackHelper::ErrorMsg("Tiles", _globalCallback, _key, ErrorMsg(_lastErrorCode)); } // ************************************************************ // get/put_Key() // ************************************************************ -STDMETHODIMP CTiles::get_Key(BSTR *pVal) +STDMETHODIMP CTiles::get_Key(BSTR* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) + AFX_MANAGE_STATE(AfxGetStaticModuleState()) - USES_CONVERSION; - *pVal = OLE2BSTR(_key); + USES_CONVERSION; + *pVal = OLE2BSTR(_key); - return S_OK; + return S_OK; } + STDMETHODIMP CTiles::put_Key(BSTR newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) + AFX_MANAGE_STATE(AfxGetStaticModuleState()) - ::SysFreeString(_key); - _key = OLE2BSTR(newVal); + SysFreeString(_key); + _key = OLE2BSTR(newVal); - return S_OK; + return S_OK; } // ********************************************************* // SleepBeforeRequestTimeout() // ********************************************************* -STDMETHODIMP CTiles::get_DelayRequestTimeout(long *pVal) +STDMETHODIMP CTiles::get_DelayRequestTimeout(long* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) + AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *pVal = _manager.get_Loader()->get_DelayRequestTimeout(); + *pVal = _manager.get_Loader()->get_DelayRequestTimeout(); - return S_OK; + return S_OK; } + STDMETHODIMP CTiles::put_DelayRequestTimeout(long newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) + AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if (newVal > 10000) newVal = 10000; - if (newVal < 0) newVal = 0; + if (newVal > 10000) newVal = 10000; + if (newVal < 0) newVal = 0; - _manager.get_Loader()->set_DelayRequestTimeout(newVal); + _manager.get_Loader()->set_DelayRequestTimeout(newVal); - return S_OK; + return S_OK; } // ********************************************************* // ScalingRatio() // ********************************************************* -STDMETHODIMP CTiles::get_ScalingRatio(double *pVal) +STDMETHODIMP CTiles::get_ScalingRatio(double* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *pVal = _manager.scalingRatio();; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + *pVal = _manager.scalingRatio();; + return S_OK; } STDMETHODIMP CTiles::put_ScalingRatio(double newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) + AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if (newVal < 0.5 || newVal > 4.0) - { - ErrorMessage(tkINVALID_PARAMETER_VALUE); - return S_OK; - } + if (newVal < 0.5 || newVal > 4.0) + { + ErrorMessage(tkINVALID_PARAMETER_VALUE); + return S_OK; + } - _manager.scalingRatio(newVal); + _manager.scalingRatio(newVal); - return S_OK; + return S_OK; } // ********************************************************* @@ -194,10 +197,10 @@ STDMETHODIMP CTiles::put_ScalingRatio(double newVal) // ********************************************************* STDMETHODIMP CTiles::AutodetectProxy(VARIANT_BOOL* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - - *retVal = HttpProxyHelper::AutodetectProxy(); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + *retVal = HttpProxyHelper::AutodetectProxy(); + return S_OK; } // ********************************************************* @@ -205,10 +208,10 @@ STDMETHODIMP CTiles::AutodetectProxy(VARIANT_BOOL* retVal) // ********************************************************* STDMETHODIMP CTiles::SetProxy(BSTR address, int port, VARIANT_BOOL* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - USES_CONVERSION; - *retVal = HttpProxyHelper::SetProxy(OLE2A(address), port); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + USES_CONVERSION; + *retVal = HttpProxyHelper::SetProxy(OLE2A(address), port); + return S_OK; } // ********************************************************* @@ -216,10 +219,10 @@ STDMETHODIMP CTiles::SetProxy(BSTR address, int port, VARIANT_BOOL* retVal) // ********************************************************* STDMETHODIMP CTiles::SetProxyAuthentication(BSTR username, BSTR password, BSTR domain, VARIANT_BOOL* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - USES_CONVERSION; - *retVal = _provider->SetAuthorization(username, password, domain) ? VARIANT_TRUE : VARIANT_FALSE; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + USES_CONVERSION; + *retVal = BaseProvider::SetAuthorization(username, password, domain) ? VARIANT_TRUE : VARIANT_FALSE; + return S_OK; } // ********************************************************* @@ -227,9 +230,9 @@ STDMETHODIMP CTiles::SetProxyAuthentication(BSTR username, BSTR password, BSTR d // ********************************************************* STDMETHODIMP CTiles::ClearProxyAuthorization() { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - _provider->ClearAuthorization(); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + BaseProvider::ClearAuthorization(); + return S_OK; } // ********************************************************* @@ -237,24 +240,23 @@ STDMETHODIMP CTiles::ClearProxyAuthorization() // ********************************************************* STDMETHODIMP CTiles::get_Proxy(BSTR* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - USES_CONVERSION; - CString s; - s = HttpProxyHelper::m_proxyAddress; - if (s.GetLength() == 0) - { - *retVal = A2BSTR(""); - } - else - { - CString format = s + ":%d"; - short num = HttpProxyHelper::m_proxyPort; - s.Format(format, num); - *retVal = A2BSTR(s); - } + USES_CONVERSION; + CString s = HttpProxyHelper::m_proxyAddress; + if (s.GetLength() == 0) + { + *retVal = A2BSTR(""); + } + else + { + const CString format = s + ":%d"; + const short num = HttpProxyHelper::m_proxyPort; + s.Format(format, num); + *retVal = A2BSTR(s); + } - return S_OK; + return S_OK; } // ************************************************************ @@ -262,16 +264,17 @@ STDMETHODIMP CTiles::get_Proxy(BSTR* retVal) // ************************************************************ STDMETHODIMP CTiles::get_CurrentZoom(int* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retVal = -1; + *retVal = -1; - IMapViewCallback* map = _manager.get_MapCallback(); - if (map) { - *retVal = map->_ChooseZoom(_provider, _manager.scalingRatio(), false); - } + IMapViewCallback* map = _manager.get_MapCallback(); + if (map) + { + *retVal = map->_ChooseZoom(_provider, _manager.scalingRatio(), false); + } - return S_OK; + return S_OK; } // ********************************************************* @@ -280,28 +283,29 @@ STDMETHODIMP CTiles::get_CurrentZoom(int* retVal) // checks whether all the tiles are present in cache bool CTiles::TilesAreInCache(IMapViewCallback* map, tkTileProvider providerId) { - BaseProvider* provider = providerId == ProviderNone ? _provider : get_Provider(providerId); + BaseProvider* provider = providerId == ProviderNone ? _provider : get_Provider(providerId); - if (!_visible || !provider) { - // there is no valid provider, so no need to schedule download - return true; - } + if (!_visible || !provider) + { + // there is no valid provider, so no need to schedule download + return true; + } - return _manager.TilesAreInCache(provider); + return _manager.TilesAreInCache(provider); } // ********************************************************* // LoadTiles // ********************************************************* -void CTiles::LoadTiles(bool isSnapshot, CString key) +void CTiles::LoadTiles(bool isSnapshot, const CString& key) { - if (!_visible) - { - _manager.Clear(); - return; - } + if (!_visible) + { + _manager.Clear(); + return; + } - _manager.LoadTiles(_provider, isSnapshot, key); + _manager.LoadTiles(_provider, isSnapshot, key); } // ********************************************************* @@ -309,28 +313,29 @@ void CTiles::LoadTiles(bool isSnapshot, CString key) // ********************************************************* STDMETHODIMP CTiles::get_Provider(tkTileProvider* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - CustomTileProvider* p = dynamic_cast(_provider); - *pVal = p ? tkTileProvider::ProviderCustom : (tkTileProvider)_provider->Id; + auto* p = dynamic_cast(_provider); + *pVal = p ? tkTileProvider::ProviderCustom : (tkTileProvider)_provider->Id; - return S_OK; + return S_OK; } STDMETHODIMP CTiles::put_Provider(tkTileProvider newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - if (newVal < 0 || newVal >= tkTileProvider::ProviderCustom) { - return S_OK; - } + if (newVal < 0 || newVal >= tkTileProvider::ProviderCustom) + { + return S_OK; + } - if (_provider->Id != newVal && newVal != tkTileProvider::ProviderCustom) - { - put_ProviderId((int)newVal); - } + if (_provider->Id != newVal && newVal != tkTileProvider::ProviderCustom) + { + put_ProviderId((int)newVal); + } - return S_OK; + return S_OK; } // ********************************************************* @@ -338,12 +343,12 @@ STDMETHODIMP CTiles::put_Provider(tkTileProvider newVal) // ********************************************************* STDMETHODIMP CTiles::get_ProviderName(BSTR* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - USES_CONVERSION; - *retVal = _provider ? A2BSTR(_provider->Name) : A2BSTR(""); + USES_CONVERSION; + *retVal = _provider ? A2BSTR(_provider->Name) : A2BSTR(""); - return S_OK; + return S_OK; } // ********************************************************* @@ -351,15 +356,16 @@ STDMETHODIMP CTiles::get_ProviderName(BSTR* retVal) // ********************************************************* STDMETHODIMP CTiles::get_GridLinesVisible(VARIANT_BOOL* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *pVal = _manager.get_GridLinesVisible() ? VARIANT_TRUE : VARIANT_TRUE; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *pVal = _manager.get_GridLinesVisible() ? VARIANT_TRUE : VARIANT_TRUE; + return S_OK; } + STDMETHODIMP CTiles::put_GridLinesVisible(VARIANT_BOOL newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - _manager.set_GridLinesVisible(newVal ? true : false); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + _manager.set_GridLinesVisible(newVal ? true : false); + return S_OK; } // ********************************************************* @@ -367,15 +373,16 @@ STDMETHODIMP CTiles::put_GridLinesVisible(VARIANT_BOOL newVal) // ********************************************************* STDMETHODIMP CTiles::get_MinScaleToCache(int* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *pVal = _minScaleToCache; // TODO: use in caching process - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *pVal = _minScaleToCache; // TODO: use in caching process + return S_OK; } + STDMETHODIMP CTiles::put_MinScaleToCache(int newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - _minScaleToCache = newVal; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + _minScaleToCache = newVal; + return S_OK; } // ********************************************************* @@ -383,15 +390,16 @@ STDMETHODIMP CTiles::put_MinScaleToCache(int newVal) // ********************************************************* STDMETHODIMP CTiles::get_MaxScaleToCache(int* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *pVal = _maxScaleToCache; // TODO: use in caching process - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *pVal = _maxScaleToCache; // TODO: use in caching process + return S_OK; } + STDMETHODIMP CTiles::put_MaxScaleToCache(int newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - _maxScaleToCache = newVal; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + _maxScaleToCache = newVal; + return S_OK; } // ********************************************************* @@ -399,16 +407,16 @@ STDMETHODIMP CTiles::put_MaxScaleToCache(int newVal) // ********************************************************* STDMETHODIMP CTiles::get_Visible(VARIANT_BOOL* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *pVal = _visible; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *pVal = _visible; + return S_OK; } STDMETHODIMP CTiles::put_Visible(VARIANT_BOOL newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - _visible = newVal != 0; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + _visible = newVal != 0; + return S_OK; } // ********************************************************* @@ -416,11 +424,11 @@ STDMETHODIMP CTiles::put_Visible(VARIANT_BOOL newVal) // ********************************************************* STDMETHODIMP CTiles::get_Providers(ITileProviders** retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - if (_providers) - _providers->AddRef(); - *retVal = _providers; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + if (_providers) + _providers->AddRef(); + *retVal = _providers; + return S_OK; } // ********************************************************* @@ -428,20 +436,20 @@ STDMETHODIMP CTiles::get_Providers(ITileProviders** retVal) // ********************************************************* STDMETHODIMP CTiles::get_DoCaching(tkCacheType type, VARIANT_BOOL* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *pVal = _manager.get_Cache(type)->doCaching ? VARIANT_TRUE : VARIANT_FALSE; - - return S_OK; + *pVal = _manager.get_Cache(type)->doCaching ? VARIANT_TRUE : VARIANT_FALSE; + + return S_OK; } STDMETHODIMP CTiles::put_DoCaching(tkCacheType type, VARIANT_BOOL newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - _manager.get_Cache(type)->doCaching = newVal ? true : false; + _manager.get_Cache(type)->doCaching = newVal ? true : false; - return S_OK; + return S_OK; } // ********************************************************* @@ -449,19 +457,20 @@ STDMETHODIMP CTiles::put_DoCaching(tkCacheType type, VARIANT_BOOL newVal) // ********************************************************* STDMETHODIMP CTiles::get_UseCache(tkCacheType type, VARIANT_BOOL* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + *pVal = _manager.get_Cache(type)->useCache ? VARIANT_TRUE : VARIANT_FALSE; - *pVal = _manager.get_Cache(type)->useCache ? VARIANT_TRUE : VARIANT_FALSE; - - return S_OK; + return S_OK; } + STDMETHODIMP CTiles::put_UseCache(tkCacheType type, VARIANT_BOOL newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - _manager.get_Cache(type)->useCache = newVal ? true : false; + _manager.get_Cache(type)->useCache = newVal ? true : false; - return S_OK; + return S_OK; } // ********************************************************* @@ -469,16 +478,16 @@ STDMETHODIMP CTiles::put_UseCache(tkCacheType type, VARIANT_BOOL newVal) // ********************************************************* STDMETHODIMP CTiles::get_UseServer(VARIANT_BOOL* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *pVal = _manager.useServer(); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *pVal = _manager.useServer(); + return S_OK; } STDMETHODIMP CTiles::put_UseServer(VARIANT_BOOL newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - _manager.useServer(newVal != 0); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + _manager.useServer(newVal != 0); + return S_OK; } // ********************************************************* @@ -486,12 +495,12 @@ STDMETHODIMP CTiles::put_UseServer(VARIANT_BOOL newVal) // ********************************************************* STDMETHODIMP CTiles::get_DiskCacheFilename(BSTR* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + USES_CONVERSION; + *retVal = W2BSTR(_manager.get_DiskCache()->cache->get_Filename()); - USES_CONVERSION; - *retVal = W2BSTR(_manager.get_DiskCache()->cache->get_Filename()); - - return S_OK; + return S_OK; } // ********************************************************* @@ -499,12 +508,12 @@ STDMETHODIMP CTiles::get_DiskCacheFilename(BSTR* retVal) // ********************************************************* STDMETHODIMP CTiles::put_DiskCacheFilename(BSTR pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - USES_CONVERSION; - _manager.get_DiskCache()->cache->set_Filename(OLE2W(pVal)); + USES_CONVERSION; + _manager.get_DiskCache()->cache->set_Filename(OLE2W(pVal)); - return S_OK; + return S_OK; } // ********************************************************* @@ -512,25 +521,25 @@ STDMETHODIMP CTiles::put_DiskCacheFilename(BSTR pVal) // ********************************************************* STDMETHODIMP CTiles::get_MaxCacheSize(tkCacheType type, double* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - ITileCache* cache = TileCacheManager::get_Cache((CacheType)type); - *pVal = cache ? cache->get_MaxSize() : 0.0; + ITileCache* cache = TileCacheManager::get_Cache((CacheType)type); + *pVal = cache ? cache->get_MaxSize() : 0.0; - return S_OK; + return S_OK; } STDMETHODIMP CTiles::put_MaxCacheSize(tkCacheType type, double newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + ITileCache* cache = TileCacheManager::get_Cache((CacheType)type); + if (cache) + { + cache->set_MaxSize(newVal); + } - ITileCache* cache = TileCacheManager::get_Cache((CacheType)type); - if (cache) - { - cache->set_MaxSize(newVal); - } - - return S_OK; + return S_OK; } // ********************************************************* @@ -538,15 +547,15 @@ STDMETHODIMP CTiles::put_MaxCacheSize(tkCacheType type, double newVal) // ********************************************************* STDMETHODIMP CTiles::ClearCache(tkCacheType type) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - ITileCache* cache = TileCacheManager::get_Cache((CacheType)type); - if (cache) - { - cache->Clear(tkTileProvider::ProviderNone, 0, 100); - } - - return S_OK; + ITileCache* cache = TileCacheManager::get_Cache((CacheType)type); + if (cache) + { + cache->Clear(tkTileProvider::ProviderNone, 0, 100); + } + + return S_OK; } // ********************************************************* @@ -554,15 +563,15 @@ STDMETHODIMP CTiles::ClearCache(tkCacheType type) // ********************************************************* STDMETHODIMP CTiles::ClearCache2(tkCacheType type, int providerId, int fromScale, int toScale) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + ITileCache* cache = TileCacheManager::get_Cache((CacheType)type); + if (cache) + { + cache->Clear(providerId, fromScale, toScale); + } - ITileCache* cache = TileCacheManager::get_Cache((CacheType)type); - if (cache) - { - cache->Clear(providerId, fromScale, toScale); - } - - return S_OK; + return S_OK; } // ********************************************************* @@ -570,17 +579,17 @@ STDMETHODIMP CTiles::ClearCache2(tkCacheType type, int providerId, int fromScale // ********************************************************* STDMETHODIMP CTiles::get_CacheSize(tkCacheType type, double* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retVal = 0.0; + *retVal = 0.0; - ITileCache* cache = TileCacheManager::get_Cache((CacheType)type); - if (cache) - { - *retVal = cache->get_SizeMB(); - } - - return S_OK; + ITileCache* cache = TileCacheManager::get_Cache((CacheType)type); + if (cache) + { + *retVal = cache->get_SizeMB(); + } + + return S_OK; } // ********************************************************* @@ -588,11 +597,11 @@ STDMETHODIMP CTiles::get_CacheSize(tkCacheType type, double* retVal) // ********************************************************* STDMETHODIMP CTiles::get_CacheSize2(tkCacheType type, int providerId, int scale, double* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + *retVal = _manager.get_Cache(type)->cache->get_SizeMB(providerId, scale); - *retVal = _manager.get_Cache(type)->cache->get_SizeMB(providerId, scale); - - return S_OK; + return S_OK; } // ******************************************************** @@ -600,90 +609,95 @@ STDMETHODIMP CTiles::get_CacheSize2(tkCacheType type, int providerId, int scale, // ******************************************************** STDMETHODIMP CTiles::Serialize(BSTR* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - CPLXMLNode* psTree = this->SerializeCore("TilesClass"); - Utility::SerializeAndDestroyXmlTree(psTree, retVal); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + CPLXMLNode* psTree = this->SerializeCore("TilesClass"); + Utility::SerializeAndDestroyXmlTree(psTree, retVal); + return S_OK; } // ******************************************************** // SerializeCore() // ******************************************************** -CPLXMLNode* CTiles::SerializeCore(CString ElementName) -{ - USES_CONVERSION; - CPLXMLNode* psTree = CPLCreateXMLNode( NULL, CXT_Element, ElementName); - - if (!_visible) - Utility::CPLCreateXMLAttributeAndValue(psTree, "Visible", CPLString().Printf("%d", (int)_visible)); - - - if (_manager.get_GridLinesVisible()) - Utility::CPLCreateXMLAttributeAndValue(psTree, "GridLinesVisible", CPLString().Printf("%d", (int)_manager.get_GridLinesVisible())); - - if (_provider->Id != 0) - Utility::CPLCreateXMLAttributeAndValue(psTree, "Provider", CPLString().Printf("%d", (int)_provider->Id)); - - bool value = _manager.get_RamCache()->doCaching; - if (!value) - Utility::CPLCreateXMLAttributeAndValue(psTree, "DoRamCaching", CPLString().Printf("%d", (int)value)); - - value = _manager.get_DiskCache()->doCaching; - if (value) - Utility::CPLCreateXMLAttributeAndValue(psTree, "DoDiskCaching", CPLString().Printf("%d", (int)value)); - - value = _manager.get_RamCache()->useCache; - if (!value) - Utility::CPLCreateXMLAttributeAndValue(psTree, "UseRamCache", CPLString().Printf("%d", (int)value)); - - value = _manager.get_DiskCache()->useCache; - if (!value) - Utility::CPLCreateXMLAttributeAndValue(psTree, "UseDiskCache", CPLString().Printf("%d", (int)value)); - - if (!_manager.useServer()) - Utility::CPLCreateXMLAttributeAndValue(psTree, "UseServer", CPLString().Printf("%d", (int)_manager.useServer())); - - double dbl = _manager.get_RamCache()->cache->get_MaxSize(); - if (dbl != 100.0) - Utility::CPLCreateXMLAttributeAndValue(psTree, "MaxRamCacheSize", CPLString().Printf("%f", dbl)); - - dbl = _manager.get_DiskCache()->cache->get_MaxSize(); - if (dbl != 100.0) - Utility::CPLCreateXMLAttributeAndValue(psTree, "MaxDiskCacheSize", CPLString().Printf("%f", dbl)); - - if (_minScaleToCache != 0) - Utility::CPLCreateXMLAttributeAndValue(psTree, "MinScaleToCache", CPLString().Printf("%d", _minScaleToCache)); - - if (_maxScaleToCache != 100) - Utility::CPLCreateXMLAttributeAndValue(psTree, "MaxScaleToCache", CPLString().Printf("%d", _maxScaleToCache)); - - CStringW dbName = _manager.get_DiskCache()->cache->get_Filename(); - if (dbName.GetLength() != 0) - Utility::CPLCreateXMLAttributeAndValue(psTree, "DiskCacheFilename", Utility::ConvertToUtf8(dbName)); - - // serialization of custom providers - CPLXMLNode* psProviders = CPLCreateXMLNode( NULL, CXT_Element, "TileProviders"); - if (psProviders) - { - vector* providers = ((CTileProviders*)_providers)->GetList(); - for(size_t i = 0; i < providers->size(); i++) - { - CustomTileProvider* cp = dynamic_cast(providers->at(i)); - if (cp) - { - CPLXMLNode* psCustom = CPLCreateXMLNode( NULL, CXT_Element, "TileProvider"); - Utility::CPLCreateXMLAttributeAndValue(psCustom, "Id", CPLString().Printf("%d", cp->Id)); - Utility::CPLCreateXMLAttributeAndValue(psCustom, "Name", cp->Name); - Utility::CPLCreateXMLAttributeAndValue(psCustom, "Url", cp->get_UrlFormat()); - Utility::CPLCreateXMLAttributeAndValue(psCustom, "Projection", CPLString().Printf("%d", (int)cp->get_Projection())); - Utility::CPLCreateXMLAttributeAndValue(psCustom, "MinZoom", CPLString().Printf("%d", cp->get_MinZoom())); - Utility::CPLCreateXMLAttributeAndValue(psCustom, "MaxZoom", CPLString().Printf("%d", cp->get_MaxZoom())); - CPLAddXMLChild(psProviders, psCustom); - } - } - CPLAddXMLChild(psTree, psProviders); - } - return psTree; +CPLXMLNode* CTiles::SerializeCore(const CString& elementName) +{ + USES_CONVERSION; + CPLXMLNode* psTree = CPLCreateXMLNode(nullptr, CXT_Element, elementName); + + if (!_visible) + Utility::CPLCreateXMLAttributeAndValue(psTree, "Visible", CPLString().Printf("%d", (int)_visible)); + + if (_manager.get_GridLinesVisible()) + Utility::CPLCreateXMLAttributeAndValue(psTree, "GridLinesVisible", + CPLString().Printf("%d", (int)_manager.get_GridLinesVisible())); + + if (_provider->Id != 0) + Utility::CPLCreateXMLAttributeAndValue(psTree, "Provider", CPLString().Printf("%d", (int)_provider->Id)); + + bool value = _manager.get_RamCache()->doCaching; + if (!value) + Utility::CPLCreateXMLAttributeAndValue(psTree, "DoRamCaching", CPLString().Printf("%d", (int)value)); + + value = _manager.get_DiskCache()->doCaching; + if (value) + Utility::CPLCreateXMLAttributeAndValue(psTree, "DoDiskCaching", CPLString().Printf("%d", (int)value)); + + value = _manager.get_RamCache()->useCache; + if (!value) + Utility::CPLCreateXMLAttributeAndValue(psTree, "UseRamCache", CPLString().Printf("%d", (int)value)); + + value = _manager.get_DiskCache()->useCache; + if (!value) + Utility::CPLCreateXMLAttributeAndValue(psTree, "UseDiskCache", CPLString().Printf("%d", (int)value)); + + if (!_manager.useServer()) + Utility::CPLCreateXMLAttributeAndValue(psTree, "UseServer", + CPLString().Printf("%d", (int)_manager.useServer())); + + double dbl = _manager.get_RamCache()->cache->get_MaxSize(); + if (dbl != 100.0) + Utility::CPLCreateXMLAttributeAndValue(psTree, "MaxRamCacheSize", CPLString().Printf("%f", dbl)); + + dbl = _manager.get_DiskCache()->cache->get_MaxSize(); + if (dbl != 100.0) + Utility::CPLCreateXMLAttributeAndValue(psTree, "MaxDiskCacheSize", CPLString().Printf("%f", dbl)); + + if (_minScaleToCache != 0) + Utility::CPLCreateXMLAttributeAndValue(psTree, "MinScaleToCache", CPLString().Printf("%d", _minScaleToCache)); + + if (_maxScaleToCache != 100) + Utility::CPLCreateXMLAttributeAndValue(psTree, "MaxScaleToCache", CPLString().Printf("%d", _maxScaleToCache)); + + CStringW dbName = _manager.get_DiskCache()->cache->get_Filename(); + if (dbName.GetLength() != 0) + Utility::CPLCreateXMLAttributeAndValue(psTree, "DiskCacheFilename", Utility::ConvertToUtf8(dbName)); + + // serialization of custom providers + CPLXMLNode* psProviders = CPLCreateXMLNode(nullptr, CXT_Element, "TileProviders"); + if (psProviders) + { + vector* providers = ((CTileProviders*)_providers)->GetList(); + for (size_t i = 0; i < providers->size(); i++) + { + CustomTileProvider* cp = dynamic_cast(providers->at(i)); + if (cp) + { + CPLXMLNode* psCustom = CPLCreateXMLNode(nullptr, CXT_Element, "TileProvider"); + Utility::CPLCreateXMLAttributeAndValue(psCustom, "Id", CPLString().Printf("%d", cp->Id)); + Utility::CPLCreateXMLAttributeAndValue(psCustom, "Name", cp->Name); + Utility::CPLCreateXMLAttributeAndValue(psCustom, "Url", cp->get_UrlFormat()); + Utility::CPLCreateXMLAttributeAndValue(psCustom, "Projection", + CPLString().Printf("%d", (int)cp->get_Projection())); + Utility::CPLCreateXMLAttributeAndValue(psCustom, "MinZoom", + CPLString().Printf("%d", cp->get_MinZoom())); + Utility::CPLCreateXMLAttributeAndValue(psCustom, "MaxZoom", + CPLString().Printf("%d", cp->get_MaxZoom())); + Utility::CPLCreateXMLAttributeAndValue(psCustom, "Copyright", cp->get_Copyright()); + CPLAddXMLChild(psProviders, psCustom); + } + } + CPLAddXMLChild(psTree, psProviders); + } + return psTree; } // ******************************************************** @@ -691,39 +705,39 @@ CPLXMLNode* CTiles::SerializeCore(CString ElementName) // ******************************************************** STDMETHODIMP CTiles::Deserialize(BSTR newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - USES_CONVERSION; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + USES_CONVERSION; - CString s = OLE2CA(newVal); - CPLXMLNode* node = CPLParseXMLString(s.GetString()); - if (node) - { - CPLXMLNode* nodeTiles = CPLGetXMLNode(node, "=TilesClass"); - if (nodeTiles) - { - DeserializeCore(nodeTiles); - } - CPLDestroyXMLNode(node); - } - return S_OK; + CString s = OLE2CA(newVal); + CPLXMLNode* node = CPLParseXMLString(s.GetString()); + if (node) + { + CPLXMLNode* nodeTiles = CPLGetXMLNode(node, "=TilesClass"); + if (nodeTiles) + { + DeserializeCore(nodeTiles); + } + CPLDestroyXMLNode(node); + } + return S_OK; } -void setBoolean(CPLXMLNode *node, CString name, bool& value) +void setBoolean(CPLXMLNode* node, const CString& name, bool& value) { - CString s = CPLGetXMLValue( node, name, NULL ); - if (s != "") value = atoi( s ) == 0? false : true; + const CString s = CPLGetXMLValue(node, name, nullptr); + if (s != "") value = atoi(s) == 0 ? false : true; } -void setInteger(CPLXMLNode *node, CString name, int& value) +void setInteger(CPLXMLNode* node, const CString& name, int& value) { - CString s = CPLGetXMLValue( node, name, NULL ); - if (s != "") value = atoi( s ); + const CString s = CPLGetXMLValue(node, name, nullptr); + if (s != "") value = atoi(s); } -void setDouble(CPLXMLNode *node, CString name, double& value) +void setDouble(CPLXMLNode* node, const CString& name, double& value) { - CString s = CPLGetXMLValue( node, name, NULL ); - if (s != "") value = Utility::atof_custom( s ); + const CString s = CPLGetXMLValue(node, name, nullptr); + if (s != "") value = Utility::atof_custom(s); } // ******************************************************** @@ -731,95 +745,101 @@ void setDouble(CPLXMLNode *node, CString name, double& value) // ******************************************************** bool CTiles::DeserializeCore(CPLXMLNode* node) { - if (!node) - return false; + if (!node) + return false; + + SetDefaults(); + + setBoolean(node, "Visible", _visible); - SetDefaults(); + bool temp; + setBoolean(node, "GridLinesVisible", temp); + _manager.set_GridLinesVisible(temp); - setBoolean(node, "Visible", _visible); + setBoolean(node, "DoRamCaching", temp); + _manager.get_RamCache()->doCaching = temp; - bool temp; - setBoolean(node, "GridLinesVisible", temp); - _manager.set_GridLinesVisible(temp); - - setBoolean(node, "DoRamCaching", temp); - _manager.get_RamCache()->doCaching = temp; + setBoolean(node, "DoDiskCaching", temp); + _manager.get_DiskCache()->doCaching = temp; - setBoolean(node, "DoDiskCaching", temp); - _manager.get_DiskCache()->doCaching = temp; + setBoolean(node, "UseRamCache", temp); + _manager.get_RamCache()->useCache = temp; - setBoolean(node, "UseRamCache", temp); - _manager.get_RamCache()->useCache = temp; + setBoolean(node, "UseDiskCache", temp); + _manager.get_DiskCache()->useCache = temp; - setBoolean(node, "UseDiskCache", temp); - _manager.get_DiskCache()->useCache = temp; + setBoolean(node, "UseServer", temp); + _manager.useServer(temp); - setBoolean(node, "UseServer", temp); - _manager.useServer(temp); + CString s = CPLGetXMLValue(node, "Provider", nullptr); + if (s != "") this->put_ProviderId(atoi(s)); - CString s = CPLGetXMLValue( node, "Provider", NULL ); - if (s != "") this->put_ProviderId(atoi( s )); + double dbl; + setDouble(node, "MaxRamCacheSize", dbl); + _manager.get_RamCache()->cache->set_MaxSize(dbl); - double dbl; - setDouble(node, "MaxRamCacheSize", dbl); - _manager.get_RamCache()->cache->set_MaxSize(dbl); + setDouble(node, "MaxDiskCacheSize", dbl); + _manager.get_DiskCache()->cache->set_MaxSize(dbl); - setDouble(node, "MaxDiskCacheSize", dbl); - _manager.get_DiskCache()->cache->set_MaxSize(dbl); + setInteger(node, "MinScaleToCache", _minScaleToCache); + setInteger(node, "MaxScaleToCache", _maxScaleToCache); - setInteger(node, "MinScaleToCache", _minScaleToCache); - setInteger(node, "MaxScaleToCache", _maxScaleToCache); - - USES_CONVERSION; - s = CPLGetXMLValue( node, "DiskCacheFilename", NULL ); - if (s != "") { - _manager.get_DiskCache()->cache->set_Filename(Utility::ConvertFromUtf8(s)); - } + USES_CONVERSION; + s = CPLGetXMLValue(node, "DiskCacheFilename", nullptr); + if (s != "") + { + _manager.get_DiskCache()->cache->set_Filename(Utility::ConvertFromUtf8(s)); + } - // custom providers - CPLXMLNode* nodeProviders = CPLGetXMLNode(node, "TileProviders"); - if (nodeProviders) - { - // don't clear providers as it will clear the cache as well, - // if provider with certain id exists, it simply won't be added twice - vector* providers = ((CTileProviders*)_providers)->GetList(); + // custom providers + CPLXMLNode* nodeProviders = CPLGetXMLNode(node, "TileProviders"); + if (nodeProviders) + { + // don't clear providers as it will clear the cache as well, + // if provider with certain id exists, it simply won't be added twice + // Not used: vector* providers = ((CTileProviders*)_providers)->GetList(); - CPLXMLNode* nodeProvider = nodeProviders->psChild; - while (nodeProvider) - { - if (strcmp(nodeProvider->pszValue, "TileProvider") == 0) - { - int id, minZoom, maxZoom, projection; - CString url, name; - - s = CPLGetXMLValue( nodeProvider, "Id", NULL ); - if (s != "") id = atoi(s); + CPLXMLNode* nodeProvider = nodeProviders->psChild; + while (nodeProvider) + { + if (strcmp(nodeProvider->pszValue, "TileProvider") == 0) + { + int id = 0, minZoom = 0, maxZoom = 0, projection = 0; + CString url, name, copyright; - s = CPLGetXMLValue( nodeProvider, "MinZoom", NULL ); - if (s != "") minZoom = atoi(s); + s = CPLGetXMLValue(nodeProvider, "Id", nullptr); + if (s != "") id = atoi(s); - s = CPLGetXMLValue( nodeProvider, "MaxZoom", NULL ); - if (s != "") maxZoom = atoi(s); + s = CPLGetXMLValue(nodeProvider, "MinZoom", nullptr); + if (s != "") minZoom = atoi(s); - s = CPLGetXMLValue( nodeProvider, "Projection", NULL ); - if (s != "") projection = atoi(s); + s = CPLGetXMLValue(nodeProvider, "MaxZoom", nullptr); + if (s != "") maxZoom = atoi(s); - s = CPLGetXMLValue( nodeProvider, "Url", NULL ); - if (s != "") url = s; + s = CPLGetXMLValue(nodeProvider, "Projection", nullptr); + if (s != "") projection = atoi(s); - s = CPLGetXMLValue( nodeProvider, "Name", NULL ); - if (s != "") name = s; + s = CPLGetXMLValue(nodeProvider, "Url", nullptr); + if (s != "") url = s; - VARIANT_BOOL vb; - CComBSTR bstrName(name); - CComBSTR bstrUrl(url); + s = CPLGetXMLValue(nodeProvider, "Name", nullptr); + if (s != "") name = s; - _providers->Add(id, bstrName, bstrUrl, (tkTileProjection)projection, minZoom, maxZoom, &vb); - } - nodeProvider = nodeProvider->psNext; - } - } - return true; + s = CPLGetXMLValue(nodeProvider, "Copyright", nullptr); + if (s != "") copyright = s; + + VARIANT_BOOL vb; + CComBSTR bstrName(name); + CComBSTR bstrUrl(url); + CComBSTR bstrCopyright(copyright); + + _providers->Add(id, bstrName, bstrUrl, (tkTileProjection)projection, minZoom, maxZoom, bstrCopyright, + &vb); + } + nodeProvider = nodeProvider->psNext; + } + } + return true; } // ********************************************************* @@ -827,38 +847,38 @@ bool CTiles::DeserializeCore(CPLXMLNode* node) // ********************************************************* STDMETHODIMP CTiles::get_ProviderId(int* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - if (!_visible) - { - *retVal = -1; - return S_OK; - } + if (!_visible) + { + *retVal = -1; + return S_OK; + } - *retVal = _provider->Id; - return S_OK; + *retVal = _provider->Id; + return S_OK; } STDMETHODIMP CTiles::put_ProviderId(int providerId) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - bool visibleOld = _visible; - put_Visible(providerId == -1 ? VARIANT_FALSE : VARIANT_TRUE); + const bool visibleOld = _visible; + put_Visible(providerId == -1 ? VARIANT_FALSE : VARIANT_TRUE); - BaseProvider* provider = get_Provider(providerId); + BaseProvider* provider = get_Provider(providerId); - if (_provider != provider || visibleOld != _visible) - { - _reloadNeeded = true; - } + if (_provider != provider || visibleOld != _visible) + { + _reloadNeeded = true; + } - if (provider) - { - _provider = provider; - } + if (provider) + { + _provider = provider; + } - return S_OK; + return S_OK; } // ********************************************************* @@ -866,133 +886,137 @@ STDMETHODIMP CTiles::put_ProviderId(int providerId) // ********************************************************* STDMETHODIMP CTiles::GetTilesIndices(IExtents* boundsDegrees, int zoom, int providerId, IExtents** retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + *retVal = nullptr; - *retVal = NULL; + if (!boundsDegrees) + { + ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); + return S_OK; + } - if (!boundsDegrees) - { - ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); - return S_OK; - } - - double xMin, xMax, yMin, yMax, zMin, zMax; - boundsDegrees->GetBounds(&xMin, &yMin, &zMin, &xMax, &yMax, &zMax); - - BaseProvider* provider = get_Provider(providerId); - if (provider) - { - CPoint p1; - provider->get_Projection()->FromLatLngToXY(PointLatLng(yMax, xMin), zoom, p1); + double xMin, xMax, yMin, yMax, zMin, zMax; + boundsDegrees->GetBounds(&xMin, &yMin, &zMin, &xMax, &yMax, &zMax); - CPoint p2; - provider->get_Projection()->FromLatLngToXY(PointLatLng(yMin, xMax), zoom, p2); + BaseProvider* provider = get_Provider(providerId); + if (provider) + { + CPoint p1; + provider->get_Projection()->FromLatLngToXY(PointLatLng(yMax, xMin), zoom, p1); - IExtents* ext = NULL; - ComHelper::CreateExtents(&ext); - ext->SetBounds(p1.x, p1.y, 0.0, p2.x, p2.y, 0.0); - *retVal = ext; - } + CPoint p2; + provider->get_Projection()->FromLatLngToXY(PointLatLng(yMin, xMax), zoom, p2); - return S_OK; + IExtents* ext = nullptr; + ComHelper::CreateExtents(&ext); + ext->SetBounds(p1.x, p1.y, 0.0, p2.x, p2.y, 0.0); + *retVal = ext; + } + + return S_OK; } // ********************************************************* // Prefetch // ********************************************************* -STDMETHODIMP CTiles::Prefetch(double minLat, double maxLat, double minLng, double maxLng, int zoom, int providerId, - IStopExecution* stop, LONG* retVal) +STDMETHODIMP CTiles::Prefetch(double minLat, double maxLat, double minLng, double maxLng, int zoom, int providerId, + IStopExecution* stop, LONG* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + *retVal = -1; - *retVal = -1; + BaseProvider* p = get_Provider(providerId); + if (!p) + { + ErrorMessage(tkINVALID_PROVIDER_ID); + return S_OK; + } - BaseProvider* p = get_Provider(providerId); - if (!p) - { - ErrorMessage(tkINVALID_PROVIDER_ID); - return S_OK; - } - - CRect indices; - Extent ext(minLng, maxLng, minLat, maxLat); - p->get_Projection()->getTileRectXY(ext, zoom, indices); + CRect indices; + const Extent ext(minLng, maxLng, minLat, maxLat); + p->get_Projection()->getTileRectXY(ext, zoom, indices); - Prefetch2(indices.left, indices.right, indices.bottom, indices.top, zoom, providerId, stop, retVal); + Prefetch2(indices.left, indices.right, indices.bottom, indices.top, zoom, providerId, stop, retVal); - return S_OK; + return S_OK; } // ********************************************************* // Prefetch2 // ********************************************************* -STDMETHODIMP CTiles::Prefetch2(int minX, int maxX, int minY, int maxY, int zoom, int providerId, IStopExecution* stop, LONG* retVal) +STDMETHODIMP CTiles::Prefetch2(int minX, int maxX, int minY, int maxY, int zoom, int providerId, IStopExecution* stop, + LONG* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retVal = -1; + *retVal = -1; - BaseProvider* provider = get_Provider(providerId); - if (!provider) - { - ErrorMessage(tkINVALID_PROVIDER_ID); - return S_OK; - } + BaseProvider* provider = get_Provider(providerId); + if (!provider) + { + ErrorMessage(tkINVALID_PROVIDER_ID); + return S_OK; + } - PrefetchManager* manager = PrefetchManagerFactory::Create(TileCacheManager::get_Cache(tctSqliteCache)); - if (manager) - { - USES_CONVERSION; - CRect rect(minX, minY, maxX, maxY); - *retVal = manager->Prefetch(provider, rect, zoom, _globalCallback, stop); - } + PrefetchManager* manager = PrefetchManagerFactory::Create(TileCacheManager::get_Cache(tctSqliteCache)); + if (manager) + { + USES_CONVERSION; + const CRect rect(minX, minY, maxX, maxY); + *retVal = manager->Prefetch(provider, rect, zoom, _globalCallback, stop); + } - return S_OK; + return S_OK; } // ********************************************************* // PrefetchToFolder() // ********************************************************* // Writes tiles to the specified folder -STDMETHODIMP CTiles::PrefetchToFolder(IExtents* ext, int zoom, int providerId, BSTR savePath, BSTR fileExt, IStopExecution* stop, LONG* retVal) +STDMETHODIMP CTiles::PrefetchToFolder(IExtents* ext, int zoom, int providerId, BSTR savePath, BSTR fileExt, + IStopExecution* stop, LONG* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - BaseProvider* provider = get_Provider(providerId); - if (!provider) - { - ErrorMessage(tkINVALID_PROVIDER_ID); - *retVal = -1; - return S_OK; - } + BaseProvider* provider = get_Provider(providerId); + if (!provider) + { + ErrorMessage(tkINVALID_PROVIDER_ID); + *retVal = -1; + return S_OK; + } - USES_CONVERSION; - CStringW path = OLE2W(savePath); - if (path.GetLength() > 0 && path.GetAt(path.GetLength() - 1) != L'\\') - { - path += L"\\"; - } + LogBulkDownloadStarted(ext, zoom, provider, savePath, fileExt); - if (!Utility::DirExists(path)) - { - CallbackHelper::ErrorMsg("PrefetchManager", NULL, "", "Folder doesn't exist: ", W2A(path)); - return -1; - } + USES_CONVERSION; + CStringW path = OLE2W(savePath); + if (path.GetLength() > 0 && path.GetAt(path.GetLength() - 1) != L'\\') + { + path += L"\\"; + } - DiskCache* cache = new DiskCache(path, fileExt); + if (!Utility::DirExists(path)) + { + CallbackHelper::ErrorMsg("PrefetchManager", nullptr, "", "Folder doesn't exist: ", W2A(path)); + return -1; + } - PrefetchManager* manager = PrefetchManagerFactory::Create(cache); - if (manager) - { - CRect indices; - Extent extents(ext); - provider->get_Projection()->getTileRectXY(extents, zoom, indices); + DiskCache* cache = new DiskCache(path, fileExt); - USES_CONVERSION; - *retVal = manager->Prefetch(provider, indices, zoom, _globalCallback, stop); - } + PrefetchManager* manager = PrefetchManagerFactory::Create(cache); + if (manager) + { + CRect indices; + const Extent extents(ext); + provider->get_Projection()->getTileRectXY(extents, zoom, indices); - return S_OK; + USES_CONVERSION; + *retVal = manager->Prefetch(provider, indices, zoom, _globalCallback, stop); + } + + return S_OK; } // ********************************************************* @@ -1000,14 +1024,15 @@ STDMETHODIMP CTiles::PrefetchToFolder(IExtents* ext, int zoom, int providerId, B // ********************************************************* STDMETHODIMP CTiles::get_DiskCacheCount(int providerId, int zoom, int xMin, int xMax, int yMin, int yMax, LONG* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + ITileCache* cache = TileCacheManager::get_Cache(tctSqliteCache); + if (cache) + { + cache->get_TileCount(providerId, zoom, CRect(xMin, yMax, xMax, yMin)); + } - ITileCache* cache = TileCacheManager::get_Cache(tctSqliteCache); - if (cache) { - cache->get_TileCount(providerId, zoom, CRect(xMin, yMax, xMax, yMin)); - } - - return S_OK; + return S_OK; } // ********************************************************* @@ -1015,16 +1040,17 @@ STDMETHODIMP CTiles::get_DiskCacheCount(int providerId, int zoom, int xMin, int // ********************************************************* STDMETHODIMP CTiles::CheckConnection(BSTR url, VARIANT_BOOL* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - if (_provider != NULL) - { - USES_CONVERSION; - *retVal = SecureHttpClient::CheckConnection(OLE2A(url)) ? VARIANT_TRUE : VARIANT_FALSE; - } - else { - *retVal = VARIANT_FALSE; - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + if (_provider != nullptr) + { + USES_CONVERSION; + *retVal = SecureHttpClient::CheckConnection(OLE2A(url)) ? VARIANT_TRUE : VARIANT_FALSE; + } + else + { + *retVal = VARIANT_FALSE; + } + return S_OK; } // ********************************************************* @@ -1032,41 +1058,41 @@ STDMETHODIMP CTiles::CheckConnection(BSTR url, VARIANT_BOOL* retVal) // ********************************************************* STDMETHODIMP CTiles::GetTileBounds(int providerId, int zoom, int tileX, int tileY, IExtents** retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + *retVal = VARIANT_FALSE; + + BaseProvider* provider = get_Provider(providerId); + if (!provider) + { + ErrorMessage(tkINVALID_PROVIDER_ID); + return S_OK; + } - *retVal = VARIANT_FALSE; + CSize size; + provider->get_Projection()->GetTileMatrixSizeXY(zoom, size); - BaseProvider* provider = get_Provider(providerId); - if (!provider) - { - ErrorMessage(tkINVALID_PROVIDER_ID); - return S_OK; - } - - CSize size; - provider->get_Projection()->GetTileMatrixSizeXY(zoom, size); + if (tileX < 0 || tileX > size.cx - 1 || tileY < 0 || tileY > size.cy - 1) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + return S_OK; + } - if (tileX < 0 || tileX > size.cx - 1 || tileY < 0 || tileY > size.cy - 1) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - return S_OK; - } - - CPoint pnt1(tileX, tileY); - CPoint pnt2(tileX + 1, tileY + 1); - PointLatLng p1, p2; + const CPoint pnt1(tileX, tileY); + const CPoint pnt2(tileX + 1, tileY + 1); + PointLatLng p1, p2; - provider->get_Projection()->FromXYToLatLng(pnt1, zoom, p1); - provider->get_Projection()->FromXYToLatLng(pnt2, zoom, p2); + provider->get_Projection()->FromXYToLatLng(pnt1, zoom, p1); + provider->get_Projection()->FromXYToLatLng(pnt2, zoom, p2); - IExtents* ext = NULL; - ComHelper::CreateExtents(&ext); + IExtents* ext = nullptr; + ComHelper::CreateExtents(&ext); - ext->SetBounds(p1.Lng, p1.Lat, 0.0, p2.Lng, p2.Lat, 0.0); + ext->SetBounds(p1.Lng, p1.Lat, 0.0, p2.Lng, p2.Lat, 0.0); - *retVal = ext; - - return S_OK; + *retVal = ext; + + return S_OK; } // ************************************************************ @@ -1074,9 +1100,9 @@ STDMETHODIMP CTiles::GetTileBounds(int providerId, int zoom, int tileX, int tile // ************************************************************ STDMETHODIMP CTiles::get_MaxZoom(int* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retVal = _provider->get_MaxZoom(); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *retVal = _provider->get_MaxZoom(); + return S_OK; } // ************************************************************ @@ -1084,9 +1110,9 @@ STDMETHODIMP CTiles::get_MaxZoom(int* retVal) // ************************************************************ STDMETHODIMP CTiles::get_MinZoom(int* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retVal = _provider->get_MinZoom(); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *retVal = _provider->get_MinZoom(); + return S_OK; } // ************************************************************ @@ -1094,21 +1120,22 @@ STDMETHODIMP CTiles::get_MinZoom(int* retVal) // ************************************************************ STDMETHODIMP CTiles::get_ServerProjection(IGeoProjection** retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retVal= NULL; + *retVal = nullptr; - BaseProjection* p = _provider->get_Projection(); - if (p) - { - *retVal = p->get_ServerProjection(); + BaseProjection* p = _provider->get_Projection(); + if (p) + { + *retVal = p->get_ServerProjection(); - if (*retVal) { - (*retVal)->AddRef(); - } - } + if (*retVal) + { + (*retVal)->AddRef(); + } + } - return S_OK; + return S_OK; } // ************************************************************ @@ -1116,39 +1143,41 @@ STDMETHODIMP CTiles::get_ServerProjection(IGeoProjection** retVal) // ************************************************************ STDMETHODIMP CTiles::get_ProjectionStatus(tkTilesProjectionStatus* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retVal = tkTilesProjectionStatus::tpsEmptyOrInvalid; + *retVal = tkTilesProjectionStatus::tpsEmptyOrInvalid; - IMapViewCallback* map = _manager.get_MapCallback(); - if (!map) { - return S_OK; - } + IMapViewCallback* map = _manager.get_MapCallback(); + if (!map) + { + return S_OK; + } - IGeoProjection* gp = map->_GetMapProjection(); + IGeoProjection* gp = map->_GetMapProjection(); - IGeoProjection* gpServer = _provider->get_Projection()->get_ServerProjection(); + IGeoProjection* gpServer = _provider->get_Projection()->get_ServerProjection(); - if (gp && gpServer) - { - VARIANT_BOOL vb; - gp->get_IsSame(gpServer, &vb); + if (gp && gpServer) + { + VARIANT_BOOL vb; + gp->get_IsSame(gpServer, &vb); - if (vb) { - *retVal = tkTilesProjectionStatus::tpsNative; - } - else - { - gpServer->StartTransform(gp, &vb); - if (vb) - { - *retVal = tkTilesProjectionStatus::tpsCompatible; - gpServer->StopTransform(); - } - } - } + if (vb) + { + *retVal = tkTilesProjectionStatus::tpsNative; + } + else + { + gpServer->StartTransform(gp, &vb); + if (vb) + { + *retVal = tkTilesProjectionStatus::tpsCompatible; + gpServer->StopTransform(); + } + } + } - return S_OK; + return S_OK; } // ************************************************************ @@ -1156,15 +1185,16 @@ STDMETHODIMP CTiles::get_ProjectionStatus(tkTilesProjectionStatus* retVal) // ************************************************************ STDMETHODIMP CTiles::get_ProxyAuthenticationScheme(tkProxyAuthentication* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *pVal = m_globalSettings.proxyAuthentication; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *pVal = m_globalSettings.proxyAuthentication; + return S_OK; } + STDMETHODIMP CTiles::put_ProxyAuthenticationScheme(tkProxyAuthentication newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - m_globalSettings.proxyAuthentication = newVal; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + m_globalSettings.proxyAuthentication = newVal; + return S_OK; } // ************************************************************ @@ -1172,10 +1202,30 @@ STDMETHODIMP CTiles::put_ProxyAuthenticationScheme(tkProxyAuthentication newVal) // ************************************************************ STDMETHODIMP CTiles::get_ProjectionIsSphericalMercator(VARIANT_BOOL* pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *pVal = _provider->get_Projection()->IsSphericalMercator() ? VARIANT_TRUE : VARIANT_FALSE; - - return S_OK; + *pVal = _provider->get_Projection()->IsSphericalMercator() ? VARIANT_TRUE : VARIANT_FALSE; + + return S_OK; } +// ********************************************************* +// LogBulkDownloadStarted +// ********************************************************* +void CTiles::LogBulkDownloadStarted(IExtents* ext, const int zoom, BaseProvider* provider, BSTR savePath, BSTR fileExt) +{ + if (tilesLogger.IsOpened()) + { + CComBSTR extentString; + ext->ToDebugString(&extentString); + + tilesLogger.out() << "\n"; + tilesLogger.out() << "Start PrefetchToFolder:\n"; + tilesLogger.out() << "Extent " << CString(extentString) << endl; + tilesLogger.out() << "Zoom " << zoom << endl; + tilesLogger.out() << "Provider " << provider->Name << endl; + tilesLogger.out() << "SavePath " << CString(savePath) << endl; + tilesLogger.out() << "FileExt " << CString(fileExt) << endl; + tilesLogger.out() << "---------------------" << endl; + } +} diff --git a/src/COM classes/Tiles.h b/src/COM classes/Tiles.h index e6d2e8d0..665accbf 100644 --- a/src/COM classes/Tiles.h +++ b/src/COM classes/Tiles.h @@ -21,12 +21,11 @@ ************************************************************************************** * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ - +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper + #pragma once -#include "TileCore.h" #include "BaseProvider.h" #include "TileProviders.h" -#include "WmsCustomProvider.h" #include "TileManager.h" #include "SQLiteCache.h" #include "PrefetchManager.h" @@ -37,180 +36,184 @@ using namespace std; #endif class ATL_NO_VTABLE CTiles : - public CComObjectRootEx, - public CComCoClass, - public IDispatchImpl + public CComObjectRootEx, + public CComCoClass, + public IDispatchImpl { public: - CTiles() - : _manager(true), _mercatorProjection(NULL), _reloadNeeded(true) - { - _pUnkMarshaler = NULL; - _key = SysAllocString(L""); - _globalCallback = NULL; - _lastErrorCode = tkNO_ERROR; - - ComHelper::CreateInstance(idTileProviders, (IDispatch**)&_providers); - ((CTileProviders*)_providers)->put_Tiles(this); - - SetDefaults(); - - gReferenceCounter.AddRef(tkInterface::idTiles); - } - - ~CTiles() - { - SysFreeString(_key); - - ClearAll(); - - if (_mercatorProjection) { - _mercatorProjection->Release(); - } - - gReferenceCounter.Release(tkInterface::idTiles); - } - - void ClearAll() - { - Stop(); - - _manager.Clear(); - - if (_providers) - { - _providers->Release(); - _providers = NULL; - } - } - - void SetDefaults() - { - _provider = ((CTileProviders*)_providers)->get_Provider((int)tkTileProvider::OpenStreetMap); - _visible = true; - _minScaleToCache = 0; - _maxScaleToCache = 100; - } - - DECLARE_REGISTRY_RESOURCEID(IDR_TILES) - - BEGIN_COM_MAP(CTiles) - COM_INTERFACE_ENTRY(ITiles) - COM_INTERFACE_ENTRY(IDispatch) - COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal, _pUnkMarshaler.p) - END_COM_MAP() - - DECLARE_PROTECT_FINAL_CONSTRUCT() - DECLARE_GET_CONTROLLING_UNKNOWN() - - HRESULT FinalConstruct() - { - return CoCreateFreeThreadedMarshaler(GetControllingUnknown(), &_pUnkMarshaler.p); - return S_OK; - } - - void FinalRelease() - { - _pUnkMarshaler.Release(); - } - - CComPtr _pUnkMarshaler; + CTiles() + : _manager(true), _mercatorProjection(nullptr), _reloadNeeded(true) + { + _pUnkMarshaler = nullptr; + _key = SysAllocString(L""); + _globalCallback = nullptr; + _lastErrorCode = tkNO_ERROR; + + ComHelper::CreateInstance(idTileProviders, (IDispatch**)&_providers); + ((CTileProviders*)_providers)->put_Tiles(this); + + SetDefaults(); + + gReferenceCounter.AddRef(tkInterface::idTiles); + } + + ~CTiles() + { + SysFreeString(_key); + + ClearAll(); + + if (_mercatorProjection) + { + _mercatorProjection->Release(); + } + + gReferenceCounter.Release(tkInterface::idTiles); + } + + void ClearAll() + { + Stop(); + + _manager.Clear(); + + if (_providers) + { + _providers->Release(); + _providers = nullptr; + } + } + + void SetDefaults() + { + _provider = ((CTileProviders*)_providers)->get_Provider((int)tkTileProvider::OpenStreetMap); + _visible = true; + _minScaleToCache = 0; + _maxScaleToCache = 100; + } + + DECLARE_REGISTRY_RESOURCEID(IDR_TILES) + +BEGIN_COM_MAP(CTiles) + COM_INTERFACE_ENTRY(ITiles) + COM_INTERFACE_ENTRY(IDispatch) + COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal, _pUnkMarshaler.p) + END_COM_MAP() + + DECLARE_PROTECT_FINAL_CONSTRUCT() +DECLARE_GET_CONTROLLING_UNKNOWN() + + HRESULT FinalConstruct() + { + return CoCreateFreeThreadedMarshaler(GetControllingUnknown(), &_pUnkMarshaler.p); + } + + void FinalRelease() + { + _pUnkMarshaler.Release(); + } + + CComPtr _pUnkMarshaler; public: - STDMETHOD(get_LastErrorCode)(/*[out, retval]*/ long *pVal); - STDMETHOD(get_ErrorMsg)(/*[in]*/ long ErrorCode, /*[out, retval]*/ BSTR *pVal); - STDMETHOD(get_GlobalCallback)(/*[out, retval]*/ ICallback * *pVal); - STDMETHOD(put_GlobalCallback)(/*[in]*/ ICallback * newVal); - STDMETHOD(get_Key)(/*[out, retval]*/ BSTR *pVal); - STDMETHOD(put_Key)(/*[in]*/ BSTR newVal); - STDMETHOD(get_Visible)(VARIANT_BOOL* pVal); - STDMETHOD(put_Visible)(VARIANT_BOOL newVal); - STDMETHOD(get_GridLinesVisible)(VARIANT_BOOL* pVal); - STDMETHOD(put_GridLinesVisible)(VARIANT_BOOL newVal); - STDMETHOD(get_Provider)(tkTileProvider* pVal); - STDMETHOD(put_Provider)(tkTileProvider newVal); - STDMETHOD(get_DoCaching)(tkCacheType type, VARIANT_BOOL* pVal); - STDMETHOD(put_DoCaching)(tkCacheType type, VARIANT_BOOL newVal); - STDMETHOD(get_UseCache)(tkCacheType type, VARIANT_BOOL* pVal); - STDMETHOD(put_UseCache)(tkCacheType type, VARIANT_BOOL newVal); - STDMETHOD(get_UseServer)(VARIANT_BOOL* pVal); - STDMETHOD(put_UseServer)(VARIANT_BOOL newVal); - STDMETHOD(get_MaxCacheSize)(tkCacheType type, double* pVal); - STDMETHOD(put_MaxCacheSize)(tkCacheType type, double newVal); - STDMETHOD(get_MinScaleToCache)(int* pVal); - STDMETHOD(put_MinScaleToCache)(int newVal); - STDMETHOD(get_MaxScaleToCache)(int* pVal); - STDMETHOD(put_MaxScaleToCache)(int newVal); - STDMETHOD(ClearCache)(tkCacheType type); - STDMETHOD(ClearCache2)(tkCacheType type, int providerId, int fromScale = 0, int toScale = 100); - STDMETHOD(get_CacheSize)(tkCacheType type, double* retVal); - STDMETHOD(get_CacheSize2)(tkCacheType type, int providerId, int scale, double* retVal); - STDMETHOD(Serialize)(BSTR* retVal); - STDMETHOD(Deserialize)(BSTR newVal); - STDMETHOD(SetProxy)(BSTR address, int port, VARIANT_BOOL* retVal); - STDMETHOD(get_Proxy)(BSTR* retVal); - STDMETHOD(AutodetectProxy)(VARIANT_BOOL* retVal); - STDMETHOD(get_DiskCacheFilename)(BSTR* retVal); - STDMETHOD(put_DiskCacheFilename)(BSTR pVal); - STDMETHOD(get_Providers)(ITileProviders** retVal); - STDMETHOD(get_ProviderId)(int* retVal); - STDMETHOD(put_ProviderId)(int newVal); - STDMETHOD(GetTilesIndices)(IExtents* boundsDegrees, int zoom, int provider, IExtents** retVal); - STDMETHOD(get_DiskCacheCount)(int provider, int zoom, int xMin, int xMax, int yMin, int yMax, LONG* retVal); - STDMETHOD(get_ProviderName)(BSTR* retVal); - STDMETHOD(CheckConnection)(BSTR url, VARIANT_BOOL* retVal); - STDMETHOD(GetTileBounds)(int provider, int zoom, int tileX, int tileY, IExtents** retVal); - STDMETHOD(get_CurrentZoom)(int* retVal); - STDMETHOD(get_MaxZoom)(int* retVal); - STDMETHOD(get_MinZoom)(int* pVal); - STDMETHOD(get_ServerProjection)(IGeoProjection** retVal); - STDMETHOD(get_ProjectionStatus)(tkTilesProjectionStatus* retVal); - STDMETHOD(get_ScalingRatio)(double* pVal); - STDMETHOD(put_ScalingRatio)(double newVal); - STDMETHOD(SetProxyAuthentication)(BSTR username, BSTR password, BSTR domain, VARIANT_BOOL* retVal); - STDMETHOD(ClearProxyAuthorization)(); - STDMETHOD(get_ProxyAuthenticationScheme)(tkProxyAuthentication* pVal); - STDMETHOD(put_ProxyAuthenticationScheme)(tkProxyAuthentication newVal); - STDMETHOD(get_DelayRequestTimeout)(long* retVal); - STDMETHOD(put_DelayRequestTimeout)(long pVal); - STDMETHOD(Prefetch)(double minLat, double maxLat, double minLng, double maxLng, int zoom, int provider, IStopExecution* stop, LONG* retVal); - STDMETHOD(Prefetch2)(int minX, int maxX, int minY, int maxY, int zoom, int provider, IStopExecution* stop, LONG* retVal); - STDMETHOD(PrefetchToFolder)(IExtents* ext, int zoom, int providerId, BSTR savePath, BSTR fileExt, IStopExecution* stop, LONG* retVal); - STDMETHOD(get_ProjectionIsSphericalMercator)(VARIANT_BOOL* pVal); + STDMETHOD(get_LastErrorCode)(/*[out, retval]*/ long* pVal); + STDMETHOD(get_ErrorMsg)(/*[in]*/ long ErrorCode, /*[out, retval]*/ BSTR* pVal); + STDMETHOD(get_GlobalCallback)(/*[out, retval]*/ ICallback* * pVal); + STDMETHOD(put_GlobalCallback)(/*[in]*/ ICallback* newVal); + STDMETHOD(get_Key)(/*[out, retval]*/ BSTR* pVal); + STDMETHOD(put_Key)(/*[in]*/ BSTR newVal); + STDMETHOD(get_Visible)(VARIANT_BOOL* pVal); + STDMETHOD(put_Visible)(VARIANT_BOOL newVal); + STDMETHOD(get_GridLinesVisible)(VARIANT_BOOL* pVal); + STDMETHOD(put_GridLinesVisible)(VARIANT_BOOL newVal); + STDMETHOD(get_Provider)(tkTileProvider* pVal); + STDMETHOD(put_Provider)(tkTileProvider newVal); + STDMETHOD(get_DoCaching)(tkCacheType type, VARIANT_BOOL* pVal); + STDMETHOD(put_DoCaching)(tkCacheType type, VARIANT_BOOL newVal); + STDMETHOD(get_UseCache)(tkCacheType type, VARIANT_BOOL* pVal); + STDMETHOD(put_UseCache)(tkCacheType type, VARIANT_BOOL newVal); + STDMETHOD(get_UseServer)(VARIANT_BOOL* pVal); + STDMETHOD(put_UseServer)(VARIANT_BOOL newVal); + STDMETHOD(get_MaxCacheSize)(tkCacheType type, double* pVal); + STDMETHOD(put_MaxCacheSize)(tkCacheType type, double newVal); + STDMETHOD(get_MinScaleToCache)(int* pVal); + STDMETHOD(put_MinScaleToCache)(int newVal); + STDMETHOD(get_MaxScaleToCache)(int* pVal); + STDMETHOD(put_MaxScaleToCache)(int newVal); + STDMETHOD(ClearCache)(tkCacheType type); + STDMETHOD(ClearCache2)(tkCacheType type, int providerId, int fromScale = 0, int toScale = 100); + STDMETHOD(get_CacheSize)(tkCacheType type, double* retVal); + STDMETHOD(get_CacheSize2)(tkCacheType type, int providerId, int scale, double* retVal); + STDMETHOD(Serialize)(BSTR* retVal); + STDMETHOD(Deserialize)(BSTR newVal); + STDMETHOD(SetProxy)(BSTR address, int port, VARIANT_BOOL* retVal); + STDMETHOD(get_Proxy)(BSTR* retVal); + STDMETHOD(AutodetectProxy)(VARIANT_BOOL* retVal); + STDMETHOD(get_DiskCacheFilename)(BSTR* retVal); + STDMETHOD(put_DiskCacheFilename)(BSTR pVal); + STDMETHOD(get_Providers)(ITileProviders** retVal); + STDMETHOD(get_ProviderId)(int* retVal); + STDMETHOD(put_ProviderId)(int newVal); + STDMETHOD(GetTilesIndices)(IExtents* boundsDegrees, int zoom, int provider, IExtents** retVal); + STDMETHOD(get_DiskCacheCount)(int provider, int zoom, int xMin, int xMax, int yMin, int yMax, LONG* retVal); + STDMETHOD(get_ProviderName)(BSTR* retVal); + STDMETHOD(CheckConnection)(BSTR url, VARIANT_BOOL* retVal); + STDMETHOD(GetTileBounds)(int provider, int zoom, int tileX, int tileY, IExtents** retVal); + STDMETHOD(get_CurrentZoom)(int* retVal); + STDMETHOD(get_MaxZoom)(int* retVal); + STDMETHOD(get_MinZoom)(int* pVal); + STDMETHOD(get_ServerProjection)(IGeoProjection** retVal); + STDMETHOD(get_ProjectionStatus)(tkTilesProjectionStatus* retVal); + STDMETHOD(get_ScalingRatio)(double* pVal); + STDMETHOD(put_ScalingRatio)(double newVal); + STDMETHOD(SetProxyAuthentication)(BSTR username, BSTR password, BSTR domain, VARIANT_BOOL* retVal); + STDMETHOD(ClearProxyAuthorization)(); + STDMETHOD(get_ProxyAuthenticationScheme)(tkProxyAuthentication* pVal); + STDMETHOD(put_ProxyAuthenticationScheme)(tkProxyAuthentication newVal); + STDMETHOD(get_DelayRequestTimeout)(long* retVal); + STDMETHOD(put_DelayRequestTimeout)(long pVal); + STDMETHOD(Prefetch)(double minLat, double maxLat, double minLng, double maxLng, int zoom, int provider, + IStopExecution* stop, LONG* retVal); + STDMETHOD(Prefetch2)(int minX, int maxX, int minY, int maxY, int zoom, int provider, IStopExecution* stop, + LONG* retVal); + static void LogBulkDownloadStarted(IExtents* ext, int zoom, BaseProvider* provider, BSTR savePath, BSTR fileExt); + STDMETHOD(PrefetchToFolder)(IExtents* ext, int zoom, int providerId, BSTR savePath, BSTR fileExt, + IStopExecution* stop, LONG* retVal); + STDMETHOD(get_ProjectionIsSphericalMercator)(VARIANT_BOOL* pVal); private: - long _lastErrorCode; - ICallback * _globalCallback; - BSTR _key; - bool _visible; - int _minScaleToCache; - int _maxScaleToCache; - ITileProviders* _providers; - TileManager _manager; - BaseProvider* _provider; - IGeoProjection* _mercatorProjection; - bool _reloadNeeded; + long _lastErrorCode; + ICallback* _globalCallback; + BSTR _key; + bool _visible; + int _minScaleToCache; + int _maxScaleToCache; + ITileProviders* _providers; + TileManager _manager; + BaseProvider* _provider; + IGeoProjection* _mercatorProjection; + bool _reloadNeeded; private: - void ErrorMessage(long ErrorCode); - void SetAuthorization(BSTR userName, BSTR password, BSTR domain); - SQLiteCache* get_SqliteCache() { return dynamic_cast(TileCacheManager::get_Cache(tctSqliteCache)); } - BaseProvider* get_Provider(int providerId); + void ErrorMessage(long ErrorCode); + void SetAuthorization(BSTR userName, BSTR password, BSTR domain); + SQLiteCache* get_SqliteCache() { return dynamic_cast(TileCacheManager::get_Cache(tctSqliteCache)); } + BaseProvider* get_Provider(int providerId) const; public: - // properties - TileManager* get_Manager() { return &_manager; } - BaseProvider* get_Provider() { return _provider; } - bool get_ReloadNeeded(); + // properties + TileManager* get_Manager() { return &_manager; } + BaseProvider* get_Provider() { return _provider; } + bool get_ReloadNeeded(); public: - void Init(IMapViewCallback* map) {_manager.set_MapCallback(map); } - void LoadTiles(bool isSnapshot, CString key = ""); - bool TilesAreInCache(IMapViewCallback* map, tkTileProvider providerId = ProviderNone); - bool DeserializeCore(CPLXMLNode* node); - CPLXMLNode* SerializeCore(CString ElementName); - BaseProjection* get_Projection(){ return _provider->get_Projection(); } - void Stop(); + void Init(IMapViewCallback* map) { _manager.set_MapCallback(map); } + void LoadTiles(bool isSnapshot, const CString& key = ""); + bool TilesAreInCache(IMapViewCallback* map, tkTileProvider providerId = ProviderNone); + bool DeserializeCore(CPLXMLNode* node); + CPLXMLNode* SerializeCore(const CString& ElementName); + BaseProjection* get_Projection() { return _provider->get_Projection(); } + void Stop(); }; OBJECT_ENTRY_AUTO(__uuidof(Tiles), CTiles) diff --git a/src/COM classes/Utils.cpp b/src/COM classes/Utils.cpp index cfaf504c..3b4bc7cc 100644 --- a/src/COM classes/Utils.cpp +++ b/src/COM classes/Utils.cpp @@ -17,6 +17,7 @@ //******************************************************************************************************** //Contributor(s): dpa, angela, cdm, Rob Cairns, lsu // 09-06-2017 jfaust - Add EPSGUnitConversion, return tkUnitsOfMeasure associated with specified EPSG code +// 11-06-2018 jfaust - Add GetAngle function //******************************************************************************************************** #include "stdafx.h" @@ -43,6 +44,9 @@ #include "TableHelper.h" #include "xtiffio.h" #include "ShapeHelper.h" +#include "GeosHelper.h" +#include "GeosConverter.h" +#include "GeometryHelper.h" #pragma warning(disable:4996) @@ -54,280 +58,314 @@ static char THIS_FILE[] = __FILE__; STDMETHODIMP CUtils::PointInPolygon(IShape *Shape, IPoint *TestPoint, VARIANT_BOOL *retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - - if( Shape != NULL ) - { - _pip_cache_parts.clear(); - _pip_cache_pointsX.clear(); - _pip_cache_pointsY.clear(); - - ShpfileType shptype; - Shape->get_ShapeType(&shptype); - - if(shptype != SHP_POLYGON && shptype != SHP_POLYGONZ && shptype != SHP_POLYGONM ) - { - *retval = FALSE; - this->ErrorMessage(tkINCOMPATIBLE_SHAPE_TYPE); - return S_OK; - } - - ((CShape*)Shape)->get_ExtentsXY(_pip_left,_pip_bottom,_pip_right,_pip_top); - - long numParts = 0; - long numPoints = 0; - Shape->get_NumParts(&numParts); - Shape->get_NumPoints(&numPoints); - - if(numParts == 0)//ah 11/22/05, for in-memory shapes that have no parts - { - _pip_cache_parts.push_back(0); - } - else - { - long part =0; - for( int j = 0; j < numParts; j++ ) - { - Shape->get_Part(j,&part); - _pip_cache_parts.push_back(part); - } - } - - double pointX = 0.0; - double pointY = 0.0; - VARIANT_BOOL vbretval; - for( int i = 0; i < numPoints; i++ ) - { - Shape->get_XY(i, &pointX, &pointY, &vbretval); - _pip_cache_pointsX.push_back(pointX); - _pip_cache_pointsY.push_back(pointY); - } - } - - if( TestPoint != NULL ) - { - double test_pointX, test_pointY; - TestPoint->get_X(&test_pointX); - TestPoint->get_Y(&test_pointY); - - //Initial Test on Bounds - if( test_pointX < _pip_left ) - { - *retval = FALSE; - return S_OK; - } - else if( test_pointX > _pip_right ) - { - *retval = FALSE; - return S_OK; - } - if( test_pointY < _pip_bottom ) - { - *retval = FALSE; - return S_OK; - } - else if( test_pointY > _pip_top ) - { - *retval = FALSE; - return S_OK; - } - - //X = U; - //Y = V; - //Always drop Z Coordinate and Project on XY Plane - int beg_part = 0; - int end_part = 0; - - int number_in_polygons = 0; - int numCacheParts = (int)_pip_cache_parts.size();//ah 6/6/05 - - for( int j = 0; j < numCacheParts; j++ ) - { - beg_part = _pip_cache_parts[j]; - if( (numCacheParts - 1) > j ) - end_part = _pip_cache_parts[j+1]; - else - end_part = _pip_cache_pointsX.size(); - - int SH = 0; - int NSH = 0; - int NC = 0; //number_crossings - - for( int i = beg_part; i < end_part - 1; i++ ) - { - long corner_two_index = i + 1; - if( i == _pip_cache_pointsX.size() - 1 ) - corner_two_index = beg_part; - - //Translate points to origin centered on test_point - double corner_oneX = _pip_cache_pointsX[i] - test_pointX; - double corner_oneY = _pip_cache_pointsY[i] - test_pointY; - double corner_twoX = _pip_cache_pointsX[corner_two_index] - test_pointX; - double corner_twoY = _pip_cache_pointsY[corner_two_index] - test_pointY; - - set_sign( corner_oneY, SH ); - set_sign( corner_twoY, NSH ); - - if( does_cross( SH, NSH, corner_oneX, corner_oneY, corner_twoX, corner_twoY ) ) - NC++; - SH = NSH; - } - //ODD Crossings = point_in_part - if( NC % 2 != 0 ) - number_in_polygons++; - } + AFX_MANAGE_STATE(AfxGetStaticModuleState()) - //cout<get_ShapeType2D(&shptype); + + if (shptype != SHP_POLYGON) + { + *retval = VARIANT_FALSE; + this->ErrorMessage(tkINCOMPATIBLE_SHAPE_TYPE); + return S_OK; + } + + double xMin, yMin, xMax, yMax; + double test_pointX, test_pointY; + + // get extents of shape + ((CShape*)Shape)->get_ExtentsXY(xMin, yMin, xMax, yMax); + + // get test point coords + TestPoint->get_X(&test_pointX); + TestPoint->get_Y(&test_pointY); + // do the simple check + if (test_pointX < xMin || test_pointY < yMin || test_pointX > xMax || test_pointY > yMax) + { + *retval = VARIANT_FALSE; + return S_OK; + } + + // extract parts and points for testing + long numParts = 0; + long numPoints = 0; + long* Parts; + Point2D* Points; + Shape->get_NumParts(&numParts); + Shape->get_NumPoints(&numPoints); + + Parts = new long[numParts + 1]; + Points = new Point2D[numPoints]; + + for (int nPart = numParts - 1; nPart >= 0; nPart--) + Shape->get_Part(nPart, &Parts[nPart]); + + VARIANT_BOOL vbretval; + for (int nPoint = numPoints - 1; nPoint >= 0; nPoint--) + { + Shape->get_XY(nPoint, &Points[nPoint].x, &Points[nPoint].y, &vbretval); + } + Parts[numParts] = numPoints; + + int CrossCount = 0; + // work backwards through Parts since we only know the first point of each Part + for (int nPart = numParts - 1; nPart >= 0; nPart--) + { + const int nPointMin = Parts[nPart]; + const int nPointMax = Parts[nPart + 1] - 1; + int i, j; + // algorithm by W. Randolph Franklin; for a detailed explanation, see + // https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html + // note that i starts at first point, while j starts at last point, then follows behind i + for (i = nPointMin, j = nPointMax - 1; i < nPointMax; j = i++) + { + if (((Points[i].y > test_pointY) != (Points[j].y > test_pointY)) && + (test_pointX < (Points[j].x - Points[i].x) * (test_pointY - Points[i].y) / (Points[j].y - Points[i].y) + Points[i].x)) + // we have a crossing + CrossCount++; + } + } + delete[] Points; + delete[] Parts; + + *retval = CrossCount & 1 ? VARIANT_TRUE : VARIANT_FALSE; + return S_OK; + } + else + { + *retval = VARIANT_FALSE; + this->ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); + return S_OK; + } + + //if( TestPoint != NULL ) + //{ + // double test_pointX, test_pointY; + // TestPoint->get_X(&test_pointX); + // TestPoint->get_Y(&test_pointY); + // + // //Initial Test on Bounds + // if( test_pointX < _pip_left ) + // { + // *retval = FALSE; + // return S_OK; + // } + // else if( test_pointX > _pip_right ) + // { + // *retval = FALSE; + // return S_OK; + // } + // if( test_pointY < _pip_bottom ) + // { + // *retval = FALSE; + // return S_OK; + // } + // else if( test_pointY > _pip_top ) + // { + // *retval = FALSE; + // return S_OK; + // } + // + // //X = U; + // //Y = V; + // //Always drop Z Coordinate and Project on XY Plane + // int beg_part = 0; + // int end_part = 0; + + // int number_in_polygons = 0; + // int numCacheParts = (int)_pip_cache_parts.size();//ah 6/6/05 + // + // for( int j = 0; j < numCacheParts; j++ ) + // { + // beg_part = _pip_cache_parts[j]; + // if( (numCacheParts - 1) > j ) + // end_part = _pip_cache_parts[j+1]; + // else + // end_part = _pip_cache_pointsX.size(); + + // int SH = 0; + // int NSH = 0; + // int NC = 0; //number_crossings + + // for( int i = beg_part; i < end_part - 1; i++ ) + // { + // long corner_two_index = i + 1; + // if( i == _pip_cache_pointsX.size() - 1 ) + // corner_two_index = beg_part; + // + // //Translate points to origin centered on test_point + // double corner_oneX = _pip_cache_pointsX[i] - test_pointX; + // double corner_oneY = _pip_cache_pointsY[i] - test_pointY; + // double corner_twoX = _pip_cache_pointsX[corner_two_index] - test_pointX; + // double corner_twoY = _pip_cache_pointsY[corner_two_index] - test_pointY; + // + // set_sign( corner_oneY, SH ); + // set_sign( corner_twoY, NSH ); + // + // if( does_cross( SH, NSH, corner_oneX, corner_oneY, corner_twoX, corner_twoY ) ) + // NC++; + // SH = NSH; + // } + // //ODD Crossings = point_in_part + // if( NC % 2 != 0 ) + // number_in_polygons++; + // } + + // //cout< 0 && corner_twoX > 0 ) - return true; - else if( corner_oneX > 0 || corner_twoX > 0 ) - { - //b = v - u*m - double m = ( corner_twoX - corner_oneX )/ - ( corner_twoY - corner_oneY ); - - if( ( corner_oneX - corner_oneY*m ) > 0 ) - return true; - else - return false; - } - else - return false; - } - else - return false; +inline bool CUtils::does_cross(int SH, int NSH, double corner_oneX, double corner_oneY, double corner_twoX, double corner_twoY) +{ + if (SH != NSH) + { + if (corner_oneX > 0 && corner_twoX > 0) + return true; + else if (corner_oneX > 0 || corner_twoX > 0) + { + //b = v - u*m + double m = (corner_twoX - corner_oneX) / + (corner_twoY - corner_oneY); + + if ((corner_oneX - corner_oneY*m) > 0) + return true; + else + return false; + } + else + return false; + } + else + return false; } -inline void CUtils::set_sign( double val, int & SH ) -{ if( val < 0 ) - SH = -1; - else - SH = 1; +inline void CUtils::set_sign(double val, int & SH) +{ + if (val < 0) + SH = -1; + else + SH = 1; } STDMETHODIMP CUtils::GridReplace(IGrid *Grid, VARIANT OldValue, VARIANT NewValue, ICallback *cBack, VARIANT_BOOL *retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - USES_CONVERSION; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + USES_CONVERSION; - *retval = VARIANT_FALSE; - ICallback* callback = cBack ? cBack : _globalCallback; + *retval = VARIANT_FALSE; + ICallback* callback = cBack ? cBack : _globalCallback; - if( !Grid ) - { - ErrorMessage(callback, tkUNEXPECTED_NULL_PARAMETER); - return S_OK; - } + if (!Grid) + { + ErrorMessage(callback, tkUNEXPECTED_NULL_PARAMETER); + return S_OK; + } + + long ncols = 0, nrows = 0; + IGridHeader * header = NULL; + Grid->get_Header(&header); + header->get_NumberCols(&ncols); + header->get_NumberRows(&nrows); + if (ncols <= 0 || nrows <= 0) + { + ErrorMessage(callback, tkZERO_ROWS_OR_COLS); + return S_OK; + } + header->Release(); - long ncols = 0, nrows = 0; - IGridHeader * header = NULL; - Grid->get_Header(&header); - header->get_NumberCols(&ncols); - header->get_NumberRows(&nrows); - if( ncols <= 0 || nrows <= 0 ) - { - ErrorMessage(callback, tkZERO_ROWS_OR_COLS); - return S_OK; - } - header->Release(); + double oldValue = 0; + dVal(OldValue, oldValue); - double oldValue = 0; - dVal(OldValue,oldValue); - - VARIANT vval; - VariantInit(&vval); - double val; - - long percent = 0, newpercent = 0; - double total = nrows * ncols; - - for( int j = 0; j < nrows; j++ ) - { - for( int i = 0; i < ncols; i++ ) - { - Grid->get_Value(i,j,&vval); - dVal(vval,val); - if( val == oldValue ) - Grid->put_Value(i,j,NewValue); - } - CallbackHelper::Progress(callback, j, nrows, "GridReplace", _key, percent); - } - - VariantClear(&vval); - *retval = VARIANT_TRUE; - return S_OK; + VARIANT vval; + VariantInit(&vval); + double val; + + long percent = 0, newpercent = 0; + double total = nrows * ncols; + + for (int j = 0; j < nrows; j++) + { + for (int i = 0; i < ncols; i++) + { + Grid->get_Value(i, j, &vval); + dVal(vval, val); + if (val == oldValue) + Grid->put_Value(i, j, NewValue); + } + CallbackHelper::Progress(callback, j, nrows, "GridReplace", _key, percent); + } + + VariantClear(&vval); + *retval = VARIANT_TRUE; + return S_OK; } STDMETHODIMP CUtils::GridInterpolateNoData(IGrid *Grid, ICallback *cBack, VARIANT_BOOL *retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) + AFX_MANAGE_STATE(AfxGetStaticModuleState()) - ICallback* callback = cBack ? cBack : _globalCallback; + ICallback* callback = cBack ? cBack : _globalCallback; - if( Grid == NULL ) - { - ErrorMessage(callback, tkUNEXPECTED_NULL_PARAMETER); - *retval = VARIANT_FALSE; - return S_OK; - }//if - - long ncols = 0, nrows = 0; - double nodatavalue; - IGridHeader * header = NULL; - VARIANT nodataval_variant; - VariantInit(&nodataval_variant); - Grid->get_Header(&header); - header->get_NumberCols(&ncols); - header->get_NumberRows(&nrows); - header->get_NodataValue(&nodataval_variant); - dVal(nodataval_variant, nodatavalue); - VariantClear(&nodataval_variant); - - if(ncols <= 0 || nrows <= 0) - { - *retval = VARIANT_FALSE; - ErrorMessage(callback, tkZERO_ROWS_OR_COLS); - return S_OK; - }//if + if (Grid == NULL) + { + ErrorMessage(callback, tkUNEXPECTED_NULL_PARAMETER); + *retval = VARIANT_FALSE; + return S_OK; + }//if + + long ncols = 0, nrows = 0; + double nodatavalue; + IGridHeader * header = NULL; + VARIANT nodataval_variant; + VariantInit(&nodataval_variant); + Grid->get_Header(&header); + header->get_NumberCols(&ncols); + header->get_NumberRows(&nrows); + header->get_NodataValue(&nodataval_variant); + dVal(nodataval_variant, nodatavalue); + VariantClear(&nodataval_variant); + + if (ncols <= 0 || nrows <= 0) + { + *retval = VARIANT_FALSE; + ErrorMessage(callback, tkZERO_ROWS_OR_COLS); + return S_OK; + }//if - GridInterpolate gi(Grid,nodatavalue,nrows,ncols); + GridInterpolate gi(Grid, nodatavalue, nrows, ncols); - CallbackHelper::Progress(callback, 0, "GridInterpolateNoData", _key ); + CallbackHelper::Progress(callback, 0, "GridInterpolateNoData", _key); - gi.Interpolate(0,0); - - CallbackHelper::Progress(callback, 25, "GridInterpolateNoData", _key); - - gi.Interpolate(0,ncols-1); - - CallbackHelper::Progress(callback, 50, "GridInterpolateNoData", _key); - - gi.Interpolate(nrows-1,0); - - CallbackHelper::Progress(callback, 75, "GridInterpolateNoData", _key); - - gi.Interpolate(nrows-1,ncols-1); - - CallbackHelper::Progress(callback, 100, "GridInterpolateNoData", _key); + gi.Interpolate(0, 0); - *retval = VARIANT_TRUE; + CallbackHelper::Progress(callback, 25, "GridInterpolateNoData", _key); - return S_OK; + gi.Interpolate(0, ncols - 1); + + CallbackHelper::Progress(callback, 50, "GridInterpolateNoData", _key); + + gi.Interpolate(nrows - 1, 0); + + CallbackHelper::Progress(callback, 75, "GridInterpolateNoData", _key); + + gi.Interpolate(nrows - 1, ncols - 1); + + CallbackHelper::Progress(callback, 100, "GridInterpolateNoData", _key); + + *retval = VARIANT_TRUE; + + return S_OK; } // ************************************************************ @@ -335,259 +373,261 @@ STDMETHODIMP CUtils::GridInterpolateNoData(IGrid *Grid, ICallback *cBack, VARIAN // ************************************************************ STDMETHODIMP CUtils::RemoveColinearPoints(IShapefile * Shapes, double LinearTolerance, ICallback *cBack, VARIANT_BOOL *retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) + AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if( Shapes == NULL ) - { - *retval = NULL; - ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); - return S_OK; - } + if (Shapes == NULL) + { + *retval = NULL; + ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); + return S_OK; + } - ICallback* callback = cBack ? cBack : _globalCallback; + ICallback* callback = cBack ? cBack : _globalCallback; - ShpfileType shptype; - Shapes->get_ShapefileType(&shptype); + ShpfileType shptype; + Shapes->get_ShapefileType(&shptype); - if( shptype != SHP_POLYLINE && shptype != SHP_POLYGON ) - { - *retval = VARIANT_FALSE; - if( shptype == SHP_POLYLINEZ || shptype == SHP_POLYGONZ || - shptype == SHP_POLYLINEM || shptype == SHP_POLYGONM ) - _lastErrorCode = tkUNSUPPORTED_SHAPEFILE_TYPE; - else - _lastErrorCode = tkINCOMPATIBLE_SHAPEFILE_TYPE; + if (shptype != SHP_POLYLINE && shptype != SHP_POLYGON) + { + *retval = VARIANT_FALSE; + if (shptype == SHP_POLYLINEZ || shptype == SHP_POLYGONZ || + shptype == SHP_POLYLINEM || shptype == SHP_POLYGONM) + _lastErrorCode = tkUNSUPPORTED_SHAPEFILE_TYPE; + else + _lastErrorCode = tkINCOMPATIBLE_SHAPEFILE_TYPE; - ErrorMessage(callback, _lastErrorCode); + ErrorMessage(callback, _lastErrorCode); - return S_OK; - } + return S_OK; + } - if (!((CShapefile*)Shapes)->ValidateInput(Shapes, "RemoveColinearPoints", "Shapes", VARIANT_FALSE, "Utils")) - return S_OK; + if (!((CShapefile*)Shapes)->ValidateInput(Shapes, "RemoveColinearPoints", "Shapes", VARIANT_FALSE, "Utils")) + return S_OK; - VARIANT_BOOL vbretval; - Shapes->StartEditingShapes(FALSE,cBack,&vbretval); - long numShapes; - Shapes->get_NumShapes(&numShapes); - - long percent = 0, cnt = 0; - double total = 2*numShapes; - - XRedBlack rb; + VARIANT_BOOL vbretval; + Shapes->StartEditingShapes(FALSE, cBack, &vbretval); + long numShapes; + Shapes->get_NumShapes(&numShapes); - if( shptype == SHP_POLYLINE ) - { - } - else if( shptype == SHP_POLYGON ) - { - for( int currentShape = 0; currentShape < numShapes; currentShape++ ) - { - IShape * shape = NULL; - ((CShapefile*)Shapes)->GetValidatedShape(currentShape,&shape); - if (!shape) continue; - - long numPoints = 0, forward_index = 0, backward_index = 0; - shape->get_NumPoints(&numPoints); - - double x, y; - VARIANT_BOOL vbretval; - - for( int point_index = 0; point_index < numPoints; point_index++ ) - { - shape->get_XY(point_index, &x, &y, &vbretval); - - POINT p; - p.x = (LONG)x; - p.y = (LONG)y; - rb.Insert( p ); - - forward_index = point_index + 1; - backward_index = point_index - 1; - if( forward_index >= numPoints ) - forward_index = 0; - if( backward_index < 0 ) - backward_index = numPoints - 1; - - VARIANT_BOOL vbretval; - double onex, oney, twox, twoy, threex, threey; - shape->get_XY(backward_index, &onex, &oney, &vbretval); - shape->get_XY(forward_index, &twox, &twoy, &vbretval); - shape->get_XY(point_index, &threex, &threey, &vbretval); - - POINT one; - one.x = (LONG)onex; - one.y = (LONG)oney; - - POINT two; - two.x = (LONG)twox; - two.y = (LONG)twoy; - - POINT three; - three.x = (LONG)threex; - three.y = (LONG)threey; - - YRedBlackNode * prbn = rb.GetNode( three ); - if( prbn != NULL ) - { - if( prbn->canSetColinear ) - { - if( isColinear( one, two, three, LinearTolerance ) ) - prbn->isColinear = true; - else - { prbn->isColinear = false; - prbn->canSetColinear = false; - } - } - } - } - - cnt++; - - CallbackHelper::Progress(callback, cnt, total, "RemoveColinearPoints", _key, percent); - - std::deque< POINT > PointsToKeep; - for( currentShape = 0; currentShape < numShapes; currentShape++ ) - { - double x, y; - VARIANT_BOOL vbretval; - - for( int point_index = 0; point_index < numPoints; point_index++ ) - { - shape->get_XY(point_index, &x, &y, &vbretval); - POINT p; - p.x = (LONG)x; p.y = (LONG)y; - rb.Insert( p ); - - YRedBlackNode * prbn = rb.GetNode( p ); - if( prbn != NULL ) - { - if( prbn->isColinear == true && prbn->useCount < 2 ) - { //Don't Keep the Point - } - else - PointsToKeep.push_back( prbn->Element ); - } - } - - for( int ns = 0; ns < numPoints; ns++ ) - shape->DeletePoint(0,&vbretval); - - PointsToKeep.push_back( PointsToKeep[0] ); - for( int i = 0; i < (int)PointsToKeep.size(); i++ ) - { - IPoint * pnt = NULL; - ComHelper::CreatePoint(&pnt); - - pnt->put_X( PointsToKeep[i].x ); - pnt->put_Y( PointsToKeep[i].y ); - long pntpos = i; - shape->InsertPoint( pnt, &pntpos, &vbretval ); - } - - PointsToKeep.clear(); - - cnt++; - CallbackHelper::Progress(callback, cnt, total, "RemoveColinearPoints", _key, percent); - } - - shape->Release(); - shape = NULL; - } - } - - // --------------------------------------------------- - // Validating output - // --------------------------------------------------- - CallbackHelper::ProgressCompleted(_globalCallback, _key); - - ((CShapefile*)Shapes)->ValidateOutput(&Shapes, "RemoveColinearPoints", "Utils"); + long percent = 0, cnt = 0; + double total = 2 * numShapes; - if (Shapes) { - Shapes->StopEditingShapes(TRUE,FALSE,cBack,&vbretval); - } + XRedBlack rb; - return S_OK; + if (shptype == SHP_POLYLINE) + { + } + else if (shptype == SHP_POLYGON) + { + for (int currentShape = 0; currentShape < numShapes; currentShape++) + { + IShape * shape = NULL; + ((CShapefile*)Shapes)->GetValidatedShape(currentShape, &shape); + if (!shape) continue; + + long numPoints = 0, forward_index = 0, backward_index = 0; + shape->get_NumPoints(&numPoints); + + double x, y; + VARIANT_BOOL vbretval; + + for (int point_index = 0; point_index < numPoints; point_index++) + { + shape->get_XY(point_index, &x, &y, &vbretval); + + POINT p; + p.x = (LONG)x; + p.y = (LONG)y; + rb.Insert(p); + + forward_index = point_index + 1; + backward_index = point_index - 1; + if (forward_index >= numPoints) + forward_index = 0; + if (backward_index < 0) + backward_index = numPoints - 1; + + VARIANT_BOOL vbretval; + double onex, oney, twox, twoy, threex, threey; + shape->get_XY(backward_index, &onex, &oney, &vbretval); + shape->get_XY(forward_index, &twox, &twoy, &vbretval); + shape->get_XY(point_index, &threex, &threey, &vbretval); + + POINT one; + one.x = (LONG)onex; + one.y = (LONG)oney; + + POINT two; + two.x = (LONG)twox; + two.y = (LONG)twoy; + + POINT three; + three.x = (LONG)threex; + three.y = (LONG)threey; + + YRedBlackNode * prbn = rb.GetNode(three); + if (prbn != NULL) + { + if (prbn->canSetColinear) + { + if (isColinear(one, two, three, LinearTolerance)) + prbn->isColinear = true; + else + { + prbn->isColinear = false; + prbn->canSetColinear = false; + } + } + } + } + + cnt++; + + CallbackHelper::Progress(callback, cnt, total, "RemoveColinearPoints", _key, percent); + + std::deque< POINT > PointsToKeep; + for (currentShape = 0; currentShape < numShapes; currentShape++) + { + double x, y; + VARIANT_BOOL vbretval; + + for (int point_index = 0; point_index < numPoints; point_index++) + { + shape->get_XY(point_index, &x, &y, &vbretval); + POINT p; + p.x = (LONG)x; p.y = (LONG)y; + rb.Insert(p); + + YRedBlackNode * prbn = rb.GetNode(p); + if (prbn != NULL) + { + if (prbn->isColinear == true && prbn->useCount < 2) + { //Don't Keep the Point + } + else + PointsToKeep.push_back(prbn->Element); + } + } + + for (int ns = 0; ns < numPoints; ns++) + shape->DeletePoint(0, &vbretval); + + PointsToKeep.push_back(PointsToKeep[0]); + for (int i = 0; i < (int)PointsToKeep.size(); i++) + { + IPoint * pnt = NULL; + ComHelper::CreatePoint(&pnt); + + pnt->put_X(PointsToKeep[i].x); + pnt->put_Y(PointsToKeep[i].y); + long pntpos = i; + shape->InsertPoint(pnt, &pntpos, &vbretval); + } + + PointsToKeep.clear(); + + cnt++; + CallbackHelper::Progress(callback, cnt, total, "RemoveColinearPoints", _key, percent); + } + + shape->Release(); + shape = NULL; + } + } + + // --------------------------------------------------- + // Validating output + // --------------------------------------------------- + CallbackHelper::ProgressCompleted(_globalCallback, _key); + + ((CShapefile*)Shapes)->ValidateOutput(&Shapes, "RemoveColinearPoints", "Utils"); + + if (Shapes) { + Shapes->StopEditingShapes(TRUE, FALSE, cBack, &vbretval); + } + + return S_OK; } -bool CUtils::isColinear( POINT one, POINT two, POINT test, double tolerance ) +bool CUtils::isColinear(POINT one, POINT two, POINT test, double tolerance) { - double dx = two.x - one.x; - double dy = two.y - one.y; - if( dx != 0 ) - { //Test to see if the slopes are equal - double m1 = dy/dx; - double dx2 = two.x - test.x; - double dy2 = two.y - test.y; - if( dx2 != 0 ) - { - double m2 = dy2/dx2; - if( ( m1 > 0 && m2 > 0 ) || ( m1 < 0 && m2 < 0 ) ) - { - if( fabs( m1 - m2 ) < tolerance ) - return true; - else - return false; - } - else if( m1 == 0 && m2 == 0 ) - return true; - else - return false; - } - else - return false; - } - //Vertical Line - //Check the X's - else - { if( one.x == test.x ) - return true; - else - return false; - } + double dx = two.x - one.x; + double dy = two.y - one.y; + if (dx != 0) + { //Test to see if the slopes are equal + double m1 = dy / dx; + double dx2 = two.x - test.x; + double dy2 = two.y - test.y; + if (dx2 != 0) + { + double m2 = dy2 / dx2; + if ((m1 > 0 && m2 > 0) || (m1 < 0 && m2 < 0)) + { + if (fabs(m1 - m2) < tolerance) + return true; + else + return false; + } + else if (m1 == 0 && m2 == 0) + return true; + else + return false; + } + else + return false; + } + //Vertical Line + //Check the X's + else + { + if (one.x == test.x) + return true; + else + return false; + } } STDMETHODIMP CUtils::get_Length(IShape *Shape, double *pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - - if( Shape == NULL ) - { - *pVal = 0.0; - this->ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); - return S_OK; - } - Shape->get_Length(pVal); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + + if (Shape == NULL) + { + *pVal = 0.0; + this->ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); + return S_OK; + } + Shape->get_Length(pVal); + return S_OK; } STDMETHODIMP CUtils::get_Perimeter(IShape *Shape, double *pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) + AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if( Shape == NULL ) - { - *pVal = 0.0; - this->ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); - return S_OK; - } - - Shape->get_Perimeter(pVal); - return S_OK; + if (Shape == NULL) + { + *pVal = 0.0; + this->ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); + return S_OK; + } + + Shape->get_Perimeter(pVal); + return S_OK; } STDMETHODIMP CUtils::get_Area(IShape *Shape, double *pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if( Shape == NULL ) - { - *pVal = 0.0; - this->ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); - return S_OK; - } - - Shape->get_Area(pVal); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + if (Shape == NULL) + { + *pVal = 0.0; + this->ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); + return S_OK; + } + + Shape->get_Area(pVal); + return S_OK; } @@ -599,29 +639,29 @@ STDMETHODIMP CUtils::get_Area(IShape *Shape, double *pVal) //Returns true if clockwise, false if counter-clockwise. bool CUtils::is_clockwise(Poly *polygon) { - int numPoints = polygon->polyX.size(); - double area = 0; - for(int i = 0; i <= numPoints-2; i++) - { - double oneX = polygon->polyX[i]; - double oneY = polygon->polyY[i]; - double twoX = polygon->polyX[i+1]; - double twoY = polygon->polyY[i+1]; - - double trap_area = ((oneX * twoY) - (twoX * oneY)); - area += trap_area; - } - if(area > 0) - { - //if area is positive, polygon vertices are oriented - //counter-clockwise about the normal. - return false; - } - else - { - //polygon vertices are oriented clockwise about the normal. - return true; - } + int numPoints = polygon->polyX.size(); + double area = 0; + for (int i = 0; i <= numPoints - 2; i++) + { + double oneX = polygon->polyX[i]; + double oneY = polygon->polyY[i]; + double twoX = polygon->polyX[i + 1]; + double twoY = polygon->polyY[i + 1]; + + double trap_area = ((oneX * twoY) - (twoX * oneY)); + area += trap_area; + } + if (area > 0) + { + //if area is positive, polygon vertices are oriented + //counter-clockwise about the normal. + return false; + } + else + { + //polygon vertices are oriented clockwise about the normal. + return true; + } } //ah 2/24/06 //v3: Checks to see if a polygon is oriented clockwise about the normal. @@ -629,750 +669,757 @@ bool CUtils::is_clockwise(Poly *polygon) //Returns true if clockwise, false if counter-clockwise. bool CUtils::is_clockwise(IShape *Shape) { - long numPoints = 0; - Shape->get_NumPoints(&numPoints); - double area = 0; - - //calculate cross product for all vertices except last one (repeat of first) - double oneX, oneY, twoX, twoY; - VARIANT_BOOL vbretval; - for(int i = 0; i <= numPoints-2; i++) - { - Shape->get_XY(i, &oneX, &oneY, &vbretval); - Shape->get_XY(i+1, &twoX, &twoY, &vbretval); - double trap_area = ((oneX * twoY) - (twoX * oneY)); - area += trap_area; - } - if(area > 0) - { - //if area is positive, polygon vertices are oriented - //counter-clockwise about the normal. - return false; - } - else - { - //polygon vertices are oriented clockwise about the normal. - return true; - } + long numPoints = 0; + Shape->get_NumPoints(&numPoints); + double area = 0; + + //calculate cross product for all vertices except last one (repeat of first) + double oneX, oneY, twoX, twoY; + VARIANT_BOOL vbretval; + for (int i = 0; i <= numPoints - 2; i++) + { + Shape->get_XY(i, &oneX, &oneY, &vbretval); + Shape->get_XY(i + 1, &twoX, &twoY, &vbretval); + double trap_area = ((oneX * twoY) - (twoX * oneY)); + area += trap_area; + } + if (area > 0) + { + //if area is positive, polygon vertices are oriented + //counter-clockwise about the normal. + return false; + } + else + { + //polygon vertices are oriented clockwise about the normal. + return true; + } } -bool CUtils::is_clockwise( double x0, double y0, double x1, double y1, double x2, double y2) +bool CUtils::is_clockwise(double x0, double y0, double x1, double y1, double x2, double y2) { - //See http://astronomy.swin.edu.au/~pbourke/geometry/clockwise/, by Paul Bourke - //for better explanation of using cross product to find clockwiseness. - double crossProduct = (x1 - x0)*(y2 - y1) - (y1 - y0)*(x2 - x1); - - //a negative cross product means that we have a clockwise polygon - if( crossProduct < 0 ) - return true; - else - return false; + //See http://astronomy.swin.edu.au/~pbourke/geometry/clockwise/, by Paul Bourke + //for better explanation of using cross product to find clockwiseness. + double crossProduct = (x1 - x0)*(y2 - y1) - (y1 - y0)*(x2 - x1); + + //a negative cross product means that we have a clockwise polygon + if (crossProduct < 0) + return true; + else + return false; } STDMETHODIMP CUtils::get_LastErrorCode(long *pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) + AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *pVal = _lastErrorCode; - _lastErrorCode = tkNO_ERROR; + *pVal = _lastErrorCode; + _lastErrorCode = tkNO_ERROR; - return S_OK; + return S_OK; } STDMETHODIMP CUtils::get_ErrorMsg(long ErrorCode, BSTR *pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - USES_CONVERSION; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + USES_CONVERSION; - *pVal = A2BSTR(ErrorMsg(ErrorCode)); + *pVal = A2BSTR(ErrorMsg(ErrorCode)); - return S_OK; + return S_OK; } STDMETHODIMP CUtils::get_GlobalCallback(ICallback **pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) + AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *pVal = _globalCallback; - if( _globalCallback != NULL ) - { - _globalCallback->AddRef(); - } - return S_OK; + *pVal = _globalCallback; + if (_globalCallback != NULL) + { + _globalCallback->AddRef(); + } + return S_OK; } STDMETHODIMP CUtils::put_GlobalCallback(ICallback *newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - ComHelper::SetRef(newVal, (IDispatch**)&_globalCallback); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + ComHelper::SetRef(newVal, (IDispatch**)&_globalCallback); + return S_OK; } STDMETHODIMP CUtils::get_Key(BSTR *pVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - USES_CONVERSION; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + USES_CONVERSION; - *pVal = OLE2BSTR(_key); + *pVal = OLE2BSTR(_key); - return S_OK; + return S_OK; } STDMETHODIMP CUtils::put_Key(BSTR newVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - USES_CONVERSION; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + USES_CONVERSION; - ::SysFreeString(_key); - _key = OLE2BSTR(newVal); + ::SysFreeString(_key); + _key = OLE2BSTR(newVal); - return S_OK; + return S_OK; } STDMETHODIMP CUtils::GridMerge(VARIANT Grids, BSTR MergeFilename, VARIANT_BOOL InRam, GridFileType GrdFileType, ICallback *cBack, IGrid **retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - USES_CONVERSION; - - CComBSTR final_projection(""); - - ICallback* callback = cBack ? cBack : _globalCallback; - - //Check the Array Type - if( Grids.vt != (VT_ARRAY|VT_DISPATCH) ) - { - *retval = NULL; - ErrorMessage(callback, tkINVALID_VARIANT_TYPE); - return S_OK; - } + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + USES_CONVERSION; - //Check the Array Size - SAFEARRAY * arr = Grids.parray; - SAFEARRAYBOUND arraybound = arr->rgsabound[0]; - if( arraybound.cElements <= 0 ) - { - *retval = NULL; - return S_OK; - } + CComBSTR final_projection(""); - std::deque allGrids; - int gridSize = 0; //size of allGrids -- ah 6/6/03 - - int elements = (int)arraybound.cElements; //ah 6/6/03 - - for( int index = 0; index < elements; index++ ) - { - IUnknown * unknown = NULL; - long ind = index; - SafeArrayGetElement( arr, &ind, &unknown ); - - if( unknown == NULL ) - continue; - - //Determine if the IUnknown supports the IGrid Interface - IGrid * grid = NULL; - if( unknown->QueryInterface( IID_IGrid, (void**)&grid) != S_OK ) - { - *retval = NULL; - ErrorMessage(callback, tkINTERFACE_NOT_SUPPORTED); - - gridSize = (int)allGrids.size(); //ah 6/6/05 - for( int c = 0; c < gridSize; c++ ) - { - allGrids[c]->Release(); - allGrids[c] = NULL; - } - return S_OK; - } - else - allGrids.push_back( grid ); - } + ICallback* callback = cBack ? cBack : _globalCallback; - if( allGrids.size() <= 0 ) - { - *retval = NULL; - return S_OK; - } + //Check the Array Type + if (Grids.vt != (VT_ARRAY | VT_DISPATCH)) + { + *retval = NULL; + ErrorMessage(callback, tkINVALID_VARIANT_TYPE); + return S_OK; + } + + //Check the Array Size + SAFEARRAY * arr = Grids.parray; + SAFEARRAYBOUND arraybound = arr->rgsabound[0]; + if (arraybound.cElements <= 0) + { + *retval = NULL; + return S_OK; + } - gridSize = (int)allGrids.size(); - ProjectionTools * pt = new ProjectionTools(); - for( int i = 0; i < gridSize; i++ ) - { - char * currentComparingProj = NULL; - if( i == 0 ) - { - IGridHeader * header = NULL; - allGrids[i]->get_Header(&header); - header->get_Projection(&final_projection); - currentComparingProj = OLE2A(final_projection); - header->Release(); - } - else - { - CComBSTR bstrProj; - IGridHeader * header = NULL; - allGrids[i]->get_Header(&header); - header->get_Projection(&bstrProj); - char * nextProj = OLE2A(bstrProj); - if (!pt->IsSameProjection(currentComparingProj, nextProj)) - { - ErrorMessage(callback, tkGRID_MERGE_PROJECTION_MISMATCH); - } - header->Release(); - } - } + std::deque allGrids; + int gridSize = 0; //size of allGrids -- ah 6/6/03 - delete pt; - - double final_xllcenter; - double final_yllcenter; - double final_xurcenter; - double final_yurcenter; - double final_dx = 1; - double final_dy = 1; - GridDataType final_dType = UnknownDataType; - long final_ncols, final_nrows; - - double ind_xllcenter; - double ind_yllcenter; - double ind_xurcenter; - double ind_yurcenter; - double ind_dx = 1; - double ind_dy = 1; - GridDataType ind_dType = UnknownDataType; - long ind_ncols, ind_nrows; - VARIANT vndv; - VariantInit(&vndv); - double ndv; - - long percent = 0, cnt = 0; - double total = 0.0; - - //Get the bounds and DataType for the final grid - gridSize = (int)allGrids.size(); - for( int i = 0; i < gridSize; i++ ) - { if( i == 0 ) - { - IGridHeader * header = NULL; - allGrids[i]->get_Header(&header); - header->get_dX(&final_dx); - header->get_dY(&final_dy); - header->get_XllCenter(&final_xllcenter); - header->get_YllCenter(&final_yllcenter); - header->get_NodataValue(&vndv); - dVal(vndv,ndv); - - header->get_NumberCols(&final_ncols); - header->get_NumberRows(&final_nrows); - - final_xurcenter = final_xllcenter + (final_ncols-1)*final_dx; - final_yurcenter = final_yllcenter + (final_nrows-1)*final_dy; - total = final_ncols*final_nrows; - header->Release(); - allGrids[i]->get_DataType(&final_dType); - } - else - { - IGridHeader * header = NULL; - allGrids[i]->get_Header(&header); - header->get_dX(&ind_dx); - header->get_dY(&ind_dy); - header->get_XllCenter(&ind_xllcenter); - header->get_YllCenter(&ind_yllcenter); - - header->get_NumberCols(&ind_ncols); - header->get_NumberRows(&ind_nrows); - - ind_xurcenter = ind_xllcenter + (ind_ncols-1)*ind_dx; - ind_yurcenter = ind_yllcenter + (ind_nrows-1)*ind_dy; - header->Release(); - allGrids[i]->get_DataType(&ind_dType); - - if( ind_dType > final_dType && ind_dType != UnknownDataType ) - final_dType = ind_dType; - - if( ind_dx != final_dx ) - { - *retval = NULL; - ErrorMessage(callback, tkINCOMPATIBLE_DX); - - gridSize = (int)allGrids.size(); - for( int c = 0; c < gridSize; c++ ) - { allGrids[c]->Release(); - allGrids[c] = NULL; - } - VariantClear(&vndv); - return S_OK; - } - else if( ind_dy != final_dy ) - { - *retval = NULL; - ErrorMessage(callback, tkINCOMPATIBLE_DY); - - gridSize = (int)allGrids.size(); - for( int c = 0; c < gridSize; c++ ) - { allGrids[c]->Release(); - allGrids[c] = NULL; - } - VariantClear(&vndv); - return S_OK; - } - - if( ind_xllcenter < final_xllcenter ) - final_xllcenter = ind_xllcenter; - if( ind_yllcenter < final_yllcenter ) - final_yllcenter = ind_yllcenter; - - if( ind_xurcenter > final_xurcenter ) - final_xurcenter = ind_xurcenter; - if( ind_yurcenter > final_yurcenter ) - final_yurcenter = ind_yurcenter; - total += ind_ncols*ind_nrows; - } - } + int elements = (int)arraybound.cElements; //ah 6/6/03 - if( final_dType == UnknownDataType || final_dType == InvalidDataType ) - { - *retval = NULL; - ErrorMessage(callback, tkINVALID_DATA_TYPE); + for (int index = 0; index < elements; index++) + { + IUnknown * unknown = NULL; + long ind = index; + SafeArrayGetElement(arr, &ind, &unknown); + + if (unknown == NULL) + continue; + + //Determine if the IUnknown supports the IGrid Interface + IGrid * grid = NULL; + if (unknown->QueryInterface(IID_IGrid, (void**)&grid) != S_OK) + { + *retval = NULL; + ErrorMessage(callback, tkINTERFACE_NOT_SUPPORTED); + + gridSize = (int)allGrids.size(); //ah 6/6/05 + for (int c = 0; c < gridSize; c++) + { + allGrids[c]->Release(); + allGrids[c] = NULL; + } + return S_OK; + } + else + allGrids.push_back(grid); + } + + if (allGrids.size() <= 0) + { + *retval = NULL; + return S_OK; + } - gridSize = (int)allGrids.size(); - - for( int c = 0; c < gridSize; c++ ) - { allGrids[c]->Release(); - allGrids[c] = NULL; - } - VariantClear(&vndv); - return S_OK; - } + gridSize = (int)allGrids.size(); + ProjectionTools * pt = new ProjectionTools(); + for (int i = 0; i < gridSize; i++) + { + char * currentComparingProj = NULL; + if (i == 0) + { + IGridHeader * header = NULL; + allGrids[i]->get_Header(&header); + header->get_Projection(&final_projection); + currentComparingProj = OLE2A(final_projection); + header->Release(); + } + else + { + CComBSTR bstrProj; + IGridHeader * header = NULL; + allGrids[i]->get_Header(&header); + header->get_Projection(&bstrProj); + char * nextProj = OLE2A(bstrProj); + if (!pt->IsSameProjection(currentComparingProj, nextProj)) + { + ErrorMessage(callback, tkGRID_MERGE_PROJECTION_MISMATCH); + } + header->Release(); + } + } + + delete pt; + + double final_xllcenter; + double final_yllcenter; + double final_xurcenter; + double final_yurcenter; + double final_dx = 1; + double final_dy = 1; + GridDataType final_dType = UnknownDataType; + long final_ncols, final_nrows; + + double ind_xllcenter; + double ind_yllcenter; + double ind_xurcenter; + double ind_yurcenter; + double ind_dx = 1; + double ind_dy = 1; + GridDataType ind_dType = UnknownDataType; + long ind_ncols, ind_nrows; + VARIANT vndv; + VariantInit(&vndv); + double ndv; + + long percent = 0, cnt = 0; + double total = 0.0; + + //Get the bounds and DataType for the final grid + gridSize = (int)allGrids.size(); + for (int i = 0; i < gridSize; i++) + { + if (i == 0) + { + IGridHeader * header = NULL; + allGrids[i]->get_Header(&header); + header->get_dX(&final_dx); + header->get_dY(&final_dy); + header->get_XllCenter(&final_xllcenter); + header->get_YllCenter(&final_yllcenter); + header->get_NodataValue(&vndv); + dVal(vndv, ndv); + + header->get_NumberCols(&final_ncols); + header->get_NumberRows(&final_nrows); + + final_xurcenter = final_xllcenter + (final_ncols - 1)*final_dx; + final_yurcenter = final_yllcenter + (final_nrows - 1)*final_dy; + total = final_ncols*final_nrows; + header->Release(); + allGrids[i]->get_DataType(&final_dType); + } + else + { + IGridHeader * header = NULL; + allGrids[i]->get_Header(&header); + header->get_dX(&ind_dx); + header->get_dY(&ind_dy); + header->get_XllCenter(&ind_xllcenter); + header->get_YllCenter(&ind_yllcenter); + + header->get_NumberCols(&ind_ncols); + header->get_NumberRows(&ind_nrows); + + ind_xurcenter = ind_xllcenter + (ind_ncols - 1)*ind_dx; + ind_yurcenter = ind_yllcenter + (ind_nrows - 1)*ind_dy; + header->Release(); + allGrids[i]->get_DataType(&ind_dType); + + if (ind_dType > final_dType && ind_dType != UnknownDataType) + final_dType = ind_dType; + + if (ind_dx != final_dx) + { + *retval = NULL; + ErrorMessage(callback, tkINCOMPATIBLE_DX); + + gridSize = (int)allGrids.size(); + for (int c = 0; c < gridSize; c++) + { + allGrids[c]->Release(); + allGrids[c] = NULL; + } + VariantClear(&vndv); + return S_OK; + } + else if (ind_dy != final_dy) + { + *retval = NULL; + ErrorMessage(callback, tkINCOMPATIBLE_DY); + + gridSize = (int)allGrids.size(); + for (int c = 0; c < gridSize; c++) + { + allGrids[c]->Release(); + allGrids[c] = NULL; + } + VariantClear(&vndv); + return S_OK; + } + + if (ind_xllcenter < final_xllcenter) + final_xllcenter = ind_xllcenter; + if (ind_yllcenter < final_yllcenter) + final_yllcenter = ind_yllcenter; + + if (ind_xurcenter > final_xurcenter) + final_xurcenter = ind_xurcenter; + if (ind_yurcenter > final_yurcenter) + final_yurcenter = ind_yurcenter; + total += ind_ncols*ind_nrows; + } + } + + if (final_dType == UnknownDataType || final_dType == InvalidDataType) + { + *retval = NULL; + ErrorMessage(callback, tkINVALID_DATA_TYPE); - if( total <= 0 ) - { - *retval = NULL; - gridSize = (int)allGrids.size(); - - for( int c = 0; c < gridSize; c++ ) - { - allGrids[c]->Release(); - allGrids[c] = NULL; - } - VariantClear(&vndv); - return S_OK; - } + gridSize = (int)allGrids.size(); - IGridHeader * final_header = NULL; - CoCreateInstance( CLSID_GridHeader, NULL, CLSCTX_INPROC_SERVER, IID_IGridHeader, (void**)&final_header ); - final_header->put_NumberCols( (long)((final_xurcenter - final_xllcenter)/final_dx)+1 ); - final_header->put_NumberRows( (long)((final_yurcenter - final_yllcenter)/final_dy)+1 ); - final_header->put_dX( final_dx ); - final_header->put_dY( final_dy ); - final_header->put_NodataValue( vndv ); - final_header->put_XllCenter( final_xllcenter ); - final_header->put_YllCenter( final_yllcenter ); - final_header->put_Projection(final_projection); - - VARIANT_BOOL vbretval = FALSE; - CoCreateInstance(CLSID_Grid,NULL,CLSCTX_INPROC_SERVER,IID_IGrid,(void**)retval); - (*retval)->CreateNew( MergeFilename, final_header, final_dType, vndv, InRam, GrdFileType, cBack, &vbretval ); - final_header->Release(); - - if( vbretval == FALSE ) - { - *retval = NULL; - gridSize = (int)allGrids.size(); - for( int c = 0; c < gridSize; c++ ) - { allGrids[c]->Release(); - allGrids[c] = NULL; - } - VariantClear(&vndv); - return S_OK; - } + for (int c = 0; c < gridSize; c++) + { + allGrids[c]->Release(); + allGrids[c] = NULL; + } + VariantClear(&vndv); + return S_OK; + } - //Merge the grids - long xoffset = 0, yoffset = 0; - double val, nodata_value; - VARIANT vval; - VariantInit(&vval); - - gridSize = (int)allGrids.size(); - for( int n = gridSize - 1; n >= 0; n-- ) - { - IGridHeader * header = NULL; - allGrids[n]->get_Header(&header); - header->get_XllCenter(&ind_xllcenter); - header->get_YllCenter(&ind_yllcenter); - header->get_NumberCols(&ind_ncols); - header->get_NumberRows(&ind_nrows); - header->get_NodataValue(&vndv); - dVal(vndv,nodata_value); - header->Release(); - - (*retval)->ProjToCell(ind_xllcenter,ind_yllcenter+(ind_nrows-1)*final_dy,&xoffset,&yoffset); - - for( int j = 0; j < ind_nrows; j++ ) - { - for( int i = 0; i < ind_ncols; i++ ) - { - allGrids[n]->get_Value( i, j, &vval ); - dVal( vval, val ); - - if( val != nodata_value && !(val < -2147483640 && nodata_value < -2147483640)) - (*retval)->put_Value( i + xoffset, j + yoffset, vval ); - - CallbackHelper::Progress(callback, ++cnt, total, "GridMerge", _key, percent); - } - } - } + if (total <= 0) + { + *retval = NULL; + gridSize = (int)allGrids.size(); + + for (int c = 0; c < gridSize; c++) + { + allGrids[c]->Release(); + allGrids[c] = NULL; + } + VariantClear(&vndv); + return S_OK; + } + + IGridHeader * final_header = NULL; + CoCreateInstance(CLSID_GridHeader, NULL, CLSCTX_INPROC_SERVER, IID_IGridHeader, (void**)&final_header); + final_header->put_NumberCols((long)((final_xurcenter - final_xllcenter) / final_dx) + 1); + final_header->put_NumberRows((long)((final_yurcenter - final_yllcenter) / final_dy) + 1); + final_header->put_dX(final_dx); + final_header->put_dY(final_dy); + final_header->put_NodataValue(vndv); + final_header->put_XllCenter(final_xllcenter); + final_header->put_YllCenter(final_yllcenter); + final_header->put_Projection(final_projection); + + VARIANT_BOOL vbretval = FALSE; + CoCreateInstance(CLSID_Grid, NULL, CLSCTX_INPROC_SERVER, IID_IGrid, (void**)retval); + (*retval)->CreateNew(MergeFilename, final_header, final_dType, vndv, InRam, GrdFileType, cBack, &vbretval); + final_header->Release(); + + if (vbretval == FALSE) + { + *retval = NULL; + gridSize = (int)allGrids.size(); + for (int c = 0; c < gridSize; c++) + { + allGrids[c]->Release(); + allGrids[c] = NULL; + } + VariantClear(&vndv); + return S_OK; + } + + //Merge the grids + long xoffset = 0, yoffset = 0; + double val, nodata_value; + VARIANT vval; + VariantInit(&vval); + + gridSize = (int)allGrids.size(); + for (int n = gridSize - 1; n >= 0; n--) + { + IGridHeader * header = NULL; + allGrids[n]->get_Header(&header); + header->get_XllCenter(&ind_xllcenter); + header->get_YllCenter(&ind_yllcenter); + header->get_NumberCols(&ind_ncols); + header->get_NumberRows(&ind_nrows); + header->get_NodataValue(&vndv); + dVal(vndv, nodata_value); + header->Release(); + + (*retval)->ProjToCell(ind_xllcenter, ind_yllcenter + (ind_nrows - 1)*final_dy, &xoffset, &yoffset); + + for (int j = 0; j < ind_nrows; j++) + { + for (int i = 0; i < ind_ncols; i++) + { + allGrids[n]->get_Value(i, j, &vval); + dVal(vval, val); + + if (val != nodata_value && !(val < -2147483640 && nodata_value < -2147483640)) + (*retval)->put_Value(i + xoffset, j + yoffset, vval); + + CallbackHelper::Progress(callback, ++cnt, total, "GridMerge", _key, percent); + } + } + } + + gridSize = (int)allGrids.size(); + for (int c = 0; c < gridSize; c++) + { + allGrids[c]->Release(); + allGrids[c] = NULL; + } - gridSize = (int)allGrids.size(); - for( int c = 0; c < gridSize; c++ ) - { allGrids[c]->Release(); - allGrids[c] = NULL; - } + VariantClear(&vndv); + VariantClear(&vval); - VariantClear(&vndv); - VariantClear(&vval); - - return S_OK; + return S_OK; } STDMETHODIMP CUtils::ShapeMerge(IShapefile *Shapes, long IndexOne, long IndexTwo, ICallback *cBack, IShape **retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - ErrorMessage(tkMETHOD_NOT_IMPLEMENTED); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + ErrorMessage(tkMETHOD_NOT_IMPLEMENTED); + return S_OK; } STDMETHODIMP CUtils::GridToGrid(IGrid *Grid, GridDataType OutDataType, ICallback *cBack, IGrid **retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - USES_CONVERSION; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + USES_CONVERSION; - ICallback* callback = cBack ? cBack : _globalCallback; + ICallback* callback = cBack ? cBack : _globalCallback; - if( Grid == NULL ) - { - *retval = NULL; - ErrorMessage(callback, tkUNEXPECTED_NULL_PARAMETER); - return S_OK; - } + if (Grid == NULL) + { + *retval = NULL; + ErrorMessage(callback, tkUNEXPECTED_NULL_PARAMETER); + return S_OK; + } - long ncols = 0, nrows = 0; + long ncols = 0, nrows = 0; - IGridHeader * header = NULL; - Grid->get_Header(&header); - header->get_NumberCols(&ncols); - header->get_NumberRows(&nrows); + IGridHeader * header = NULL; + Grid->get_Header(&header); + header->get_NumberCols(&ncols); + header->get_NumberRows(&nrows); - if( ncols <= 0 || nrows <= 0 ) - { - *retval = NULL; - ErrorMessage(callback, tkZERO_ROWS_OR_COLS); - return S_OK; - } + if (ncols <= 0 || nrows <= 0) + { + *retval = NULL; + ErrorMessage(callback, tkZERO_ROWS_OR_COLS); + return S_OK; + } - VARIANT vndv; - VariantInit(&vndv); - header->get_NodataValue(&vndv); + VARIANT vndv; + VariantInit(&vndv); + header->get_NodataValue(&vndv); - CoCreateInstance(CLSID_Grid,NULL,CLSCTX_INPROC_SERVER,IID_IGrid,(void**)retval); - VARIANT_BOOL vbretval; - (*retval)->CreateNew(m_globalSettings.emptyBstr, header, OutDataType, vndv, TRUE, UseExtension, cBack, &vbretval); - header->Release(); + CoCreateInstance(CLSID_Grid, NULL, CLSCTX_INPROC_SERVER, IID_IGrid, (void**)retval); + VARIANT_BOOL vbretval; + (*retval)->CreateNew(m_globalSettings.emptyBstr, header, OutDataType, vndv, TRUE, UseExtension, cBack, &vbretval); + header->Release(); - VARIANT val; - VariantInit(&val); - long percent = 0, newpercent = 0; - - for( int j = 0; j < nrows; j++ ) - { for( int i = 0; i < ncols; i++ ) - { - Grid->get_Value(i,j,&val); - (*retval)->put_Value(i,j,val); - } - - CallbackHelper::Progress(callback, j, nrows, "Grid2Grid", _key, percent); - } + VARIANT val; + VariantInit(&val); + long percent = 0, newpercent = 0; - VariantClear(&vndv); - VariantClear(&val); + for (int j = 0; j < nrows; j++) + { + for (int i = 0; i < ncols; i++) + { + Grid->get_Value(i, j, &val); + (*retval)->put_Value(i, j, val); + } - return S_OK; + CallbackHelper::Progress(callback, j, nrows, "Grid2Grid", _key, percent); + } + + VariantClear(&vndv); + VariantClear(&val); + + return S_OK; } STDMETHODIMP CUtils::ShapeToShapeZ(IShapefile * Shapefile, IGrid *Grid, ICallback *cBack, IShapefile **retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - - if( Shapefile == NULL || Grid == NULL ) - { - *retval = NULL; - this->ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); - return S_OK; - } - - long ncols = 0, nrows = 0; - IGridHeader * header = NULL; - Grid->get_Header(&header); - header->get_NumberCols(&ncols); - header->get_NumberRows(&nrows); - header->Release(); - - double left=0, bottom=0, right=0, top=0; - Grid->CellToProj(0,0,&left,&bottom); - Grid->CellToProj(ncols-1,nrows-1,&right,&top); - - IExtents * box = NULL; - Shapefile->get_Extents(&box); - double s_left=0,s_bottom=0,s_right=0,s_top=0,nval; - box->GetBounds(&s_left,&s_bottom,&nval,&s_right,&s_top,&nval); - box->Release(); - - //Check the bounds - if( s_left < left || s_right > right || s_bottom < bottom || s_top < top ) - { - *retval = NULL; - this->ErrorMessage(tkSHAPEFILE_LARGER_THAN_GRID); - return S_OK; - } - - VARIANT_BOOL vbretval; - ShpfileType shapetype = SHP_NULLSHAPE; - Shapefile->get_ShapefileType(&shapetype); - CoCreateInstance(CLSID_Shapefile,NULL,CLSCTX_INPROC_SERVER,IID_IShapefile,(void**)retval); - (*retval)->CreateNew(m_globalSettings.emptyBstr,shapetype,&vbretval); - - long numFields = 0; - Shapefile->get_NumFields(&numFields); - for( int i = 0; i < numFields; i++ ) - { - IField * field = NULL; - Shapefile->get_Field(i,&field); - long fpos = i; - (*retval)->EditInsertField(field,&fpos,cBack,&vbretval); - field->Release(); - } - - long numShapes = 0; - Shapefile->get_NumShapes(&numShapes); - for( int j = 0; j < numShapes; j++ ) - { - IShape * shape = NULL; - Shapefile->get_Shape(j,&shape); - - long numPoints = 0; - shape->get_NumPoints(&numPoints); - int p = 0; - for( p = 0; p < numPoints; p++ ) - { - double x = 0, y = 0; - double z = 0; - - IPoint * point = NULL; - shape->get_Point(p,&point); - point->get_X(&x); - point->get_Y(&y); - point->put_Z(z); - point->Release(); - - long col = 0, row = 0; - Grid->ProjToCell(x,y,&col,&row); - VARIANT val; - VariantInit(&val); - Grid->get_Value(col,row,&val); - dVal(val,z); - VariantClear(&val); - } - long spos = p; - (*retval)->EditInsertShape(shape,&spos,&vbretval); - shape->Release(); - - VARIANT cval; - VariantInit(&cval); - for( int k = 0; k < numFields; k++ ) - { - Shapefile->get_CellValue(k,j,&cval); - (*retval)->EditCellValue(k,j,cval,&vbretval); - } - VariantClear(&cval); - } - - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + + if (Shapefile == NULL || Grid == NULL) + { + *retval = NULL; + this->ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); + return S_OK; + } + + long ncols = 0, nrows = 0; + IGridHeader * header = NULL; + Grid->get_Header(&header); + header->get_NumberCols(&ncols); + header->get_NumberRows(&nrows); + header->Release(); + + double left = 0, bottom = 0, right = 0, top = 0; + Grid->CellToProj(0, 0, &left, &bottom); + Grid->CellToProj(ncols - 1, nrows - 1, &right, &top); + + IExtents * box = NULL; + Shapefile->get_Extents(&box); + double s_left = 0, s_bottom = 0, s_right = 0, s_top = 0, nval; + box->GetBounds(&s_left, &s_bottom, &nval, &s_right, &s_top, &nval); + box->Release(); + + //Check the bounds + if (s_left < left || s_right > right || s_bottom < bottom || s_top < top) + { + *retval = NULL; + this->ErrorMessage(tkSHAPEFILE_LARGER_THAN_GRID); + return S_OK; + } + + VARIANT_BOOL vbretval; + ShpfileType shapetype = SHP_NULLSHAPE; + Shapefile->get_ShapefileType(&shapetype); + CoCreateInstance(CLSID_Shapefile, NULL, CLSCTX_INPROC_SERVER, IID_IShapefile, (void**)retval); + (*retval)->CreateNew(m_globalSettings.emptyBstr, shapetype, &vbretval); + + long numFields = 0; + Shapefile->get_NumFields(&numFields); + for (int i = 0; i < numFields; i++) + { + IField * field = NULL; + Shapefile->get_Field(i, &field); + long fpos = i; + (*retval)->EditInsertField(field, &fpos, cBack, &vbretval); + field->Release(); + } + + long numShapes = 0; + Shapefile->get_NumShapes(&numShapes); + for (int j = 0; j < numShapes; j++) + { + IShape * shape = NULL; + Shapefile->get_Shape(j, &shape); + + long numPoints = 0; + shape->get_NumPoints(&numPoints); + int p = 0; + for (p = 0; p < numPoints; p++) + { + double x = 0, y = 0; + double z = 0; + + IPoint * point = NULL; + shape->get_Point(p, &point); + point->get_X(&x); + point->get_Y(&y); + point->put_Z(z); + point->Release(); + + long col = 0, row = 0; + Grid->ProjToCell(x, y, &col, &row); + VARIANT val; + VariantInit(&val); + Grid->get_Value(col, row, &val); + dVal(val, z); + VariantClear(&val); + } + long spos = p; + (*retval)->EditInsertShape(shape, &spos, &vbretval); + shape->Release(); + + VARIANT cval; + VariantInit(&cval); + for (int k = 0; k < numFields; k++) + { + Shapefile->get_CellValue(k, j, &cval); + (*retval)->EditCellValue(k, j, cval, &vbretval); + } + VariantClear(&cval); + } + + return S_OK; } STDMETHODIMP CUtils::TinToShapefile(ITin *Tin, ShpfileType Type, ICallback *cBack, IShapefile **retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - USES_CONVERSION; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + USES_CONVERSION; - if( Tin == NULL ) - { - *retval = NULL; - this->ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); - return S_OK; - } + if (Tin == NULL) + { + *retval = NULL; + this->ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); + return S_OK; + } - ICallback* callback = cBack ? cBack : _globalCallback; - - long numTriangles = 0; - Tin->get_NumTriangles(&numTriangles); - long numVertices = 0; - Tin->get_NumVertices(&numVertices); - long percent = 0, newpercent = 0; - double total = numTriangles; - - CoCreateInstance(CLSID_Shapefile,NULL,CLSCTX_INPROC_SERVER,IID_IShapefile,(void**)retval); - - if( Type == SHP_POLYGON || Type == SHP_POLYGONZ ) - { - //Create shapefile - VARIANT_BOOL vbretval; - (*retval)->CreateNew(m_globalSettings.emptyBstr, Type, &vbretval); - - long pos = 0; - IField * field = NULL; - CoCreateInstance(CLSID_Field,NULL,CLSCTX_INPROC_SERVER,IID_IField,(void**)&field); - CComBSTR bstrName("Tin"); - field->put_Name(bstrName); - field->put_Type(INTEGER_FIELD); - field->put_Width(5); - (*retval)->EditInsertField(field,&pos,cBack,&vbretval); - field->Release(); - field = NULL; - - long vtx1, vtx2, vtx3; - double x1, y1, z1; - double x2, y2, z2; - double x3, y3, z3; - - for( long i = 0; i < numTriangles; i++ ) - { - Tin->Triangle(i,&vtx1,&vtx2,&vtx3); - Tin->Vertex(vtx1,&x1,&y1,&z1); - Tin->Vertex(vtx2,&x3,&y3,&z3); - Tin->Vertex(vtx3,&x2,&y2,&z2); - - IShape * shape = NULL; - ComHelper::CreateShape(&shape); - shape->Create(Type,&vbretval); - - pos = 0; - shape->InsertPart(0,&pos,&vbretval); - - IPoint * point = NULL; - ComHelper::CreatePoint(&point); - point->put_X(x1); - point->put_Y(y1); - point->put_Z(z1); - pos = 0; - shape->InsertPoint(point,&pos,&vbretval); - point->Release(); - point = NULL; - - ComHelper::CreatePoint(&point); - point->put_X(x2); - point->put_Y(y2); - point->put_Z(z2); - pos = 1; - shape->InsertPoint(point,&pos,&vbretval); - point->Release(); - point = NULL; - - ComHelper::CreatePoint(&point); - point->put_X(x3); - point->put_Y(y3); - point->put_Z(z3); - pos = 2; - shape->InsertPoint(point,&pos,&vbretval); - point->Release(); - point = NULL; - - ComHelper::CreatePoint(&point); - point->put_X(x1); - point->put_Y(y1); - point->put_Z(z1); - pos = 3; - shape->InsertPoint(point,&pos,&vbretval); - point->Release(); - point = NULL; - - pos = i; - (*retval)->EditInsertShape(shape,&pos,&vbretval); - shape->Release(); - shape = NULL; - - VARIANT val; - VariantInit(&val); - val.vt = VT_I4; - val.lVal = i; - (*retval)->EditCellValue(0,pos,val,&vbretval); - - CallbackHelper::Progress(callback, i, total, "TinToShapefile", _key, percent); - - VariantClear(&val); - - } - } - else if( Type == SHP_POINT || Type == SHP_POINTZ ) - { - //Create shapefile - VARIANT_BOOL vbretval; - (*retval)->CreateNew(m_globalSettings.emptyBstr, Type, &vbretval); - - long pos = 0; - IField * field = NULL; - CoCreateInstance(CLSID_Field,NULL,CLSCTX_INPROC_SERVER,IID_IField,(void**)&field); - - CComBSTR bstrName("Vertex"); - field->put_Name(bstrName); - field->put_Type(INTEGER_FIELD); - field->put_Width(5); - (*retval)->EditInsertField(field,&pos,cBack,&vbretval); - field->Release(); - field = NULL; - - double x1, y1, z1; - - for( long i = 0; i < numVertices; i++ ) - { - Tin->Vertex(i,&x1,&y1,&z1); - - IShape * shape = NULL; - ComHelper::CreateShape(&shape); - shape->Create(Type,&vbretval); - - IPoint * point = NULL; - ComHelper::CreatePoint(&point); - point->put_X(x1); - point->put_Y(y1); - point->put_Z(z1); - pos = 0; - shape->InsertPoint(point,&pos,&vbretval); - point->Release(); - point = NULL; - - (*retval)->EditInsertShape(shape,&i,&vbretval); - shape->Release(); - shape = NULL; - - VARIANT val; - VariantInit(&val); - val.vt = VT_I4; - val.lVal = i; - (*retval)->EditCellValue(0,i,val,&vbretval); - - CallbackHelper::Progress(callback, i, total, "TinToShapefile", _key, percent); - - VariantClear(&val); - } - } + ICallback* callback = cBack ? cBack : _globalCallback; - if (*retval) - { - ((CShapefile*)(*retval))->ValidateOutput(retval, "GridToShapefile", "Utils"); - } + long numTriangles = 0; + Tin->get_NumTriangles(&numTriangles); + long numVertices = 0; + Tin->get_NumVertices(&numVertices); + long percent = 0, newpercent = 0; + double total = numTriangles; - return S_OK; + CoCreateInstance(CLSID_Shapefile, NULL, CLSCTX_INPROC_SERVER, IID_IShapefile, (void**)retval); + + if (Type == SHP_POLYGON || Type == SHP_POLYGONZ) + { + //Create shapefile + VARIANT_BOOL vbretval; + (*retval)->CreateNew(m_globalSettings.emptyBstr, Type, &vbretval); + + long pos = 0; + IField * field = NULL; + CoCreateInstance(CLSID_Field, NULL, CLSCTX_INPROC_SERVER, IID_IField, (void**)&field); + CComBSTR bstrName("Tin"); + field->put_Name(bstrName); + field->put_Type(INTEGER_FIELD); + field->put_Width(5); + (*retval)->EditInsertField(field, &pos, cBack, &vbretval); + field->Release(); + field = NULL; + + long vtx1, vtx2, vtx3; + double x1, y1, z1; + double x2, y2, z2; + double x3, y3, z3; + + for (long i = 0; i < numTriangles; i++) + { + Tin->Triangle(i, &vtx1, &vtx2, &vtx3); + Tin->Vertex(vtx1, &x1, &y1, &z1); + Tin->Vertex(vtx2, &x3, &y3, &z3); + Tin->Vertex(vtx3, &x2, &y2, &z2); + + IShape * shape = NULL; + ComHelper::CreateShape(&shape); + shape->Create(Type, &vbretval); + + pos = 0; + shape->InsertPart(0, &pos, &vbretval); + + IPoint * point = NULL; + ComHelper::CreatePoint(&point); + point->put_X(x1); + point->put_Y(y1); + point->put_Z(z1); + pos = 0; + shape->InsertPoint(point, &pos, &vbretval); + point->Release(); + point = NULL; + + ComHelper::CreatePoint(&point); + point->put_X(x2); + point->put_Y(y2); + point->put_Z(z2); + pos = 1; + shape->InsertPoint(point, &pos, &vbretval); + point->Release(); + point = NULL; + + ComHelper::CreatePoint(&point); + point->put_X(x3); + point->put_Y(y3); + point->put_Z(z3); + pos = 2; + shape->InsertPoint(point, &pos, &vbretval); + point->Release(); + point = NULL; + + ComHelper::CreatePoint(&point); + point->put_X(x1); + point->put_Y(y1); + point->put_Z(z1); + pos = 3; + shape->InsertPoint(point, &pos, &vbretval); + point->Release(); + point = NULL; + + pos = i; + (*retval)->EditInsertShape(shape, &pos, &vbretval); + shape->Release(); + shape = NULL; + + VARIANT val; + VariantInit(&val); + val.vt = VT_I4; + val.lVal = i; + (*retval)->EditCellValue(0, pos, val, &vbretval); + + CallbackHelper::Progress(callback, i, total, "TinToShapefile", _key, percent); + + VariantClear(&val); + + } + } + else if (Type == SHP_POINT || Type == SHP_POINTZ) + { + //Create shapefile + VARIANT_BOOL vbretval; + (*retval)->CreateNew(m_globalSettings.emptyBstr, Type, &vbretval); + + long pos = 0; + IField * field = NULL; + CoCreateInstance(CLSID_Field, NULL, CLSCTX_INPROC_SERVER, IID_IField, (void**)&field); + + CComBSTR bstrName("Vertex"); + field->put_Name(bstrName); + field->put_Type(INTEGER_FIELD); + field->put_Width(5); + (*retval)->EditInsertField(field, &pos, cBack, &vbretval); + field->Release(); + field = NULL; + + double x1, y1, z1; + + for (long i = 0; i < numVertices; i++) + { + Tin->Vertex(i, &x1, &y1, &z1); + + IShape * shape = NULL; + ComHelper::CreateShape(&shape); + shape->Create(Type, &vbretval); + + IPoint * point = NULL; + ComHelper::CreatePoint(&point); + point->put_X(x1); + point->put_Y(y1); + point->put_Z(z1); + pos = 0; + shape->InsertPoint(point, &pos, &vbretval); + point->Release(); + point = NULL; + + (*retval)->EditInsertShape(shape, &i, &vbretval); + shape->Release(); + shape = NULL; + + VARIANT val; + VariantInit(&val); + val.vt = VT_I4; + val.lVal = i; + (*retval)->EditCellValue(0, i, val, &vbretval); + + CallbackHelper::Progress(callback, i, total, "TinToShapefile", _key, percent); + + VariantClear(&val); + } + } + + if (*retval) + { + ((CShapefile*)(*retval))->ValidateOutput(retval, "GridToShapefile", "Utils"); + } + + return S_OK; } @@ -1391,17 +1438,18 @@ STDMETHODIMP CUtils::TinToShapefile(ITin *Tin, ShpfileType Type, ICallback *cBac # define TRACE_BORDER -2228 # define CURRENT_POLYGON -2229 - enum UTILS_DIRECTION - { NONE, - RIGHT, - UPRIGHT, - UP, - UPLEFT, - LEFT, - DOWNLEFT, - DOWN, - DOWNRIGHT - }; +enum UTILS_DIRECTION +{ + NONE, + RIGHT, + UPRIGHT, + UP, + UPLEFT, + LEFT, + DOWNLEFT, + DOWN, + DOWNRIGHT +}; # endif @@ -1410,1230 +1458,1259 @@ STDMETHODIMP CUtils::TinToShapefile(ITin *Tin, ShpfileType Type, ICallback *cBac // *************************************************** STDMETHODIMP CUtils::GridToShapefile(IGrid *Grid, IGrid *ConnectionGrid, ICallback *cBack, IShapefile **retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - - ICallback* callback = cBack ? cBack : _globalCallback; - - if( Grid == NULL ) - { *retval = NULL; - ErrorMessage(callback, tkUNEXPECTED_NULL_PARAMETER); - return S_OK; - } - - //Create a grid that is twice the size of the original - IGridHeader * header = NULL; - Grid->get_Header(&header); - long cols = 0, rows = 0; - header->get_NumberCols(&cols); - header->get_NumberRows(&rows); - - GridDataType dType = UnknownDataType; - Grid->get_DataType(&dType); - - double dx, dy, xllCenter, yllCenter; - - VARIANT vndv; - VariantInit(&vndv); - header->get_NodataValue(&vndv); - header->get_dX(&dx); - header->get_dY(&dy); - header->get_XllCenter(&xllCenter); - header->get_YllCenter(&yllCenter); - header->Release(); - header = NULL; - - if( cols <= 0 || rows <= 0 ) - { *retval = NULL; - ErrorMessage(callback, tkZERO_ROWS_OR_COLS); - VariantClear(&vndv); - return S_OK; - } - - long exp_cols = 2*cols + 1; - long exp_rows = 2*rows + 1; - - long percent = 0, newpercent = 0; - double polygon_id = 0; - double nodata_value = -1; - dVal(vndv,nodata_value); - - double expand_nodata_value = nodata_value; - double total = rows; - - VARIANT_BOOL vbretval = FALSE; - - CoCreateInstance(CLSID_Grid,NULL,CLSCTX_INPROC_SERVER,IID_IGrid,(void**)&_expand_grid); - IGridHeader * expand_header = NULL; - CoCreateInstance(CLSID_GridHeader,NULL,CLSCTX_INPROC_SERVER,IID_IGridHeader,(void**)&expand_header); - expand_header->put_dX( dx*.5 ); - expand_header->put_dY( dy*.5 ); - expand_header->put_XllCenter( xllCenter - dx*.5 ); - expand_header->put_YllCenter( yllCenter - dy*.5 ); - expand_header->put_NodataValue( vndv ); - expand_header->put_NumberCols( exp_cols ); - expand_header->put_NumberRows( exp_rows ); - - CString tempFilename = Utility::GetTempFilename(".bgd"); - CComBSTR bstr(tempFilename); - _expand_grid->CreateNew(bstr, expand_header, dType, vndv, VARIANT_TRUE, UseExtension, cBack, &vbretval); - - expand_header->Release(); - expand_header = NULL; - - _connection_grid = ConnectionGrid; - - double expand_value = 0; - for( int y = rows - 1; y>= 0; y-- ) - { for( int x = 0; x < cols; x++ ) - { - polygon_id = getValue( Grid, x, y ); - - //Expand as the center cell - int expand_x = 2*x + 1; - int expand_y = 2*y + 1; - - //Expanded Cell arrangement - //4 3 2 - //5 0 1 - //6 7 8 - - //Mark the cell as a border if it already has a value - //Cell 0 - expand_value = getValue( _expand_grid, expand_x, expand_y ); - //Set the polygon_id - if( expand_value == expand_nodata_value ) - setValue( _expand_grid, expand_x, expand_y, polygon_id ); - - //Cell 1 - expand_value = getValue( _expand_grid, expand_x + 1, expand_y ); - //set the polygon id - if( expand_value == expand_nodata_value ) - setValue( _expand_grid, expand_x + 1, expand_y, polygon_id ); - //polygon_id already exists and is the same... reset it - else if( expand_value == polygon_id ) - setValue( _expand_grid, expand_x + 1, expand_y, polygon_id ); - //set the cell as a border - else if( expand_value >= 0 ) - setValue(_expand_grid, expand_x + 1, expand_y, BORDER ); - - //Cell 2 - expand_value = getValue( _expand_grid, expand_x + 1, expand_y + 1 ); - if( expand_value == expand_nodata_value ) - setValue( _expand_grid, expand_x + 1, expand_y + 1, polygon_id ); - else if( expand_value == polygon_id ) - setValue( _expand_grid, expand_x + 1, expand_y + 1, polygon_id ); - else if( expand_value >= 0 ) - setValue( _expand_grid, expand_x + 1, expand_y + 1, BORDER ); - else if( expand_value == BORDER ) - { expand_value -= 1; - setValue( _expand_grid, expand_x + 1, expand_y + 1, expand_value ); - } - else if( expand_value == DOUBLE_BORDER ) - { if( is_decision( _expand_grid, expand_x + 1, expand_y + 1 ) ) - setValue( _expand_grid, expand_x + 1, expand_y + 1, DECISION ); - else - setValue( _expand_grid, expand_x + 1, expand_y + 1, BORDER ); - } - - //Cell 3 - expand_value = getValue( _expand_grid, expand_x, expand_y + 1 ); - //set the polygon id - if( expand_value == expand_nodata_value ) - setValue( _expand_grid, expand_x, expand_y + 1, polygon_id ); - //polygon_id already exists and is the same... reset it - else if( expand_value == polygon_id ) - setValue( _expand_grid, expand_x, expand_y + 1, polygon_id ); - //set the cell as a border - else if( expand_value >= 0 ) - setValue( _expand_grid, expand_x, expand_y + 1, BORDER ); - - //Cell 4 - expand_value = getValue( _expand_grid, expand_x - 1, expand_y + 1 ); - if( expand_value == expand_nodata_value ) - setValue( _expand_grid, expand_x - 1, expand_y + 1, polygon_id ); - else if( expand_value == polygon_id ) - setValue( _expand_grid, expand_x - 1, expand_y + 1, polygon_id ); - else if( expand_value >= 0 ) - setValue( _expand_grid, expand_x - 1, expand_y + 1, BORDER ); - else if( expand_value == BORDER ) - { expand_value -= 1; - setValue( _expand_grid, expand_x - 1, expand_y + 1, expand_value ); - } - else if( expand_value == DOUBLE_BORDER ) - { if( is_decision( _expand_grid, expand_x - 1, expand_y + 1 ) ) - setValue( _expand_grid, expand_x - 1, expand_y + 1, DECISION ); - else - setValue( _expand_grid, expand_x - 1, expand_y + 1, BORDER ); - } - - //Cell 5 - expand_value = getValue( _expand_grid, expand_x - 1, expand_y ); - //set the polygon id - if( expand_value == expand_nodata_value ) - setValue( _expand_grid, expand_x - 1, expand_y, polygon_id ); - //polygon_id already exists and is the same... reset it - else if( expand_value == polygon_id ) - setValue( _expand_grid, expand_x - 1, expand_y, polygon_id ); - //set the cell as a border - else if( expand_value >= 0 ) - setValue( _expand_grid, expand_x - 1, expand_y, BORDER ); - - //Cell 6 - expand_value = getValue( _expand_grid, expand_x - 1, expand_y - 1 ); - if( expand_value == expand_nodata_value ) - setValue( _expand_grid, expand_x - 1, expand_y - 1, polygon_id ); - else if( expand_value == polygon_id ) - setValue( _expand_grid, expand_x - 1, expand_y - 1, polygon_id ); - else if( expand_value >= 0 ) - setValue( _expand_grid, expand_x - 1, expand_y - 1, BORDER ); - else if( expand_value == BORDER ) - { expand_value -= 1; - setValue( _expand_grid, expand_x - 1, expand_y - 1, expand_value ); - } - else if( expand_value == DOUBLE_BORDER ) - { if( is_decision( _expand_grid, expand_x - 1, expand_y - 1 ) ) - setValue( _expand_grid, expand_x - 1, expand_y - 1, DECISION ); - else - setValue( _expand_grid, expand_x - 1, expand_y - 1, BORDER ); - } - - //Cell 7 - expand_value = getValue( _expand_grid, expand_x, expand_y - 1 ); - //set the polygon id - if( expand_value == expand_nodata_value ) - setValue( _expand_grid, expand_x, expand_y - 1, polygon_id ); - //polygon_id already exists and is the same... reset it - else if( expand_value == polygon_id ) - setValue( _expand_grid, expand_x, expand_y - 1, polygon_id ); - //set the cell as a border - else if( expand_value >= 0 ) - setValue( _expand_grid, expand_x, expand_y - 1, BORDER ); - - //Cell 8 - expand_value = getValue( _expand_grid, expand_x + 1, expand_y - 1 ); - if( expand_value == expand_nodata_value ) - setValue( _expand_grid, expand_x + 1, expand_y - 1, polygon_id ); - else if( expand_value == polygon_id ) - setValue( _expand_grid, expand_x + 1, expand_y - 1, polygon_id ); - else if( expand_value >= 0 ) - setValue( _expand_grid, expand_x + 1, expand_y - 1, BORDER ); - else if( expand_value == BORDER ) - { expand_value -= 1; - setValue( _expand_grid, expand_x + 1, expand_y - 1, expand_value ); - } - else if( expand_value == DOUBLE_BORDER ) - { if( is_decision( _expand_grid, expand_x + 1, expand_y - 1 ) ) - setValue( _expand_grid, expand_x + 1, expand_y - 1, DECISION ); - else - setValue( _expand_grid, expand_x + 1, expand_y - 1, BORDER ); - } - } - - CallbackHelper::Progress(callback, rows - y - 1, total, "Polygon Creation: Expanding Grid", _key, percent); - } - - //Mark edges of the expanded grid that have a polygon id as BORDER - // and reset DOUBLE_BORDER to BORDER - //Mark edges of a polygon that have a nodata_value by them - newpercent = 0; - percent = 0; - total = exp_rows; - for( int j = 0; j < exp_rows; j++ ) - { - for( int i = 0; i < exp_cols; i++ ) - { - polygon_id = getValue( _expand_grid, i, j ); - if( polygon_id != expand_nodata_value ) - { - if( j == 0 ) - setValue( _expand_grid, i, 0, BORDER ); - else if( j == exp_rows - 1 ) - setValue( _expand_grid, i, exp_rows - 1, BORDER ); - else if( i == 0 ) - setValue( _expand_grid, 0, j, BORDER ); - else if( i == exp_cols - 1 ) - setValue( _expand_grid, exp_cols - 1, j, BORDER ); - else - { - if( polygon_id == DOUBLE_BORDER ) - setValue( _expand_grid, i, j, BORDER ); - //Convex polygon test - else if( polygon_id == DECISION ) - { - if (_connection_grid != NULL) - { - long contract_x = ( i - 1 )/2; - long contract_y = ( j - 1 )/2; - - IGridHeader * cHeader = NULL; - _connection_grid->get_Header(&cHeader); - VARIANT cndv; - VariantInit(&cndv); - cHeader->get_NodataValue(&cndv); - cHeader->Release(); - cHeader = NULL; - - double nodata = -1; - dVal(cndv,nodata); - - VariantClear(&cndv); - - double decision = getValue( _connection_grid, contract_x, contract_y ); - if( decision == nodata ) - { _expand_grid->Close(&vbretval); - *retval = NULL; - ErrorMessage(callback, tkCONCAVE_POLYGONS); - return S_OK; - } - } - } - //Test to see if it is on a nodata border - else - { //4 3 2 - //5 0 1 - //6 7 8 - - //Cell 1 - double cell1 = getValue( _expand_grid, i+1, j ); - //Cell 2 - double cell2 = getValue( _expand_grid, i+1, j-1 ); - //Cell 3 - double cell3 = getValue( _expand_grid, i, j-1 ); - //Cell 4 - double cell4 = getValue( _expand_grid, i-1, j-1 ); - //Cell 5 - double cell5 = getValue( _expand_grid, i-1, j ); - //Cell 6 - double cell6 = getValue( _expand_grid, i-1, j+1 ); - //Cell 7 - double cell7 = getValue( _expand_grid, i, j+1 ); - //Cell 8 - double cell8 = getValue( _expand_grid, i+1, j+1 ); - - if( cell1 == expand_nodata_value || - cell2 == expand_nodata_value || - cell3 == expand_nodata_value || - cell4 == expand_nodata_value || - cell5 == expand_nodata_value || - cell6 == expand_nodata_value || - cell7 == expand_nodata_value || - cell8 == expand_nodata_value ) - { - if( i%2 == 0 && j%2 == 0 ) - { if( is_decision( _expand_grid, i, j ) ) - setValue( _expand_grid, i, j, DECISION ); - else - setValue( _expand_grid, i, j, BORDER ); - } - else - setValue( _expand_grid, i, j, BORDER ); - } - } - } - } - } - - CallbackHelper::Progress(callback, j, total, "Polygon Creation: Marking Borders", _key, percent); - } - - //Create the shapefile - CoCreateInstance(CLSID_Shapefile,NULL,CLSCTX_INPROC_SERVER,IID_IShapefile,(void**)retval); - (*retval)->CreateNew(m_globalSettings.emptyBstr, SHP_POLYGON, &vbretval); - - long fieldpos = 0; - IField * field = NULL; - CoCreateInstance(CLSID_Field,NULL,CLSCTX_INPROC_SERVER,IID_IField,(void**)&field); - CComBSTR bstrName("PolygonID"); - field->put_Name(bstrName); - field->put_Type(INTEGER_FIELD); - field->put_Width(10); - (*retval)->EditInsertField(field,&fieldpos,cBack,&vbretval); - field->Release(); - field = NULL; - - //Create the polygons - //Only look at the center value of the cells - std::deque< RasterPoint > polygon; - int number_polygons = 0; - double xllcenter = xllCenter - dx*.5; - double yllcenter = yllCenter - dy*.5; - double nodata = expand_nodata_value; - - total = rows; - newpercent = 0; - percent = 0; - for( int py = 0; py < rows; py++) //rows - 1; py>= 0; py-- ) - { for( int px = 0; px < cols; px++ ) - { - //Expand as the center cell - long expand_x = 2*px + 1; - long expand_y = 2*py + 1; - polygon_id = getValue( _expand_grid, expand_x, expand_y ); - - //Check to see if polygon id is a specially marked cell or nodata - if( polygon_id >= 0 ) - //if( polygon_id == 148 ) - { - //Clear the polygon list - polygon.clear(); - - //Mark the edge with - // a special value. - mark_edge( polygon_id, expand_x - 1, expand_y - 1 ); - - //Always start Trace from Cell 3 - trace_polygon( expand_x - 1, expand_y - 1, polygon ); - - //Flood fill the polygon with nodata - scan_fill_to_edge( nodata, expand_x, expand_y + 1 ); - - if( polygon.size() > 3 ) - { - long endIndex = polygon.size()-1; - - double x1 = polygon[0].column, - x2 = polygon[1].column, - y1 = polygon[0].row, - y2 = polygon[1].row; - - cppVector v1(x2 - x1,y2 - y1,0.0); - - x1 = polygon[endIndex].column; - x2 = polygon[0].column; - y1 = polygon[endIndex].row; - y2 = polygon[0].row; - - cppVector v2(x2 - x1,y2 - y1,0.0); - - cppVector cross = v1.crossProduct(v2); - - //Invert Polygon to Clockwise if necessary - //Counter ClockWise - if( cross.getk() > 0.0 ) - { - std::deque reverse_polygon; - for( int point = 0; point < (int)polygon.size(); point++ ) - reverse_polygon.push_front( polygon[point] ); - polygon = reverse_polygon; - } - - - //Write the final polygon - IShape * shape = NULL; - ComHelper::CreateShape(&shape); - shape->Create(SHP_POLYGON,&vbretval); - long ppos = 0; - shape->InsertPart(0,&ppos,&vbretval); - - double poly_x = 0, poly_y = 0; - long ras_x = 0, ras_y = 0; - long last_point_index = -1; - int point = 0; - for( point = 0; point < (int)polygon.size(); point++) //(int)polygon.size()-1; point >= 0; point-- ) - { - ras_x = polygon[point].column; - ras_y = polygon[point].row; - - //Check if the raster_point is a joint - - //Find the four cells in the flow grid - //Constrict to center cell of connection_grid - //( x, y ) == Cell 0 - //4 3 2 - //5 0 1 - //6 7 8 - - //Cell 4 - double cell4_x = ( (ras_x-1) - 1 )/2; - double cell4_y = ( (ras_y-1) - 1 )/2; - //Cell 6 - double cell6_x = ( (ras_x-1) - 1 )/2; - double cell6_y = ( (ras_y+1) - 1 )/2; - //Cell 2 - double cell2_x = ( (ras_x+1) - 1 )/2; - double cell2_y = ( (ras_y-1) - 1 )/2; - //Cell 8 - double cell8_x = ( (ras_x+1) - 1 )/2; - double cell8_y = ( (ras_y+1) - 1 )/2; - - double cell2 = getValue( Grid, (long)cell2_x, (long)cell2_y ); - double cell8 = getValue( Grid, (long)cell8_x, (long)cell8_y ); - double cell4 = getValue( Grid, (long)cell4_x, (long)cell4_y ); - double cell6 = getValue( Grid, (long)cell6_x, (long)cell6_y ); - - bool write_point = true; - if( is_joint( cell2, cell8, cell4, cell6 ) ) - write_point = true; - else if( ras_x == 0 || ras_x == exp_cols - 1 ) - { write_point = true; - } - else if( ras_y == 0 || ras_y == exp_rows - 1 ) - { write_point = true; - } - //Remove the Jagged Points if it isn't a joint - else - { long ras_behind_x = 0, ras_behind_y = 0; - long ras_front_x = 0, ras_front_y = 0; - - if( point == 0 ) - { ras_behind_x = polygon[polygon.size()-1].column; - ras_behind_y = polygon[polygon.size()-1].row; - } - else - { ras_behind_x = polygon[point-1].column; - ras_behind_y = polygon[point-1].row; - } - - if( point == polygon.size() -1 ) - { ras_front_x = polygon[0].column; - ras_front_y = polygon[0].row; - } - else - { ras_front_x = polygon[point+1].column; - ras_front_y = polygon[point+1].row; - } - - if( abs( ras_behind_x - ras_front_x ) == 1 && - abs( ras_behind_y - ras_front_y ) == 1 ) - write_point = false; - } - - //Write the point if it's a joint or not a jaggy - if( write_point == true ) - { if( last_point_index < 0 ) - last_point_index = point; - _expand_grid->CellToProj( polygon[point].column, polygon[point].row, &poly_x, &poly_y ); - IPoint * ipoint = NULL; - ComHelper::CreatePoint(&ipoint); - ipoint->put_X(poly_x); - ipoint->put_Y(poly_y); - long pindex = point; - shape->InsertPoint( ipoint, &pindex, &vbretval ); - ipoint->Release(); - ipoint = NULL; - } - } - //Write the last point again - _expand_grid->CellToProj( polygon[last_point_index].column, polygon[last_point_index].row, &poly_x, &poly_y ); - IPoint * ipoint = NULL; - ComHelper::CreatePoint(&ipoint); - ipoint->put_X(poly_x); - ipoint->put_Y(poly_y); - long pindex = point + 1; - shape->InsertPoint( ipoint, &pindex, &vbretval ); - ipoint->Release(); - ipoint = NULL; - - long sindex = number_polygons; - (*retval)->EditInsertShape(shape,&sindex,&vbretval); - - shape->Release(); - shape = NULL; - - VARIANT cval; - VariantInit(&cval); - cval.vt = VT_I4; - cval.lVal = (LONG)polygon_id; - (*retval)->EditCellValue(fieldpos,sindex,cval,&vbretval); - number_polygons++; - VariantClear(&cval); - } - } - } + AFX_MANAGE_STATE(AfxGetStaticModuleState()) - CallbackHelper::Progress(callback, py, rows, "Polygon Creation: Creating Polygons", _key, percent); - } + ICallback* callback = cBack ? cBack : _globalCallback; - _expand_grid->Close(&vbretval); - _expand_grid->Release(); - _expand_grid = NULL; - - VariantClear(&vndv); - - if (*retval) - { - ((CShapefile*)(*retval))->ValidateOutput(retval, "GridToShapefile", "Utils"); - } + if (Grid == NULL) + { + *retval = NULL; + ErrorMessage(callback, tkUNEXPECTED_NULL_PARAMETER); + return S_OK; + } + + //Create a grid that is twice the size of the original + IGridHeader * header = NULL; + Grid->get_Header(&header); + long cols = 0, rows = 0; + header->get_NumberCols(&cols); + header->get_NumberRows(&rows); + + GridDataType dType = UnknownDataType; + Grid->get_DataType(&dType); + + double dx, dy, xllCenter, yllCenter; + + VARIANT vndv; + VariantInit(&vndv); + header->get_NodataValue(&vndv); + header->get_dX(&dx); + header->get_dY(&dy); + header->get_XllCenter(&xllCenter); + header->get_YllCenter(&yllCenter); + header->Release(); + header = NULL; + + if (cols <= 0 || rows <= 0) + { + *retval = NULL; + ErrorMessage(callback, tkZERO_ROWS_OR_COLS); + VariantClear(&vndv); + return S_OK; + } + + long exp_cols = 2 * cols + 1; + long exp_rows = 2 * rows + 1; + + long percent = 0, newpercent = 0; + double polygon_id = 0; + double nodata_value = -1; + dVal(vndv, nodata_value); + + double expand_nodata_value = nodata_value; + double total = rows; + + VARIANT_BOOL vbretval = FALSE; + + CoCreateInstance(CLSID_Grid, NULL, CLSCTX_INPROC_SERVER, IID_IGrid, (void**)&_expand_grid); + IGridHeader * expand_header = NULL; + CoCreateInstance(CLSID_GridHeader, NULL, CLSCTX_INPROC_SERVER, IID_IGridHeader, (void**)&expand_header); + expand_header->put_dX(dx*.5); + expand_header->put_dY(dy*.5); + expand_header->put_XllCenter(xllCenter - dx*.5); + expand_header->put_YllCenter(yllCenter - dy*.5); + expand_header->put_NodataValue(vndv); + expand_header->put_NumberCols(exp_cols); + expand_header->put_NumberRows(exp_rows); + + CString tempFilename = Utility::GetTempFilename(".bgd"); + CComBSTR bstr(tempFilename); + _expand_grid->CreateNew(bstr, expand_header, dType, vndv, VARIANT_TRUE, UseExtension, cBack, &vbretval); + + expand_header->Release(); + expand_header = NULL; + + _connection_grid = ConnectionGrid; + + double expand_value = 0; + for (int y = rows - 1; y >= 0; y--) + { + for (int x = 0; x < cols; x++) + { + polygon_id = getValue(Grid, x, y); + + //Expand as the center cell + int expand_x = 2 * x + 1; + int expand_y = 2 * y + 1; + + //Expanded Cell arrangement + //4 3 2 + //5 0 1 + //6 7 8 + + //Mark the cell as a border if it already has a value + //Cell 0 + expand_value = getValue(_expand_grid, expand_x, expand_y); + //Set the polygon_id + if (expand_value == expand_nodata_value) + setValue(_expand_grid, expand_x, expand_y, polygon_id); + + //Cell 1 + expand_value = getValue(_expand_grid, expand_x + 1, expand_y); + //set the polygon id + if (expand_value == expand_nodata_value) + setValue(_expand_grid, expand_x + 1, expand_y, polygon_id); + //polygon_id already exists and is the same... reset it + else if (expand_value == polygon_id) + setValue(_expand_grid, expand_x + 1, expand_y, polygon_id); + //set the cell as a border + else if (expand_value >= 0) + setValue(_expand_grid, expand_x + 1, expand_y, BORDER); + + //Cell 2 + expand_value = getValue(_expand_grid, expand_x + 1, expand_y + 1); + if (expand_value == expand_nodata_value) + setValue(_expand_grid, expand_x + 1, expand_y + 1, polygon_id); + else if (expand_value == polygon_id) + setValue(_expand_grid, expand_x + 1, expand_y + 1, polygon_id); + else if (expand_value >= 0) + setValue(_expand_grid, expand_x + 1, expand_y + 1, BORDER); + else if (expand_value == BORDER) + { + expand_value -= 1; + setValue(_expand_grid, expand_x + 1, expand_y + 1, expand_value); + } + else if (expand_value == DOUBLE_BORDER) + { + if (is_decision(_expand_grid, expand_x + 1, expand_y + 1)) + setValue(_expand_grid, expand_x + 1, expand_y + 1, DECISION); + else + setValue(_expand_grid, expand_x + 1, expand_y + 1, BORDER); + } + + //Cell 3 + expand_value = getValue(_expand_grid, expand_x, expand_y + 1); + //set the polygon id + if (expand_value == expand_nodata_value) + setValue(_expand_grid, expand_x, expand_y + 1, polygon_id); + //polygon_id already exists and is the same... reset it + else if (expand_value == polygon_id) + setValue(_expand_grid, expand_x, expand_y + 1, polygon_id); + //set the cell as a border + else if (expand_value >= 0) + setValue(_expand_grid, expand_x, expand_y + 1, BORDER); + + //Cell 4 + expand_value = getValue(_expand_grid, expand_x - 1, expand_y + 1); + if (expand_value == expand_nodata_value) + setValue(_expand_grid, expand_x - 1, expand_y + 1, polygon_id); + else if (expand_value == polygon_id) + setValue(_expand_grid, expand_x - 1, expand_y + 1, polygon_id); + else if (expand_value >= 0) + setValue(_expand_grid, expand_x - 1, expand_y + 1, BORDER); + else if (expand_value == BORDER) + { + expand_value -= 1; + setValue(_expand_grid, expand_x - 1, expand_y + 1, expand_value); + } + else if (expand_value == DOUBLE_BORDER) + { + if (is_decision(_expand_grid, expand_x - 1, expand_y + 1)) + setValue(_expand_grid, expand_x - 1, expand_y + 1, DECISION); + else + setValue(_expand_grid, expand_x - 1, expand_y + 1, BORDER); + } + + //Cell 5 + expand_value = getValue(_expand_grid, expand_x - 1, expand_y); + //set the polygon id + if (expand_value == expand_nodata_value) + setValue(_expand_grid, expand_x - 1, expand_y, polygon_id); + //polygon_id already exists and is the same... reset it + else if (expand_value == polygon_id) + setValue(_expand_grid, expand_x - 1, expand_y, polygon_id); + //set the cell as a border + else if (expand_value >= 0) + setValue(_expand_grid, expand_x - 1, expand_y, BORDER); + + //Cell 6 + expand_value = getValue(_expand_grid, expand_x - 1, expand_y - 1); + if (expand_value == expand_nodata_value) + setValue(_expand_grid, expand_x - 1, expand_y - 1, polygon_id); + else if (expand_value == polygon_id) + setValue(_expand_grid, expand_x - 1, expand_y - 1, polygon_id); + else if (expand_value >= 0) + setValue(_expand_grid, expand_x - 1, expand_y - 1, BORDER); + else if (expand_value == BORDER) + { + expand_value -= 1; + setValue(_expand_grid, expand_x - 1, expand_y - 1, expand_value); + } + else if (expand_value == DOUBLE_BORDER) + { + if (is_decision(_expand_grid, expand_x - 1, expand_y - 1)) + setValue(_expand_grid, expand_x - 1, expand_y - 1, DECISION); + else + setValue(_expand_grid, expand_x - 1, expand_y - 1, BORDER); + } + + //Cell 7 + expand_value = getValue(_expand_grid, expand_x, expand_y - 1); + //set the polygon id + if (expand_value == expand_nodata_value) + setValue(_expand_grid, expand_x, expand_y - 1, polygon_id); + //polygon_id already exists and is the same... reset it + else if (expand_value == polygon_id) + setValue(_expand_grid, expand_x, expand_y - 1, polygon_id); + //set the cell as a border + else if (expand_value >= 0) + setValue(_expand_grid, expand_x, expand_y - 1, BORDER); + + //Cell 8 + expand_value = getValue(_expand_grid, expand_x + 1, expand_y - 1); + if (expand_value == expand_nodata_value) + setValue(_expand_grid, expand_x + 1, expand_y - 1, polygon_id); + else if (expand_value == polygon_id) + setValue(_expand_grid, expand_x + 1, expand_y - 1, polygon_id); + else if (expand_value >= 0) + setValue(_expand_grid, expand_x + 1, expand_y - 1, BORDER); + else if (expand_value == BORDER) + { + expand_value -= 1; + setValue(_expand_grid, expand_x + 1, expand_y - 1, expand_value); + } + else if (expand_value == DOUBLE_BORDER) + { + if (is_decision(_expand_grid, expand_x + 1, expand_y - 1)) + setValue(_expand_grid, expand_x + 1, expand_y - 1, DECISION); + else + setValue(_expand_grid, expand_x + 1, expand_y - 1, BORDER); + } + } + + CallbackHelper::Progress(callback, rows - y - 1, total, "Polygon Creation: Expanding Grid", _key, percent); + } + + //Mark edges of the expanded grid that have a polygon id as BORDER + // and reset DOUBLE_BORDER to BORDER + //Mark edges of a polygon that have a nodata_value by them + newpercent = 0; + percent = 0; + total = exp_rows; + for (int j = 0; j < exp_rows; j++) + { + for (int i = 0; i < exp_cols; i++) + { + polygon_id = getValue(_expand_grid, i, j); + if (polygon_id != expand_nodata_value) + { + if (j == 0) + setValue(_expand_grid, i, 0, BORDER); + else if (j == exp_rows - 1) + setValue(_expand_grid, i, exp_rows - 1, BORDER); + else if (i == 0) + setValue(_expand_grid, 0, j, BORDER); + else if (i == exp_cols - 1) + setValue(_expand_grid, exp_cols - 1, j, BORDER); + else + { + if (polygon_id == DOUBLE_BORDER) + setValue(_expand_grid, i, j, BORDER); + //Convex polygon test + else if (polygon_id == DECISION) + { + if (_connection_grid != NULL) + { + long contract_x = (i - 1) / 2; + long contract_y = (j - 1) / 2; + + IGridHeader * cHeader = NULL; + _connection_grid->get_Header(&cHeader); + VARIANT cndv; + VariantInit(&cndv); + cHeader->get_NodataValue(&cndv); + cHeader->Release(); + cHeader = NULL; + + double nodata = -1; + dVal(cndv, nodata); + + VariantClear(&cndv); + + double decision = getValue(_connection_grid, contract_x, contract_y); + if (decision == nodata) + { + _expand_grid->Close(&vbretval); + *retval = NULL; + ErrorMessage(callback, tkCONCAVE_POLYGONS); + return S_OK; + } + } + } + //Test to see if it is on a nodata border + else + { //4 3 2 + //5 0 1 + //6 7 8 + + //Cell 1 + double cell1 = getValue(_expand_grid, i + 1, j); + //Cell 2 + double cell2 = getValue(_expand_grid, i + 1, j - 1); + //Cell 3 + double cell3 = getValue(_expand_grid, i, j - 1); + //Cell 4 + double cell4 = getValue(_expand_grid, i - 1, j - 1); + //Cell 5 + double cell5 = getValue(_expand_grid, i - 1, j); + //Cell 6 + double cell6 = getValue(_expand_grid, i - 1, j + 1); + //Cell 7 + double cell7 = getValue(_expand_grid, i, j + 1); + //Cell 8 + double cell8 = getValue(_expand_grid, i + 1, j + 1); + + if (cell1 == expand_nodata_value || + cell2 == expand_nodata_value || + cell3 == expand_nodata_value || + cell4 == expand_nodata_value || + cell5 == expand_nodata_value || + cell6 == expand_nodata_value || + cell7 == expand_nodata_value || + cell8 == expand_nodata_value) + { + if (i % 2 == 0 && j % 2 == 0) + { + if (is_decision(_expand_grid, i, j)) + setValue(_expand_grid, i, j, DECISION); + else + setValue(_expand_grid, i, j, BORDER); + } + else + setValue(_expand_grid, i, j, BORDER); + } + } + } + } + } + + CallbackHelper::Progress(callback, j, total, "Polygon Creation: Marking Borders", _key, percent); + } + + //Create the shapefile + CoCreateInstance(CLSID_Shapefile, NULL, CLSCTX_INPROC_SERVER, IID_IShapefile, (void**)retval); + (*retval)->CreateNew(m_globalSettings.emptyBstr, SHP_POLYGON, &vbretval); + + long fieldpos = 0; + IField * field = NULL; + CoCreateInstance(CLSID_Field, NULL, CLSCTX_INPROC_SERVER, IID_IField, (void**)&field); + CComBSTR bstrName("PolygonID"); + field->put_Name(bstrName); + field->put_Type(INTEGER_FIELD); + field->put_Width(10); + (*retval)->EditInsertField(field, &fieldpos, cBack, &vbretval); + field->Release(); + field = NULL; + + //Create the polygons + //Only look at the center value of the cells + std::deque< RasterPoint > polygon; + int number_polygons = 0; + double xllcenter = xllCenter - dx*.5; + double yllcenter = yllCenter - dy*.5; + double nodata = expand_nodata_value; + + total = rows; + newpercent = 0; + percent = 0; + for (int py = 0; py < rows; py++) //rows - 1; py>= 0; py-- ) + { + for (int px = 0; px < cols; px++) + { + //Expand as the center cell + long expand_x = 2 * px + 1; + long expand_y = 2 * py + 1; + polygon_id = getValue(_expand_grid, expand_x, expand_y); + + //Check to see if polygon id is a specially marked cell or nodata + if (polygon_id >= 0) + //if( polygon_id == 148 ) + { + //Clear the polygon list + polygon.clear(); + + //Mark the edge with + // a special value. + mark_edge(polygon_id, expand_x - 1, expand_y - 1); + + //Always start Trace from Cell 3 + trace_polygon(expand_x - 1, expand_y - 1, polygon); + + //Flood fill the polygon with nodata + scan_fill_to_edge(nodata, expand_x, expand_y + 1); + + if (polygon.size() > 3) + { + long endIndex = polygon.size() - 1; + + double x1 = polygon[0].column, + x2 = polygon[1].column, + y1 = polygon[0].row, + y2 = polygon[1].row; + + cppVector v1(x2 - x1, y2 - y1, 0.0); + + x1 = polygon[endIndex].column; + x2 = polygon[0].column; + y1 = polygon[endIndex].row; + y2 = polygon[0].row; + + cppVector v2(x2 - x1, y2 - y1, 0.0); + + cppVector cross = v1.crossProduct(v2); + + //Invert Polygon to Clockwise if necessary + //Counter ClockWise + if (cross.getk() > 0.0) + { + std::deque reverse_polygon; + for (int point = 0; point < (int)polygon.size(); point++) + reverse_polygon.push_front(polygon[point]); + polygon = reverse_polygon; + } + + + //Write the final polygon + IShape * shape = NULL; + ComHelper::CreateShape(&shape); + shape->Create(SHP_POLYGON, &vbretval); + long ppos = 0; + shape->InsertPart(0, &ppos, &vbretval); + + double poly_x = 0, poly_y = 0; + long ras_x = 0, ras_y = 0; + long last_point_index = -1; + int point = 0; + for (point = 0; point < (int)polygon.size(); point++) //(int)polygon.size()-1; point >= 0; point-- ) + { + ras_x = polygon[point].column; + ras_y = polygon[point].row; + + //Check if the raster_point is a joint + + //Find the four cells in the flow grid + //Constrict to center cell of connection_grid + //( x, y ) == Cell 0 + //4 3 2 + //5 0 1 + //6 7 8 + + //Cell 4 + double cell4_x = ((ras_x - 1) - 1) / 2; + double cell4_y = ((ras_y - 1) - 1) / 2; + //Cell 6 + double cell6_x = ((ras_x - 1) - 1) / 2; + double cell6_y = ((ras_y + 1) - 1) / 2; + //Cell 2 + double cell2_x = ((ras_x + 1) - 1) / 2; + double cell2_y = ((ras_y - 1) - 1) / 2; + //Cell 8 + double cell8_x = ((ras_x + 1) - 1) / 2; + double cell8_y = ((ras_y + 1) - 1) / 2; + + double cell2 = getValue(Grid, (long)cell2_x, (long)cell2_y); + double cell8 = getValue(Grid, (long)cell8_x, (long)cell8_y); + double cell4 = getValue(Grid, (long)cell4_x, (long)cell4_y); + double cell6 = getValue(Grid, (long)cell6_x, (long)cell6_y); + + bool write_point = true; + if (is_joint(cell2, cell8, cell4, cell6)) + write_point = true; + else if (ras_x == 0 || ras_x == exp_cols - 1) + { + write_point = true; + } + else if (ras_y == 0 || ras_y == exp_rows - 1) + { + write_point = true; + } + //Remove the Jagged Points if it isn't a joint + else + { + long ras_behind_x = 0, ras_behind_y = 0; + long ras_front_x = 0, ras_front_y = 0; + + if (point == 0) + { + ras_behind_x = polygon[polygon.size() - 1].column; + ras_behind_y = polygon[polygon.size() - 1].row; + } + else + { + ras_behind_x = polygon[point - 1].column; + ras_behind_y = polygon[point - 1].row; + } + + if (point == polygon.size() - 1) + { + ras_front_x = polygon[0].column; + ras_front_y = polygon[0].row; + } + else + { + ras_front_x = polygon[point + 1].column; + ras_front_y = polygon[point + 1].row; + } + + if (abs(ras_behind_x - ras_front_x) == 1 && + abs(ras_behind_y - ras_front_y) == 1) + write_point = false; + } + + //Write the point if it's a joint or not a jaggy + if (write_point == true) + { + if (last_point_index < 0) + last_point_index = point; + _expand_grid->CellToProj(polygon[point].column, polygon[point].row, &poly_x, &poly_y); + IPoint * ipoint = NULL; + ComHelper::CreatePoint(&ipoint); + ipoint->put_X(poly_x); + ipoint->put_Y(poly_y); + long pindex = point; + shape->InsertPoint(ipoint, &pindex, &vbretval); + ipoint->Release(); + ipoint = NULL; + } + } + //Write the last point again + _expand_grid->CellToProj(polygon[last_point_index].column, polygon[last_point_index].row, &poly_x, &poly_y); + IPoint * ipoint = NULL; + ComHelper::CreatePoint(&ipoint); + ipoint->put_X(poly_x); + ipoint->put_Y(poly_y); + long pindex = point + 1; + shape->InsertPoint(ipoint, &pindex, &vbretval); + ipoint->Release(); + ipoint = NULL; + + long sindex = number_polygons; + (*retval)->EditInsertShape(shape, &sindex, &vbretval); + + shape->Release(); + shape = NULL; + + VARIANT cval; + VariantInit(&cval); + cval.vt = VT_I4; + cval.lVal = (LONG)polygon_id; + (*retval)->EditCellValue(fieldpos, sindex, cval, &vbretval); + number_polygons++; + VariantClear(&cval); + } + } + } + + CallbackHelper::Progress(callback, py, rows, "Polygon Creation: Creating Polygons", _key, percent); + } + + _expand_grid->Close(&vbretval); + _expand_grid->Release(); + _expand_grid = NULL; + + VariantClear(&vndv); + + if (*retval) + { + ((CShapefile*)(*retval))->ValidateOutput(retval, "GridToShapefile", "Utils"); + } - return S_OK; + return S_OK; } //Determine if a given index of [] map is a DECISION SPOT . . . //DECISION SPOT is a point where multiple path options are available -inline bool CUtils::is_decision( IGrid * g, int x, int y ) -{ - // ( x, y ) corresponds to 0 - - //4 3 2 - //5 0 1 - //6 7 8 - - //Check if cells (2 & 6) or (4 & 8) have the same polygon id. - //If they do then this is a DECISION SPOT if there are three - //unique values. - - double four = getValue( g, x - 1, y - 1 ); - double eight = getValue( g, x + 1, y + 1 ); - double two = getValue( g, x + 1, y - 1 ); - double six = getValue( g, x - 1, y + 1 ); - - IGridHeader * header = NULL; - g->get_Header(&header); - VARIANT vval; - VariantInit(&vval); - header->get_NodataValue(&vval); - double nodata = -1; - dVal(vval,nodata); - VariantClear(&vval); - header->Release(); - header = NULL; - - if( two == six ) - { if( two == four || two == eight ) - return false; - else if( six == four || six == eight ) - return false; - - return true; - } - else if( four == eight ) - { if( four == two || four == six ) - return false; - else if( eight == two || eight == six ) - return false; - return true; - } - - return false; +inline bool CUtils::is_decision(IGrid * g, int x, int y) +{ + // ( x, y ) corresponds to 0 + + //4 3 2 + //5 0 1 + //6 7 8 + + //Check if cells (2 & 6) or (4 & 8) have the same polygon id. + //If they do then this is a DECISION SPOT if there are three + //unique values. + + double four = getValue(g, x - 1, y - 1); + double eight = getValue(g, x + 1, y + 1); + double two = getValue(g, x + 1, y - 1); + double six = getValue(g, x - 1, y + 1); + + IGridHeader * header = NULL; + g->get_Header(&header); + VARIANT vval; + VariantInit(&vval); + header->get_NodataValue(&vval); + double nodata = -1; + dVal(vval, nodata); + VariantClear(&vval); + header->Release(); + header = NULL; + + if (two == six) + { + if (two == four || two == eight) + return false; + else if (six == four || six == eight) + return false; + + return true; + } + else if (four == eight) + { + if (four == two || four == six) + return false; + else if (eight == two || eight == six) + return false; + return true; + } + + return false; } //Mark the edges so the polygon can be traced -void CUtils::mark_edge( double & polygon_id, long x, long y ) -{ - std::stack< RasterPoint > stack; - std::vector< RasterPoint > decisions; - RasterPoint pt; - //Change the BORDERS to TRACE_BORDERS - stack.push( RasterPoint( x, y ) ); - int i = x, j = y; - while( !stack.empty() ) - { - pt = stack.top(); - i = pt.column; - j = pt.row; - //i = stack[0].column; - //j = stack[0].row; - stack.pop(); //_front(); - //4 3 2 - //5 0 1 - //6 7 8 - //Cell 1 - double cell1 = getValue( _expand_grid, i+1, j ); - //Cell 2 - double cell2 = getValue( _expand_grid, i+1, j-1 ); - //Cell 3 - double cell3 = getValue( _expand_grid, i, j-1 ); - //Cell 4 - double cell4 = getValue( _expand_grid, i-1, j-1 ); - //Cell 5 - double cell5 = getValue( _expand_grid, i-1, j ); - //Cell 6 - double cell6 = getValue( _expand_grid, i-1, j+1 ); - //Cell 7 - double cell7 = getValue( _expand_grid, i, j+1 ); - //Cell 8 - double cell8 = getValue( _expand_grid, i+1, j+1 ); - - if( cell1 == polygon_id || - cell2 == polygon_id || - cell3 == polygon_id || - cell4 == polygon_id || - cell5 == polygon_id || - cell6 == polygon_id || - cell7 == polygon_id || - cell8 == polygon_id ) - { - if( cell8 == BORDER ) - stack.push( RasterPoint( i+1, j+1 )); - if( cell7 == BORDER ) - stack.push( RasterPoint( i, j+1 )); - if( cell6 == BORDER ) - stack.push( RasterPoint( i-1, j+1 )); - if( cell5 == BORDER ) - stack.push( RasterPoint( i-1, j )); - if( cell4 == BORDER ) - stack.push( RasterPoint( i-1, j-1 )); - if( cell3 == BORDER ) - stack.push( RasterPoint( i, j-1 )); - if( cell2 == BORDER ) - stack.push( RasterPoint( i+1, j-1 )); - if( cell1 == BORDER ) - stack.push( RasterPoint( i+1, j )); - - //Don't reset if DECISION - if( getValue( _expand_grid, i, j ) == BORDER ) - setValue( _expand_grid, i, j, TRACE_BORDER ); - //Push diagonals if this is a DECISION - else if( getValue( _expand_grid, i, j ) == DECISION ) - { stack.push( RasterPoint( i+1, j+1 ) ); - stack.push( RasterPoint( i-1, j+1 ) ); - stack.push( RasterPoint( i-1, j-1 ) ); - stack.push( RasterPoint( i+1, j-1 ) ); - } - } - } - - //Mark Polygon as the CURRENT_POLYGON - stack.push( RasterPoint( x+1, y+1 ) ); - while( !stack.empty() ) - { - pt = stack.top(); - i = pt.column; - j = pt.row; - //i = stack[0].column; - //j = stack[0].row; - stack.pop(); - //4 3 2 - //5 0 1 - //6 7 8 - //Cell 1 - double cell1 = getValue( _expand_grid, i+1, j ); - if( cell1 == polygon_id || cell1 == DECISION ) - stack.push( RasterPoint( i+1, j )); - //Cell 2 - double cell2 = getValue( _expand_grid, i+1, j-1 ); - if( cell2 == polygon_id || cell2 == DECISION ) - stack.push( RasterPoint( i+1, j-1 )); - //Cell 3 - double cell3 = getValue( _expand_grid, i, j-1 ); - if( cell3 == polygon_id || cell3 == DECISION ) - stack.push( RasterPoint( i, j-1 )); - //Cell 4 - double cell4 = getValue( _expand_grid, i-1, j-1 ); - if( cell4 == polygon_id || cell4 == DECISION ) - stack.push( RasterPoint( i-1, j-1 )); - //Cell 5 - double cell5 = getValue( _expand_grid, i-1, j ); - if( cell5 == polygon_id || cell5 == DECISION ) - stack.push( RasterPoint( i-1, j )); - //Cell 6 - double cell6 = getValue( _expand_grid, i-1, j+1 ); - if( cell6 == polygon_id || cell6 == DECISION ) - stack.push( RasterPoint( i-1, j+1 )); - //Cell 7 - double cell7 = getValue( _expand_grid, i, j+1 ); - if( cell7 == polygon_id || cell7 == DECISION ) - stack.push( RasterPoint( i, j+1 )); - //Cell 8 - double cell8 = getValue( _expand_grid, i+1, j+1 ); - if( cell8 == polygon_id || cell8 == DECISION ) - stack.push( RasterPoint( i+1, j+1 )); - - //Don't reset if DECISION - if( getValue( _expand_grid, i, j ) == polygon_id ) - setValue( _expand_grid, i, j, CURRENT_POLYGON ); - //Push diagonals if this is a DECISION - else if( getValue( _expand_grid, i, j ) == DECISION ) - { setValue( _expand_grid, i, j, CURRENT_POLYGON ); - decisions.push_back( RasterPoint( i, j ) ); - stack.push( RasterPoint( i+1, j+1 ) ); - stack.push( RasterPoint( i-1, j+1 ) ); - stack.push( RasterPoint( i-1, j-1 ) ); - stack.push( RasterPoint( i+1, j-1 ) ); - } - } - - for( int d1 = 0; d1 < (int)decisions.size(); d1++ ) - setValue( _expand_grid, decisions[d1].column, decisions[d1].row, DECISION ); -} +void CUtils::mark_edge(double & polygon_id, long x, long y) +{ + std::stack< RasterPoint > stack; + std::vector< RasterPoint > decisions; + RasterPoint pt; + //Change the BORDERS to TRACE_BORDERS + stack.push(RasterPoint(x, y)); + int i = x, j = y; + while (!stack.empty()) + { + pt = stack.top(); + i = pt.column; + j = pt.row; + //i = stack[0].column; + //j = stack[0].row; + stack.pop(); //_front(); + //4 3 2 + //5 0 1 + //6 7 8 + //Cell 1 + double cell1 = getValue(_expand_grid, i + 1, j); + //Cell 2 + double cell2 = getValue(_expand_grid, i + 1, j - 1); + //Cell 3 + double cell3 = getValue(_expand_grid, i, j - 1); + //Cell 4 + double cell4 = getValue(_expand_grid, i - 1, j - 1); + //Cell 5 + double cell5 = getValue(_expand_grid, i - 1, j); + //Cell 6 + double cell6 = getValue(_expand_grid, i - 1, j + 1); + //Cell 7 + double cell7 = getValue(_expand_grid, i, j + 1); + //Cell 8 + double cell8 = getValue(_expand_grid, i + 1, j + 1); + + if (cell1 == polygon_id || + cell2 == polygon_id || + cell3 == polygon_id || + cell4 == polygon_id || + cell5 == polygon_id || + cell6 == polygon_id || + cell7 == polygon_id || + cell8 == polygon_id) + { + if (cell8 == BORDER) + stack.push(RasterPoint(i + 1, j + 1)); + if (cell7 == BORDER) + stack.push(RasterPoint(i, j + 1)); + if (cell6 == BORDER) + stack.push(RasterPoint(i - 1, j + 1)); + if (cell5 == BORDER) + stack.push(RasterPoint(i - 1, j)); + if (cell4 == BORDER) + stack.push(RasterPoint(i - 1, j - 1)); + if (cell3 == BORDER) + stack.push(RasterPoint(i, j - 1)); + if (cell2 == BORDER) + stack.push(RasterPoint(i + 1, j - 1)); + if (cell1 == BORDER) + stack.push(RasterPoint(i + 1, j)); + + //Don't reset if DECISION + if (getValue(_expand_grid, i, j) == BORDER) + setValue(_expand_grid, i, j, TRACE_BORDER); + //Push diagonals if this is a DECISION + else if (getValue(_expand_grid, i, j) == DECISION) + { + stack.push(RasterPoint(i + 1, j + 1)); + stack.push(RasterPoint(i - 1, j + 1)); + stack.push(RasterPoint(i - 1, j - 1)); + stack.push(RasterPoint(i + 1, j - 1)); + } + } + } + + //Mark Polygon as the CURRENT_POLYGON + stack.push(RasterPoint(x + 1, y + 1)); + while (!stack.empty()) + { + pt = stack.top(); + i = pt.column; + j = pt.row; + //i = stack[0].column; + //j = stack[0].row; + stack.pop(); + //4 3 2 + //5 0 1 + //6 7 8 + //Cell 1 + double cell1 = getValue(_expand_grid, i + 1, j); + if (cell1 == polygon_id || cell1 == DECISION) + stack.push(RasterPoint(i + 1, j)); + //Cell 2 + double cell2 = getValue(_expand_grid, i + 1, j - 1); + if (cell2 == polygon_id || cell2 == DECISION) + stack.push(RasterPoint(i + 1, j - 1)); + //Cell 3 + double cell3 = getValue(_expand_grid, i, j - 1); + if (cell3 == polygon_id || cell3 == DECISION) + stack.push(RasterPoint(i, j - 1)); + //Cell 4 + double cell4 = getValue(_expand_grid, i - 1, j - 1); + if (cell4 == polygon_id || cell4 == DECISION) + stack.push(RasterPoint(i - 1, j - 1)); + //Cell 5 + double cell5 = getValue(_expand_grid, i - 1, j); + if (cell5 == polygon_id || cell5 == DECISION) + stack.push(RasterPoint(i - 1, j)); + //Cell 6 + double cell6 = getValue(_expand_grid, i - 1, j + 1); + if (cell6 == polygon_id || cell6 == DECISION) + stack.push(RasterPoint(i - 1, j + 1)); + //Cell 7 + double cell7 = getValue(_expand_grid, i, j + 1); + if (cell7 == polygon_id || cell7 == DECISION) + stack.push(RasterPoint(i, j + 1)); + //Cell 8 + double cell8 = getValue(_expand_grid, i + 1, j + 1); + if (cell8 == polygon_id || cell8 == DECISION) + stack.push(RasterPoint(i + 1, j + 1)); + + //Don't reset if DECISION + if (getValue(_expand_grid, i, j) == polygon_id) + setValue(_expand_grid, i, j, CURRENT_POLYGON); + //Push diagonals if this is a DECISION + else if (getValue(_expand_grid, i, j) == DECISION) + { + setValue(_expand_grid, i, j, CURRENT_POLYGON); + decisions.push_back(RasterPoint(i, j)); + stack.push(RasterPoint(i + 1, j + 1)); + stack.push(RasterPoint(i - 1, j + 1)); + stack.push(RasterPoint(i - 1, j - 1)); + stack.push(RasterPoint(i + 1, j - 1)); + } + } + + for (int d1 = 0; d1 < (int)decisions.size(); d1++) + setValue(_expand_grid, decisions[d1].column, decisions[d1].row, DECISION); +} //Erase the CurrenClipperLib::Polygon -void CUtils::scan_fill_to_edge( double & nodata, long x, long y ) -{ - std::stack< RasterPoint > stack; - std::vector< RasterPoint > decisions; - RasterPoint pt; - - //Erase the Polygon - int i = x, j = y; - stack.push( RasterPoint( x, y-1 ) ); - while( !stack.empty() ) - { - pt = stack.top(); - i = pt.column; - j = pt.row; - stack.pop(); - //4 3 2 - //5 0 1 - //6 7 8 - //Cell 1 - double cell1 = getValue( _expand_grid, i+1, j ); - if( cell1 == CURRENT_POLYGON || cell1 == DECISION ) - stack.push( RasterPoint( i+1, j )); - //Cell 2 - double cell2 = getValue( _expand_grid, i+1, j-1 ); - if( cell2 == CURRENT_POLYGON || cell2 == DECISION ) - stack.push( RasterPoint( i+1, j-1 )); - //Cell 3 - double cell3 = getValue( _expand_grid, i, j-1 ); - if( cell3 == CURRENT_POLYGON || cell3 == DECISION ) - stack.push( RasterPoint( i, j-1 )); - //Cell 4 - double cell4 = getValue( _expand_grid, i-1, j-1 ); - if( cell4 == CURRENT_POLYGON || cell4 == DECISION ) - stack.push( RasterPoint( i-1, j-1 )); - //Cell 5 - double cell5 = getValue( _expand_grid, i-1, j ); - if( cell5 == CURRENT_POLYGON || cell5 == DECISION ) - stack.push( RasterPoint( i-1, j )); - //Cell 6 - double cell6 = getValue( _expand_grid, i-1, j+1 ); - if( cell6 == CURRENT_POLYGON || cell6 == DECISION ) - stack.push( RasterPoint( i-1, j+1 )); - //Cell 7 - double cell7 = getValue( _expand_grid, i, j+1 ); - if( cell7 == CURRENT_POLYGON || cell7 == DECISION ) - stack.push( RasterPoint( i, j+1 )); - //Cell 8 - double cell8 = getValue( _expand_grid, i+1, j+1 ); - if( cell8 == CURRENT_POLYGON || cell8 == DECISION ) - stack.push( RasterPoint( i+1, j+1 )); - - //Don't reset if DECISION - if( getValue( _expand_grid, i, j ) == CURRENT_POLYGON ) - setValue( _expand_grid, i, j, nodata ); - //Push diagonals if this is a DECISION - else if( getValue( _expand_grid, i, j ) == DECISION ) - { setValue( _expand_grid, i, j, nodata ); - decisions.push_back( RasterPoint( i, j ) ); - stack.push( RasterPoint( i+1, j+1 ) ); - stack.push( RasterPoint( i-1, j+1 ) ); - stack.push( RasterPoint( i-1, j-1 ) ); - stack.push( RasterPoint( i+1, j-1 ) ); - } - } - - for( int d1 = 0; d1 < (int)decisions.size(); d1++ ) - setValue( _expand_grid, decisions[d1].column, decisions[d1].row, DECISION ); -} - -inline double CUtils::getValue( IGrid * Grid, long column, long row ) -{ VARIANT vval; - VariantInit(&vval); - Grid->get_Value(column,row,&vval); - double val = 0; - dVal(vval,val); - VariantClear(&vval); - return val; +void CUtils::scan_fill_to_edge(double & nodata, long x, long y) +{ + std::stack< RasterPoint > stack; + std::vector< RasterPoint > decisions; + RasterPoint pt; + + //Erase the Polygon + int i = x, j = y; + stack.push(RasterPoint(x, y - 1)); + while (!stack.empty()) + { + pt = stack.top(); + i = pt.column; + j = pt.row; + stack.pop(); + //4 3 2 + //5 0 1 + //6 7 8 + //Cell 1 + double cell1 = getValue(_expand_grid, i + 1, j); + if (cell1 == CURRENT_POLYGON || cell1 == DECISION) + stack.push(RasterPoint(i + 1, j)); + //Cell 2 + double cell2 = getValue(_expand_grid, i + 1, j - 1); + if (cell2 == CURRENT_POLYGON || cell2 == DECISION) + stack.push(RasterPoint(i + 1, j - 1)); + //Cell 3 + double cell3 = getValue(_expand_grid, i, j - 1); + if (cell3 == CURRENT_POLYGON || cell3 == DECISION) + stack.push(RasterPoint(i, j - 1)); + //Cell 4 + double cell4 = getValue(_expand_grid, i - 1, j - 1); + if (cell4 == CURRENT_POLYGON || cell4 == DECISION) + stack.push(RasterPoint(i - 1, j - 1)); + //Cell 5 + double cell5 = getValue(_expand_grid, i - 1, j); + if (cell5 == CURRENT_POLYGON || cell5 == DECISION) + stack.push(RasterPoint(i - 1, j)); + //Cell 6 + double cell6 = getValue(_expand_grid, i - 1, j + 1); + if (cell6 == CURRENT_POLYGON || cell6 == DECISION) + stack.push(RasterPoint(i - 1, j + 1)); + //Cell 7 + double cell7 = getValue(_expand_grid, i, j + 1); + if (cell7 == CURRENT_POLYGON || cell7 == DECISION) + stack.push(RasterPoint(i, j + 1)); + //Cell 8 + double cell8 = getValue(_expand_grid, i + 1, j + 1); + if (cell8 == CURRENT_POLYGON || cell8 == DECISION) + stack.push(RasterPoint(i + 1, j + 1)); + + //Don't reset if DECISION + if (getValue(_expand_grid, i, j) == CURRENT_POLYGON) + setValue(_expand_grid, i, j, nodata); + //Push diagonals if this is a DECISION + else if (getValue(_expand_grid, i, j) == DECISION) + { + setValue(_expand_grid, i, j, nodata); + decisions.push_back(RasterPoint(i, j)); + stack.push(RasterPoint(i + 1, j + 1)); + stack.push(RasterPoint(i - 1, j + 1)); + stack.push(RasterPoint(i - 1, j - 1)); + stack.push(RasterPoint(i + 1, j - 1)); + } + } + + for (int d1 = 0; d1 < (int)decisions.size(); d1++) + setValue(_expand_grid, decisions[d1].column, decisions[d1].row, DECISION); } -inline void CUtils::setValue( IGrid * Grid, long column, long row, double val ) -{ VARIANT vval; - VariantInit(&vval); - vval.vt = VT_R8; - vval.dblVal = val; - Grid->put_Value(column,row,vval); - VariantClear(&vval); +inline double CUtils::getValue(IGrid * Grid, long column, long row) +{ + VARIANT vval; + VariantInit(&vval); + Grid->get_Value(column, row, &vval); + double val = 0; + dVal(vval, val); + VariantClear(&vval); + return val; } -void CUtils::trace_polygon( long x, long y, std::deque & polygon ) -{ - polygon.push_back( RasterPoint( x, y ) ); +inline void CUtils::setValue(IGrid * Grid, long column, long row, double val) +{ + VARIANT vval; + VariantInit(&vval); + vval.vt = VT_R8; + vval.dblVal = val; + Grid->put_Value(column, row, vval); + VariantClear(&vval); +} - std::stack stack; - RasterPoint pt; +void CUtils::trace_polygon(long x, long y, std::deque & polygon) +{ + polygon.push_back(RasterPoint(x, y)); - stack.push(RasterPoint(x,y)); + std::stack stack; + RasterPoint pt; - while( !stack.empty() ) - { - pt = stack.top(); - x = pt.column; - y = pt.row; - //x = stack[0].column; - //y = stack[0].row; - stack.pop(); - - - - //Reset the current grid value back to BORDER - if( getValue( _expand_grid, x, y ) == TRACE_BORDER ) - setValue( _expand_grid, x, y, BORDER ); - - // ( x, y ) corresponds to 0 - //4 3 2 - //5 0 1 - //6 7 8 - - bool moved_turtle = false; - //Restrict to Cardinal Directions - //Cell 1 - if( getValue( _expand_grid, x + 1, y ) == TRACE_BORDER ) - { - moved_turtle = true; - //trace_polygon( x + 1, y, polygon ); - stack.push(RasterPoint(x + 1, y)); - polygon.push_back(RasterPoint(x + 1, y)); - } - //Cell 3 - else if( getValue( _expand_grid, x, y - 1 ) == TRACE_BORDER ) - { - moved_turtle = true; - //trace_polygon( x, y - 1, polygon ); - stack.push(RasterPoint(x, y - 1)); - polygon.push_back(RasterPoint(x, y - 1)); - } - //Cell 5 - else if( getValue( _expand_grid, x - 1, y ) == TRACE_BORDER ) - { - moved_turtle = true; - //trace_polygon( x - 1, y, polygon ); - stack.push(RasterPoint(x - 1, y)); - polygon.push_back(RasterPoint(x - 1, y)); - } - //Cell 7 - else if( getValue( _expand_grid, x, y + 1 ) == TRACE_BORDER ) - { - moved_turtle = true; - //trace_polygon( x, y + 1, polygon ); - stack.push(RasterPoint(x, y + 1)); - polygon.push_back(RasterPoint(x, y + 1)); - } - - //Look for a Decision Node ... - //Cell 1 - if( !moved_turtle ) - { - if( getValue( _expand_grid, x + 1, y ) == DECISION ) - { - if( _connection_grid == NULL ) - { - polygon.clear(); - return; - } - - //Find the four cells in the flow grid - //Constrict to center cell of connection_grid - //( x, y ) now == Cell 5 - //4 3 2 - //5 0 1 - //6 7 8 - - //Cell 2 - _cell2_x = ( (x+2) - 1 )/2; - _cell2_y = ( (y-1) - 1 )/2; - //Cell 4 - _cell4_x = ( (x) - 1 )/2; - _cell4_y = ( (y-1) - 1 )/2; - //Cell 6 - _cell6_x = ( (x) - 1 )/2; - cell6_y = ( (y+1) - 1 )/2; - //Cell 8 - _cell8_x = ( (x+2) - 1 )/2; - _cell8_y = ( (y+1) - 1 )/2; - - _flow2 = getValue( _connection_grid, _cell2_x, _cell2_y ); - _flow8 = getValue( _connection_grid, _cell8_x, _cell8_y ); - _flow4 = getValue( _connection_grid, _cell4_x, _cell4_y ); - _flow6 = getValue( _connection_grid, _cell6_x, cell6_y ); - - //Move to Up-Right ... maybe - if( _flow2 == 6 || _flow6 == 2 ) - { - if( getValue( _expand_grid, x + 1, y - 1 ) == TRACE_BORDER ) - { - moved_turtle = true; - //trace_polygon( x + 1, y - 1, polygon ); - stack.push(RasterPoint( x + 1, y - 1)); - polygon.push_back(RasterPoint(x + 1, y - 1)); - } - } - //Move to Down-Right ... maybe - else if( _flow4 == 8 || _flow8 == 4 ) - { - if( getValue( _expand_grid, x + 1, y + 1 ) == TRACE_BORDER ) - { - moved_turtle = true; - //trace_polygon( x + 1, y + 1, polygon ); - stack.push(RasterPoint(x + 1, y + 1)); - polygon.push_back(RasterPoint(x + 1, y + 1)); - } - } - //Cannot be determined by flow directions - else - { - if( getValue( _expand_grid, x, y - 1 ) == CURRENT_POLYGON ) - { - moved_turtle = true; - //trace_polygon( x + 1, y - 1, polygon ); - stack.push(RasterPoint(x + 1, y - 1)); - polygon.push_back(RasterPoint(x + 1, y - 1)); - } - else - { - moved_turtle = true; - //trace_polygon( x + 1, y + 1, polygon ); - stack.push(RasterPoint(x + 1, y + 1)); - polygon.push_back(RasterPoint(x + 1, y + 1)); - } - } - } - } - - //Cell 3 - if( !moved_turtle ) - { - if( getValue( _expand_grid, x, y - 1 ) == DECISION ) - { - if( _connection_grid == NULL ) - { - polygon.clear(); - return; - } - - //Find the four cells in the flow grid - //Constrict to center cell of connection_grid - //( x, y ) now == Cell 7 - //4 3 2 - //5 0 1 - //6 7 8 - - //Cell 2 - _cell2_x = ( (x+1) - 1 )/2; - _cell2_y = ( (y-2) - 1 )/2; - //Cell 4 - _cell4_x = ( (x-1) - 1 )/2; - _cell4_y = ( (y-2) - 1 )/2; - //Cell 6 - _cell6_x = ( (x-1) - 1 )/2; - cell6_y = ( (y) - 1 )/2; - //Cell 8 - _cell8_x = ( (x+1) - 1 )/2; - _cell8_y = ( (y) - 1 )/2; - - _flow2 = getValue( _connection_grid, _cell2_x, _cell2_y ); - _flow8 = getValue( _connection_grid, _cell8_x, _cell8_y ); - _flow4 = getValue( _connection_grid, _cell4_x, _cell4_y ); - _flow6 = getValue( _connection_grid, _cell6_x, cell6_y ); - - //Move to Up-Right ... maybe - if( _flow2 == 6 || _flow6 == 2 ) - { - if( getValue( _expand_grid, x + 1, y - 1 ) == TRACE_BORDER ) - { - moved_turtle = true; - //trace_polygon( x + 1, y - 1, polygon ); - stack.push(RasterPoint(x + 1, y - 1)); - polygon.push_back(RasterPoint(x + 1, y - 1)); - } - } - //Move to Up-Left ... maybe - else if( _flow4 == 8 || _flow8 == 4 ) - { - if( getValue( _expand_grid, x - 1, y - 1 ) == TRACE_BORDER ) - { - moved_turtle = true; - //trace_polygon( x - 1, y - 1, polygon ); - stack.push(RasterPoint(x - 1, y - 1)); - polygon.push_back(RasterPoint(x - 1, y - 1)); - } - } - //Cannot be determined by flow directions - else - { - if( getValue( _expand_grid, x - 1, y ) == CURRENT_POLYGON ) - { - moved_turtle = true; - //trace_polygon( x - 1, y - 1, polygon ); - stack.push(RasterPoint(x - 1, y - 1)); - polygon.push_back(RasterPoint(x - 1, y - 1)); - } - else - { - moved_turtle = true; - //trace_polygon( x + 1, y - 1, polygon ); - stack.push(RasterPoint(x + 1, y - 1)); - polygon.push_back(RasterPoint(x + 1, y - 1)); - } - } - } - } - - //Cell 5 - if( !moved_turtle ) - { - if( getValue( _expand_grid, x - 1, y ) == DECISION ) - { - if( _connection_grid == NULL ) - { - polygon.clear(); - return; - } - - //Find the four cells in the flow grid - //Constrict to center cell of connection_grid - //( x, y ) now == Cell 1 - //4 3 2 - //5 0 1 - //6 7 8 - - //Cell 2 - _cell2_x = ( (x) - 1 )/2; - _cell2_y = ( (y-1) - 1 )/2; - //Cell 4 - _cell4_x = ( (x-2) - 1 )/2; - _cell4_y = ( (y-1) - 1 )/2; - //Cell 6 - _cell6_x = ( (x-2) - 1 )/2; - cell6_y = ( (y+1) - 1 )/2; - //Cell 8 - _cell8_x = ( (x) - 1 )/2; - _cell8_y = ( (y+1) - 1 )/2; - - _flow2 = getValue( _connection_grid, _cell2_x, _cell2_y ); - _flow8 = getValue( _connection_grid, _cell8_x, _cell8_y ); - _flow4 = getValue( _connection_grid, _cell4_x, _cell4_y ); - _flow6 = getValue( _connection_grid, _cell6_x, cell6_y ); - - //Move to Down-Left ... maybe - if( _flow2 == 6 || _flow6 == 2 ) - { - if( getValue( _expand_grid, x - 1, y + 1 ) == TRACE_BORDER ) - { - moved_turtle = true; - //trace_polygon( x - 1, y + 1, polygon ); - stack.push(RasterPoint(x - 1, y + 1)); - polygon.push_back(RasterPoint(x - 1, y + 1)); - } - } - //Move to Up-Left ... maybe - else if( _flow4 == 8 || _flow8 == 4 ) - { - if( getValue( _expand_grid, x - 1, y - 1 ) == TRACE_BORDER ) - { - moved_turtle = true; - //trace_polygon( x - 1, y - 1, polygon ); - stack.push(RasterPoint(x - 1, y - 1)); - polygon.push_back(RasterPoint(x - 1, y - 1)); - } - } - //Cannot be determined by flow directions - else - { - if( getValue( _expand_grid, x, y - 1 ) == CURRENT_POLYGON ) - { - moved_turtle = true; - //trace_polygon( x - 1, y - 1, polygon ); - stack.push(RasterPoint(x - 1, y - 1)); - polygon.push_back(RasterPoint(x - 1, y - 1)); - } - else - { - moved_turtle = true; - //trace_polygon( x - 1, y + 1, polygon ); - stack.push(RasterPoint(x - 1, y + 1)); - polygon.push_back(RasterPoint(x - 1, y + 1)); - } - } - } - } - - //Cell 7 - if( !moved_turtle ) { - if( getValue( _expand_grid, x, y + 1 ) == DECISION ) - { - if( _connection_grid == NULL ) - { - polygon.clear(); - return; - } - - //Find the four cells in the flow grid - //Constrict to center cell of connection_grid - //( x, y ) now == Cell 3 - //4 3 2 - //5 0 1 - //6 7 8 - - //Cell 2 - _cell2_x = ( (x+1) - 1 )/2; - _cell2_y = ( (y) - 1 )/2; - //Cell 4 - _cell4_x = ( (x-1) - 1 )/2; - _cell4_y = ( (y) - 1 )/2; - //Cell 6 - _cell6_x = ( (x-1) - 1 )/2; - cell6_y = ( (y+2) - 1 )/2; - //Cell 8 - _cell8_x = ( (x+1) - 1 )/2; - _cell8_y = ( (y+2) - 1 )/2; - - _flow2 = getValue( _connection_grid, _cell2_x, _cell2_y ); - _flow8 = getValue( _connection_grid, _cell8_x, _cell8_y ); - _flow4 = getValue( _connection_grid, _cell4_x, _cell4_y ); - _flow6 = getValue( _connection_grid, _cell6_x, cell6_y ); - - //Move to Down-Left ... maybe - if( _flow2 == 6 || _flow6 == 2 ) - { - if( getValue( _expand_grid, x - 1, y + 1 ) == TRACE_BORDER ) - { - moved_turtle = true; - //trace_polygon( x - 1, y + 1, polygon ); - stack.push(RasterPoint(x - 1, y + 1)); - polygon.push_back(RasterPoint(x - 1, y + 1)); - } - } - //Move to Down-Right ... maybe - else if( _flow4 == 8 || _flow8 == 4 ) - { - if( getValue( _expand_grid, x + 1, y + 1 ) == TRACE_BORDER ) - { - moved_turtle = true; - //trace_polygon( x + 1, y + 1, polygon ); - stack.push(RasterPoint( x + 1, y + 1)); - polygon.push_back(RasterPoint(x + 1, y + 1)); - } - } - //Cannot be determined by flow directions - else - { - if( getValue( _expand_grid, x - 1, y ) == CURRENT_POLYGON ) - { - moved_turtle = true; - //trace_polygon( x - 1, y + 1, polygon ); - stack.push(RasterPoint(x - 1, y + 1)); - polygon.push_back(RasterPoint(x - 1, y + 1)); - } - else - { - moved_turtle = true; - //trace_polygon( x + 1, y + 1, polygon ); - stack.push(RasterPoint(x + 1, y + 1)); - polygon.push_back(RasterPoint(x + 1, y + 1)); - } - } - } - } - - } + stack.push(RasterPoint(x, y)); + + while (!stack.empty()) + { + pt = stack.top(); + x = pt.column; + y = pt.row; + //x = stack[0].column; + //y = stack[0].row; + stack.pop(); + + + + //Reset the current grid value back to BORDER + if (getValue(_expand_grid, x, y) == TRACE_BORDER) + setValue(_expand_grid, x, y, BORDER); + + // ( x, y ) corresponds to 0 + //4 3 2 + //5 0 1 + //6 7 8 + + bool moved_turtle = false; + //Restrict to Cardinal Directions + //Cell 1 + if (getValue(_expand_grid, x + 1, y) == TRACE_BORDER) + { + moved_turtle = true; + //trace_polygon( x + 1, y, polygon ); + stack.push(RasterPoint(x + 1, y)); + polygon.push_back(RasterPoint(x + 1, y)); + } + //Cell 3 + else if (getValue(_expand_grid, x, y - 1) == TRACE_BORDER) + { + moved_turtle = true; + //trace_polygon( x, y - 1, polygon ); + stack.push(RasterPoint(x, y - 1)); + polygon.push_back(RasterPoint(x, y - 1)); + } + //Cell 5 + else if (getValue(_expand_grid, x - 1, y) == TRACE_BORDER) + { + moved_turtle = true; + //trace_polygon( x - 1, y, polygon ); + stack.push(RasterPoint(x - 1, y)); + polygon.push_back(RasterPoint(x - 1, y)); + } + //Cell 7 + else if (getValue(_expand_grid, x, y + 1) == TRACE_BORDER) + { + moved_turtle = true; + //trace_polygon( x, y + 1, polygon ); + stack.push(RasterPoint(x, y + 1)); + polygon.push_back(RasterPoint(x, y + 1)); + } + + //Look for a Decision Node ... + //Cell 1 + if (!moved_turtle) + { + if (getValue(_expand_grid, x + 1, y) == DECISION) + { + if (_connection_grid == NULL) + { + polygon.clear(); + return; + } + + //Find the four cells in the flow grid + //Constrict to center cell of connection_grid + //( x, y ) now == Cell 5 + //4 3 2 + //5 0 1 + //6 7 8 + + //Cell 2 + _cell2_x = ((x + 2) - 1) / 2; + _cell2_y = ((y - 1) - 1) / 2; + //Cell 4 + _cell4_x = ((x)-1) / 2; + _cell4_y = ((y - 1) - 1) / 2; + //Cell 6 + _cell6_x = ((x)-1) / 2; + cell6_y = ((y + 1) - 1) / 2; + //Cell 8 + _cell8_x = ((x + 2) - 1) / 2; + _cell8_y = ((y + 1) - 1) / 2; + + _flow2 = getValue(_connection_grid, _cell2_x, _cell2_y); + _flow8 = getValue(_connection_grid, _cell8_x, _cell8_y); + _flow4 = getValue(_connection_grid, _cell4_x, _cell4_y); + _flow6 = getValue(_connection_grid, _cell6_x, cell6_y); + + //Move to Up-Right ... maybe + if (_flow2 == 6 || _flow6 == 2) + { + if (getValue(_expand_grid, x + 1, y - 1) == TRACE_BORDER) + { + moved_turtle = true; + //trace_polygon( x + 1, y - 1, polygon ); + stack.push(RasterPoint(x + 1, y - 1)); + polygon.push_back(RasterPoint(x + 1, y - 1)); + } + } + //Move to Down-Right ... maybe + else if (_flow4 == 8 || _flow8 == 4) + { + if (getValue(_expand_grid, x + 1, y + 1) == TRACE_BORDER) + { + moved_turtle = true; + //trace_polygon( x + 1, y + 1, polygon ); + stack.push(RasterPoint(x + 1, y + 1)); + polygon.push_back(RasterPoint(x + 1, y + 1)); + } + } + //Cannot be determined by flow directions + else + { + if (getValue(_expand_grid, x, y - 1) == CURRENT_POLYGON) + { + moved_turtle = true; + //trace_polygon( x + 1, y - 1, polygon ); + stack.push(RasterPoint(x + 1, y - 1)); + polygon.push_back(RasterPoint(x + 1, y - 1)); + } + else + { + moved_turtle = true; + //trace_polygon( x + 1, y + 1, polygon ); + stack.push(RasterPoint(x + 1, y + 1)); + polygon.push_back(RasterPoint(x + 1, y + 1)); + } + } + } + } + + //Cell 3 + if (!moved_turtle) + { + if (getValue(_expand_grid, x, y - 1) == DECISION) + { + if (_connection_grid == NULL) + { + polygon.clear(); + return; + } + + //Find the four cells in the flow grid + //Constrict to center cell of connection_grid + //( x, y ) now == Cell 7 + //4 3 2 + //5 0 1 + //6 7 8 + + //Cell 2 + _cell2_x = ((x + 1) - 1) / 2; + _cell2_y = ((y - 2) - 1) / 2; + //Cell 4 + _cell4_x = ((x - 1) - 1) / 2; + _cell4_y = ((y - 2) - 1) / 2; + //Cell 6 + _cell6_x = ((x - 1) - 1) / 2; + cell6_y = ((y)-1) / 2; + //Cell 8 + _cell8_x = ((x + 1) - 1) / 2; + _cell8_y = ((y)-1) / 2; + + _flow2 = getValue(_connection_grid, _cell2_x, _cell2_y); + _flow8 = getValue(_connection_grid, _cell8_x, _cell8_y); + _flow4 = getValue(_connection_grid, _cell4_x, _cell4_y); + _flow6 = getValue(_connection_grid, _cell6_x, cell6_y); + + //Move to Up-Right ... maybe + if (_flow2 == 6 || _flow6 == 2) + { + if (getValue(_expand_grid, x + 1, y - 1) == TRACE_BORDER) + { + moved_turtle = true; + //trace_polygon( x + 1, y - 1, polygon ); + stack.push(RasterPoint(x + 1, y - 1)); + polygon.push_back(RasterPoint(x + 1, y - 1)); + } + } + //Move to Up-Left ... maybe + else if (_flow4 == 8 || _flow8 == 4) + { + if (getValue(_expand_grid, x - 1, y - 1) == TRACE_BORDER) + { + moved_turtle = true; + //trace_polygon( x - 1, y - 1, polygon ); + stack.push(RasterPoint(x - 1, y - 1)); + polygon.push_back(RasterPoint(x - 1, y - 1)); + } + } + //Cannot be determined by flow directions + else + { + if (getValue(_expand_grid, x - 1, y) == CURRENT_POLYGON) + { + moved_turtle = true; + //trace_polygon( x - 1, y - 1, polygon ); + stack.push(RasterPoint(x - 1, y - 1)); + polygon.push_back(RasterPoint(x - 1, y - 1)); + } + else + { + moved_turtle = true; + //trace_polygon( x + 1, y - 1, polygon ); + stack.push(RasterPoint(x + 1, y - 1)); + polygon.push_back(RasterPoint(x + 1, y - 1)); + } + } + } + } + + //Cell 5 + if (!moved_turtle) + { + if (getValue(_expand_grid, x - 1, y) == DECISION) + { + if (_connection_grid == NULL) + { + polygon.clear(); + return; + } + + //Find the four cells in the flow grid + //Constrict to center cell of connection_grid + //( x, y ) now == Cell 1 + //4 3 2 + //5 0 1 + //6 7 8 + + //Cell 2 + _cell2_x = ((x)-1) / 2; + _cell2_y = ((y - 1) - 1) / 2; + //Cell 4 + _cell4_x = ((x - 2) - 1) / 2; + _cell4_y = ((y - 1) - 1) / 2; + //Cell 6 + _cell6_x = ((x - 2) - 1) / 2; + cell6_y = ((y + 1) - 1) / 2; + //Cell 8 + _cell8_x = ((x)-1) / 2; + _cell8_y = ((y + 1) - 1) / 2; + + _flow2 = getValue(_connection_grid, _cell2_x, _cell2_y); + _flow8 = getValue(_connection_grid, _cell8_x, _cell8_y); + _flow4 = getValue(_connection_grid, _cell4_x, _cell4_y); + _flow6 = getValue(_connection_grid, _cell6_x, cell6_y); + + //Move to Down-Left ... maybe + if (_flow2 == 6 || _flow6 == 2) + { + if (getValue(_expand_grid, x - 1, y + 1) == TRACE_BORDER) + { + moved_turtle = true; + //trace_polygon( x - 1, y + 1, polygon ); + stack.push(RasterPoint(x - 1, y + 1)); + polygon.push_back(RasterPoint(x - 1, y + 1)); + } + } + //Move to Up-Left ... maybe + else if (_flow4 == 8 || _flow8 == 4) + { + if (getValue(_expand_grid, x - 1, y - 1) == TRACE_BORDER) + { + moved_turtle = true; + //trace_polygon( x - 1, y - 1, polygon ); + stack.push(RasterPoint(x - 1, y - 1)); + polygon.push_back(RasterPoint(x - 1, y - 1)); + } + } + //Cannot be determined by flow directions + else + { + if (getValue(_expand_grid, x, y - 1) == CURRENT_POLYGON) + { + moved_turtle = true; + //trace_polygon( x - 1, y - 1, polygon ); + stack.push(RasterPoint(x - 1, y - 1)); + polygon.push_back(RasterPoint(x - 1, y - 1)); + } + else + { + moved_turtle = true; + //trace_polygon( x - 1, y + 1, polygon ); + stack.push(RasterPoint(x - 1, y + 1)); + polygon.push_back(RasterPoint(x - 1, y + 1)); + } + } + } + } + + //Cell 7 + if (!moved_turtle) { + if (getValue(_expand_grid, x, y + 1) == DECISION) + { + if (_connection_grid == NULL) + { + polygon.clear(); + return; + } + + //Find the four cells in the flow grid + //Constrict to center cell of connection_grid + //( x, y ) now == Cell 3 + //4 3 2 + //5 0 1 + //6 7 8 + + //Cell 2 + _cell2_x = ((x + 1) - 1) / 2; + _cell2_y = ((y)-1) / 2; + //Cell 4 + _cell4_x = ((x - 1) - 1) / 2; + _cell4_y = ((y)-1) / 2; + //Cell 6 + _cell6_x = ((x - 1) - 1) / 2; + cell6_y = ((y + 2) - 1) / 2; + //Cell 8 + _cell8_x = ((x + 1) - 1) / 2; + _cell8_y = ((y + 2) - 1) / 2; + + _flow2 = getValue(_connection_grid, _cell2_x, _cell2_y); + _flow8 = getValue(_connection_grid, _cell8_x, _cell8_y); + _flow4 = getValue(_connection_grid, _cell4_x, _cell4_y); + _flow6 = getValue(_connection_grid, _cell6_x, cell6_y); + + //Move to Down-Left ... maybe + if (_flow2 == 6 || _flow6 == 2) + { + if (getValue(_expand_grid, x - 1, y + 1) == TRACE_BORDER) + { + moved_turtle = true; + //trace_polygon( x - 1, y + 1, polygon ); + stack.push(RasterPoint(x - 1, y + 1)); + polygon.push_back(RasterPoint(x - 1, y + 1)); + } + } + //Move to Down-Right ... maybe + else if (_flow4 == 8 || _flow8 == 4) + { + if (getValue(_expand_grid, x + 1, y + 1) == TRACE_BORDER) + { + moved_turtle = true; + //trace_polygon( x + 1, y + 1, polygon ); + stack.push(RasterPoint(x + 1, y + 1)); + polygon.push_back(RasterPoint(x + 1, y + 1)); + } + } + //Cannot be determined by flow directions + else + { + if (getValue(_expand_grid, x - 1, y) == CURRENT_POLYGON) + { + moved_turtle = true; + //trace_polygon( x - 1, y + 1, polygon ); + stack.push(RasterPoint(x - 1, y + 1)); + polygon.push_back(RasterPoint(x - 1, y + 1)); + } + else + { + moved_turtle = true; + //trace_polygon( x + 1, y + 1, polygon ); + stack.push(RasterPoint(x + 1, y + 1)); + polygon.push_back(RasterPoint(x + 1, y + 1)); + } + } + } + } + + } } -inline bool CUtils::is_joint( double cell2, double cell8, double cell4, double cell6 ) -{ - int number_unique = 1; - if( cell2 != cell8 && cell2 != cell4 && cell2 != cell6 ) - number_unique++; - if( cell8 != cell2 && cell8 != cell4 && cell8 != cell6 ) - number_unique++; - if( cell4 != cell2 && cell4 != cell8 && cell4 != cell6 ) - number_unique++; - if( cell6 != cell2 && cell6 != cell8 && cell6 != cell4 ) - number_unique++; - - if( number_unique > 2 ) - return true; - return false; +inline bool CUtils::is_joint(double cell2, double cell8, double cell4, double cell6) +{ + int number_unique = 1; + if (cell2 != cell8 && cell2 != cell4 && cell2 != cell6) + number_unique++; + if (cell8 != cell2 && cell8 != cell4 && cell8 != cell6) + number_unique++; + if (cell4 != cell2 && cell4 != cell8 && cell4 != cell6) + number_unique++; + if (cell6 != cell2 && cell6 != cell8 && cell6 != cell4) + number_unique++; + + if (number_unique > 2) + return true; + return false; } @@ -2647,185 +2724,186 @@ inline bool CUtils::is_joint( double cell2, double cell8, double cell4, double c #define ROUND(X) (long)(X + .5) #endif -STDMETHODIMP CUtils::ShapefileToGrid(IShapefile * Shpfile, VARIANT_BOOL UseShapefileBounds, IGridHeader * GridHeader, double Cellsize, VARIANT_BOOL UseShapeNumber, short SingleValue,IGrid ** retval) +STDMETHODIMP CUtils::ShapefileToGrid(IShapefile * Shpfile, VARIANT_BOOL UseShapefileBounds, IGridHeader * GridHeader, double Cellsize, VARIANT_BOOL UseShapeNumber, short SingleValue, IGrid ** retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - - USES_CONVERSION; - - if( Shpfile == NULL ) - { - *retval = NULL; - this->ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); - return S_OK; - } - - if (!((CShapefile*)Shpfile)->ValidateInput(Shpfile, "ShapefileToGrid", "Shpfile", VARIANT_FALSE, "Utils")) - return S_OK; - - CoCreateInstance(CLSID_Grid,NULL,CLSCTX_INPROC_SERVER,IID_IGrid,(void**)retval); - - VARIANT_BOOL result = VARIANT_FALSE; - VARIANT vndv; //no data value - VariantInit(&vndv); - vndv.vt = VT_I2; - short NoData; - - if (UseShapefileBounds == VARIANT_FALSE) - { - if ( GridHeader == NULL) - { //if the user doesn't want to use the shapefilebounds, then they MUST - //give us a GridHeader...so, if this is null, fail - (*retval)->Release(); - *retval = NULL; - this->ErrorMessage(tkINVALID_PARAMETER_VALUE); - goto cleaning; - } - - - //attempt to create a new grid of the correct size - GridHeader->get_NodataValue(&vndv); - sVal(vndv,NoData); - - if (UseShapeNumber == VARIANT_FALSE && NoData == SingleValue) - { - //we must fail because we can't use the nodatavalue from the grid - //as the singlevalue of the shapefile being converted to a grid; - (*retval)->Release(); - *retval = NULL; - this->ErrorMessage(tkINVALID_PARAMETER_VALUE); - goto cleaning; - } - - (*retval)->CreateNew(m_globalSettings.emptyBstr,GridHeader,ShortDataType ,vndv,VARIANT_TRUE,UseExtension,_globalCallback,&result); - - if ( result == VARIANT_FALSE) - { //failed to create new grid - (*retval)->get_LastErrorCode(&_lastErrorCode); - (*retval)->Release(); - *retval = NULL; - ErrorMessage(_lastErrorCode); - goto cleaning; - } - - long NumShapes; - - Shpfile->get_NumShapes(&NumShapes); - - IShape * shape = NULL; - - short cellValue; - - //loop through each shape and examine all points within the shape - for(short CurShape = 0; CurShape < NumShapes; CurShape++) - { - if (UseShapeNumber == VARIANT_FALSE) - cellValue = SingleValue; - else - cellValue = CurShape; - - ((CShapefile*)Shpfile)->GetValidatedShape(CurShape,&shape); - if (!shape) continue; - if ( PolygonToGrid(shape,retval,cellValue) == false) - break; - shape->Release(); - } - } - else - { - return NULL; //not complete - - IGridHeader * LocalGridHeader; - (*retval)->get_Header(&LocalGridHeader); - - //attempt to create a new grid of the correct size - LocalGridHeader->get_NodataValue(&vndv); - sVal(vndv,NoData); - - //make sure that the singlevalue they want to use isn't the nodata value we are using - if (UseShapeNumber == VARIANT_FALSE && NoData == SingleValue) - { - NoData = NoData - 1; - vndv.vt = VT_I2; - vndv.iVal = NoData; - - LocalGridHeader->put_NodataValue(vndv); - } + AFX_MANAGE_STATE(AfxGetStaticModuleState()) - //Create a header that uses the minimum size allowed - IExtents * bndbox; - Shpfile->get_Extents(&bndbox); - - double xllcenter = 0, yllcenter = 0; - double xurcenter = 0, yurcenter = 0; - bndbox->get_xMin(&xllcenter); - bndbox->get_yMin(&yllcenter); - bndbox->get_xMax(&xurcenter); - bndbox->get_yMax(&yurcenter); - bndbox->Release(); - - if( Cellsize <= 1 ) - { *retval = NULL; - this->ErrorMessage( tkINVALID_PARAMETER_VALUE); - goto cleaning; - } + USES_CONVERSION; - long ncols = ROUND( ( xurcenter - xllcenter ) / Cellsize ) + 1; - long nrows = ROUND( ( yurcenter - yllcenter )/ Cellsize ) + 1; - - LocalGridHeader->put_dX(Cellsize); - LocalGridHeader->put_dY(Cellsize); - VARIANT vndv; - VariantInit(&vndv); // is this code reachable? - vndv.iVal = NoData; - LocalGridHeader->put_NodataValue(vndv); - LocalGridHeader->put_NumberCols(ncols); - LocalGridHeader->put_NumberRows(nrows); - LocalGridHeader->put_XllCenter(xllcenter ); - LocalGridHeader->put_YllCenter(yllcenter); - - (*retval)->CreateNew(m_globalSettings.emptyBstr,LocalGridHeader,ShortDataType ,vndv,TRUE,UseExtension,_globalCallback,&result); - - bndbox->Release(); - LocalGridHeader->Release(); - - if ( result == VARIANT_FALSE) - { //failed to create new grid - (*retval)->get_LastErrorCode(&_lastErrorCode); - (*retval)->Release(); - *retval = NULL; - ErrorMessage(_lastErrorCode); - return S_OK; - } + if (Shpfile == NULL) + { + *retval = NULL; + this->ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); + return S_OK; + } - long NumShapes; + if (!((CShapefile*)Shpfile)->ValidateInput(Shpfile, "ShapefileToGrid", "Shpfile", VARIANT_FALSE, "Utils")) + return S_OK; - Shpfile->get_NumShapes(&NumShapes); + CoCreateInstance(CLSID_Grid, NULL, CLSCTX_INPROC_SERVER, IID_IGrid, (void**)retval); - IShape * shape = NULL; - - short cellValue; + VARIANT_BOOL result = VARIANT_FALSE; + VARIANT vndv; //no data value + VariantInit(&vndv); + vndv.vt = VT_I2; + short NoData; - //loop through each shape and examine all points within the shape - for(short CurShape = 0; CurShape < NumShapes; CurShape++) - { - if (UseShapeNumber == FALSE) - cellValue = SingleValue; - else - cellValue = CurShape; - - ((CShapefile*)Shpfile)->GetValidatedShape(CurShape,&shape); - if (!shape) continue; - if ( PolygonToGrid(shape,retval,cellValue) == false) - break; - shape->Release(); - } - } + if (UseShapefileBounds == VARIANT_FALSE) + { + if (GridHeader == NULL) + { //if the user doesn't want to use the shapefilebounds, then they MUST + //give us a GridHeader...so, if this is null, fail + (*retval)->Release(); + *retval = NULL; + this->ErrorMessage(tkINVALID_PARAMETER_VALUE); + goto cleaning; + } + + + //attempt to create a new grid of the correct size + GridHeader->get_NodataValue(&vndv); + sVal(vndv, NoData); + + if (UseShapeNumber == VARIANT_FALSE && NoData == SingleValue) + { + //we must fail because we can't use the nodatavalue from the grid + //as the singlevalue of the shapefile being converted to a grid; + (*retval)->Release(); + *retval = NULL; + this->ErrorMessage(tkINVALID_PARAMETER_VALUE); + goto cleaning; + } + + (*retval)->CreateNew(m_globalSettings.emptyBstr, GridHeader, ShortDataType, vndv, VARIANT_TRUE, UseExtension, _globalCallback, &result); + + if (result == VARIANT_FALSE) + { //failed to create new grid + (*retval)->get_LastErrorCode(&_lastErrorCode); + (*retval)->Release(); + *retval = NULL; + ErrorMessage(_lastErrorCode); + goto cleaning; + } + + long NumShapes; + + Shpfile->get_NumShapes(&NumShapes); + + IShape * shape = NULL; + + short cellValue; + + //loop through each shape and examine all points within the shape + for (short CurShape = 0; CurShape < NumShapes; CurShape++) + { + if (UseShapeNumber == VARIANT_FALSE) + cellValue = SingleValue; + else + cellValue = CurShape; + + ((CShapefile*)Shpfile)->GetValidatedShape(CurShape, &shape); + if (!shape) continue; + if (PolygonToGrid(shape, retval, cellValue) == false) + break; + shape->Release(); + } + } + else + { + return NULL; //not complete + + IGridHeader * LocalGridHeader; + (*retval)->get_Header(&LocalGridHeader); + + //attempt to create a new grid of the correct size + LocalGridHeader->get_NodataValue(&vndv); + sVal(vndv, NoData); + + //make sure that the singlevalue they want to use isn't the nodata value we are using + if (UseShapeNumber == VARIANT_FALSE && NoData == SingleValue) + { + NoData = NoData - 1; + vndv.vt = VT_I2; + vndv.iVal = NoData; + + LocalGridHeader->put_NodataValue(vndv); + } + + //Create a header that uses the minimum size allowed + IExtents * bndbox; + Shpfile->get_Extents(&bndbox); + + double xllcenter = 0, yllcenter = 0; + double xurcenter = 0, yurcenter = 0; + bndbox->get_xMin(&xllcenter); + bndbox->get_yMin(&yllcenter); + bndbox->get_xMax(&xurcenter); + bndbox->get_yMax(&yurcenter); + bndbox->Release(); + + if (Cellsize <= 1) + { + *retval = NULL; + this->ErrorMessage(tkINVALID_PARAMETER_VALUE); + goto cleaning; + } + + long ncols = ROUND((xurcenter - xllcenter) / Cellsize) + 1; + long nrows = ROUND((yurcenter - yllcenter) / Cellsize) + 1; + + LocalGridHeader->put_dX(Cellsize); + LocalGridHeader->put_dY(Cellsize); + VARIANT vndv; + VariantInit(&vndv); // is this code reachable? + vndv.iVal = NoData; + LocalGridHeader->put_NodataValue(vndv); + LocalGridHeader->put_NumberCols(ncols); + LocalGridHeader->put_NumberRows(nrows); + LocalGridHeader->put_XllCenter(xllcenter); + LocalGridHeader->put_YllCenter(yllcenter); + + (*retval)->CreateNew(m_globalSettings.emptyBstr, LocalGridHeader, ShortDataType, vndv, TRUE, UseExtension, _globalCallback, &result); + + bndbox->Release(); + LocalGridHeader->Release(); + + if (result == VARIANT_FALSE) + { //failed to create new grid + (*retval)->get_LastErrorCode(&_lastErrorCode); + (*retval)->Release(); + *retval = NULL; + ErrorMessage(_lastErrorCode); + return S_OK; + } + + long NumShapes; + + Shpfile->get_NumShapes(&NumShapes); + + IShape * shape = NULL; + + short cellValue; + + //loop through each shape and examine all points within the shape + for (short CurShape = 0; CurShape < NumShapes; CurShape++) + { + if (UseShapeNumber == FALSE) + cellValue = SingleValue; + else + cellValue = CurShape; + + ((CShapefile*)Shpfile)->GetValidatedShape(CurShape, &shape); + if (!shape) continue; + if (PolygonToGrid(shape, retval, cellValue) == false) + break; + shape->Release(); + } + } cleaning: - VariantClear(&vndv); - CallbackHelper::ProgressCompleted(_globalCallback, _key); - return S_OK; + VariantClear(&vndv); + CallbackHelper::ProgressCompleted(_globalCallback, _key); + return S_OK; } // ******************************************************************* @@ -2833,300 +2911,300 @@ STDMETHODIMP CUtils::ShapefileToGrid(IShapefile * Shpfile, VARIANT_BOOL UseShape // ******************************************************************* bool CUtils::PolygonToGrid(IShape * shape, IGrid ** grid, short cellValue) { - LineBresenham Line; - std::vector ListOfPoints; - lbPoint StartPoint, EndPoint; - IPoint * shpPoint = NULL; - long NumPoints = 0; - double CurX; - double CurY; - - ListOfPoints.clear(); - - if (shape == NULL || grid == NULL) - { - this->ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); - return false; - } - - CComVariant val; - val.vt = VT_I2; //the variant needs to be declared as a short - val.iVal = cellValue; //set it equal to the value specified in parameter list + LineBresenham Line; + std::vector ListOfPoints; + lbPoint StartPoint, EndPoint; + IPoint * shpPoint = NULL; + long NumPoints = 0; + double CurX; + double CurY; - shape->get_NumPoints(&NumPoints); - for(int CurPoint = 0; CurPoint <= NumPoints; CurPoint++) - { - if ( CurPoint < NumPoints) - { - shape->get_Point(CurPoint,&shpPoint); - } - else - { - //we need to make sure to connect the first point to the last point - ShpfileType shapetype; - shape->get_ShapeType(&shapetype); + ListOfPoints.clear(); - if (shapetype == SHP_POLYGON || shapetype == SHP_POLYGONZ || shapetype == SHP_POLYGONM ) - { - shape->get_Point(0,&shpPoint); - } - else - break;//jump out of loop, we are done - } - - if ( !shpPoint ) - { - shape->get_LastErrorCode(&_lastErrorCode); - ErrorMessage(_lastErrorCode); - return false; - } - else - { - //if we got to this point, then we have a valid shape and a valid shpPoint - if ( CurPoint == 0 ) - { - //we are one the first point, so just store the point and continue - shpPoint->get_X(&CurX); - shpPoint->get_Y(&CurY); - shpPoint->Release(); - - //this next line uses the grid to calculate the location of - //the current point within the grid - (*grid)->ProjToCell(CurX,CurY,&(EndPoint.x),&(EndPoint.y)); - } - else - { - //set the previous point equal to the currentPoint - //then get the new Current point - StartPoint = EndPoint; - - shpPoint->get_X(&CurX); - shpPoint->get_Y(&CurY); - shpPoint->Release(); - - //this next line uses the grid to calculate the location of - //the current point within the grid - (*grid)->ProjToCell(CurX,CurY,&(EndPoint.x),&(EndPoint.y)); - - //now run the bresenham on the two points so far - ListOfPoints = Line.ComputeLinePoints(StartPoint,EndPoint); - - for (int i = 0; i < (int)ListOfPoints.size(); i++) - (*grid)->put_Value(ListOfPoints[i].x,ListOfPoints[i].y, val); - } - } - }//end for + if (shape == NULL || grid == NULL) + { + this->ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); + return false; + } - return true; + CComVariant val; + val.vt = VT_I2; //the variant needs to be declared as a short + val.iVal = cellValue; //set it equal to the value specified in parameter list + + shape->get_NumPoints(&NumPoints); + for (int CurPoint = 0; CurPoint <= NumPoints; CurPoint++) + { + if (CurPoint < NumPoints) + { + shape->get_Point(CurPoint, &shpPoint); + } + else + { + //we need to make sure to connect the first point to the last point + ShpfileType shapetype; + shape->get_ShapeType(&shapetype); + + if (shapetype == SHP_POLYGON || shapetype == SHP_POLYGONZ || shapetype == SHP_POLYGONM) + { + shape->get_Point(0, &shpPoint); + } + else + break;//jump out of loop, we are done + } + + if (!shpPoint) + { + shape->get_LastErrorCode(&_lastErrorCode); + ErrorMessage(_lastErrorCode); + return false; + } + else + { + //if we got to this point, then we have a valid shape and a valid shpPoint + if (CurPoint == 0) + { + //we are one the first point, so just store the point and continue + shpPoint->get_X(&CurX); + shpPoint->get_Y(&CurY); + shpPoint->Release(); + + //this next line uses the grid to calculate the location of + //the current point within the grid + (*grid)->ProjToCell(CurX, CurY, &(EndPoint.x), &(EndPoint.y)); + } + else + { + //set the previous point equal to the currentPoint + //then get the new Current point + StartPoint = EndPoint; + + shpPoint->get_X(&CurX); + shpPoint->get_Y(&CurY); + shpPoint->Release(); + + //this next line uses the grid to calculate the location of + //the current point within the grid + (*grid)->ProjToCell(CurX, CurY, &(EndPoint.x), &(EndPoint.y)); + + //now run the bresenham on the two points so far + ListOfPoints = Line.ComputeLinePoints(StartPoint, EndPoint); + + for (int i = 0; i < (int)ListOfPoints.size(); i++) + (*grid)->put_Value(ListOfPoints[i].x, ListOfPoints[i].y, val); + } + } + }//end for + + return true; } STDMETHODIMP CUtils::hBitmapToPicture(long hBitmap, IPictureDisp **retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + + HBITMAP bitmap = (HBITMAP)hBitmap; + PICTDESC pictdesc; + pictdesc.cbSizeofstruct = sizeof(PICTDESC); + pictdesc.picType = PICTYPE_BITMAP; + pictdesc.bmp.hbitmap = bitmap; + pictdesc.bmp.hpal = NULL; + *retval = NULL; + OleCreatePictureIndirect(&pictdesc, IID_IPictureDisp, TRUE, (void**)retval); + + return S_OK; +} - HBITMAP bitmap = (HBITMAP)hBitmap; - PICTDESC pictdesc; - pictdesc.cbSizeofstruct = sizeof(PICTDESC); - pictdesc.picType = PICTYPE_BITMAP; - pictdesc.bmp.hbitmap = bitmap; - pictdesc.bmp.hpal = NULL; - *retval = NULL; - OleCreatePictureIndirect(&pictdesc,IID_IPictureDisp,TRUE,(void**)retval); +STDMETHODIMP CUtils::GenerateHillShade(BSTR bstrGridFilename, BSTR bstrShadeFilename, + float z, float scale, float az, float alt, VARIANT_BOOL *retval) + /* Purpose: + Hillshade generates a shaded relief map from any GDAL-supported elevation raster + Credits: + This code was swiped from Matt Perry + perrygeo@gmail.com + http://www.perrygeo.net + Published in Gdal-dev Digest, Vol 19, Issue 20 + Usage: + hillshade input_dem output_hillshade + [-z ZFactor (default=1)] [-s scale* (default=1)] + [-az Azimuth (default=315)] [-alt Altitude (default=45)] + Notes : + Scale for Feet:Latlong use scale=370400, for Meters:LatLong use scale=111120 + */ - return S_OK; -} +{ + USES_CONVERSION; + const char *pszGridFilename = OLE2A(bstrGridFilename); + const char *pszShadeFilename = OLE2A(bstrShadeFilename); -STDMETHODIMP CUtils::GenerateHillShade(BSTR bstrGridFilename, BSTR bstrShadeFilename, - float z, float scale, float az, float alt, VARIANT_BOOL *retval) -/* Purpose: - Hillshade generates a shaded relief map from any GDAL-supported elevation raster - Credits: - This code was swiped from Matt Perry - perrygeo@gmail.com - http://www.perrygeo.net - Published in Gdal-dev Digest, Vol 19, Issue 20 - Usage: - hillshade input_dem output_hillshade - [-z ZFactor (default=1)] [-s scale* (default=1)] - [-az Azimuth (default=315)] [-alt Altitude (default=45)] - Notes : - Scale for Feet:Latlong use scale=370400, for Meters:LatLong use scale=111120 -*/ - -{ - USES_CONVERSION; - const char *pszGridFilename = OLE2A(bstrGridFilename); - const char *pszShadeFilename = OLE2A(bstrShadeFilename); - - GDALDataset *poDataset; - const float radiansToDegrees = (float)(180.0 / 3.14159); - const float degreesToRadians = (float)(3.14159 / 180.0); + GDALDataset *poDataset; + const float radiansToDegrees = (float)(180.0 / 3.14159); + const float degreesToRadians = (float)(3.14159 / 180.0); double adfGeoTransf[6]; float *win; float *shadeBuf; - float x; - float y; + float x; + float y; float aspect; - float slope; - float cang; + float slope; + float cang; int i; int j; int n; int containsNull; - const char *pszFormat = "GTiff"; - - *retval = VARIANT_FALSE; - - try - { - /* ----------------------------------- - * Default Values - */ - - if (z == NULL) z = 1.0; - if (scale == NULL) scale = 1.0; - if (az == NULL) az = 315.0; - if (alt == NULL) alt = 45.0; - - /*--------------------------------------- - * Open Dataset and get raster band (assuming it is band #1) - */ - - GDALAllRegister(); - - poDataset = GdalHelper::OpenRasterDatasetW(OLE2W(bstrGridFilename), GA_ReadOnly); - if( poDataset == NULL ) - { - CallbackHelper::ErrorMsg(Debug::Format("Generate hill shade: couldn't open dataset %s\n", pszGridFilename)); - return S_OK; - } - GDALRasterBand *poBnd; - poBnd = poDataset->GetRasterBand( 1 ); - poDataset->GetGeoTransform( adfGeoTransf ); - - /* ------------------------------------- - * Get variables from input dataset - */ - const double nsres = adfGeoTransf[5]; - const double ewres = adfGeoTransf[1]; - const float nullValue = (float) poBnd->GetNoDataValue( ); - const int nXSize = poBnd->GetXSize(); - const int nYSize = poBnd->GetYSize(); - shadeBuf = (float *) CPLMalloc(sizeof(float)*nXSize); - win = (float *) CPLMalloc(sizeof(float)*9); - - /* ----------------------------------------- - * Create the output dataset and copy over relevant metadata - */ - GDALDriver *poDriver; - poDriver = GetGDALDriverManager()->GetDriverByName(pszFormat); - GDALDataset *poShadeDS; - GDALRasterBand *poShadeBand; - char **papszOptions = NULL; - - poShadeDS = poDriver->Create(pszShadeFilename,nXSize,nYSize,1,GDT_Byte, - papszOptions ); - - if ( !poShadeDS) - return S_OK; - - poShadeDS->SetGeoTransform( adfGeoTransf ); - poShadeDS->SetProjection( poDataset->GetProjectionRef() ); - poShadeBand = poShadeDS->GetRasterBand(1); - poShadeBand->SetNoDataValue( poBnd->GetNoDataValue( ) ); - /* ------------------------------------------ - * Move a 3x3 window over each cell - * (where the cell in question is #4) - * - * 0 1 2 - * 3 4 5 - * 6 7 8 - * - */ - for ( i = 0; i < nYSize; i++) { - for ( j = 0; j < nXSize; j++) { - containsNull = 0; - - // Exclude the edges - if (i == 0 || j == 0 || i == nYSize-1 || j == nXSize-1 ) - { - // We are at the edge so write nullValue and move on - shadeBuf[j] = nullValue; - continue; - } - - // Read in 3x3 window - poBnd->RasterIO( GF_Read, j-1, i-1, 3, 3, - win, 3, 3, GDT_Float32, - 0, 0 ); + const char *pszFormat = "GTiff"; - // Check if window has null value - for ( n = 0; n <= 8; n++) { - if(win[n] == nullValue) { - containsNull = 1; - break; - } - } - - if (containsNull == 1) { - // We have nulls so write nullValue and move on - shadeBuf[j] = nullValue; - continue; - } - else - { - // We have a valid 3x3 window. - - /* --------------------------------------- - * Compute Hillshade - */ - - // First Slope ... - x = (float)(((z*win[0] + z*win[3] + z*win[3] + z*win[6]) - - (z*win[2] + z*win[5] + z*win[5] + z*win[8])) / - (8.0 * ewres * scale)); - - y = (float)(((z*win[6] + z*win[7] + z*win[7] + z*win[8]) - - (z*win[0] + z*win[1] + z*win[1] + z*win[2])) / - (8.0 * nsres * scale)); - - slope = (float)90.0 - atan(sqrt(x*x + y*y))*radiansToDegrees; - - // ... then aspect... - aspect = atan2(x,y); - - // ... then the shade value - cang = (float)(sin(alt*degreesToRadians) * sin(slope*degreesToRadians) + - cos(alt*degreesToRadians) * cos(slope*degreesToRadians) * - cos((az-90.0)*degreesToRadians - aspect)); - - if (cang <= 0.0) - cang = nullValue; - else - cang = static_cast(255.0 * cang); - - shadeBuf[j] = cang; - - } - } - - /* ----------------------------------------- - * Write Line to Raster - */ - poShadeBand->RasterIO( GF_Write, 0, i, nXSize, 1, - shadeBuf, nXSize, 1, GDT_Float32, 0, 0 ); - } + *retval = VARIANT_FALSE; - delete poShadeDS; - *retval = VARIANT_TRUE; - } - - catch(exception e) - { - CallbackHelper::ErrorMsg("Exception in GenerateHillshade."); - } + try + { + /* ----------------------------------- + * Default Values + */ + + if (z == NULL) z = 1.0; + if (scale == NULL) scale = 1.0; + if (az == NULL) az = 315.0; + if (alt == NULL) alt = 45.0; + + /*--------------------------------------- + * Open Dataset and get raster band (assuming it is band #1) + */ + + GDALAllRegister(); + + poDataset = GdalHelper::OpenRasterDatasetW(OLE2W(bstrGridFilename), GA_ReadOnly); + if (poDataset == NULL) + { + CallbackHelper::ErrorMsg(Debug::Format("Generate hill shade: couldn't open dataset %s\n", pszGridFilename)); + return S_OK; + } + GDALRasterBand *poBnd; + poBnd = poDataset->GetRasterBand(1); + poDataset->GetGeoTransform(adfGeoTransf); + + /* ------------------------------------- + * Get variables from input dataset + */ + const double nsres = adfGeoTransf[5]; + const double ewres = adfGeoTransf[1]; + const float nullValue = (float)poBnd->GetNoDataValue(); + const int nXSize = poBnd->GetXSize(); + const int nYSize = poBnd->GetYSize(); + shadeBuf = (float *)CPLMalloc(sizeof(float)*nXSize); + win = (float *)CPLMalloc(sizeof(float) * 9); + + /* ----------------------------------------- + * Create the output dataset and copy over relevant metadata + */ + GDALDriver *poDriver; + poDriver = GetGDALDriverManager()->GetDriverByName(pszFormat); + GDALDataset *poShadeDS; + GDALRasterBand *poShadeBand; + char **papszOptions = NULL; + + poShadeDS = poDriver->Create(pszShadeFilename, nXSize, nYSize, 1, GDT_Byte, + papszOptions); + + if (!poShadeDS) + return S_OK; + + poShadeDS->SetGeoTransform(adfGeoTransf); + poShadeDS->SetProjection(poDataset->GetProjectionRef()); + poShadeBand = poShadeDS->GetRasterBand(1); + poShadeBand->SetNoDataValue(poBnd->GetNoDataValue()); + /* ------------------------------------------ + * Move a 3x3 window over each cell + * (where the cell in question is #4) + * + * 0 1 2 + * 3 4 5 + * 6 7 8 + * + */ + for (i = 0; i < nYSize; i++) { + for (j = 0; j < nXSize; j++) { + containsNull = 0; + + // Exclude the edges + if (i == 0 || j == 0 || i == nYSize - 1 || j == nXSize - 1) + { + // We are at the edge so write nullValue and move on + shadeBuf[j] = nullValue; + continue; + } + + // Read in 3x3 window + poBnd->RasterIO(GF_Read, j - 1, i - 1, 3, 3, + win, 3, 3, GDT_Float32, + 0, 0); + + // Check if window has null value + for (n = 0; n <= 8; n++) { + if (win[n] == nullValue) { + containsNull = 1; + break; + } + } + + if (containsNull == 1) { + // We have nulls so write nullValue and move on + shadeBuf[j] = nullValue; + continue; + } + else + { + // We have a valid 3x3 window. + + /* --------------------------------------- + * Compute Hillshade + */ + + // First Slope ... + x = (float)(((z*win[0] + z*win[3] + z*win[3] + z*win[6]) - + (z*win[2] + z*win[5] + z*win[5] + z*win[8])) / + (8.0 * ewres * scale)); + + y = (float)(((z*win[6] + z*win[7] + z*win[7] + z*win[8]) - + (z*win[0] + z*win[1] + z*win[1] + z*win[2])) / + (8.0 * nsres * scale)); + + slope = (float)90.0 - atan(sqrt(x*x + y*y))*radiansToDegrees; + + // ... then aspect... + aspect = atan2(x, y); + + // ... then the shade value + cang = (float)(sin(alt*degreesToRadians) * sin(slope*degreesToRadians) + + cos(alt*degreesToRadians) * cos(slope*degreesToRadians) * + cos((az - 90.0)*degreesToRadians - aspect)); + + if (cang <= 0.0) + cang = nullValue; + else + cang = static_cast(255.0 * cang); + + shadeBuf[j] = cang; + + } + } + + /* ----------------------------------------- + * Write Line to Raster + */ + poShadeBand->RasterIO(GF_Write, 0, i, nXSize, 1, + shadeBuf, nXSize, 1, GDT_Float32, 0, 0); + } + + delete poShadeDS; + *retval = VARIANT_TRUE; + } + + catch (exception e) + { + CallbackHelper::ErrorMsg("Exception in GenerateHillshade."); + } - return S_OK; + return S_OK; } /************************************************************************/ @@ -3134,62 +3212,62 @@ STDMETHODIMP CUtils::GenerateHillShade(BSTR bstrGridFilename, BSTR bstrShadeFi /************************************************************************/ void CUtils::Parse(CString sOrig, int * opts) { - if (sOrig.IsEmpty()) - { - _sArr.RemoveAll(); - *opts = 0; - return; - } - - CString sTemp, sTrans, sStore; - int m, length; - char chSeps[] = " "; - - //set an initial max array size - _sArr.RemoveAll(); - sOrig.TrimRight(); - _sArr.Add( "Dummy value at 0" ); - while(1) - { - if (sOrig.GetLength() <= 0) - break; - - sOrig.TrimLeft(); - - if (sOrig[0] == '"') - { - sOrig.Delete(0); - m = sOrig.Find("\"", 0); - } - else - { - m = sOrig.FindOneOf( (LPCTSTR)chSeps ); - } - - if (m == -1) - { - _sArr.Add( sOrig ); - break; - } - - sTemp = sOrig.Mid(0, m); - _sArr.Add( sTemp ); - sTrans = sOrig.Mid(m+1, sOrig.GetLength()); - sOrig = sTrans; - } - - for (int i = 0; i < _sArr.GetCount(); i++) - { - sTemp = _sArr[i]; - length = sTemp.GetLength (); + if (sOrig.IsEmpty()) + { + _sArr.RemoveAll(); + *opts = 0; + return; + } + + CString sTemp, sTrans, sStore; + int m, length; + char chSeps[] = " "; + + //set an initial max array size + _sArr.RemoveAll(); + sOrig.TrimRight(); + _sArr.Add("Dummy value at 0"); + while (1) + { + if (sOrig.GetLength() <= 0) + break; + + sOrig.TrimLeft(); + + if (sOrig[0] == '"') + { + sOrig.Delete(0); + m = sOrig.Find("\"", 0); + } + else + { + m = sOrig.FindOneOf((LPCTSTR)chSeps); + } + + if (m == -1) + { + _sArr.Add(sOrig); + break; + } + + sTemp = sOrig.Mid(0, m); + _sArr.Add(sTemp); + sTrans = sOrig.Mid(m + 1, sOrig.GetLength()); + sOrig = sTrans; + } + + for (int i = 0; i < _sArr.GetCount(); i++) + { + sTemp = _sArr[i]; + length = sTemp.GetLength(); - if (length < 2 || sTemp[0] != '"' || sTemp[length - 1] != '"') - continue; + if (length < 2 || sTemp[0] != '"' || sTemp[length - 1] != '"') + continue; - _sArr[i] = (sTemp.Left (length - 1)).Right (length - 2); - } + _sArr[i] = (sTemp.Left(length - 1)).Right(length - 2); + } - *opts = (int)_sArr.GetCount(); + *opts = (int)_sArr.GetCount(); } // *********************************************************** @@ -3197,14 +3275,14 @@ void CUtils::Parse(CString sOrig, int * opts) // *********************************************************** STDMETHODIMP CUtils::OGRLayerToShapefile(BSTR Filename, ShpfileType shpType, ICallback *cBack, IShapefile** sf) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - ICallback* callback = cBack ? cBack : _globalCallback; - (*sf) = OgrConverter::ReadOgrLayer(Filename, callback); - if (*sf) - { - ((CShapefile*)(*sf))->ValidateOutput(sf, "OGRLayerToShapefile", "Utils"); - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + ICallback* callback = cBack ? cBack : _globalCallback; + (*sf) = OgrConverter::ReadOgrLayer(Filename, callback); + if (*sf) + { + ((CShapefile*)(*sf))->ValidateOutput(sf, "OGRLayerToShapefile", "Utils"); + } + return S_OK; } // *********************************************************** @@ -3213,93 +3291,93 @@ STDMETHODIMP CUtils::OGRLayerToShapefile(BSTR Filename, ShpfileType shpType, ICa // New implementation, based on GEOS STDMETHODIMP CUtils::ClipPolygon(PolygonOperation op, IShape* SubjectPolygon, IShape* ClipPolygon, IShape** retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *retval = NULL; - - if( SubjectPolygon == NULL || ClipPolygon == NULL ) - { - this->ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); - return S_OK; - } - - ShpfileType shptype; - SubjectPolygon->get_ShapeType (&shptype); - if( shptype != SHP_POLYGON && shptype != SHP_POLYGONM && shptype != SHP_POLYGONZ ) - { - this->ErrorMessage(tkINCOMPATIBLE_SHAPE_TYPE); - return S_OK; - } + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + *retval = NULL; - ClipPolygon->get_ShapeType(&shptype); - if( shptype != SHP_POLYGON && shptype != SHP_POLYGONM && shptype != SHP_POLYGONZ ) - { - this->ErrorMessage(tkINCOMPATIBLE_SHAPE_TYPE); - return S_OK; - } - - bool geos = true; + if (SubjectPolygon == NULL || ClipPolygon == NULL) + { + this->ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); + return S_OK; + } - if (geos) - { - OGRGeometry* geomSubject = OgrConverter::ShapeToGeometry(SubjectPolygon); - if (geomSubject == NULL) return S_OK; + ShpfileType shptype; + SubjectPolygon->get_ShapeType(&shptype); + if (shptype != SHP_POLYGON && shptype != SHP_POLYGONM && shptype != SHP_POLYGONZ) + { + this->ErrorMessage(tkINCOMPATIBLE_SHAPE_TYPE); + return S_OK; + } - OGRGeometry* geomClip = OgrConverter::ShapeToGeometry(ClipPolygon); - if (geomClip == NULL) - { - OGRGeometryFactory::destroyGeometry(geomSubject); - return S_OK; - } - - OGRGeometry* oGeom = NULL; - switch(op) - { - case DIFFERENCE_OPERATION: - oGeom = geomSubject->Difference(geomClip); - break; - case INTERSECTION_OPERATION: - oGeom = geomSubject->Intersection(geomClip); - break; - case EXCLUSIVEOR_OPERATION: - oGeom = geomSubject->SymmetricDifference(geomClip); - break; - case UNION_OPERATION: - oGeom = geomSubject->Union(geomClip); - break; - } - - OGRGeometryFactory::destroyGeometry(geomClip); - OGRGeometryFactory::destroyGeometry(geomSubject); + ClipPolygon->get_ShapeType(&shptype); + if (shptype != SHP_POLYGON && shptype != SHP_POLYGONM && shptype != SHP_POLYGONZ) + { + this->ErrorMessage(tkINCOMPATIBLE_SHAPE_TYPE); + return S_OK; + } - if (oGeom != NULL) - { - SubjectPolygon->get_ShapeType(&shptype); - - vector vShapes; - if (OgrConverter::GeometryToShapes(oGeom, &vShapes, ShapeUtility::IsM(shptype))) - { - *retval = vShapes[0]; - - ASSERT(vShapes.size() < 2); + bool geos = true; - // if there are more than one poly, we should release them - for (int i = 1; i < (int)vShapes.size(); i++) - { - if (vShapes[i] != NULL) - { - vShapes[i]->Release(); - } - } - } - OGRGeometryFactory::destroyGeometry(oGeom); - } - } - else - { - IShape* shp = ClipperConverter::ClipPolygon(ClipPolygon, SubjectPolygon, op); - *retval = shp; - } - return S_OK; + if (geos) + { + OGRGeometry* geomSubject = OgrConverter::ShapeToGeometry(SubjectPolygon); + if (geomSubject == NULL) return S_OK; + + OGRGeometry* geomClip = OgrConverter::ShapeToGeometry(ClipPolygon); + if (geomClip == NULL) + { + OGRGeometryFactory::destroyGeometry(geomSubject); + return S_OK; + } + + OGRGeometry* oGeom = NULL; + switch (op) + { + case DIFFERENCE_OPERATION: + oGeom = geomSubject->Difference(geomClip); + break; + case INTERSECTION_OPERATION: + oGeom = geomSubject->Intersection(geomClip); + break; + case EXCLUSIVEOR_OPERATION: + oGeom = geomSubject->SymDifference(geomClip); + break; + case UNION_OPERATION: + oGeom = geomSubject->Union(geomClip); + break; + } + + OGRGeometryFactory::destroyGeometry(geomClip); + OGRGeometryFactory::destroyGeometry(geomSubject); + + if (oGeom != NULL) + { + SubjectPolygon->get_ShapeType(&shptype); + + vector vShapes; + if (OgrConverter::GeometryToShapes(oGeom, &vShapes, ShapeUtility::IsM(shptype))) + { + *retval = vShapes[0]; + + ASSERT(vShapes.size() < 2); + + // if there are more than one poly, we should release them + for (int i = 1; i < (int)vShapes.size(); i++) + { + if (vShapes[i] != NULL) + { + vShapes[i]->Release(); + } + } + } + OGRGeometryFactory::destroyGeometry(oGeom); + } + } + else + { + IShape* shp = ClipperConverter::ClipPolygon(ClipPolygon, SubjectPolygon, op); + *retval = shp; + } + return S_OK; } #pragma region MergeImages @@ -3308,138 +3386,138 @@ STDMETHODIMP CUtils::ClipPolygon(PolygonOperation op, IShape* SubjectPolygon, IS // ****************************************************************** STDMETHODIMP CUtils::MergeImages(/*[in]*/SAFEARRAY* InputNames, /*[in]*/BSTR OutputName, /*out,retval*/VARIANT_BOOL* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *retVal = VARIANT_FALSE; - USES_CONVERSION; - - // Check dimensions of the array. + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + *retVal = VARIANT_FALSE; + USES_CONVERSION; + + // Check dimensions of the array. if (SafeArrayGetDim(InputNames) != 1) - { + { // most likely this error will be caught while marshalling the array - // AfxThrowOleDispatchException(1002, "Type Mismatch in Parameter. Pass a one-dimensional array"); - ErrorMessage(tkFAILED_TO_READ_INPUT_NAMES); - return S_OK; - } + // AfxThrowOleDispatchException(1002, "Type Mismatch in Parameter. Pass a one-dimensional array"); + ErrorMessage(tkFAILED_TO_READ_INPUT_NAMES); + return S_OK; + } - LONG cElements, lLBound, lUBound; - HRESULT hr; - hr = SafeArrayGetLBound(InputNames, 1, &lLBound); + LONG cElements, lLBound, lUBound; + HRESULT hr; + hr = SafeArrayGetLBound(InputNames, 1, &lLBound); if (FAILED(hr)) - { + { ErrorMessage(tkFAILED_TO_READ_INPUT_NAMES); - return S_OK; - } - - hr = SafeArrayGetUBound(InputNames, 1, &lUBound); + return S_OK; + } + + hr = SafeArrayGetUBound(InputNames, 1, &lUBound); if (FAILED(hr)) - { - ErrorMessage(tkFAILED_TO_READ_INPUT_NAMES); - return S_OK; - } - - // TODO: add check that we have an array of BSTR and not the other data type - BSTR HUGEP *pbstr; - hr = SafeArrayAccessData(InputNames, (void HUGEP* FAR*)&pbstr); + { + ErrorMessage(tkFAILED_TO_READ_INPUT_NAMES); + return S_OK; + } + + // TODO: add check that we have an array of BSTR and not the other data type + BSTR HUGEP *pbstr; + hr = SafeArrayAccessData(InputNames, (void HUGEP* FAR*)&pbstr); if (FAILED(hr)) - { - ErrorMessage(tkFAILED_TO_READ_INPUT_NAMES); - return S_OK; - } - - CallbackHelper::Progress(_globalCallback, 0, "Loading input images...", _key); + { + ErrorMessage(tkFAILED_TO_READ_INPUT_NAMES); + return S_OK; + } - VARIANT_BOOL vbretval; - std::vector images; - cElements = lUBound-lLBound + 1; + CallbackHelper::Progress(_globalCallback, 0, "Loading input images...", _key); + + VARIANT_BOOL vbretval; + std::vector images; + cElements = lUBound - lLBound + 1; for (int i = 0; i < cElements; i++) { - IImage* img = NULL; - CoCreateInstance(CLSID_Image,NULL,CLSCTX_INPROC_SERVER,IID_IImage,(void**)&img); - img->Open(pbstr[i], USE_FILE_EXTENSION, VARIANT_FALSE, NULL, &vbretval); - - if (vbretval) - { - images.push_back(img); - } - else - { - img->Release(); - img = NULL; - } - } - - LONG width, height; - if (images.size() <= 1) - { - ErrorMessage(tkAT_LEAST_TWO_DATASOURCES_EXPECTED); - goto Cleaning; - } - else - { - // all the images must have the same size - LONG w, h; - for (unsigned int i = 0; i < images.size(); i++ ) - { - images[i]->get_OriginalWidth(&w); - images[i]->get_OriginalHeight(&h); + IImage* img = NULL; + CoCreateInstance(CLSID_Image, NULL, CLSCTX_INPROC_SERVER, IID_IImage, (void**)&img); + img->Open(pbstr[i], USE_FILE_EXTENSION, VARIANT_FALSE, NULL, &vbretval); + + if (vbretval) + { + images.push_back(img); + } + else + { + img->Release(); + img = NULL; + } + } + + LONG width, height; + if (images.size() <= 1) + { + ErrorMessage(tkAT_LEAST_TWO_DATASOURCES_EXPECTED); + goto Cleaning; + } + else + { + // all the images must have the same size + LONG w, h; + for (unsigned int i = 0; i < images.size(); i++) + { + images[i]->get_OriginalWidth(&w); + images[i]->get_OriginalHeight(&h); + + if (i == 0) + { + width = w; + height = h; + } + else if (w != width || h != height) + { + ErrorMessage(tkIMAGES_MUST_HAVE_THE_SAME_SIZE); + goto Cleaning; + } + } + } + + // saving and copying the pixels + colour* pixels = new colour[width * height]; + for (unsigned int i = 0; i < images.size(); i++) + { + CString s; + CComBSTR name; + images[i]->get_Filename(&name); + s.Format("Processing image %d: %s", i + 1, OLE2A(name)); + int percent = (int)((double)i / (double)(images.size() - 1)*100.0); + CallbackHelper::Progress(_globalCallback, percent, s, _key); - if (i == 0) - { - width = w; - height = h; - } - else if ( w != width || h != height) - { - ErrorMessage(tkIMAGES_MUST_HAVE_THE_SAME_SIZE); - goto Cleaning; - } - } - } + CImageClass* img = (CImageClass*)images[i]; + img->SaveNotNullPixels(true); - // saving and copying the pixels - colour* pixels = new colour[width * height]; - for (unsigned int i = 0; i < images.size(); i++ ) - { - CString s; - CComBSTR name; - images[i]->get_Filename(&name); - s.Format("Processing image %d: %s", i + 1, OLE2A(name)); - int percent = (int)((double)i/(double)(images.size() - 1)*100.0); - CallbackHelper::Progress(_globalCallback, percent, s, _key); - - CImageClass* img = (CImageClass*)images[i]; - img->SaveNotNullPixels(true); - - DataPixels* source = img->m_pixels; - for (int j = 0; j < img->m_pixelsCount; j++) - { - pixels[(source + j)->position] = (source + j)->value; - } + DataPixels* source = img->m_pixels; + for (int j = 0; j < img->m_pixelsCount; j++) + { + pixels[(source + j)->position] = (source + j)->value; + } - img->ClearNotNullPixels(); - } - - // saving the results - CallbackHelper::Progress(_globalCallback, 0, "Saving result...", _key); - - unsigned char* bits = reinterpret_cast(pixels); - Utility::SaveBitmap(width, height, bits, OutputName); + img->ClearNotNullPixels(); + } + + // saving the results + CallbackHelper::Progress(_globalCallback, 0, "Saving result...", _key); + + unsigned char* bits = reinterpret_cast(pixels); + Utility::SaveBitmap(width, height, bits, OutputName); Cleaning: - // cleaning - CallbackHelper::ProgressCompleted(_globalCallback, _key); + // cleaning + CallbackHelper::ProgressCompleted(_globalCallback, _key); - for (unsigned int i = 0; i < images.size(); i++) - { - images[i]->Release(); - } - - if (pixels) - { - delete[] pixels; - pixels = NULL; - } - return S_OK; + for (unsigned int i = 0; i < images.size(); i++) + { + images[i]->Release(); + } + + if (pixels) + { + delete[] pixels; + pixels = NULL; + } + return S_OK; } @@ -3487,104 +3565,104 @@ STDMETHODIMP CUtils::MergeImages(/*[in]*/SAFEARRAY* InputNames, /*[in]*/BSTR Out // *********************************************************** STDMETHODIMP CUtils::ReprojectShapefile(IShapefile* sf, IGeoProjection* source, IGeoProjection* target, IShapefile** result) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - - // ------------------------------------------------ - // Validation - // ------------------------------------------------ - if (!sf || !source || !target) - { - ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); - *result = NULL; - return S_OK; - } - - OGRSpatialReference* ref1 = ((CGeoProjection*)source)->get_SpatialReference(); - OGRSpatialReference* ref2 = ((CGeoProjection*)target)->get_SpatialReference(); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - OGRCoordinateTransformation* transf = OGRCreateCoordinateTransformation( ref1, ref2 ); - if (!transf) - { - ErrorMessage(tkFAILED_TO_REPROJECT); - *result = NULL; - return S_OK; - } - - if(!((CShapefile*)sf)->ValidateInput(sf, "ReprojectShapefile", "sf", VARIANT_FALSE, "Utils")) - return S_OK; - - // ------------------------------------------------ - // Creating output - // ------------------------------------------------ - sf->Clone(result); - - // ------------------------------------------------ - // Processing - // ------------------------------------------------ - VARIANT_BOOL vbretval; - CComVariant var; - long numShapes, count = 0; - sf->get_NumShapes(&numShapes); - - long numFields, percent = 0; - sf->get_NumFields(&numFields); - - for (long i = 0; i < numShapes; i++) - { - CallbackHelper::Progress(_globalCallback, i, numShapes, "Reprojecting...", _key, percent); - - IShape* shp = NULL; - sf->get_Shape(i, &shp); - - IShape* shpNew = NULL; - shp->Clone(&shpNew); - - if (shpNew) - { - long numPoints; - shpNew->get_NumPoints(&numPoints); - - double x, y; - for (long j = 0; j < numPoints; j++) - { - shpNew->get_XY(j, &x, &y, &vbretval); - - // will work faster after embedding to the CShape class - BOOL res = transf->Transform( 1, &x, &y); - if (res) - { - shpNew->put_XY(j, x, y, &vbretval); - } - else - { - // if there is at least one failed point, reprojection will be interrupted - shpNew->Release(); - (*result)->Release(); - (*result) = NULL; - ErrorMessage(tkFAILED_TO_REPROJECT); - return S_OK; - } - } - - (*result)->get_NumShapes(&count); - (*result)->EditInsertShape(shpNew, &count, &vbretval); + // ------------------------------------------------ + // Validation + // ------------------------------------------------ + if (!sf || !source || !target) + { + ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); + *result = NULL; + return S_OK; + } - // copying attributes - for (long j = 0; j < numFields; j++) - { - sf->get_CellValue(j, i, &var); - (*result)->EditCellValue(j, i, var, &vbretval); - } - } - } + OGRSpatialReference* ref1 = ((CGeoProjection*)source)->get_SpatialReference(); + OGRSpatialReference* ref2 = ((CGeoProjection*)target)->get_SpatialReference(); - // -------------------------------------------------- - // Validating output - // -------------------------------------------------- - CallbackHelper::ProgressCompleted(_globalCallback, _key); - ((CShapefile*)sf)->ValidateOutput(&sf, "ReprojectShapefile", "Utils"); - - return S_OK; + OGRCoordinateTransformation* transf = OGRCreateCoordinateTransformation(ref1, ref2); + if (!transf) + { + ErrorMessage(tkFAILED_TO_REPROJECT); + *result = NULL; + return S_OK; + } + + if (!((CShapefile*)sf)->ValidateInput(sf, "ReprojectShapefile", "sf", VARIANT_FALSE, "Utils")) + return S_OK; + + // ------------------------------------------------ + // Creating output + // ------------------------------------------------ + sf->Clone(result); + + // ------------------------------------------------ + // Processing + // ------------------------------------------------ + VARIANT_BOOL vbretval; + CComVariant var; + long numShapes, count = 0; + sf->get_NumShapes(&numShapes); + + long numFields, percent = 0; + sf->get_NumFields(&numFields); + + for (long i = 0; i < numShapes; i++) + { + CallbackHelper::Progress(_globalCallback, i, numShapes, "Reprojecting...", _key, percent); + + IShape* shp = NULL; + sf->get_Shape(i, &shp); + + IShape* shpNew = NULL; + shp->Clone(&shpNew); + + if (shpNew) + { + long numPoints; + shpNew->get_NumPoints(&numPoints); + + double x, y; + for (long j = 0; j < numPoints; j++) + { + shpNew->get_XY(j, &x, &y, &vbretval); + + // will work faster after embedding to the CShape class + BOOL res = transf->Transform(1, &x, &y); + if (res) + { + shpNew->put_XY(j, x, y, &vbretval); + } + else + { + // if there is at least one failed point, reprojection will be interrupted + shpNew->Release(); + (*result)->Release(); + (*result) = NULL; + ErrorMessage(tkFAILED_TO_REPROJECT); + return S_OK; + } + } + + (*result)->get_NumShapes(&count); + (*result)->EditInsertShape(shpNew, &count, &vbretval); + + // copying attributes + for (long j = 0; j < numFields; j++) + { + sf->get_CellValue(j, i, &var); + (*result)->EditCellValue(j, i, var, &vbretval); + } + } + } + + // -------------------------------------------------- + // Validating output + // -------------------------------------------------- + CallbackHelper::ProgressCompleted(_globalCallback, _key); + ((CShapefile*)sf)->ValidateOutput(&sf, "ReprojectShapefile", "Utils"); + + return S_OK; } // ************************************************************** @@ -3592,27 +3670,27 @@ STDMETHODIMP CUtils::ReprojectShapefile(IShapefile* sf, IGeoProjection* source, // ************************************************************** void CUtils::ErrorMessage(long ErrorCode) { - _lastErrorCode = ErrorCode; - CallbackHelper::ErrorMsg("Utils", _globalCallback, _key, ErrorMsg(_lastErrorCode)); + _lastErrorCode = ErrorCode; + CallbackHelper::ErrorMsg("Utils", _globalCallback, _key, ErrorMsg(_lastErrorCode)); } void CUtils::ErrorMessage(ICallback* callback, long ErrorCode) { - _lastErrorCode = ErrorCode; - CallbackHelper::ErrorMsg("Utils", callback, _key, ErrorMsg(_lastErrorCode)); + _lastErrorCode = ErrorCode; + CallbackHelper::ErrorMsg("Utils", callback, _key, ErrorMsg(_lastErrorCode)); } void CUtils::ErrorMessage(long ErrorCode, CString customMessage) { - _lastErrorCode = ErrorCode; - CallbackHelper::ErrorMsg("Utils", _globalCallback, _key, (LPCSTR)customMessage); + _lastErrorCode = ErrorCode; + CallbackHelper::ErrorMsg("Utils", _globalCallback, _key, (LPCSTR)customMessage); } STDMETHODIMP CUtils::ColorByName(tkMapColor name, OLE_COLOR* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retVal = BGR_TO_RGB(name); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *retVal = BGR_TO_RGB(name); + return S_OK; } // ************************************************************** @@ -3620,9 +3698,9 @@ STDMETHODIMP CUtils::ColorByName(tkMapColor name, OLE_COLOR* retVal) // ************************************************************** STDMETHODIMP CUtils::ConvertDistance(tkUnitsOfMeasure sourceUnit, tkUnitsOfMeasure targetUnit, DOUBLE* value, VARIANT_BOOL* retval) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retval = Utility::ConvertDistance(sourceUnit, targetUnit, *value); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *retval = Utility::ConvertDistance(sourceUnit, targetUnit, *value); + return S_OK; } // ************************************************************** @@ -3630,31 +3708,31 @@ STDMETHODIMP CUtils::ConvertDistance(tkUnitsOfMeasure sourceUnit, tkUnitsOfMeasu // ************************************************************** STDMETHODIMP CUtils::ClipGridWithPolygon(BSTR inputGridfile, IShape* poly, BSTR resultGridfile, VARIANT_BOOL keepExtents, VARIANT_BOOL* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - - *retVal = VARIANT_FALSE; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - if (!Utility::FileExistsUnicode(inputGridfile)) - { - ErrorMessage(tkINVALID_FILENAME); - return S_OK; - } + *retVal = VARIANT_FALSE; - IGrid* grid = NULL; - CoCreateInstance(CLSID_Grid,NULL,CLSCTX_INPROC_SERVER,IID_IGrid,(void**)&grid); - - if (grid) - { - bool inRam = false; - VARIANT_BOOL vb; - grid->Open(inputGridfile, GridDataType::UnknownDataType, inRam, GridFileType::UseExtension, NULL, &vb); - if (vb) { - this->ClipGridWithPolygon2(grid, poly, resultGridfile, keepExtents, retVal); - grid->Close(&vb); - } - grid->Release(); - } - return S_OK; + if (!Utility::FileExistsUnicode(inputGridfile)) + { + ErrorMessage(tkINVALID_FILENAME); + return S_OK; + } + + IGrid* grid = NULL; + CoCreateInstance(CLSID_Grid, NULL, CLSCTX_INPROC_SERVER, IID_IGrid, (void**)&grid); + + if (grid) + { + bool inRam = false; + VARIANT_BOOL vb; + grid->Open(inputGridfile, GridDataType::UnknownDataType, inRam, GridFileType::UseExtension, NULL, &vb); + if (vb) { + this->ClipGridWithPolygon2(grid, poly, resultGridfile, keepExtents, retVal); + grid->Close(&vb); + } + grid->Release(); + } + return S_OK; } // ******************************************************** @@ -3662,367 +3740,367 @@ STDMETHODIMP CUtils::ClipGridWithPolygon(BSTR inputGridfile, IShape* poly, BSTR // ******************************************************** STDMETHODIMP CUtils::ClipGridWithPolygon2(IGrid* grid, IShape* poly, BSTR resultGridfile, VARIANT_BOOL keepExtents, VARIANT_BOOL* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - - *retVal = VARIANT_FALSE; - - if (!poly || !grid) { - ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); - return S_OK; - } + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - if (Utility::FileExistsUnicode(resultGridfile)) - { - ErrorMessage(tkFILE_EXISTS); - return S_OK; - } - - VARIANT_BOOL vb; + *retVal = VARIANT_FALSE; - // find cell bounds - IExtents* ext1 = NULL; - poly->get_Extents(&ext1); - - IExtents* ext2 = NULL; - ((CGrid*)grid)->get_Extents(&ext2); - - IExtents* bounds = NULL; - ((CExtents*)ext1)->GetIntersection(ext2, &bounds); - - ext1->Release(); - ext2->Release(); - - if (bounds) { - double xMin, yMin, zMin, xMax, yMax, zMax; - bounds->GetBounds(&xMin, &yMin, &zMin, &xMax, &yMax, &zMax); - bounds->Release(); - - long firstCol, firstRow, lastCol, lastRow; - grid->ProjToCell(xMin, yMin, &firstCol, &firstRow); - grid->ProjToCell(xMax, yMax, &lastCol, &lastRow); - - IGrid* newGrid = NULL; - - if (!keepExtents) { - newGrid = ((CGrid*)grid)->Clip(resultGridfile, firstCol, lastCol, firstRow, lastRow); - } - else { - newGrid = ((CGrid*)grid)->Clone(resultGridfile); - } + if (!poly || !grid) { + ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); + return S_OK; + } - // copy data - if (newGrid) - { - double dx, dy, xll, yll; - IGridHeader* header = NULL; - newGrid->get_Header(&header); - header->get_dX(&dx); - header->get_dY(&dy); - header->get_XllCenter(&xll); - header->get_YllCenter(&yll); - - CComVariant var; - header->get_NodataValue(&var); - double noData; - dVal(var, noData); - - long minRow = MIN(firstRow, lastRow); - long maxRow = MAX(firstRow, lastRow); - - int cmnCount = lastCol - firstCol + 1; - int rowCount = maxRow - minRow + 1; - - if (keepExtents) - { - long rowCount; - header->get_NumberRows(&rowCount); - - xll += dx * firstCol; - yll += dy * (rowCount - maxRow -1); - } - header->Release(); - - if (cmnCount > 0 && rowCount > 0) - { - double* vals = new double[cmnCount]; - long row = 0; - - CPointInPolygon pip; - if (pip.SetPolygon(poly)) - { - for (long i = minRow; i <= maxRow; i++ ) { - grid->GetFloatWindow2(i, i, firstCol, lastCol, vals, &vb); - - if (vb) { - - //double y = yll + dy * (rowCount - row - 1.5); // (rowCount - 1) - row; -0.5 - center of cell - double y = yll + dy * (rowCount - row - 1); // a fix suggested here: http://bugs.mapwindow.org/view.php?id=2349 - - pip.PrepareScanLine(y); - - // set values outside polygon to nodata - for (long j = 0; j < cmnCount; j++) - { - // double x = xll + dx * (j + 0.5); - double x = xll + dx * j; // a fix suggested here: http://bugs.mapwindow.org/view.php?id=2349 - - if (!pip.ScanPoint(x)) - { - vals[j] = noData; - } - } - - if (keepExtents) { - newGrid->PutFloatWindow2(i, i, firstCol, lastCol, vals, &vb); - row++; - } - else { - newGrid->PutRow2(row++, vals, &vb); - } - } - } - } - delete[] vals; - } - - newGrid->Save(resultGridfile, GridFileType::UseExtension, NULL, &vb); - newGrid->Close(&vb); - newGrid->Release(); - *retVal = VARIANT_TRUE; - } - } - return S_OK; + if (Utility::FileExistsUnicode(resultGridfile)) + { + ErrorMessage(tkFILE_EXISTS); + return S_OK; + } + + VARIANT_BOOL vb; + + // find cell bounds + IExtents* ext1 = NULL; + poly->get_Extents(&ext1); + + IExtents* ext2 = NULL; + ((CGrid*)grid)->get_Extents(&ext2); + + IExtents* bounds = NULL; + ((CExtents*)ext1)->GetIntersection(ext2, &bounds); + + ext1->Release(); + ext2->Release(); + + if (bounds) { + double xMin, yMin, zMin, xMax, yMax, zMax; + bounds->GetBounds(&xMin, &yMin, &zMin, &xMax, &yMax, &zMax); + bounds->Release(); + + long firstCol, firstRow, lastCol, lastRow; + grid->ProjToCell(xMin, yMin, &firstCol, &firstRow); + grid->ProjToCell(xMax, yMax, &lastCol, &lastRow); + + IGrid* newGrid = NULL; + + if (!keepExtents) { + newGrid = ((CGrid*)grid)->Clip(resultGridfile, firstCol, lastCol, firstRow, lastRow); + } + else { + newGrid = ((CGrid*)grid)->Clone(resultGridfile); + } + + // copy data + if (newGrid) + { + double dx, dy, xll, yll; + IGridHeader* header = NULL; + newGrid->get_Header(&header); + header->get_dX(&dx); + header->get_dY(&dy); + header->get_XllCenter(&xll); + header->get_YllCenter(&yll); + + CComVariant var; + header->get_NodataValue(&var); + double noData; + dVal(var, noData); + + long minRow = MIN(firstRow, lastRow); + long maxRow = MAX(firstRow, lastRow); + + int cmnCount = lastCol - firstCol + 1; + int rowCount = maxRow - minRow + 1; + + if (keepExtents) + { + long rowCount; + header->get_NumberRows(&rowCount); + + xll += dx * firstCol; + yll += dy * (rowCount - maxRow - 1); + } + header->Release(); + + if (cmnCount > 0 && rowCount > 0) + { + double* vals = new double[cmnCount]; + long row = 0; + + CPointInPolygon pip; + if (pip.SetPolygon(poly)) + { + for (long i = minRow; i <= maxRow; i++) { + grid->GetFloatWindow2(i, i, firstCol, lastCol, vals, &vb); + + if (vb) { + + //double y = yll + dy * (rowCount - row - 1.5); // (rowCount - 1) - row; -0.5 - center of cell + double y = yll + dy * (rowCount - row - 1); // a fix suggested here: http://bugs.mapwindow.org/view.php?id=2349 + + pip.PrepareScanLine(y); + + // set values outside polygon to nodata + for (long j = 0; j < cmnCount; j++) + { + // double x = xll + dx * (j + 0.5); + double x = xll + dx * j; // a fix suggested here: http://bugs.mapwindow.org/view.php?id=2349 + + if (!pip.ScanPoint(x)) + { + vals[j] = noData; + } + } + + if (keepExtents) { + newGrid->PutFloatWindow2(i, i, firstCol, lastCol, vals, &vb); + row++; + } + else { + newGrid->PutRow2(row++, vals, &vb); + } + } + } + } + delete[] vals; + } + + newGrid->Save(resultGridfile, GridFileType::UseExtension, NULL, &vb); + newGrid->Close(&vb); + newGrid->Release(); + *retVal = VARIANT_TRUE; + } + } + return S_OK; } // ******************************************************** // CreateStatisticsFields() // ******************************************************** -void CreateStatisticsFields(IShapefile* sf, std::vector& resultIndices, bool overwrite) +void CreateStatisticsFields(IShapefile* sf, std::vector& resultIndices, bool overwrite) { - CComPtr tbl = NULL; - sf->get_Table(&tbl); - - VARIANT_BOOL vb; - long index = -1; - CString fields[13] = {"Mean", "Median", "Majority", "Minority", "Minimum", "Maximum", "Range", "StD", "Sum", "MinX", "MinY", "Variety", "Count" }; - - if (overwrite) { - for (int i = 0; i < 13; i++) { - CComBSTR bstrName(fields[i]); - tbl->get_FieldIndexByName(bstrName, &index); - if (index != -1) { - tbl->EditDeleteField(index, NULL, &vb); - } - } - } - - for (int i = 0; i < 11; i++) { - CComBSTR bstrName(fields[i]); - sf->EditAddField(bstrName, FieldType::DOUBLE_FIELD, 12, 18, &index); - resultIndices.push_back(index); - } - - for (int i = 11; i < 13; i++) { - CComBSTR bstrName(fields[i]); - sf->EditAddField(bstrName, FieldType::INTEGER_FIELD, 0, 18, &index); - resultIndices.push_back(index); - } - - TableHelper::Cast(tbl)->MakeUniqueFieldNames(); + CComPtr tbl = NULL; + sf->get_Table(&tbl); + + VARIANT_BOOL vb; + long index = -1; + CString fields[13] = { "Mean", "Median", "Majority", "Minority", "Minimum", "Maximum", "Range", "StD", "Sum", "MinX", "MinY", "Variety", "Count" }; + + if (overwrite) { + for (int i = 0; i < 13; i++) { + CComBSTR bstrName(fields[i]); + tbl->get_FieldIndexByName(bstrName, &index); + if (index != -1) { + tbl->EditDeleteField(index, NULL, &vb); + } + } + } + + for (int i = 0; i < 11; i++) { + CComBSTR bstrName(fields[i]); + sf->EditAddField(bstrName, FieldType::DOUBLE_FIELD, 12, 18, &index); + resultIndices.push_back(index); + } + + for (int i = 11; i < 13; i++) { + CComBSTR bstrName(fields[i]); + sf->EditAddField(bstrName, FieldType::INTEGER_FIELD, 0, 18, &index); + resultIndices.push_back(index); + } + + TableHelper::Cast(tbl)->MakeUniqueFieldNames(); } enum GridScanMethod { - CenterWithin = 0, // center of grid cell must be within polygon to pass the data; fast but inadequate for small polygons - Intersection = 1, // bounds of polygon and cell must intersect to pass the data; all the values are weighted according - // to the area of intersection; slower, should be used for small polygons + CenterWithin = 0, // center of grid cell must be within polygon to pass the data; fast but inadequate for small polygons + Intersection = 1, // bounds of polygon and cell must intersect to pass the data; all the values are weighted according + // to the area of intersection; slower, should be used for small polygons }; // ******************************************************** // GridStatsToForPoly() // ******************************************************** bool GridStatsForPoly(IGrid* grid, IGridHeader* header, IShape* poly, Extent& bounds, - float noData, double& meanValue, double& minValue, double& maxValue) + float noData, double& meanValue, double& minValue, double& maxValue) { - VARIANT_BOOL vb; - long firstCol, firstRow, lastCol, lastRow; - grid->ProjToCell(bounds.left, bounds.bottom, &firstCol, &firstRow); - grid->ProjToCell(bounds.right, bounds.top, &lastCol, &lastRow); - - long minRow = MIN(firstRow, lastRow); - long maxRow = MAX(firstRow, lastRow); - - long gridRowCount; - header->get_NumberRows(&gridRowCount); - - // Bounds returned by grid->get_Extents call return borders of outer most pixels, - // one of which because of rounding may be shifted to one pixel. - // So let's make sure that we are inside bounds. - // Another way to fix it is to return extents defined by centers of outer most pixels, - // which will eliminate possible rounding problems. - minRow = MAX(minRow, 0); - maxRow = MIN(maxRow, gridRowCount); - - int cmnCount = lastCol - firstCol + 1; - int rowCount = maxRow - minRow + 1; - - double dx, dy, xll, yll; - header->get_dX(&dx); - header->get_dY(&dy); - header->get_XllCenter(&xll); - header->get_YllCenter(&yll); - - double yllWindow = yll + ((gridRowCount - 1) - maxRow) * dy; - double xllWindow = xll + firstCol * dx; - - if (cmnCount > 0 && rowCount > 0) - { - // determine rows & cols which correspond to a poly - float* vals = new float[cmnCount]; - int row = 0; - float sum = 0.0; - float max = -FLT_MAX; - float min = FLT_MAX; - float count = 0.0f; - - std::map values; // value; count - - GridScanMethod method = cmnCount * rowCount > 10 ? GridScanMethod::CenterWithin : GridScanMethod::Intersection; - - if (method == GridScanMethod::CenterWithin) - { - CPointInPolygon pip; - if (pip.SetPolygon(poly)) - { - for (long i = minRow; i <= maxRow; i++ ) - { - grid->GetFloatWindow(i, i, firstCol, lastCol, vals, &vb); - - if (vb) { - double y = yllWindow + dy * (rowCount - row - 1.5); // (rowCount - 1) - row; -0.5 - center of cell - pip.PrepareScanLine(y); - - // checking values within row - for (long j = 0; j < cmnCount; j++) - { - if (vals[j] == noData) - continue; - - double x = xllWindow + dx * (j + 0.5); - if (pip.ScanPoint(x)) - { - if (vals[j] < min) { - min = vals[j]; - } - if (vals[j] > max) - { - max = vals[j]; - } - sum += vals[j]; - count += 1.0f; - } - } - } - row++; - } - } - - // it may be a rare case of narrow shapes, like roads - // let's try to to use intersection logic, as we determined - // that there must be an intersection - if (count == 0.0f) - { - row = 0; - goto intersection; - } - } - else //GridScanMethod == Intersection - { -intersection: - Extent cell; - Extent intersection; - double cellArea = dx * dy; - - for (long i = minRow; i <= maxRow; i++ ) - { - grid->GetFloatWindow(i, i, firstCol, lastCol, vals, &vb); - if (vb) { - cell.bottom = yllWindow + dy * (rowCount - row - 1.5); - cell.top = yllWindow + dy * (rowCount - row - 0.5); - for (long j = 0; j < cmnCount; j++) - { - if (vals[j] == noData) - continue; - - cell.left = xllWindow + dx * (j - 0.5); - cell.right = xllWindow + dx * (j + 0.5); - if (cell.getIntersection(bounds, intersection)) - { - float weight = (float)(intersection.getArea()/cellArea); // the part of cell within polygon bounds - - if (vals[j] < min) { - min = vals[j]; - } - if (vals[j] > max) - { - max = vals[j]; - } - sum += vals[j] * weight; - count += weight; - } - } - } - row++; - } - } - delete[] vals; - - if (count > 0) - { - meanValue = sum/count; - minValue = min; - maxValue = max; - return true; - } - } - return false; + VARIANT_BOOL vb; + long firstCol, firstRow, lastCol, lastRow; + grid->ProjToCell(bounds.left, bounds.bottom, &firstCol, &firstRow); + grid->ProjToCell(bounds.right, bounds.top, &lastCol, &lastRow); + + long minRow = MIN(firstRow, lastRow); + long maxRow = MAX(firstRow, lastRow); + + long gridRowCount; + header->get_NumberRows(&gridRowCount); + + // Bounds returned by grid->get_Extents call return borders of outer most pixels, + // one of which because of rounding may be shifted to one pixel. + // So let's make sure that we are inside bounds. + // Another way to fix it is to return extents defined by centers of outer most pixels, + // which will eliminate possible rounding problems. + minRow = MAX(minRow, 0); + maxRow = MIN(maxRow, gridRowCount); + + int cmnCount = lastCol - firstCol + 1; + int rowCount = maxRow - minRow + 1; + + double dx, dy, xll, yll; + header->get_dX(&dx); + header->get_dY(&dy); + header->get_XllCenter(&xll); + header->get_YllCenter(&yll); + + double yllWindow = yll + ((gridRowCount - 1) - maxRow) * dy; + double xllWindow = xll + firstCol * dx; + + if (cmnCount > 0 && rowCount > 0) + { + // determine rows & cols which correspond to a poly + float* vals = new float[cmnCount]; + int row = 0; + float sum = 0.0; + float max = -FLT_MAX; + float min = FLT_MAX; + float count = 0.0f; + + std::map values; // value; count + + GridScanMethod method = cmnCount * rowCount > 10 ? GridScanMethod::CenterWithin : GridScanMethod::Intersection; + + if (method == GridScanMethod::CenterWithin) + { + CPointInPolygon pip; + if (pip.SetPolygon(poly)) + { + for (long i = minRow; i <= maxRow; i++) + { + grid->GetFloatWindow(i, i, firstCol, lastCol, vals, &vb); + + if (vb) { + double y = yllWindow + dy * (rowCount - row - 1.5); // (rowCount - 1) - row; -0.5 - center of cell + pip.PrepareScanLine(y); + + // checking values within row + for (long j = 0; j < cmnCount; j++) + { + if (vals[j] == noData) + continue; + + double x = xllWindow + dx * (j + 0.5); + if (pip.ScanPoint(x)) + { + if (vals[j] < min) { + min = vals[j]; + } + if (vals[j] > max) + { + max = vals[j]; + } + sum += vals[j]; + count += 1.0f; + } + } + } + row++; + } + } + + // it may be a rare case of narrow shapes, like roads + // let's try to to use intersection logic, as we determined + // that there must be an intersection + if (count == 0.0f) + { + row = 0; + goto intersection; + } + } + else //GridScanMethod == Intersection + { + intersection: + Extent cell; + Extent intersection; + double cellArea = dx * dy; + + for (long i = minRow; i <= maxRow; i++) + { + grid->GetFloatWindow(i, i, firstCol, lastCol, vals, &vb); + if (vb) { + cell.bottom = yllWindow + dy * (rowCount - row - 1.5); + cell.top = yllWindow + dy * (rowCount - row - 0.5); + for (long j = 0; j < cmnCount; j++) + { + if (vals[j] == noData) + continue; + + cell.left = xllWindow + dx * (j - 0.5); + cell.right = xllWindow + dx * (j + 0.5); + if (cell.getIntersection(bounds, intersection)) + { + float weight = (float)(intersection.getArea() / cellArea); // the part of cell within polygon bounds + + if (vals[j] < min) { + min = vals[j]; + } + if (vals[j] > max) + { + max = vals[j]; + } + sum += vals[j] * weight; + count += weight; + } + } + } + row++; + } + } + delete[] vals; + + if (count > 0) + { + meanValue = sum / count; + minValue = min; + maxValue = max; + return true; + } + } + return false; } // ******************************************************** // GridStatisticsToForPolygon() // ******************************************************** -STDMETHODIMP CUtils::GridStatisticsForPolygon(IGrid* grid, IGridHeader* header, IExtents* gridExtents, IShape* shape, - double noDataValue, double* meanValue, double* minValue, double* maxValue, VARIANT_BOOL* retVal) +STDMETHODIMP CUtils::GridStatisticsForPolygon(IGrid* grid, IGridHeader* header, IExtents* gridExtents, IShape* shape, + double noDataValue, double* meanValue, double* minValue, double* maxValue, VARIANT_BOOL* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - - *retVal = VARIANT_FALSE; - if (!grid || !header || !shape) { - ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); - return S_OK; - } - - ShpfileType type; - shape->get_ShapeType(&type); - if (type != SHP_POLYGON && type != SHP_POLYGONM && type != SHP_POLYGONZ) { - ErrorMessage(tkUNEXPECTED_SHAPE_TYPE); - return S_OK; - } - - double xMin, xMax, yMin, yMax, zMin, zMax; - gridExtents->GetBounds(&xMin, &yMin, &zMin, &xMax, &yMax, &zMax); - Extent gridBounds(xMin, xMax, yMin, yMax); - - ((CShape*)shape)->get_ExtentsXY(xMin, yMin, xMax, yMax); - Extent shapeBounds(xMin, xMax, yMin, yMax); - - Extent bounds; - if (gridBounds.getIntersection(shapeBounds, bounds)) - { - bool result = GridStatsForPoly(grid, header, shape, bounds,(float)noDataValue, *meanValue, *minValue, *maxValue); - *retVal = result ? VARIANT_TRUE: VARIANT_FALSE; - } - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + *retVal = VARIANT_FALSE; + if (!grid || !header || !shape) { + ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); + return S_OK; + } + + ShpfileType type; + shape->get_ShapeType(&type); + if (type != SHP_POLYGON && type != SHP_POLYGONM && type != SHP_POLYGONZ) { + ErrorMessage(tkUNEXPECTED_SHAPE_TYPE); + return S_OK; + } + + double xMin, xMax, yMin, yMax, zMin, zMax; + gridExtents->GetBounds(&xMin, &yMin, &zMin, &xMax, &yMax, &zMax); + Extent gridBounds(xMin, xMax, yMin, yMax); + + ((CShape*)shape)->get_ExtentsXY(xMin, yMin, xMax, yMax); + Extent shapeBounds(xMin, xMax, yMin, yMax); + + Extent bounds; + if (gridBounds.getIntersection(shapeBounds, bounds)) + { + bool result = GridStatsForPoly(grid, header, shape, bounds, (float)noDataValue, *meanValue, *minValue, *maxValue); + *retVal = result ? VARIANT_TRUE : VARIANT_FALSE; + } + return S_OK; } // ******************************************************** @@ -4030,337 +4108,337 @@ STDMETHODIMP CUtils::GridStatisticsForPolygon(IGrid* grid, IGridHeader* header, // ******************************************************** STDMETHODIMP CUtils::GridStatisticsToShapefile(IGrid* grid, IShapefile* sf, VARIANT_BOOL selectedOnly, VARIANT_BOOL overwriteFields, VARIANT_BOOL useCenterWithinMethod, VARIANT_BOOL* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retVal = VARIANT_FALSE; - if (!grid || !sf) { - ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); - return S_OK; - } - - ShpfileType type; - sf->get_ShapefileType(&type); - if (type != SHP_POLYGON && type != SHP_POLYGONM && type != SHP_POLYGONZ) { - ErrorMessage(tkUNEXPECTED_SHAPE_TYPE); - return S_OK; - } - - IExtents* extSf = NULL; - sf->get_Extents(&extSf); - - IExtents* extGrid = NULL; - ((CGrid*)grid)->get_Extents(&extGrid); - - VARIANT_BOOL vb; - ((CExtents*)extGrid)->Intersects(extSf, &vb); - extSf->Release(); - - if (!vb) { - ErrorMessage(tkBOUNDS_NOT_INTERSECT); - return S_OK; - } - - VARIANT_BOOL editing; - sf->get_EditingTable(&editing); - - if (!editing) { - sf->StartEditingTable(this->_globalCallback, &vb); // it's safe enough not to check the result - } - - std::vector fieldIndices; - CreateStatisticsFields(sf, fieldIndices, overwriteFields ? true: false); - - double dx, dy, xll, yll; - IGridHeader* header = NULL; - grid->get_Header(&header); - header->get_dX(&dx); - header->get_dY(&dy); - header->get_XllCenter(&xll); - header->get_YllCenter(&yll); - - long gridRowCount; - header->get_NumberRows(&gridRowCount); - - // MWGIS-65: - long gridColumnCount; - header->get_NumberCols(&gridColumnCount); - - // no data values - must not be used in calculations - CComVariant var; - header->get_NodataValue(&var); - float noData; - fVal(var, noData); - - header->Release(); - - long percent = -1; - long numShapes; - sf->get_NumShapes(&numShapes); - - for (long n = 0; n < numShapes; n++) - { - CallbackHelper::Progress(_globalCallback, n, numShapes, "Calculating...", _key, percent); - - sf->get_ShapeSelected(n, &vb); - if (selectedOnly && !vb) - continue; - - IShape* poly = NULL; - sf->get_Shape(n, &poly); - - IExtents* bounds = NULL; - poly->get_Extents(&extSf); - ((CExtents*)extGrid)->GetIntersection(extSf, &bounds); - extSf->Release(); - - if (bounds != NULL) - { - // determine rows & cols which correspond to a poly - double xMin, yMin, zMin, xMax, yMax, zMax; - bounds->GetBounds(&xMin, &yMin, &zMin, &xMax, &yMax, &zMax); - bounds->Release(); - - long firstCol, firstRow, lastCol, lastRow; - grid->ProjToCell(xMin, yMin, &firstCol, &firstRow); - grid->ProjToCell(xMax, yMax, &lastCol, &lastRow); - - long minRow = MIN(firstRow, lastRow); - long maxRow = MAX(firstRow, lastRow); - - // Bounds returned by grid->get_Extents call return borders of outer most pixels, - // one of which because of rounding may be shifted to one pixel. - // So let's make sure that we are inside bounds. - // Another way to fix it is to return extents defined by centers of outer most pixels, - // which will eliminate possible rounding problems. - minRow = MAX(minRow, 0); - maxRow = MIN(maxRow, gridRowCount); - - int cmnCount = lastCol - firstCol + 1; - int rowCount = maxRow - minRow + 1; - - // now find the pixels inside poly and calculate stats - if (cmnCount > 0 && rowCount > 0) - { - float* vals = new float[cmnCount]; - int row = 0; - - int count = 0; - float sumWeight = 0.0f; // a sum of cell parts which fall within polygon - float sum = 0.0; - float squares = 0.0; - float max = -FLT_MIN; - float min = FLT_MAX; - double minX = 0.0; - double minY = 0.0; - - double yllWindow = yll + ((gridRowCount - 1) - maxRow) * dy; - double xllWindow = xll + firstCol * dx; - std::map values; // value; count - - // MWGIS-66: - GridScanMethod method = useCenterWithinMethod ? GridScanMethod::CenterWithin : GridScanMethod::Intersection; - - if (method == GridScanMethod::CenterWithin) - { - CPointInPolygon pip; - if (pip.SetPolygon(poly)) - { - for (long i = minRow; i <= maxRow; i++ ) - { - grid->GetFloatWindow(i, i, firstCol, lastCol, vals, &vb); - - if (vb) { - // double y = yllWindow + dy * (rowCount - row - 1.5); // (rowCount - 1) - row; -0.5 - center of cell - double y = yllWindow + dy * (rowCount - row - 1.0); // MWGIS-65: yllWindow already use the center of the first cell. - pip.PrepareScanLine(y); - - // checking values within row - for (long j = 0; j < cmnCount; j++) - { - if (vals[j] == noData) - continue; - - // double x = xllWindow + dx * (j + 0.5); - double x = xllWindow + dx * (j); // MWGIS-65: xllWindow already use the center of the first cell. - if (pip.ScanPoint(x)) - { - if (vals[j] < min) { - min = vals[j]; - minX = x; - minY = y; - } - if (vals[j] > max) max = vals[j]; - sum += vals[j]; - squares += vals[j] * vals[j]; - count++; - - // counting unique values - if (values.find(vals[j]) == values.end()) - values[vals[j]] = 1; - else - values[vals[j]] += 1; - } - } - } - row++; - } - } - sumWeight = (float)count; - } - else //GridScanMethod == Intersection - { - Extent shape(xMin, xMax, yMin, yMax); - Extent cell; - Extent intersection; - double cellArea = dx * dy; - - for (long i = minRow; i <= maxRow; i++ ) - { - grid->GetFloatWindow(i, i, firstCol, lastCol, vals, &vb); - if (vb) { - cell.bottom = yllWindow + dy * (rowCount - row - 1.5); - cell.top = yllWindow + dy * (rowCount - row - 0.5); - for (long j = 0; j < cmnCount; j++) - { - if (vals[j] == noData) - continue; - - cell.left = xllWindow + dx * (j - 0.5); - cell.right = xllWindow + dx * (j + 0.5); - if (cell.getIntersection(shape, intersection)) - { - float weight = (float)(intersection.getArea()/cellArea); // the part of cell within polygon bounds - - if (vals[j] < min) { - min = vals[j]; - minX = (cell.right + cell.left)/2; - minY = (cell.top + cell.bottom)/2; - } - if (vals[j] > max) max = vals[j]; - sum += vals[j] * weight; - squares += vals[j] * vals[j] * weight; - sumWeight += weight; - count++; - - // counting unique values - if (values.find(vals[j]) == values.end()) - values[vals[j]] = 1; - else - values[vals[j]] += 1; - } - } - } - row++; - } - } - - if (count > 0) - { - // 0 - "Mean" - CComVariant vMean(sum/sumWeight); - sf->EditCellValue(fieldIndices[0], n, vMean, &vb); - - if (!values.empty()) - { - // 1 - "Median" - int half = count/2; - int subCount = 0; - std::map::iterator it = values.begin(); - while (it != values.end()) - { - subCount += it->second; - if (subCount > half) - { - CComVariant vMedian(it->first); - sf->EditCellValue(fieldIndices[1], n, vMedian, &vb); - break; - } - ++it; - } - - int maxCount = INT_MIN; - int minCount = INT_MAX; - float major, minor; - it = values.begin(); - while (it != values.end()) - { - if (it->second > maxCount) { - maxCount = it->second; - major = it->first; - } - if (it->second < minCount) - { - minCount = it->second; - minor = it->first; - } - ++it; - } - - // 2 - "Majority" - CComVariant vMajor(major); - sf->EditCellValue(fieldIndices[2], n, vMajor, &vb); - - // 3 - "Minority" - CComVariant vMinor(minor); - sf->EditCellValue(fieldIndices[3], n, vMinor, &vb); - } - - // 4 - "Minimum" - CComVariant vMin(min); - sf->EditCellValue(fieldIndices[4], n, vMin, &vb); - - // 5 - "Maximum" - CComVariant vMax(max); - sf->EditCellValue(fieldIndices[5], n, vMax, &vb); - - // 6 - "Range" - CComVariant vRange(max - min); - sf->EditCellValue(fieldIndices[6], n, vRange, &vb); - - // 7 - "StD" - float stddev = sqrt(squares/sumWeight - pow(sum/sumWeight, 2)); // /count - CComVariant vStd(stddev); - sf->EditCellValue(fieldIndices[7], n, vStd, &vb); - - // 8 - "Sum" - CComVariant vSum(sum); - sf->EditCellValue(fieldIndices[8], n, vSum, &vb); - - // 9 - "MinX" - CComVariant vMinX(minX); - sf->EditCellValue(fieldIndices[9], n, vMinX, &vb); - - // 10 - "MinY" - CComVariant vMinY(minY); - sf->EditCellValue(fieldIndices[10], n, vMinY, &vb); - - // 11 - "Variety" - int variety = values.size(); - CComVariant vVar(variety); - sf->EditCellValue(fieldIndices[11], n, vVar, &vb); - - // 12 - "Count" - CComVariant vCount(count); - sf->EditCellValue(fieldIndices[12], n, vCount, &vb); - } - delete[] vals; - } - } - poly->Release(); - } - extGrid->Release(); - - if (!editing) { - sf->StopEditingTable(VARIANT_TRUE, this->_globalCallback, &vb); - if (!vb) { - long code; - sf->get_LastErrorCode(&code); - this->ErrorMessage(code); - return S_OK; - } - } - - *retVal = VARIANT_TRUE; - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *retVal = VARIANT_FALSE; + if (!grid || !sf) { + ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); + return S_OK; + } + + ShpfileType type; + sf->get_ShapefileType(&type); + if (type != SHP_POLYGON && type != SHP_POLYGONM && type != SHP_POLYGONZ) { + ErrorMessage(tkUNEXPECTED_SHAPE_TYPE); + return S_OK; + } + + IExtents* extSf = NULL; + sf->get_Extents(&extSf); + + IExtents* extGrid = NULL; + ((CGrid*)grid)->get_Extents(&extGrid); + + VARIANT_BOOL vb; + ((CExtents*)extGrid)->Intersects(extSf, &vb); + extSf->Release(); + + if (!vb) { + ErrorMessage(tkBOUNDS_NOT_INTERSECT); + return S_OK; + } + + VARIANT_BOOL editing; + sf->get_EditingTable(&editing); + + if (!editing) { + sf->StartEditingTable(this->_globalCallback, &vb); // it's safe enough not to check the result + } + + std::vector fieldIndices; + CreateStatisticsFields(sf, fieldIndices, overwriteFields ? true : false); + + double dx, dy, xll, yll; + IGridHeader* header = NULL; + grid->get_Header(&header); + header->get_dX(&dx); + header->get_dY(&dy); + header->get_XllCenter(&xll); + header->get_YllCenter(&yll); + + long gridRowCount; + header->get_NumberRows(&gridRowCount); + + // MWGIS-65: + long gridColumnCount; + header->get_NumberCols(&gridColumnCount); + + // no data values - must not be used in calculations + CComVariant var; + header->get_NodataValue(&var); + float noData; + fVal(var, noData); + + header->Release(); + + long percent = -1; + long numShapes; + sf->get_NumShapes(&numShapes); + + for (long n = 0; n < numShapes; n++) + { + CallbackHelper::Progress(_globalCallback, n, numShapes, "Calculating...", _key, percent); + + sf->get_ShapeSelected(n, &vb); + if (selectedOnly && !vb) + continue; + + IShape* poly = NULL; + sf->get_Shape(n, &poly); + + IExtents* bounds = NULL; + poly->get_Extents(&extSf); + ((CExtents*)extGrid)->GetIntersection(extSf, &bounds); + extSf->Release(); + + if (bounds != NULL) + { + // determine rows & cols which correspond to a poly + double xMin, yMin, zMin, xMax, yMax, zMax; + bounds->GetBounds(&xMin, &yMin, &zMin, &xMax, &yMax, &zMax); + bounds->Release(); + + long firstCol, firstRow, lastCol, lastRow; + grid->ProjToCell(xMin, yMin, &firstCol, &firstRow); + grid->ProjToCell(xMax, yMax, &lastCol, &lastRow); + + long minRow = MIN(firstRow, lastRow); + long maxRow = MAX(firstRow, lastRow); + + // Bounds returned by grid->get_Extents call return borders of outer most pixels, + // one of which because of rounding may be shifted to one pixel. + // So let's make sure that we are inside bounds. + // Another way to fix it is to return extents defined by centers of outer most pixels, + // which will eliminate possible rounding problems. + minRow = MAX(minRow, 0); + maxRow = MIN(maxRow, gridRowCount); + + int cmnCount = lastCol - firstCol + 1; + int rowCount = maxRow - minRow + 1; + + // now find the pixels inside poly and calculate stats + if (cmnCount > 0 && rowCount > 0) + { + float* vals = new float[cmnCount]; + int row = 0; + + int count = 0; + float sumWeight = 0.0f; // a sum of cell parts which fall within polygon + float sum = 0.0; + float squares = 0.0; + float max = -FLT_MIN; + float min = FLT_MAX; + double minX = 0.0; + double minY = 0.0; + + double yllWindow = yll + ((gridRowCount - 1) - maxRow) * dy; + double xllWindow = xll + firstCol * dx; + std::map values; // value; count + + // MWGIS-66: + GridScanMethod method = useCenterWithinMethod ? GridScanMethod::CenterWithin : GridScanMethod::Intersection; + + if (method == GridScanMethod::CenterWithin) + { + CPointInPolygon pip; + if (pip.SetPolygon(poly)) + { + for (long i = minRow; i <= maxRow; i++) + { + grid->GetFloatWindow(i, i, firstCol, lastCol, vals, &vb); + + if (vb) { + // double y = yllWindow + dy * (rowCount - row - 1.5); // (rowCount - 1) - row; -0.5 - center of cell + double y = yllWindow + dy * (rowCount - row - 1.0); // MWGIS-65: yllWindow already use the center of the first cell. + pip.PrepareScanLine(y); + + // checking values within row + for (long j = 0; j < cmnCount; j++) + { + if (vals[j] == noData) + continue; + + // double x = xllWindow + dx * (j + 0.5); + double x = xllWindow + dx * (j); // MWGIS-65: xllWindow already use the center of the first cell. + if (pip.ScanPoint(x)) + { + if (vals[j] < min) { + min = vals[j]; + minX = x; + minY = y; + } + if (vals[j] > max) max = vals[j]; + sum += vals[j]; + squares += vals[j] * vals[j]; + count++; + + // counting unique values + if (values.find(vals[j]) == values.end()) + values[vals[j]] = 1; + else + values[vals[j]] += 1; + } + } + } + row++; + } + } + sumWeight = (float)count; + } + else //GridScanMethod == Intersection + { + Extent shape(xMin, xMax, yMin, yMax); + Extent cell; + Extent intersection; + double cellArea = dx * dy; + + for (long i = minRow; i <= maxRow; i++) + { + grid->GetFloatWindow(i, i, firstCol, lastCol, vals, &vb); + if (vb) { + cell.bottom = yllWindow + dy * (rowCount - row - 1.5); + cell.top = yllWindow + dy * (rowCount - row - 0.5); + for (long j = 0; j < cmnCount; j++) + { + if (vals[j] == noData) + continue; + + cell.left = xllWindow + dx * (j - 0.5); + cell.right = xllWindow + dx * (j + 0.5); + if (cell.getIntersection(shape, intersection)) + { + float weight = (float)(intersection.getArea() / cellArea); // the part of cell within polygon bounds + + if (vals[j] < min) { + min = vals[j]; + minX = (cell.right + cell.left) / 2; + minY = (cell.top + cell.bottom) / 2; + } + if (vals[j] > max) max = vals[j]; + sum += vals[j] * weight; + squares += vals[j] * vals[j] * weight; + sumWeight += weight; + count++; + + // counting unique values + if (values.find(vals[j]) == values.end()) + values[vals[j]] = 1; + else + values[vals[j]] += 1; + } + } + } + row++; + } + } + + if (count > 0) + { + // 0 - "Mean" + CComVariant vMean(sum / sumWeight); + sf->EditCellValue(fieldIndices[0], n, vMean, &vb); + + if (!values.empty()) + { + // 1 - "Median" + int half = count / 2; + int subCount = 0; + std::map::iterator it = values.begin(); + while (it != values.end()) + { + subCount += it->second; + if (subCount > half) + { + CComVariant vMedian(it->first); + sf->EditCellValue(fieldIndices[1], n, vMedian, &vb); + break; + } + ++it; + } + + int maxCount = INT_MIN; + int minCount = INT_MAX; + float major, minor; + it = values.begin(); + while (it != values.end()) + { + if (it->second > maxCount) { + maxCount = it->second; + major = it->first; + } + if (it->second < minCount) + { + minCount = it->second; + minor = it->first; + } + ++it; + } + + // 2 - "Majority" + CComVariant vMajor(major); + sf->EditCellValue(fieldIndices[2], n, vMajor, &vb); + + // 3 - "Minority" + CComVariant vMinor(minor); + sf->EditCellValue(fieldIndices[3], n, vMinor, &vb); + } + + // 4 - "Minimum" + CComVariant vMin(min); + sf->EditCellValue(fieldIndices[4], n, vMin, &vb); + + // 5 - "Maximum" + CComVariant vMax(max); + sf->EditCellValue(fieldIndices[5], n, vMax, &vb); + + // 6 - "Range" + CComVariant vRange(max - min); + sf->EditCellValue(fieldIndices[6], n, vRange, &vb); + + // 7 - "StD" + float stddev = sqrt(squares / sumWeight - pow(sum / sumWeight, 2)); // /count + CComVariant vStd(stddev); + sf->EditCellValue(fieldIndices[7], n, vStd, &vb); + + // 8 - "Sum" + CComVariant vSum(sum); + sf->EditCellValue(fieldIndices[8], n, vSum, &vb); + + // 9 - "MinX" + CComVariant vMinX(minX); + sf->EditCellValue(fieldIndices[9], n, vMinX, &vb); + + // 10 - "MinY" + CComVariant vMinY(minY); + sf->EditCellValue(fieldIndices[10], n, vMinY, &vb); + + // 11 - "Variety" + int variety = values.size(); + CComVariant vVar(variety); + sf->EditCellValue(fieldIndices[11], n, vVar, &vb); + + // 12 - "Count" + CComVariant vCount(count); + sf->EditCellValue(fieldIndices[12], n, vCount, &vb); + } + delete[] vals; + } + } + poly->Release(); + } + extGrid->Release(); + + if (!editing) { + sf->StopEditingTable(VARIANT_TRUE, this->_globalCallback, &vb); + if (!vb) { + long code; + sf->get_LastErrorCode(&code); + this->ErrorMessage(code); + return S_OK; + } + } + + *retVal = VARIANT_TRUE; + return S_OK; } // ****************************************************** @@ -4371,21 +4449,21 @@ STDMETHODIMP CUtils::GridStatisticsToShapefile(IGrid* grid, IShapefile* sf, VARI // though penalties for marshalling between apartments can significant STDMETHODIMP CUtils::CreateInstance(tkInterface interfaceId, IDispatch** retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - return ComHelper::CreateInstance(interfaceId, retVal); + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + return ComHelper::CreateInstance(interfaceId, retVal); } // ******************************************************** // GeodesicDistance() // ******************************************************** #include "..\Processing\GeograpicLib\Geodesic.hpp" -STDMETHODIMP CUtils::GeodesicDistance(double lat1, double lng1, double lat2, double lng2, double* retVal) +STDMETHODIMP CUtils::GeodesicDistance(double lat1, double lng1, double lat2, double lng2, double* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - const GeographicLib::Geodesic& geod = GeographicLib::Geodesic::WGS84; - - geod.Inverse(lat1, lng1, lat2, lng2, *retVal); - return S_OK; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + const GeographicLib::Geodesic& geod = GeographicLib::Geodesic::WGS84; + + geod.Inverse(lat1, lng1, lat2, lng2, *retVal); + return S_OK; } // ******************************************************** @@ -4393,59 +4471,59 @@ STDMETHODIMP CUtils::GeodesicDistance(double lat1, double lng1, double lat2, dou // ******************************************************** STDMETHODIMP CUtils::GeodesicArea(IShape* shapeWgs84, DOUBLE* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - if (!shapeWgs84) - { - ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); - return S_OK; - } + if (!shapeWgs84) + { + ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); + return S_OK; + } - ShpfileType shpType = ShapeHelper::GetShapeType2D(shapeWgs84); - if (shpType != SHP_POLYGON) - { - ErrorMessage(tkUNEXPECTED_SHAPE_TYPE); - return S_OK; - } + ShpfileType shpType = ShapeHelper::GetShapeType2D(shapeWgs84); + if (shpType != SHP_POLYGON) + { + ErrorMessage(tkUNEXPECTED_SHAPE_TYPE); + return S_OK; + } - long numPoints; - shapeWgs84->get_NumPoints(&numPoints); + long numPoints; + shapeWgs84->get_NumPoints(&numPoints); - if (numPoints < 3) - { - ErrorMessage(tkNOT_ENOUGH_POINTS_FOR_SHAPE_TYPE); - return S_OK; - } + if (numPoints < 3) + { + ErrorMessage(tkNOT_ENOUGH_POINTS_FOR_SHAPE_TYPE); + return S_OK; + } - VARIANT_BOOL vb; + VARIANT_BOOL vb; - vector points(numPoints); - for (long i = 0; i < numPoints; i++) - { - double x, y; - shapeWgs84->get_XY(i, &x, &y, &vb); - points.push_back(Point2D(x, y)); - } + vector points(numPoints); + for (long i = 0; i < numPoints; i++) + { + double x, y; + shapeWgs84->get_XY(i, &x, &y, &vb); + points.push_back(Point2D(x, y)); + } - *retVal = CalcPolyGeodesicArea(points); + *retVal = CalcPolyGeodesicArea(points); - return S_OK; + return S_OK; } #include "..\Processing\GeograpicLib\PolygonArea.hpp" double CalcPolyGeodesicArea(std::vector& points) { - GeographicLib::Geodesic geod(GeographicLib::Constants::WGS84_a(), GeographicLib::Constants::WGS84_f()); - GeographicLib::PolygonArea poly(geod); + GeographicLib::Geodesic geod(GeographicLib::Constants::WGS84_a(), GeographicLib::Constants::WGS84_f()); + GeographicLib::PolygonArea poly(geod); - for(size_t i = 0; i < points.size(); i++) - { - poly.AddPoint(points[i].y, points[i].x); - } + for (size_t i = 0; i < points.size(); i++) + { + poly.AddPoint(points[i].y, points[i].x); + } - double area = 0.0, perimeter = 0.0; - unsigned int r = poly.Compute(true, true, perimeter, area); - return area; + double area = 0.0, perimeter = 0.0; + unsigned int r = poly.Compute(true, true, perimeter, area); + return area; } // ******************************************************** @@ -4453,124 +4531,124 @@ double CalcPolyGeodesicArea(std::vector& points) // ******************************************************** STDMETHODIMP CUtils::MaskRaster(BSTR filename, BYTE newPerBandValue, VARIANT_BOOL* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *retVal = VARIANT_FALSE; + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + *retVal = VARIANT_FALSE; - GDALAllRegister(); + GDALAllRegister(); - USES_CONVERSION; - GDALDataset* dataset = GdalHelper::OpenRasterDatasetW(OLE2W(filename), GDALAccess::GA_Update); - if (!dataset) - { - this->ErrorMessage(tkINVALID_FILE); - return S_OK; - } - - GByte* pabyData; - *retVal = VARIANT_TRUE; - int count = dataset->GetRasterCount(); + USES_CONVERSION; + GDALDataset* dataset = GdalHelper::OpenRasterDatasetW(OLE2W(filename), GDALAccess::GA_Update); + if (!dataset) + { + this->ErrorMessage(tkINVALID_FILE); + return S_OK; + } - for(int i = 1; i <= count; i++) - { - GDALRasterBand* poBand = dataset->GetRasterBand(i); - if (poBand->GetRasterDataType() != GDT_Byte) - { - this->ErrorMessage(tkNON_SINGLE_BYTE_PER_BAND); - *retVal = VARIANT_FALSE; - break; - } - - unsigned char noData = (unsigned char)poBand->GetNoDataValue(); - - int nXBlockSize, nYBlockSize; - poBand->GetBlockSize(&nXBlockSize, &nYBlockSize); - - int nXBlocks = (poBand->GetXSize() + nXBlockSize - 1) / nXBlockSize; - int nYBlocks = (poBand->GetYSize() + nYBlockSize - 1) / nYBlockSize; - pabyData = (GByte *) CPLMalloc(nXBlockSize * nYBlockSize); - - CPLErr err = CPLErr::CE_None; - long percent = 0; - for( int iYBlock = 0; iYBlock < nYBlocks; iYBlock++ ) - { - for( int iXBlock = 0; iXBlock < nXBlocks; iXBlock++ ) - { - if( _globalCallback != NULL ) - { - double count = iYBlock * nXBlocks + iXBlock; - long newpercent = (long)((double)count/(double)(nXBlocks * nYBlocks)*100.0); - if( newpercent > percent ) - { - percent = newpercent; - CallbackHelper::Progress(_globalCallback, percent, "Calculating...", _key); - } - } - - int nXValid, nYValid; - err = poBand->ReadBlock( iXBlock, iYBlock, pabyData ); - if (err != CPLErr::CE_None) - { - CallbackHelper::ErrorMsg(Debug::Format("Error on reading band: %d; %d.", iXBlock, iYBlock)); - ErrorMessage(tkFAILED_READ_BLOCK); - *retVal = VARIANT_FALSE; - break; - } - - // Compute the portion of the block that is valid partial edge blocks. - if( (iXBlock+1) * nXBlockSize > poBand->GetXSize() ) - { - nXValid = poBand->GetXSize() - iXBlock * nXBlockSize; - } - else - { - nXValid = nXBlockSize; - } - if( (iYBlock+1) * nYBlockSize > poBand->GetYSize() ) - { - nYValid = poBand->GetYSize() - iYBlock * nYBlockSize; - } - else - { - nYValid = nYBlockSize; - } - - // loop through valid pixels - for( int iY = 0; iY < nYValid; iY++ ) - { - int y = iY * nXBlockSize; - for( int iX = 0; iX < nXValid; iX++ ) - { - if (pabyData[iX + y] != noData) - { - pabyData[iX + y] = newPerBandValue; - } - } - } - err = poBand->WriteBlock(iXBlock, iYBlock, pabyData); - if (err != CPLErr::CE_None) - { - CallbackHelper::ErrorMsg(Debug::Format("Error on writing band: %d; %d.", iXBlock, iYBlock)); - ErrorMessage(tkFAILED_WRITE_BLOCK); - *retVal = VARIANT_FALSE; - break; - } - } - if(err != CPLErr::CE_None) - break; - } - CPLFree(pabyData); + GByte* pabyData; + *retVal = VARIANT_TRUE; + int count = dataset->GetRasterCount(); - if(err != CPLErr::CE_None) - break; - } - - if (dataset) - { - delete dataset; - } + for (int i = 1; i <= count; i++) + { + GDALRasterBand* poBand = dataset->GetRasterBand(i); + if (poBand->GetRasterDataType() != GDT_Byte) + { + this->ErrorMessage(tkNON_SINGLE_BYTE_PER_BAND); + *retVal = VARIANT_FALSE; + break; + } + + unsigned char noData = (unsigned char)poBand->GetNoDataValue(); + + int nXBlockSize, nYBlockSize; + poBand->GetBlockSize(&nXBlockSize, &nYBlockSize); + + int nXBlocks = (poBand->GetXSize() + nXBlockSize - 1) / nXBlockSize; + int nYBlocks = (poBand->GetYSize() + nYBlockSize - 1) / nYBlockSize; + pabyData = (GByte *)CPLMalloc(nXBlockSize * nYBlockSize); + + CPLErr err = CPLErr::CE_None; + long percent = 0; + for (int iYBlock = 0; iYBlock < nYBlocks; iYBlock++) + { + for (int iXBlock = 0; iXBlock < nXBlocks; iXBlock++) + { + if (_globalCallback != NULL) + { + double count = iYBlock * nXBlocks + iXBlock; + long newpercent = (long)((double)count / (double)(nXBlocks * nYBlocks)*100.0); + if (newpercent > percent) + { + percent = newpercent; + CallbackHelper::Progress(_globalCallback, percent, "Calculating...", _key); + } + } + + int nXValid, nYValid; + err = poBand->ReadBlock(iXBlock, iYBlock, pabyData); + if (err != CPLErr::CE_None) + { + CallbackHelper::ErrorMsg(Debug::Format("Error on reading band: %d; %d.", iXBlock, iYBlock)); + ErrorMessage(tkFAILED_READ_BLOCK); + *retVal = VARIANT_FALSE; + break; + } + + // Compute the portion of the block that is valid partial edge blocks. + if ((iXBlock + 1) * nXBlockSize > poBand->GetXSize()) + { + nXValid = poBand->GetXSize() - iXBlock * nXBlockSize; + } + else + { + nXValid = nXBlockSize; + } + if ((iYBlock + 1) * nYBlockSize > poBand->GetYSize()) + { + nYValid = poBand->GetYSize() - iYBlock * nYBlockSize; + } + else + { + nYValid = nYBlockSize; + } + + // loop through valid pixels + for (int iY = 0; iY < nYValid; iY++) + { + int y = iY * nXBlockSize; + for (int iX = 0; iX < nXValid; iX++) + { + if (pabyData[iX + y] != noData) + { + pabyData[iX + y] = newPerBandValue; + } + } + } + err = poBand->WriteBlock(iXBlock, iYBlock, pabyData); + if (err != CPLErr::CE_None) + { + CallbackHelper::ErrorMsg(Debug::Format("Error on writing band: %d; %d.", iXBlock, iYBlock)); + ErrorMessage(tkFAILED_WRITE_BLOCK); + *retVal = VARIANT_FALSE; + break; + } + } + if (err != CPLErr::CE_None) + break; + } + CPLFree(pabyData); + + if (err != CPLErr::CE_None) + break; + } + + if (dataset) + { + delete dataset; + } - CallbackHelper::ProgressCompleted(_globalCallback, _key); - return S_OK; + CallbackHelper::ProgressCompleted(_globalCallback, _key); + return S_OK; } #pragma region "Gdal error handler" @@ -4578,22 +4656,22 @@ ICallback* gdalCallback; void CPL_STDCALL GdalErrorHandler(CPLErr eErrClass, int err_no, const char *msg) { - if (gdalCallback) { - CString s = msg; - s = "GDAL error: " + s; - CallbackHelper::ErrorMsg("Utils", gdalCallback, "GDAL", s); - } + if (gdalCallback) { + CString s = msg; + s = "GDAL error: " + s; + CallbackHelper::ErrorMsg("Utils", gdalCallback, "GDAL", s); + } } -void ClearGdalErrorHandler() +void ClearGdalErrorHandler() { - gdalCallback = NULL; + gdalCallback = NULL; } -void SetGdalErrorHandler(ICallback* callback) +void SetGdalErrorHandler(ICallback* callback) { - gdalCallback = callback; - CPLSetErrorHandler(&GdalErrorHandler); + gdalCallback = callback; + CPLSetErrorHandler(&GdalErrorHandler); } #pragma endregion @@ -4603,163 +4681,163 @@ void SetGdalErrorHandler(ICallback* callback) // ******************************************************** STDMETHODIMP CUtils::CopyNodataValues(BSTR sourceFilename, BSTR destFilename, VARIANT_BOOL* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - *retVal = VARIANT_FALSE; - GDALAllRegister(); + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + *retVal = VARIANT_FALSE; + GDALAllRegister(); - if (_globalCallback) { - SetGdalErrorHandler(_globalCallback); - } + if (_globalCallback) { + SetGdalErrorHandler(_globalCallback); + } - USES_CONVERSION; + USES_CONVERSION; - GDALDataset* dsSource = GdalHelper::OpenRasterDatasetW(OLE2W(sourceFilename), GDALAccess::GA_ReadOnly); - - GDALDataset* dsDest = GdalHelper::OpenRasterDatasetW(OLE2W(destFilename), GDALAccess::GA_ReadOnly); + GDALDataset* dsSource = GdalHelper::OpenRasterDatasetW(OLE2W(sourceFilename), GDALAccess::GA_ReadOnly); - if (!dsSource || !dsDest) - { - this->ErrorMessage(tkINVALID_FILE); - } - else - { - if(dsSource->GetRasterCount() != dsDest->GetRasterCount() || - dsSource->GetRasterXSize() != dsDest->GetRasterXSize() || - dsSource->GetRasterYSize() != dsDest->GetRasterYSize()) { - this->ErrorMessage(tkINPUT_RASTERS_DIFFER); - } - else - { - GByte* pabyDataSource, *pabyDataDest; - *retVal = VARIANT_TRUE; - int count = dsSource->GetRasterCount(); - - for(int i = 1; i <= count; i++) - { - GDALRasterBand* bandSource = dsSource->GetRasterBand(i); - GDALRasterBand* bandDest = dsDest->GetRasterBand(i); - if (bandSource->GetRasterDataType() != GDT_Byte || - bandDest->GetRasterDataType() != GDT_Byte) - { - this->ErrorMessage(tkNON_SINGLE_BYTE_PER_BAND); - *retVal = VARIANT_FALSE; - break; - } - - int nXBlockSize, nYBlockSize; - int nXBlockSize2, nYBlockSize2; - bandSource->GetBlockSize(&nXBlockSize, &nYBlockSize); - bandDest->GetBlockSize(&nXBlockSize2, &nYBlockSize2); - - if (nXBlockSize != nXBlockSize2 || nYBlockSize != nYBlockSize2) { - this->ErrorMessage(tkINPUT_RASTERS_DIFFER); - *retVal = VARIANT_FALSE; - break; - } - - unsigned char noData = (unsigned char)bandSource->GetNoDataValue(); - - int nXBlocks = (bandSource->GetXSize() + nXBlockSize - 1) / nXBlockSize; - int nYBlocks = (bandSource->GetYSize() + nYBlockSize - 1) / nYBlockSize; - pabyDataSource = (GByte *) CPLMalloc(nXBlockSize * nYBlockSize); - pabyDataDest = (GByte *) CPLMalloc(nXBlockSize * nYBlockSize); - - CPLErr err = CPLErr::CE_None; - long percent = 0; - for( int iYBlock = 0; iYBlock < nYBlocks; iYBlock++ ) - { - for( int iXBlock = 0; iXBlock < nXBlocks; iXBlock++ ) - { - if( _globalCallback != NULL ) - { - double count = iYBlock * nXBlocks + iXBlock; - long newpercent = (long)((double)count/(double)(nXBlocks * nYBlocks)*100.0); - if( newpercent > percent ) - { - percent = newpercent; - CString s; - s.Format("Calculating band %d", i); - CallbackHelper::Progress(_globalCallback, percent, s, _key); - } - } - - int nXValid, nYValid; - CPLErr err1 = bandSource->ReadBlock( iXBlock, iYBlock, pabyDataSource ); - CPLErr err2 = bandDest->ReadBlock( iXBlock, iYBlock, pabyDataDest ); - if (err1 != CPLErr::CE_None || err2 != CPLErr::CE_None) - { - CallbackHelper::ErrorMsg(Debug::Format("Error on reading band: %d; %d.", iXBlock, iYBlock)); - ErrorMessage(tkFAILED_READ_BLOCK); - *retVal = VARIANT_FALSE; - break; - } - - // Compute the portion of the block that is valid partial edge blocks. - if( (iXBlock+1) * nXBlockSize > bandSource->GetXSize() ) - { - nXValid = bandSource->GetXSize() - iXBlock * nXBlockSize; - } - else - { - nXValid = nXBlockSize; - } - if( (iYBlock+1) * nYBlockSize > bandSource->GetYSize() ) - { - nYValid = bandSource->GetYSize() - iYBlock * nYBlockSize; - } - else - { - nYValid = nYBlockSize; - } - - // loop through valid pixels - for( int iY = 0; iY < nYValid; iY++ ) - { - int y = iY * nXBlockSize; - for( int iX = 0; iX < nXValid; iX++ ) - { - if (pabyDataSource[iX + y] == noData) - { - pabyDataDest[iX + y] = noData; - } - } - } - - err = bandDest->WriteBlock(/*iXBlock*/ -1, iYBlock, pabyDataDest); - if (err != CPLErr::CE_None) - { - CallbackHelper::ErrorMsg(Debug::Format("Error on writing band: %d; %d.", iXBlock, iYBlock)); - ErrorMessage(tkFAILED_WRITE_BLOCK); - *retVal = VARIANT_FALSE; - break; - } - } - if(err != CPLErr::CE_None) - break; - } - CPLFree(pabyDataSource); - CPLFree(pabyDataDest); + GDALDataset* dsDest = GdalHelper::OpenRasterDatasetW(OLE2W(destFilename), GDALAccess::GA_ReadOnly); - if(err != CPLErr::CE_None || (*retVal == VARIANT_FALSE)) - break; - } - } - } - - if (dsSource) - delete dsSource; - - if (dsDest) - delete dsDest; - - - - if( _globalCallback != NULL ) - { - ClearGdalErrorHandler(); - CallbackHelper::ProgressCompleted(_globalCallback); - } - return S_OK; + if (!dsSource || !dsDest) + { + this->ErrorMessage(tkINVALID_FILE); + } + else + { + if (dsSource->GetRasterCount() != dsDest->GetRasterCount() || + dsSource->GetRasterXSize() != dsDest->GetRasterXSize() || + dsSource->GetRasterYSize() != dsDest->GetRasterYSize()) { + this->ErrorMessage(tkINPUT_RASTERS_DIFFER); + } + else + { + GByte* pabyDataSource, *pabyDataDest; + *retVal = VARIANT_TRUE; + int count = dsSource->GetRasterCount(); + + for (int i = 1; i <= count; i++) + { + GDALRasterBand* bandSource = dsSource->GetRasterBand(i); + GDALRasterBand* bandDest = dsDest->GetRasterBand(i); + if (bandSource->GetRasterDataType() != GDT_Byte || + bandDest->GetRasterDataType() != GDT_Byte) + { + this->ErrorMessage(tkNON_SINGLE_BYTE_PER_BAND); + *retVal = VARIANT_FALSE; + break; + } + + int nXBlockSize, nYBlockSize; + int nXBlockSize2, nYBlockSize2; + bandSource->GetBlockSize(&nXBlockSize, &nYBlockSize); + bandDest->GetBlockSize(&nXBlockSize2, &nYBlockSize2); + + if (nXBlockSize != nXBlockSize2 || nYBlockSize != nYBlockSize2) { + this->ErrorMessage(tkINPUT_RASTERS_DIFFER); + *retVal = VARIANT_FALSE; + break; + } + + unsigned char noData = (unsigned char)bandSource->GetNoDataValue(); + + int nXBlocks = (bandSource->GetXSize() + nXBlockSize - 1) / nXBlockSize; + int nYBlocks = (bandSource->GetYSize() + nYBlockSize - 1) / nYBlockSize; + pabyDataSource = (GByte *)CPLMalloc(nXBlockSize * nYBlockSize); + pabyDataDest = (GByte *)CPLMalloc(nXBlockSize * nYBlockSize); + + CPLErr err = CPLErr::CE_None; + long percent = 0; + for (int iYBlock = 0; iYBlock < nYBlocks; iYBlock++) + { + for (int iXBlock = 0; iXBlock < nXBlocks; iXBlock++) + { + if (_globalCallback != NULL) + { + double count = iYBlock * nXBlocks + iXBlock; + long newpercent = (long)((double)count / (double)(nXBlocks * nYBlocks)*100.0); + if (newpercent > percent) + { + percent = newpercent; + CString s; + s.Format("Calculating band %d", i); + CallbackHelper::Progress(_globalCallback, percent, s, _key); + } + } + + int nXValid, nYValid; + CPLErr err1 = bandSource->ReadBlock(iXBlock, iYBlock, pabyDataSource); + CPLErr err2 = bandDest->ReadBlock(iXBlock, iYBlock, pabyDataDest); + if (err1 != CPLErr::CE_None || err2 != CPLErr::CE_None) + { + CallbackHelper::ErrorMsg(Debug::Format("Error on reading band: %d; %d.", iXBlock, iYBlock)); + ErrorMessage(tkFAILED_READ_BLOCK); + *retVal = VARIANT_FALSE; + break; + } + + // Compute the portion of the block that is valid partial edge blocks. + if ((iXBlock + 1) * nXBlockSize > bandSource->GetXSize()) + { + nXValid = bandSource->GetXSize() - iXBlock * nXBlockSize; + } + else + { + nXValid = nXBlockSize; + } + if ((iYBlock + 1) * nYBlockSize > bandSource->GetYSize()) + { + nYValid = bandSource->GetYSize() - iYBlock * nYBlockSize; + } + else + { + nYValid = nYBlockSize; + } + + // loop through valid pixels + for (int iY = 0; iY < nYValid; iY++) + { + int y = iY * nXBlockSize; + for (int iX = 0; iX < nXValid; iX++) + { + if (pabyDataSource[iX + y] == noData) + { + pabyDataDest[iX + y] = noData; + } + } + } + + err = bandDest->WriteBlock(/*iXBlock*/ -1, iYBlock, pabyDataDest); + if (err != CPLErr::CE_None) + { + CallbackHelper::ErrorMsg(Debug::Format("Error on writing band: %d; %d.", iXBlock, iYBlock)); + ErrorMessage(tkFAILED_WRITE_BLOCK); + *retVal = VARIANT_FALSE; + break; + } + } + if (err != CPLErr::CE_None) + break; + } + CPLFree(pabyDataSource); + CPLFree(pabyDataDest); + + if (err != CPLErr::CE_None || (*retVal == VARIANT_FALSE)) + break; + } + } + } + + if (dsSource) + delete dsSource; + + if (dsDest) + delete dsDest; + + + + if (_globalCallback != NULL) + { + ClearGdalErrorHandler(); + CallbackHelper::ProgressCompleted(_globalCallback); + } + return S_OK; } // ******************************************************** @@ -4767,294 +4845,294 @@ STDMETHODIMP CUtils::CopyNodataValues(BSTR sourceFilename, BSTR destFilename, VA // ******************************************************** STDMETHODIMP CUtils::ErrorMsgFromObject(IDispatch * comClass, BSTR* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()) - if (!comClass) - { - ErrorMsg(tkUNEXPECTED_NULL_PARAMETER); - *retVal = A2BSTR(""); - return S_OK; - } - long errorCode; - - ICharts* Charts = NULL; - comClass->QueryInterface(IID_ICharts, (void**)(&Charts)); - if (Charts) - { - Charts->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - Charts->Release(); - return S_OK; - } - - IColorScheme* ColorScheme = NULL; - comClass->QueryInterface(IID_IColorScheme, (void**)(&ColorScheme)); - if (ColorScheme) - { - ColorScheme->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - ColorScheme->Release(); - return S_OK; - } - - IESRIGridManager* ESRIGridManager = NULL; - comClass->QueryInterface(IID_IESRIGridManager, (void**)(&ESRIGridManager)); - if (ESRIGridManager) - { - ESRIGridManager->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - ESRIGridManager->Release(); - return S_OK; - } - - //IExtents* Extents = NULL; - - IField* Field = NULL; - comClass->QueryInterface(IID_IField, (void**)(&Field)); - if (Field) - { - Field->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - Field->Release(); - return S_OK; - } - - IGeoProjection* GeoProjection = NULL; - comClass->QueryInterface(IID_IGeoProjection, (void**)(&GeoProjection)); - if (GeoProjection) - { - GeoProjection->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - GeoProjection->Release(); - return S_OK; - } - - //IGlobalSettings* GlobalSettings = NULL; - - IGrid* Grid = NULL; - comClass->QueryInterface(IID_IGrid, (void**)(&Grid)); - if (Grid) - { - Grid->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - Grid->Release(); - return S_OK; - } - - IGridColorBreak* GridColorBreak = NULL; - comClass->QueryInterface(IID_IGridColorBreak, (void**)(&GridColorBreak)); - if (GridColorBreak) - { - GridColorBreak->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - GridColorBreak->Release(); - return S_OK; - } - - IGridColorScheme* GridColorScheme = NULL; - comClass->QueryInterface(IID_IGridColorScheme, (void**)(&GridColorScheme)); - if (GridColorScheme) - { - GridColorScheme->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - GridColorScheme->Release(); - return S_OK; - } - - IGridHeader* GridHeader = NULL; - comClass->QueryInterface(IID_IGridHeader, (void**)(&GridHeader)); - if (GridHeader) - { - GridHeader->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - GridHeader->Release(); - return S_OK; - } + AFX_MANAGE_STATE(AfxGetStaticModuleState()) + if (!comClass) + { + ErrorMsg(tkUNEXPECTED_NULL_PARAMETER); + *retVal = A2BSTR(""); + return S_OK; + } + long errorCode; + + ICharts* Charts = NULL; + comClass->QueryInterface(IID_ICharts, (void**)(&Charts)); + if (Charts) + { + Charts->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + Charts->Release(); + return S_OK; + } + + IColorScheme* ColorScheme = NULL; + comClass->QueryInterface(IID_IColorScheme, (void**)(&ColorScheme)); + if (ColorScheme) + { + ColorScheme->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + ColorScheme->Release(); + return S_OK; + } + + IESRIGridManager* ESRIGridManager = NULL; + comClass->QueryInterface(IID_IESRIGridManager, (void**)(&ESRIGridManager)); + if (ESRIGridManager) + { + ESRIGridManager->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + ESRIGridManager->Release(); + return S_OK; + } - IImage* Image = NULL; - comClass->QueryInterface(IID_IImage, (void**)(&Image)); - if (Image) - { - Image->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - Image->Release(); - return S_OK; - } + //IExtents* Extents = NULL; - //ILabelCategory* LabelCategory = NULL; + IField* Field = NULL; + comClass->QueryInterface(IID_IField, (void**)(&Field)); + if (Field) + { + Field->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + Field->Release(); + return S_OK; + } + + IGeoProjection* GeoProjection = NULL; + comClass->QueryInterface(IID_IGeoProjection, (void**)(&GeoProjection)); + if (GeoProjection) + { + GeoProjection->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + GeoProjection->Release(); + return S_OK; + } - ILabels* Labels = NULL; - comClass->QueryInterface(IID_ILabels, (void**)(&Labels)); - if (Labels) - { - Labels->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - Labels->Release(); - return S_OK; - } + //IGlobalSettings* GlobalSettings = NULL; - ILinePattern* LinePattern = NULL; - comClass->QueryInterface(IID_ILinePattern, (void**)(&LinePattern)); - if (LinePattern) - { - LinePattern->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - LinePattern->Release(); - return S_OK; - } + IGrid* Grid = NULL; + comClass->QueryInterface(IID_IGrid, (void**)(&Grid)); + if (Grid) + { + Grid->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + Grid->Release(); + return S_OK; + } + + IGridColorBreak* GridColorBreak = NULL; + comClass->QueryInterface(IID_IGridColorBreak, (void**)(&GridColorBreak)); + if (GridColorBreak) + { + GridColorBreak->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + GridColorBreak->Release(); + return S_OK; + } + + IGridColorScheme* GridColorScheme = NULL; + comClass->QueryInterface(IID_IGridColorScheme, (void**)(&GridColorScheme)); + if (GridColorScheme) + { + GridColorScheme->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + GridColorScheme->Release(); + return S_OK; + } + + IGridHeader* GridHeader = NULL; + comClass->QueryInterface(IID_IGridHeader, (void**)(&GridHeader)); + if (GridHeader) + { + GridHeader->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + GridHeader->Release(); + return S_OK; + } + + IImage* Image = NULL; + comClass->QueryInterface(IID_IImage, (void**)(&Image)); + if (Image) + { + Image->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + Image->Release(); + return S_OK; + } - //ILineSegment* LineSegment = NULL; + //ILabelCategory* LabelCategory = NULL; - //IMeasuring* Measuring = NULL; + ILabels* Labels = NULL; + comClass->QueryInterface(IID_ILabels, (void**)(&Labels)); + if (Labels) + { + Labels->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + Labels->Release(); + return S_OK; + } + + ILinePattern* LinePattern = NULL; + comClass->QueryInterface(IID_ILinePattern, (void**)(&LinePattern)); + if (LinePattern) + { + LinePattern->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + LinePattern->Release(); + return S_OK; + } - IPoint* Point = NULL; - comClass->QueryInterface(IID_IPoint, (void**)(&Point)); - if (Point) - { - Point->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - Point->Release(); - return S_OK; - } + //ILineSegment* LineSegment = NULL; - IShape* Shape = NULL; - comClass->QueryInterface(IID_IShape, (void**)(&Shape)); - if (Shape) - { - Shape->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - Shape->Release(); - return S_OK; - } + //IMeasuring* Measuring = NULL; - IShapeDrawingOptions* ShapeDrawingOptions = NULL; - comClass->QueryInterface(IID_IShapeDrawingOptions, (void**)(&ShapeDrawingOptions)); - if (ShapeDrawingOptions) - { - ShapeDrawingOptions->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - ShapeDrawingOptions->Release(); - return S_OK; - } - - IShapefile* Shapefile = NULL; - comClass->QueryInterface(IID_IShapefile, (void**)(&Shapefile)); - if (Shapefile) - { - Shapefile->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - Shapefile->Release(); - return S_OK; - } - - IShapefileCategories* ShapefileCategories = NULL; - comClass->QueryInterface(IID_IShapefileCategories, (void**)(&ShapefileCategories)); - if (ShapefileCategories) - { - ShapefileCategories->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - ShapefileCategories->Release(); - return S_OK; - } + IPoint* Point = NULL; + comClass->QueryInterface(IID_IPoint, (void**)(&Point)); + if (Point) + { + Point->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + Point->Release(); + return S_OK; + } + + IShape* Shape = NULL; + comClass->QueryInterface(IID_IShape, (void**)(&Shape)); + if (Shape) + { + Shape->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + Shape->Release(); + return S_OK; + } + + IShapeDrawingOptions* ShapeDrawingOptions = NULL; + comClass->QueryInterface(IID_IShapeDrawingOptions, (void**)(&ShapeDrawingOptions)); + if (ShapeDrawingOptions) + { + ShapeDrawingOptions->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + ShapeDrawingOptions->Release(); + return S_OK; + } + + IShapefile* Shapefile = NULL; + comClass->QueryInterface(IID_IShapefile, (void**)(&Shapefile)); + if (Shapefile) + { + Shapefile->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + Shapefile->Release(); + return S_OK; + } + + IShapefileCategories* ShapefileCategories = NULL; + comClass->QueryInterface(IID_IShapefileCategories, (void**)(&ShapefileCategories)); + if (ShapefileCategories) + { + ShapefileCategories->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + ShapefileCategories->Release(); + return S_OK; + } - //IShapefileCategory* ShapefileCategory = NULL; + //IShapefileCategory* ShapefileCategory = NULL; - //IShapefileColorBreak* ShapefileColorBreak = NULL; + //IShapefileColorBreak* ShapefileColorBreak = NULL; #ifdef OLD_API - IShapefileColorScheme* ShapefileColorScheme = NULL; - comClass->QueryInterface(IID_IShapefileColorScheme, (void**)(&ShapefileColorScheme)); - if (ShapefileColorScheme) - { - ShapefileColorScheme->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - ShapefileColorScheme->Release(); - return S_OK; - } + IShapefileColorScheme* ShapefileColorScheme = NULL; + comClass->QueryInterface(IID_IShapefileColorScheme, (void**)(&ShapefileColorScheme)); + if (ShapefileColorScheme) + { + ShapefileColorScheme->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + ShapefileColorScheme->Release(); + return S_OK; + } #endif - IShapeNetwork* ShapeNetwork = NULL; - comClass->QueryInterface(IID_IShapeNetwork, (void**)(&ShapeNetwork)); - if (ShapeNetwork) - { - ShapeNetwork->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - ShapeNetwork->Release(); - return S_OK; - } - - CComPtr Table = NULL; - comClass->QueryInterface(IID_ITable, (void**)(&Table)); - if (Table) - { - Table->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - return S_OK; - } - - ITileProviders* TileProviders = NULL; - comClass->QueryInterface(IID_ITileProviders, (void**)(&TileProviders)); - if (TileProviders) - { - TileProviders->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - TileProviders->Release(); - return S_OK; - } - - ITin* Tin = NULL; - comClass->QueryInterface(IID_ITin, (void**)(&Tin)); - if (Tin) - { - Tin->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - Tin->Release(); - return S_OK; - } - - IUtils* Utils = NULL; - comClass->QueryInterface(IID_IUtils, (void**)(&Utils)); - if (Utils) - { - Utils->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - Utils->Release(); - return S_OK; - } - - IVector* Vector = NULL; - comClass->QueryInterface(IID_IVector, (void**)(&Vector)); - if (Vector) - { - Vector->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - Vector->Release(); - return S_OK; - } - - IFileManager* FileManager = NULL; - comClass->QueryInterface(IID_IFileManager, (void**)(&FileManager)); - if (FileManager) - { - FileManager->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - FileManager->Release(); - return S_OK; - } - - //IShapeValidationInfo* ShapeValidationInfo = NULL; + IShapeNetwork* ShapeNetwork = NULL; + comClass->QueryInterface(IID_IShapeNetwork, (void**)(&ShapeNetwork)); + if (ShapeNetwork) + { + ShapeNetwork->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + ShapeNetwork->Release(); + return S_OK; + } + + CComPtr Table = NULL; + comClass->QueryInterface(IID_ITable, (void**)(&Table)); + if (Table) + { + Table->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + return S_OK; + } + + ITileProviders* TileProviders = NULL; + comClass->QueryInterface(IID_ITileProviders, (void**)(&TileProviders)); + if (TileProviders) + { + TileProviders->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + TileProviders->Release(); + return S_OK; + } + + ITin* Tin = NULL; + comClass->QueryInterface(IID_ITin, (void**)(&Tin)); + if (Tin) + { + Tin->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + Tin->Release(); + return S_OK; + } + + IUtils* Utils = NULL; + comClass->QueryInterface(IID_IUtils, (void**)(&Utils)); + if (Utils) + { + Utils->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + Utils->Release(); + return S_OK; + } + + IVector* Vector = NULL; + comClass->QueryInterface(IID_IVector, (void**)(&Vector)); + if (Vector) + { + Vector->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + Vector->Release(); + return S_OK; + } + + IFileManager* FileManager = NULL; + comClass->QueryInterface(IID_IFileManager, (void**)(&FileManager)); + if (FileManager) + { + FileManager->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + FileManager->Release(); + return S_OK; + } - IFieldStatOperations* FieldStatOperations = NULL; - comClass->QueryInterface(IID_IFieldStatOperations, (void**)(&FieldStatOperations)); - if (FieldStatOperations) - { - FieldStatOperations->get_LastErrorCode(&errorCode); - get_ErrorMsg(errorCode, retVal); - FieldStatOperations->Release(); - return S_OK; - } - - *retVal = A2BSTR("Unknown class"); - return S_OK; + //IShapeValidationInfo* ShapeValidationInfo = NULL; + + IFieldStatOperations* FieldStatOperations = NULL; + comClass->QueryInterface(IID_IFieldStatOperations, (void**)(&FieldStatOperations)); + if (FieldStatOperations) + { + FieldStatOperations->get_LastErrorCode(&errorCode); + get_ErrorMsg(errorCode, retVal); + FieldStatOperations->Release(); + return S_OK; + } + + *retVal = A2BSTR("Unknown class"); + return S_OK; } // ******************************************************** @@ -5062,37 +5140,37 @@ STDMETHODIMP CUtils::ErrorMsgFromObject(IDispatch * comClass, BSTR* retVal) // ******************************************************** HRESULT CUtils::TileProjectionToGeoProjectionCore(tkTileProjection projection, VARIANT_BOOL useCache, IGeoProjection** retVal) { - *retVal = NULL; + *retVal = NULL; - if (projection != Amersfoort && projection != SphericalMercator) - return S_OK; + if (projection != Amersfoort && projection != SphericalMercator) + return S_OK; - if (_tileProjections[projection] && useCache) - { - // let's take it from cache - _tileProjections[projection]->AddRef(); - *retVal = _tileProjections[projection]; - } - else - { - IGeoProjection* gp = NULL; - ComHelper::CreateInstance(idGeoProjection, (IDispatch**)&gp); - VARIANT_BOOL vb; - switch(projection) - { - case tkTileProjection::SphericalMercator: - gp->SetGoogleMercator(&vb); - break; - case tkTileProjection::Amersfoort: - gp->ImportFromEPSG(EPSG_AMERSFOORT, &vb); - //gp->ImportFromProj4(A2BSTR("+proj=sterea +lat_0=52.15616055555555 +lon_0=5.38763888888889 +k=0.9999079 +x_0=155000 +y_0=463000 +ellps=bessel +units=m +no_defs"), &vb); - break; - } - _tileProjections[projection] = gp; - gp->AddRef(); - *retVal = gp; - } - return S_OK; + if (_tileProjections[projection] && useCache) + { + // let's take it from cache + _tileProjections[projection]->AddRef(); + *retVal = _tileProjections[projection]; + } + else + { + IGeoProjection* gp = NULL; + ComHelper::CreateInstance(idGeoProjection, (IDispatch**)&gp); + VARIANT_BOOL vb; + switch (projection) + { + case tkTileProjection::SphericalMercator: + gp->SetGoogleMercator(&vb); + break; + case tkTileProjection::Amersfoort: + gp->ImportFromEPSG(EPSG_AMERSFOORT, &vb); + //gp->ImportFromProj4(A2BSTR("+proj=sterea +lat_0=52.15616055555555 +lon_0=5.38763888888889 +k=0.9999079 +x_0=155000 +y_0=463000 +ellps=bessel +units=m +no_defs"), &vb); + break; + } + _tileProjections[projection] = gp; + gp->AddRef(); + *retVal = gp; + } + return S_OK; } // ******************************************************** @@ -5100,8 +5178,8 @@ HRESULT CUtils::TileProjectionToGeoProjectionCore(tkTileProjection projection, V // ******************************************************** STDMETHODIMP CUtils::TileProjectionToGeoProjection(tkTileProjection projection, IGeoProjection** retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - return TileProjectionToGeoProjectionCore(projection, VARIANT_FALSE, retVal); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + return TileProjectionToGeoProjectionCore(projection, VARIANT_FALSE, retVal); } // ******************************************************** @@ -5109,14 +5187,14 @@ STDMETHODIMP CUtils::TileProjectionToGeoProjection(tkTileProjection projection, // ******************************************************** STDMETHODIMP CUtils::get_ComUsageReport(VARIANT_BOOL unreleasedOnly, BSTR* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - CString s = gReferenceCounter.GetReport(unreleasedOnly ? true: false); - USES_CONVERSION; - *retVal = A2BSTR(s); + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + CString s = gReferenceCounter.GetReport(unreleasedOnly ? true : false); + USES_CONVERSION; + *retVal = A2BSTR(s); - //Debug::WriteLine("Constructor count: %d; %d", m_globalSettings.ctorCount, m_globalSettings.dtorCount); + //Debug::WriteLine("Constructor count: %d; %d", m_globalSettings.ctorCount, m_globalSettings.dtorCount); - return S_OK; + return S_OK; } #pragma region Raster Calculator @@ -5127,37 +5205,37 @@ STDMETHODIMP CUtils::get_ComUsageReport(VARIANT_BOOL unreleasedOnly, BSTR* retVa // Accesses data in safe array of BSTR bool CUtils::ValidateInputNames(SAFEARRAY* InputNames, LONG& lLBound, LONG& lUBound, BSTR **pbstr) { - // Check dimensions of the array. - if (SafeArrayGetDim(InputNames) != 1) - { - // most likely this error will be caught while marshalling the array - ErrorMessage(tkFAILED_TO_READ_INPUT_NAMES); - return false; - } + // Check dimensions of the array. + if (SafeArrayGetDim(InputNames) != 1) + { + // most likely this error will be caught while marshalling the array + ErrorMessage(tkFAILED_TO_READ_INPUT_NAMES); + return false; + } - HRESULT hr; - hr = SafeArrayGetLBound(InputNames, 1, &lLBound); - if (FAILED(hr)) - { - ErrorMessage(tkFAILED_TO_READ_INPUT_NAMES); - return false; - } + HRESULT hr; + hr = SafeArrayGetLBound(InputNames, 1, &lLBound); + if (FAILED(hr)) + { + ErrorMessage(tkFAILED_TO_READ_INPUT_NAMES); + return false; + } - hr = SafeArrayGetUBound(InputNames, 1, &lUBound); - if (FAILED(hr)) - { - ErrorMessage(tkFAILED_TO_READ_INPUT_NAMES); - return false; - } + hr = SafeArrayGetUBound(InputNames, 1, &lUBound); + if (FAILED(hr)) + { + ErrorMessage(tkFAILED_TO_READ_INPUT_NAMES); + return false; + } - // TODO: add check that we have an array of BSTR and not the other data type - hr = SafeArrayAccessData(InputNames, (void HUGEP* FAR*)pbstr); - if (FAILED(hr)) - { - ErrorMessage(tkFAILED_TO_READ_INPUT_NAMES); - return false; - } - return true; + // TODO: add check that we have an array of BSTR and not the other data type + hr = SafeArrayAccessData(InputNames, (void HUGEP* FAR*)pbstr); + if (FAILED(hr)) + { + ErrorMessage(tkFAILED_TO_READ_INPUT_NAMES); + return false; + } + return true; } // ******************************************************** @@ -5165,312 +5243,312 @@ bool CUtils::ValidateInputNames(SAFEARRAY* InputNames, LONG& lLBound, LONG& lUBo // ******************************************************** GDALDriverH OpenOutputDriver(CString driverName) { - GDALDriverH outputDriver = GDALGetDriverByName( driverName ); - if ( outputDriver ) - { - char **driverMetadata = GDALGetMetadata( outputDriver, NULL ); - if ( !CSLFetchBoolean( driverMetadata, GDAL_DCAP_CREATE, false ) ) - { - outputDriver = NULL; //driver exist, but it does not support the create operation - } - } - return outputDriver; + GDALDriverH outputDriver = GDALGetDriverByName(driverName); + if (outputDriver) + { + char **driverMetadata = GDALGetMetadata(outputDriver, NULL); + if (!CSLFetchBoolean(driverMetadata, GDAL_DCAP_CREATE, false)) + { + outputDriver = NULL; //driver exist, but it does not support the create operation + } + } + return outputDriver; } // ******************************************************** // OpenOutputFile() // ******************************************************** -GDALDataset* OpenOutputFile( GDALDriverH outputDriver, CStringW filename, int xSize, int ySize, GDALDataset* sourceTransform ) +GDALDataset* OpenOutputFile(GDALDriverH outputDriver, CStringW filename, int xSize, int ySize, GDALDataset* sourceTransform) { - m_globalSettings.SetGdalUtf8(true); + m_globalSettings.SetGdalUtf8(true); - char **papszOptions = NULL; - GDALDataset* outputDataset = (GDALDataset *)GDALCreate( outputDriver, Utility::ConvertToUtf8(filename), xSize, ySize, 1, GDT_Float32, papszOptions ); - - double transform[6]; - sourceTransform->GetGeoTransform((double*)&transform); - outputDataset->SetGeoTransform(transform); + char **papszOptions = NULL; + GDALDataset* outputDataset = (GDALDataset *)GDALCreate(outputDriver, Utility::ConvertToUtf8(filename), xSize, ySize, 1, GDT_Float32, papszOptions); + + double transform[6]; + sourceTransform->GetGeoTransform((double*)&transform); + outputDataset->SetGeoTransform(transform); - outputDataset->SetProjection( sourceTransform->GetProjectionRef() ); + outputDataset->SetProjection(sourceTransform->GetProjectionRef()); - m_globalSettings.SetGdalUtf8(false); - return outputDataset; + m_globalSettings.SetGdalUtf8(false); + return outputDataset; } -int atoi_custom( const char *c ) { - int value = 0; - int sign = 1; - if( *c == '+' || *c == '-' ) { - if( *c == '-' ) sign = -1; - c++; - } - while ( isdigit( *c ) ) { - value *= 10; - value += (int) (*c-'0'); - c++; - } - return value * sign; +int atoi_custom(const char *c) { + int value = 0; + int sign = 1; + if (*c == '+' || *c == '-') { + if (*c == '-') sign = -1; + c++; + } + while (isdigit(*c)) { + value *= 10; + value += (int)(*c - '0'); + c++; + } + return value * sign; } // ******************************************************** // CalculateRaster() // ******************************************************** STDMETHODIMP CUtils::CalculateRaster(SAFEARRAY* InputNames, BSTR expression, BSTR outputFilename, BSTR gdalOutputFormat, - float outputNodataValue, ICallback* callback, BSTR* errorMsg, VARIANT_BOOL* retVal) + float outputNodataValue, ICallback* callback, BSTR* errorMsg, VARIANT_BOOL* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retVal = VARIANT_FALSE; - - CustomExpression expr; - CString err; - GDALDataset* dtOutput = NULL; // used in clean-up when something false, throws an exception when is it not yet created. - - // -------------------------------------------------------- - // Open input - // -------------------------------------------------------- - LONG lBound, uBound; - BSTR* names; - if (!ValidateInputNames(InputNames, lBound, uBound, &names)) - { - *errorMsg = A2BSTR(ErrorMsg(_lastErrorCode)); - return S_OK; - } - - int count = uBound - lBound + 1; - if (count < 2) - { - ErrorMessage(tkAT_LEAST_TWO_DATASOURCES_EXPECTED); - *errorMsg = A2BSTR(ErrorMsg(_lastErrorCode)); - return S_OK; - } - - int xSize = 0; - int ySize = 0; - - float* calcData = NULL; - - GDALAllRegister(); - - USES_CONVERSION; - map datasets; - for (int i = 0; i < count; i++) - { - CStringW path = OLE2W(names[i]); - CString name = W2A(Utility::GetNameFromPath(path)); - name = name.MakeLower(); - - GDALDataset* dt = GdalHelper::OpenRasterDatasetW(path, GDALAccess::GA_ReadOnly); - if (dt) - { - // perhaps check rotation and create north up dataset in case bounds should be supported - /*double inputGeoTransform[6]; - if ( GDALGetGeoTransform( inputDataset, inputGeoTransform ) == CE_None - && ( inputGeoTransform[1] < 0.0 - || inputGeoTransform[2] != 0.0 - || inputGeoTransform[4] != 0.0 - || inputGeoTransform[5] > 0.0 ) ) - { - GDALDatasetH vDataset = GDALAutoCreateWarpedVRT( inputDataset, NULL, NULL, GRA_NearestNeighbour, 0.2, NULL ); - mInputDatasets.push_back( vDataset ); - mInputDatasets.push_back( inputDataset ); - inputDataset = vDataset; - } - else - { - mInputDatasets.push_back( inputDataset ); - }*/ - } - else - { - CStringW temp = L"Failed to open dataset: " + path; - *errorMsg = W2BSTR(temp); - goto cleaning; - } - - if (dt) - datasets [name] = dt; - - if (i == 0) - { - // take size from the first one - xSize = dt->GetRasterXSize(); - ySize = dt->GetRasterYSize(); - } - else - { - // make sure that others are of the same size - if (dt->GetRasterXSize() != xSize || dt->GetRasterYSize() != ySize ) - { - ErrorMessage(tkIMAGES_MUST_HAVE_THE_SAME_SIZE); - *errorMsg = A2BSTR(ErrorMsg(_lastErrorCode)); - goto cleaning; - } - } - } - - if (datasets.size() < 2) - { - *errorMsg = A2BSTR(ErrorMsg(tkAT_LEAST_TWO_DATASOURCES_EXPECTED)); - ErrorMessage(_lastErrorCode); - goto cleaning; - } - - // -------------------------------------------------------- - // Creating output - // -------------------------------------------------------- - GDALDriverH driver = OpenOutputDriver(OLE2A(gdalOutputFormat)); - // GDALDataset* dtOutput = NULL; Moved up, because it fails on clean up - if (driver != NULL) - { - dtOutput = OpenOutputFile(driver, OLE2W(outputFilename), xSize, ySize, datasets.begin()->second); - } - - if (!dtOutput) - { - ErrorMsg(tkINVALID_FILENAME); - *errorMsg = A2BSTR("Failed to create output dataset"); - goto cleaning; - } + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *retVal = VARIANT_FALSE; + + CustomExpression expr; + CStringW err; + GDALDataset* dtOutput = NULL; // used in clean-up when something false, throws an exception when is it not yet created. + + // -------------------------------------------------------- + // Open input + // -------------------------------------------------------- + LONG lBound, uBound; + BSTR* names; + if (!ValidateInputNames(InputNames, lBound, uBound, &names)) + { + *errorMsg = A2BSTR(ErrorMsg(_lastErrorCode)); + return S_OK; + } - // setting no data value - GDALRasterBand* bandOutput = dtOutput->GetRasterBand(1); - bandOutput->SetNoDataValue(outputNodataValue); + int count = uBound - lBound + 1; + if (count < 2) + { + ErrorMessage(tkAT_LEAST_TWO_DATASOURCES_EXPECTED); + *errorMsg = A2BSTR(ErrorMsg(_lastErrorCode)); + return S_OK; + } - if (!expr.Parse(OLE2A(expression), false, err)) - { - ErrorMessage(tkINVALID_EXPRESSION); - *errorMsg = A2BSTR(err); - goto cleaning; - } - - // -------------------------------------------------------- - // looking for source bands - // -------------------------------------------------------- - int numFields = expr.get_NumFields(); - for(int i = 0; i < numFields; i++) - { - CString name = expr.get_FieldName(i); - int pos = name.Find('@', 0); - if (pos == -1 || pos == name.GetLength() - 1) - { - ErrorMessage(tkINVALID_EXPRESSION); - err.Format("Invalid formula field: %s; @ sign to separate filename and band index is expected", name); - *errorMsg = A2BSTR(err); - goto cleaning; - } + int xSize = 0; + int ySize = 0; - CString dtName = name.Mid(0, pos).MakeLower(); - if (datasets.find(dtName) != datasets.end()) - { - GDALDataset* dt = datasets[dtName]; - int bandIndex = atoi_custom(name.Mid(pos + 1)); - GDALRasterBand* band = dt->GetRasterBand(bandIndex); - if (band) - { - CExpressionValue* val = expr.get_FieldValue(i); - val->band(band); - double nodv = band->GetNoDataValue(); - val->matrix(new RasterMatrix(xSize, 1, new float[xSize * 1], nodv)); - } - else - { - ErrorMessage(tkINVALID_EXPRESSION); - err.Format("Band wasn't found: %s", name); - *errorMsg = A2BSTR(err); - goto cleaning; - } - } - else - { - ErrorMessage(tkINVALID_EXPRESSION); - err.Format("Invalid formula field: %s; no dataset with such name in input names", name); - *errorMsg = A2BSTR(err); - goto cleaning; - } - } - - // -------------------------------------------------------- - // doing calculations - // -------------------------------------------------------- - long numColumns = xSize; - long numRows = ySize; - long percent = 0; - calcData = new float[numColumns]; - - for(long i = 0; i < numRows; i++ ) - { - CallbackHelper::Progress(callback, i, numRows, "Calculating", _key, percent); + float* calcData = NULL; - for(int j = 0; j < numFields; j++) - { - CExpressionValue* val = expr.get_FieldValue(j); - GDALRasterBand* band = val->band(); - double nodv = band->GetNoDataValue(); - val->matrix(new RasterMatrix(xSize, 1, new float[xSize * 1], nodv)); - band->RasterIO(GF_Read, 0, i, numColumns, 1, val->matrix()->data(), numColumns, 1, GDALDataType::GDT_Float32, 0, 0); - } + GDALAllRegister(); - CString errorMsg; - CExpressionValue* resultVal = expr.Calculate(errorMsg); - if (resultVal) - { - RasterMatrix* resultMatrix = resultVal->matrix(); + USES_CONVERSION; + map datasets; + for (int i = 0; i < count; i++) + { + CStringW path = OLE2W(names[i]); + CStringW name = Utility::GetNameFromPath(path); + name = name.MakeLower(); + + GDALDataset* dt = GdalHelper::OpenRasterDatasetW(path, GDALAccess::GA_ReadOnly); + if (dt) + { + // perhaps check rotation and create north up dataset in case bounds should be supported + /*double inputGeoTransform[6]; + if ( GDALGetGeoTransform( inputDataset, inputGeoTransform ) == CE_None + && ( inputGeoTransform[1] < 0.0 + || inputGeoTransform[2] != 0.0 + || inputGeoTransform[4] != 0.0 + || inputGeoTransform[5] > 0.0 ) ) + { + GDALDatasetH vDataset = GDALAutoCreateWarpedVRT( inputDataset, NULL, NULL, GRA_NearestNeighbour, 0.2, NULL ); + mInputDatasets.push_back( vDataset ); + mInputDatasets.push_back( inputDataset ); + inputDataset = vDataset; + } + else + { + mInputDatasets.push_back( inputDataset ); + }*/ + } + else + { + CStringW temp = L"Failed to open dataset: " + path; + *errorMsg = W2BSTR(temp); + goto cleaning; + } + + if (dt) + datasets[name] = dt; + + if (i == 0) + { + // take size from the first one + xSize = dt->GetRasterXSize(); + ySize = dt->GetRasterYSize(); + } + else + { + // make sure that others are of the same size + if (dt->GetRasterXSize() != xSize || dt->GetRasterYSize() != ySize) + { + ErrorMessage(tkIMAGES_MUST_HAVE_THE_SAME_SIZE); + *errorMsg = A2BSTR(ErrorMsg(_lastErrorCode)); + goto cleaning; + } + } + } + + if (datasets.size() < 2) + { + *errorMsg = A2BSTR(ErrorMsg(tkAT_LEAST_TWO_DATASOURCES_EXPECTED)); + ErrorMessage(_lastErrorCode); + goto cleaning; + } + + // -------------------------------------------------------- + // Creating output + // -------------------------------------------------------- + GDALDriverH driver = OpenOutputDriver(OLE2A(gdalOutputFormat)); + // GDALDataset* dtOutput = NULL; Moved up, because it fails on clean up + if (driver != NULL) + { + dtOutput = OpenOutputFile(driver, OLE2W(outputFilename), xSize, ySize, datasets.begin()->second); + } - bool resultIsNumber = resultMatrix->isNumber(); - + if (!dtOutput) + { + ErrorMsg(tkINVALID_FILENAME); + *errorMsg = W2BSTR(L"Failed to create output dataset"); + goto cleaning; + } - if ( resultIsNumber ) //scalar result. Insert number for every pixel - { - for ( int j = 0; j < numColumns; ++j ) - { - calcData[j] = (float)resultMatrix->number(); - } - } - else //result is real matrix - { - memcpy(calcData, resultMatrix->data(), resultMatrix->GetBufferSize()); - } + // setting no data value + GDALRasterBand* bandOutput = dtOutput->GetRasterBand(1); + bandOutput->SetNoDataValue(outputNodataValue); - float ndv = (float)resultMatrix->nodataValue(); - int count = 0; - for ( int j = 0; j < numColumns; ++j ) - { - if ( calcData[j] == ndv ) - { - calcData[j] = outputNodataValue; - count++; - } - } - - if (bandOutput->RasterIO( GF_Write, 0, i, numColumns, 1, calcData, numColumns, 1, GDT_Float32, 0, 0 ) != CE_None ) - goto cleaning; - - expr.ReleaseArrays(); - } - } - *retVal = VARIANT_TRUE; + if (!expr.Parse(OLE2W(expression), false, err)) + { + ErrorMessage(tkINVALID_EXPRESSION); + *errorMsg = W2BSTR(err); + goto cleaning; + } + + // -------------------------------------------------------- + // looking for source bands + // -------------------------------------------------------- + int numFields = expr.get_NumFields(); + for (int i = 0; i < numFields; i++) + { + CStringW name = expr.get_FieldName(i); + int pos = name.Find('@', 0); + if (pos == -1 || pos == name.GetLength() - 1) + { + ErrorMessage(tkINVALID_EXPRESSION); + err.Format(L"Invalid formula field: %s; @ sign to separate filename and band index is expected", name); + *errorMsg = W2BSTR(err); + goto cleaning; + } + + CStringW dtName = name.Mid(0, pos).MakeLower(); + if (datasets.find(dtName) != datasets.end()) + { + GDALDataset* dt = datasets[dtName]; + int bandIndex = _wtoi(name.Mid(pos + 1)); + GDALRasterBand* band = dt->GetRasterBand(bandIndex); + if (band) + { + CExpressionValue* val = expr.get_FieldValue(i); + val->band(band); + double nodv = band->GetNoDataValue(); + val->matrix(new RasterMatrix(xSize, 1, new float[xSize * 1], nodv)); + } + else + { + ErrorMessage(tkINVALID_EXPRESSION); + err.Format(L"Band wasn't found: %s", name); + *errorMsg = W2BSTR(err); + goto cleaning; + } + } + else + { + ErrorMessage(tkINVALID_EXPRESSION); + err.Format(L"Invalid formula field: %s; no dataset with such name in input names", name); + *errorMsg = W2BSTR(err); + goto cleaning; + } + } + + // -------------------------------------------------------- + // doing calculations + // -------------------------------------------------------- + long numColumns = xSize; + long numRows = ySize; + long percent = 0; + calcData = new float[numColumns]; + + for (long i = 0; i < numRows; i++) + { + CallbackHelper::Progress(callback, i, numRows, "Calculating", _key, percent); + + for (int j = 0; j < numFields; j++) + { + CExpressionValue* val = expr.get_FieldValue(j); + GDALRasterBand* band = val->band(); + double nodv = band->GetNoDataValue(); + val->matrix(new RasterMatrix(xSize, 1, new float[xSize * 1], nodv)); + band->RasterIO(GF_Read, 0, i, numColumns, 1, val->matrix()->data(), numColumns, 1, GDALDataType::GDT_Float32, 0, 0); + } + + CStringW errorMsg; + CExpressionValue* resultVal = expr.Calculate(errorMsg); + if (resultVal) + { + RasterMatrix* resultMatrix = resultVal->matrix(); + + bool resultIsNumber = resultMatrix->isNumber(); + + + if (resultIsNumber) //scalar result. Insert number for every pixel + { + for (int j = 0; j < numColumns; ++j) + { + calcData[j] = (float)resultMatrix->number(); + } + } + else //result is real matrix + { + memcpy(calcData, resultMatrix->data(), resultMatrix->GetBufferSize()); + } + + float ndv = (float)resultMatrix->nodataValue(); + int count = 0; + for (int j = 0; j < numColumns; ++j) + { + if (calcData[j] == ndv) + { + calcData[j] = outputNodataValue; + count++; + } + } + + if (bandOutput->RasterIO(GF_Write, 0, i, numColumns, 1, calcData, numColumns, 1, GDT_Float32, 0, 0) != CE_None) + goto cleaning; + + expr.ReleaseArrays(); + } + } + *retVal = VARIANT_TRUE; cleaning: - // ------------------------------------------------------ - // Cleaning - // ------------------------------------------------------ - if (calcData) - delete[] calcData; - - expr.Clear(); - CallbackHelper::ProgressCompleted(callback); - map::iterator it = datasets.begin(); - while (it != datasets.end()) - { - GDALDataset* dt = it->second; - if (dt) - GdalHelper::CloseDataset(dt); - it++; - } - if (dtOutput) - GdalHelper::CloseDataset(dtOutput); - - return S_OK; + // ------------------------------------------------------ + // Cleaning + // ------------------------------------------------------ + if (calcData) + delete[] calcData; + + expr.Clear(); + CallbackHelper::ProgressCompleted(callback); + map::iterator it = datasets.begin(); + while (it != datasets.end()) + { + GDALDataset* dt = it->second; + if (dt) + GdalHelper::CloseDataset(dt); + it++; + } + if (dtOutput) + GdalHelper::CloseDataset(dtOutput); + + return S_OK; } #pragma endregion @@ -5481,190 +5559,190 @@ STDMETHODIMP CUtils::CalculateRaster(SAFEARRAY* InputNames, BSTR expression, BST // ******************************************************** bool CUtils::ParseSafeArray(SAFEARRAY* arr, LONG& lLBound, LONG& lUBound, void **pbstr) { - // Check dimensions of the array. - if (SafeArrayGetDim(arr) != 1) - { - // most likely this error will be caught while marshalling the array - ErrorMessage(tkINVALID_PARAMETERS_ARRAY); - return false; - } + // Check dimensions of the array. + if (SafeArrayGetDim(arr) != 1) + { + // most likely this error will be caught while marshalling the array + ErrorMessage(tkINVALID_PARAMETERS_ARRAY); + return false; + } - HRESULT hr; - hr = SafeArrayGetLBound(arr, 1, &lLBound); - if (FAILED(hr)) - { - ErrorMessage(tkINVALID_PARAMETERS_ARRAY); - return false; - } + HRESULT hr; + hr = SafeArrayGetLBound(arr, 1, &lLBound); + if (FAILED(hr)) + { + ErrorMessage(tkINVALID_PARAMETERS_ARRAY); + return false; + } - hr = SafeArrayGetUBound(arr, 1, &lUBound); - if (FAILED(hr)) - { - ErrorMessage(tkINVALID_PARAMETERS_ARRAY); - return false; - } + hr = SafeArrayGetUBound(arr, 1, &lUBound); + if (FAILED(hr)) + { + ErrorMessage(tkINVALID_PARAMETERS_ARRAY); + return false; + } - hr = SafeArrayAccessData(arr, (void HUGEP* FAR*)pbstr); - if (FAILED(hr)) - { - ErrorMessage(tkINVALID_PARAMETERS_ARRAY); - return false; - } - return true; + hr = SafeArrayAccessData(arr, (void HUGEP* FAR*)pbstr); + if (FAILED(hr)) + { + ErrorMessage(tkINVALID_PARAMETERS_ARRAY); + return false; + } + return true; } // ******************************************************** // ReclassifyRaster() // ******************************************************** -STDMETHODIMP CUtils::ReclassifyRaster(BSTR Filename, int bandIndex, BSTR outputName, SAFEARRAY* LowBounds, - SAFEARRAY* HighBounds, SAFEARRAY* NewValues, - BSTR gdalOutputFormat, ICallback* cBack, VARIANT_BOOL* retVal) +STDMETHODIMP CUtils::ReclassifyRaster(BSTR Filename, int bandIndex, BSTR outputName, SAFEARRAY* LowBounds, + SAFEARRAY* HighBounds, SAFEARRAY* NewValues, + BSTR gdalOutputFormat, ICallback* cBack, VARIANT_BOOL* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retVal = VARIANT_FALSE; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *retVal = VARIANT_FALSE; - USES_CONVERSION; - CStringW name = OLE2W(Filename); - CStringW outName = OLE2W(outputName); - - if (!Utility::FileExistsW(name)) - { - ErrorMessage(tkFILE_NOT_EXISTS); - return S_OK; - } - - //if (Utility::FileExistsW(outName)) - //{ - // ErrorMessage(tkFILE_EXISTS); - // return S_OK; - //} - - double* lows, *highs, *vals; - LONG lb1, ub1, lb2, ub2, lb3, ub3; - if (!ParseSafeArray(LowBounds, lb1, ub1, (void**)&lows)) - { - return S_OK; - } + USES_CONVERSION; + CStringW name = OLE2W(Filename); + CStringW outName = OLE2W(outputName); - if (!ParseSafeArray(HighBounds, lb2, ub2, (void**)&highs)) - { - return S_OK; - } + if (!Utility::FileExistsW(name)) + { + ErrorMessage(tkFILE_NOT_EXISTS); + return S_OK; + } + + //if (Utility::FileExistsW(outName)) + //{ + // ErrorMessage(tkFILE_EXISTS); + // return S_OK; + //} + + double* lows, *highs, *vals; + LONG lb1, ub1, lb2, ub2, lb3, ub3; + if (!ParseSafeArray(LowBounds, lb1, ub1, (void**)&lows)) + { + return S_OK; + } - if (!ParseSafeArray(NewValues, lb3, ub3, (void**)&vals)) - { - return S_OK; - } + if (!ParseSafeArray(HighBounds, lb2, ub2, (void**)&highs)) + { + return S_OK; + } - long length = ub1 - lb1; - if (ub2 - lb2 != length || ub3 - lb3 != length || length == 0) - { - ErrorMessage(tkINVALID_PARAMETERS_ARRAY); - return S_OK; - } - - std::deque breaks; - for (int i = 0; i <= length; i++) - { - BreakVal b; - b.highVal = highs[i]; - b.lowVal = lows[i]; - b.newVal = vals[i]; - breaks.push_back(b); - } + if (!ParseSafeArray(NewValues, lb3, ub3, (void**)&vals)) + { + return S_OK; + } - GDALDataset* dtOutput = NULL; - float* data = NULL; + long length = ub1 - lb1; + if (ub2 - lb2 != length || ub3 - lb3 != length || length == 0) + { + ErrorMessage(tkINVALID_PARAMETERS_ARRAY); + return S_OK; + } - // ------------------------------------------------------- - // Source - // ------------------------------------------------------- - GDALDataset* dt = GdalHelper::OpenRasterDatasetW(name, GDALAccess::GA_ReadOnly); - if (!dt) - { - ErrorMessage(tkCANT_OPEN_FILE); - return S_OK; - } - int xSize = dt->GetRasterXSize(); - int ySize = dt->GetRasterYSize(); + std::deque breaks; + for (int i = 0; i <= length; i++) + { + BreakVal b; + b.highVal = highs[i]; + b.lowVal = lows[i]; + b.newVal = vals[i]; + breaks.push_back(b); + } + + GDALDataset* dtOutput = NULL; + float* data = NULL; + + // ------------------------------------------------------- + // Source + // ------------------------------------------------------- + GDALDataset* dt = GdalHelper::OpenRasterDatasetW(name, GDALAccess::GA_ReadOnly); + if (!dt) + { + ErrorMessage(tkCANT_OPEN_FILE); + return S_OK; + } + int xSize = dt->GetRasterXSize(); + int ySize = dt->GetRasterYSize(); + + int bandCount = dt->GetRasterCount(); + if (bandIndex > bandCount) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + goto cleaning; + } + GDALRasterBand* band = dt->GetRasterBand(bandIndex); + if (!band) + { + ErrorMessage(tkINDEX_OUT_OF_BOUNDS); + goto cleaning; + } + + // ------------------------------------------------------- + // Creating output + // ------------------------------------------------------- + GDALDriverH driver = OpenOutputDriver(OLE2A(gdalOutputFormat)); + if (driver != NULL) + { + dtOutput = OpenOutputFile(driver, OLE2W(outputName), xSize, ySize, dt); + } - int bandCount = dt->GetRasterCount(); - if (bandIndex > bandCount) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - goto cleaning; - } - GDALRasterBand* band = dt->GetRasterBand(bandIndex); - if (!band) - { - ErrorMessage(tkINDEX_OUT_OF_BOUNDS); - goto cleaning; - } - - // ------------------------------------------------------- - // Creating output - // ------------------------------------------------------- - GDALDriverH driver = OpenOutputDriver(OLE2A(gdalOutputFormat)); - if (driver != NULL) - { - dtOutput = OpenOutputFile(driver, OLE2W(outputName), xSize, ySize, dt); - } + if (!dtOutput) + { + ErrorMsg(tkCANT_CREATE_FILE); + goto cleaning; + } - if (!dtOutput) - { - ErrorMsg(tkCANT_CREATE_FILE); - goto cleaning; - } + // setting no data value + GDALRasterBand* bandOutput = dtOutput->GetRasterBand(1); - // setting no data value - GDALRasterBand* bandOutput = dtOutput->GetRasterBand(1); - - - float nodv = static_cast(band->GetNoDataValue()); - bandOutput->SetNoDataValue(static_cast(nodv)); - - // ------------------------------------------------------- - // Processing - // ------------------------------------------------------- - long numColumns = xSize; - long numRows = ySize; - long percent = 0; - data = new float[numColumns]; - long index; - for(long i = 0; i < numRows; i++ ) - { - CallbackHelper::Progress(cBack, i, numRows, "Calculating", _key, percent); - - band->RasterIO(GF_Read, 0, i, numColumns, 1, data, numColumns, 1, GDALDataType::GDT_Float32, 0, 0); + float nodv = static_cast(band->GetNoDataValue()); + bandOutput->SetNoDataValue(static_cast(nodv)); - for(int j = 0; j < numColumns; j++) - { - index = findBreak(breaks, (double)(*(data + j))); - if (index != -1) - { - *(data + j) = (float)breaks[index].newVal; - } - else - { - // leave it untouched - } + // ------------------------------------------------------- + // Processing + // ------------------------------------------------------- + long numColumns = xSize; + long numRows = ySize; + long percent = 0; + data = new float[numColumns]; + long index; - } - if (bandOutput->RasterIO( GF_Write, 0, i, numColumns, 1, data, numColumns, 1, GDT_Float32, 0, 0 ) != CE_None ) - goto cleaning; - } - *retVal = VARIANT_TRUE; + for (long i = 0; i < numRows; i++) + { + CallbackHelper::Progress(cBack, i, numRows, "Calculating", _key, percent); + + band->RasterIO(GF_Read, 0, i, numColumns, 1, data, numColumns, 1, GDALDataType::GDT_Float32, 0, 0); + + for (int j = 0; j < numColumns; j++) + { + index = findBreak(breaks, (double)(*(data + j))); + if (index != -1) + { + *(data + j) = (float)breaks[index].newVal; + } + else + { + // leave it untouched + } + + } + if (bandOutput->RasterIO(GF_Write, 0, i, numColumns, 1, data, numColumns, 1, GDT_Float32, 0, 0) != CE_None) + goto cleaning; + } + *retVal = VARIANT_TRUE; cleaning: - if (data) - delete[] data; - if (dt) - GdalHelper::CloseDataset(dt); - if (dtOutput) - GdalHelper::CloseDataset(dtOutput); - - return S_OK; + if (data) + delete[] data; + if (dt) + GdalHelper::CloseDataset(dt); + if (dtOutput) + GdalHelper::CloseDataset(dtOutput); + + return S_OK; } // ************************************************* @@ -5672,86 +5750,238 @@ STDMETHODIMP CUtils::ReclassifyRaster(BSTR Filename, int bandIndex, BSTR outputN // ************************************************* STDMETHODIMP CUtils::IsTiffGrid(BSTR Filename, VARIANT_BOOL* retVal) { - AFX_MANAGE_STATE(AfxGetStaticModuleState()); - *retVal = VARIANT_FALSE; + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + *retVal = VARIANT_FALSE; - try - { - USES_CONVERSION; - const char* name = OLE2CA(Filename); - TIFF *tiff = XTIFFOpen(name, "r"); // TIFF-level descriptor - if (tiff) - { - int w = 0, h = 0; + try + { + USES_CONVERSION; + const char* name = OLE2CA(Filename); + TIFF *tiff = XTIFFOpen(name, "r"); // TIFF-level descriptor + if (tiff) + { + int w = 0, h = 0; + + tdir_t d = 0; + TIFFSetDirectory(tiff, d); + + uint32 SamplesPerPixel = 0; + + TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &w); + TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &h); + TIFFGetField(tiff, TIFFTAG_SAMPLESPERPIXEL, &SamplesPerPixel); + + uint16 photo = 0; + // If it's a color-mapped palette, consider it an image -- + // it's probably an image (USGS DLG or USGS Quad Map most commonly) + TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photo); + + XTIFFClose(tiff); + + if (photo == PHOTOMETRIC_PALETTE) {} + else if (SamplesPerPixel == 1) + { + *retVal = VARIANT_TRUE; + } + } + } + catch (...) + { + CallbackHelper::ErrorMsg("Exception in Utils.IsTiffGrid"); + } + return S_OK; +} + +// ************************************************* +// EPSGUnitConversion() +// ************************************************* +STDMETHODIMP CUtils::EPSGUnitConversion(int EPSGUnitCode, tkUnitsOfMeasure* retVal) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); - tdir_t d = 0; - TIFFSetDirectory(tiff, d); + // convert common EPSG codes to internal enumeration + switch (EPSGUnitCode) + { + case 9001: + // International Meter + *retVal = umMeters; + break; + case 9002: + case 9003: + case 9004: + // International Foot, US Survey Foot, and American Foot + *retVal = umFeets; + break; + case 9035: + // US Survey Mile + *retVal = umMiles; + break; + case 9036: + // Kilometer + *retVal = umKilometers; + break; + case 9102: + // Degree + *retVal = umDecimalDegrees; + break; + default: + // not sure the best default, using Degrees + ErrorMessage(tkINVALID_PARAMETER_VALUE); + *retVal = umDecimalDegrees; + } + + return S_OK; +} - uint32 SamplesPerPixel = 0; +// ************************************************* +// GetAngle() +// ************************************************* +STDMETHODIMP CUtils::GetAngle(IPoint* firstPoint, IPoint* secondPoint, double* retVal) +{ + double x1, y1, x2, y2, dx, dy; + // get delta X and Y + firstPoint->get_X(&x1); + firstPoint->get_Y(&y1); + secondPoint->get_X(&x2); + secondPoint->get_Y(&y2); + dx = x2 - x1; + dy = y2 - y1; + // return angle in degrees + *retVal = GeometryHelper::GetPointAngleDeg(dx, dy); + // + return S_OK; +} - TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &w); - TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &h); - TIFFGetField(tiff, TIFFTAG_SAMPLESPERPIXEL, &SamplesPerPixel); +inline bool CUtils::almostEqual(double d1, double d2, double tolerance) +{ + return (abs(d2 - d2) < tolerance); +} - uint16 photo = 0; - // If it's a color-mapped palette, consider it an image -- - // it's probably an image (USGS DLG or USGS Quad Map most commonly) - TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photo); +// ************************************************* +// LineInterpolatePoint() +// ************************************************* +STDMETHODIMP CUtils::LineInterpolatePoint(IShape* sourceLine, IPoint* startPoint, double distance, VARIANT_BOOL normalized, IPoint **retVal) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()) - XTIFFClose(tiff); + // initialize return point to null + *retVal = nullptr; - if (photo == PHOTOMETRIC_PALETTE){} - else if (SamplesPerPixel == 1) - { - *retVal = VARIANT_TRUE; - } - } + // basic validation + if (sourceLine == NULL || startPoint == NULL) + { + this->ErrorMessage(tkUNEXPECTED_NULL_PARAMETER); + return S_OK; } - catch (...) + + // source geometry has to be a polyline + ShpfileType shapeType; + sourceLine->get_ShapeType2D(&shapeType); + if (shapeType != ShpfileType::SHP_POLYLINE) { - CallbackHelper::ErrorMsg("Exception in Utils.IsTiffGrid"); + ErrorMessage(tkINCOMPATIBLE_SHAPE_TYPE, CString("Incoming Shape must be a Polyline")); + return S_OK; } - return S_OK; -} -// ************************************************* -// EPSGUnitConversion() -// ************************************************* -STDMETHODIMP CUtils::EPSGUnitConversion(int EPSGUnitCode, tkUnitsOfMeasure* retVal) -{ - AFX_MANAGE_STATE(AfxGetStaticModuleState()); + // line properties + double lineLength; + sourceLine->get_Length(&lineLength); + long numPoints; + sourceLine->get_NumPoints(&numPoints); + // first and last point + IPoint* firstPoint; + IPoint* lastPoint; + sourceLine->get_Point(0, &firstPoint); + sourceLine->get_Point(numPoints-1, &lastPoint); + + // point properties + long index; + double x, y; + startPoint->get_X(&x); + startPoint->get_Y(&y); + + // wrap startPoint in Shape + IShape* shapePoint = nullptr; + ComHelper::CreateShape(&shapePoint); + if (shapePoint) + { + shapePoint->put_ShapeType(ShpfileType::SHP_POINT); + shapePoint->AddPoint(x, y, &index); + } + + //// is startPoint on the line? + /////////////////////////////// + //// first, project point onto the line + //OGRGeometry* ogrLine = OgrConverter::ShapeToGeometry(sourceLine); + //GEOSGeometry* geosLine = GeosHelper::ExportToGeos(ogrLine); + //OGRGeometry* ogrPoint = OgrConverter::ShapeToGeometry(shapePoint); + //GEOSGeometry* geosPoint = GeosHelper::ExportToGeos(ogrPoint); + //double startPosition = GeosHelper::Project(geosLine, geosPoint); + //// now interpolate from start position (convert back to shape) + //geosPoint = GeosHelper::Interpolate(geosLine, startPosition); + //ogrPoint = GeosHelper::CreateFromGEOS(geosPoint); + //shapePoint = OgrConverter::GeometryToShape(ogrPoint, false); + //// if startPoint and interpolated Point are (roughly) the same, then startPoint is on the line + + // I considered validating that the incoming point was indeed on the line, + // but that may not be necessary. We can leave it up to the caller, and + // allow for a point that is 'near' the line, and we use the 'projected' + // distance along the line as the start point. + + // out philosophy will be to use the initial distance along the line as + // out startint point, add the specified distance to the starting point, + // then interpolate the total distance from the starting of the line. - // convert common EPSG codes to internal enumeration - switch (EPSGUnitCode) + + // project startPoint onto the line (first converting to GEOS geometries) + GEOSGeom geosLine = GeosConverter::ShapeToGeom(sourceLine); + GEOSGeom geosPoint = GeosConverter::ShapeToGeom(shapePoint); + shapePoint->Release(); + // projected position along line + double startPosition = GeosHelper::Project(geosLine, geosPoint); + GeosHelper::DestroyGeometry(geosPoint); + // now add user-specified distance + startPosition += distance; + // make sure start point is within the line + if (startPosition <= 0.0) + { + // return start-of-line? + firstPoint->get_X(&x); + firstPoint->get_Y(&y); + ComHelper::CreatePoint(retVal); + (*retVal)->Set(x, y); + } + else if (startPosition >= lineLength) + { + // return end-of-line? + lastPoint->get_X(&x); + lastPoint->get_Y(&y); + ComHelper::CreatePoint(retVal); + (*retVal)->Set(x, y); + } + else { - case 9001: - // International Meter - *retVal = umMeters; - break; - case 9002: - case 9003: - case 9004: - // International Foot, US Survey Foot, and American Foot - *retVal = umFeets; - break; - case 9035: - // US Survey Mile - *retVal = umMiles; - break; - case 9036: - // Kilometer - *retVal = umKilometers; - break; - case 9102: - // Degree - *retVal = umDecimalDegrees; - break; - default: - // not sure the best default, using Degrees - ErrorMessage(tkINVALID_PARAMETER_VALUE); - *retVal = umDecimalDegrees; + // else interpolate total distance along line + geosPoint = GeosHelper::Interpolate(geosLine, startPosition); + GeosHelper::DestroyGeometry(geosLine); + if (geosPoint) + { + // we have a Point, convert to IPoint + vector shapes; + GeosConverter::GeomToShapes(geosPoint, &shapes, false); + if (shapes.size() > 0) + { + VARIANT_BOOL bVal; + shapes[0]->get_XY(0, &x, &y, &bVal); + shapes[0]->Release(); + if (bVal == VARIANT_TRUE) + { + ComHelper::CreatePoint(retVal); + (*retVal)->Set(x, y); + } + } + } } return S_OK; } - diff --git a/src/COM classes/Utils.h b/src/COM classes/Utils.h index d187c43a..b2691caf 100644 --- a/src/COM classes/Utils.h +++ b/src/COM classes/Utils.h @@ -190,6 +190,8 @@ class ATL_NO_VTABLE CUtils : STDMETHOD(GetWGS84ProjectionName)(tkWgs84Projection projectionID, BSTR* retVal); STDMETHOD(GetProjectionNameByID)(int SRID, BSTR* retVal); STDMETHOD(GetProjectionList)(tkProjectionSet projectionSets, VARIANT* list, VARIANT_BOOL* retVal); + STDMETHOD(GetAngle)(IPoint* firstPoint, IPoint* secondPoint, double* retVal); + STDMETHOD(LineInterpolatePoint)(IShape* sourceLine, IPoint* startPoint, double distance, VARIANT_BOOL normalized, IPoint **retVal); private: struct RasterPoint @@ -317,6 +319,8 @@ class ATL_NO_VTABLE CUtils : CString customErrorMessage(); bool LoadProjectionStrings(); + inline bool almostEqual(double d1, double d2, double tolerance = FLT_EPSILON); + public: HRESULT TileProjectionToGeoProjectionCore(tkTileProjection projection, VARIANT_BOOL useCache, IGeoProjection** retVal); diff --git a/src/COM classes/Utils_GridToImage.cpp b/src/COM classes/Utils_GridToImage.cpp index 2c09db7c..5945c584 100644 --- a/src/COM classes/Utils_GridToImage.cpp +++ b/src/COM classes/Utils_GridToImage.cpp @@ -839,7 +839,7 @@ void CUtils::WriteWorldFile(CStringW worldFile, CStringW imageFile, double dx, d fprintf(fout,"%.14f\n",yupLeft); fprintf(fout,"%s\n","[tkImageCom]"); - fprintf(fout,"%s %s\n","ImageFile",imageFile); + fprintf(fout,"%s %s\n","ImageFile",(LPCSTR)CString(imageFile)); fflush(fout); fclose(fout); } diff --git a/src/ComHelpers/SelectionHelper.cpp b/src/ComHelpers/SelectionHelper.cpp index 1f584646..becec6a8 100644 --- a/src/ComHelpers/SelectionHelper.cpp +++ b/src/ComHelpers/SelectionHelper.cpp @@ -10,177 +10,177 @@ // PolylineIntersection // ***************************************************** bool SelectionHelper::PolylineIntersection(std::vector& xPts, std::vector& yPts, std::vector& parts, - double& b_minX, double& b_maxX, double& b_minY, double& b_maxY, double& Tolerance) + double& b_minX, double& b_maxX, double& b_minY, double& b_maxY, double& Tolerance) { - double cy = (b_minY + b_maxY) * .5; - double cx = (b_minX + b_maxX) * .5; - - int beg_part = 0; - int end_part = 0; - - size_t numpoints = xPts.size(); - long numparts = parts.size(); - - for (long i = 0; i < numparts; i++) - { - beg_part = parts[i]; - if (beg_part < 0) beg_part = 0; - end_part = (int)(parts.size() - 1) > i ? parts[i + 1] : numpoints; - - //for(size_t j = 0; j < numpoints - 1; j++) - for (long j = beg_part; j < end_part - 1; j++) - { - double p1x = xPts[j]; - double p1y = yPts[j]; - double p2x = xPts[j + 1]; - double p2y = yPts[j + 1]; - - //Test for inclusion p1 - if (p1y <= b_maxY && p1y >= b_minY && p1x <= b_maxX && p1x >= b_minX) - { - return true; - } - - // test the Y line - if ((p1y > b_maxY && p2y > b_maxY) || (p1y < b_minY && p2y < b_minY)) - { - continue; - } - - // test the X line - if ((p1x > b_maxX && p2x > b_maxX) || (p1x < b_minX && p2x < b_minX)) - { - continue; - } - - double dx = p2x - p1x; - double dy = p2y - p1y; - - // check for vertical lines - if (fabs(dy) <= Tolerance) - { - return true; - } - // check for horizontal lines - if (fabs(dx) <= Tolerance) - { - return true; - } - - // generate the equation of the line - double m = dy / dx; - double b = p1y - m*p1x; - - double pm = -1 * (dx / dy); - double pb = cy - pm*cx; - double mx = (pb - b) / (m - pm); - double my = m*mx + b; - - // test for inclusion mx/my - if (my <= b_maxY && - my >= b_minY && - mx <= b_maxX && - mx >= b_minX) - { - return true; - } - } - } - return false; + double cy = (b_minY + b_maxY) * .5; + double cx = (b_minX + b_maxX) * .5; + + int beg_part = 0; + int end_part = 0; + + size_t numpoints = xPts.size(); + long numparts = parts.size(); + + for (long i = 0; i < numparts; i++) + { + beg_part = parts[i]; + if (beg_part < 0) beg_part = 0; + end_part = (int)(parts.size() - 1) > i ? parts[i + 1] : numpoints; + + //for(size_t j = 0; j < numpoints - 1; j++) + for (long j = beg_part; j < end_part - 1; j++) + { + double p1x = xPts[j]; + double p1y = yPts[j]; + double p2x = xPts[j + 1]; + double p2y = yPts[j + 1]; + + //Test for inclusion p1 + if (p1y <= b_maxY && p1y >= b_minY && p1x <= b_maxX && p1x >= b_minX) + { + return true; + } + + // test the Y line + if ((p1y > b_maxY && p2y > b_maxY) || (p1y < b_minY && p2y < b_minY)) + { + continue; + } + + // test the X line + if ((p1x > b_maxX && p2x > b_maxX) || (p1x < b_minX && p2x < b_minX)) + { + continue; + } + + double dx = p2x - p1x; + double dy = p2y - p1y; + + // check for vertical lines + if (fabs(dy) <= Tolerance) + { + return true; + } + // check for horizontal lines + if (fabs(dx) <= Tolerance) + { + return true; + } + + // generate the equation of the line + double m = dy / dx; + double b = p1y - m*p1x; + + double pm = -1 * (dx / dy); + double pb = cy - pm*cx; + double mx = (pb - b) / (m - pm); + double my = m*mx + b; + + // test for inclusion mx/my + if (my <= b_maxY && + my >= b_minY && + mx <= b_maxX && + mx >= b_minX) + { + return true; + } + } + } + return false; } // ***************************************************** // PolygonIntersection // ***************************************************** bool SelectionHelper::PolygonIntersection(std::vector& xPts, std::vector& yPts, std::vector& parts, - double& b_minX, double& b_maxX, double& b_minY, double& b_maxY, double& Tolerance) + double& b_minX, double& b_maxX, double& b_minY, double& b_maxY, double& Tolerance) { - double cy = (b_minY + b_maxY) * .5; - double cx = (b_minX + b_maxX) * .5; - - int beg_part = 0; - int end_part = 0; - - bool selected = false; - long numparts = parts.size(); - size_t numpoints = xPts.size(); - - for (long j = 0; j < numparts && !selected; j++) - { - beg_part = parts[j]; - if (beg_part < 0) - beg_part = 0; - - if ((int)(parts.size() - 1) > j) - end_part = parts[j + 1]; - else - end_part = numpoints; - - for (long k = beg_part; k < end_part - 1; k++) - { - double p1x = xPts[k]; - double p1y = yPts[k]; - double p2x = xPts[k + 1]; - double p2y = yPts[k + 1]; - - // test for inclusion p1 - if (p1y <= b_maxY && - p1y >= b_minY && - p1x <= b_maxX && - p1x >= b_minX) - { - return true; - } - - // test the Y line - if ((p1y > b_maxY && p2y > b_maxY) || - (p1y < b_minY && p2y < b_minY)) - { - continue; - } - - // test the X line - if ((p1x > b_maxX && p2x > b_maxX) || - (p1x < b_minX && p2x < b_minX)) - { - continue; - } - - double dx = p2x - p1x; - double dy = p2y - p1y; - - // check for vertical lines - if (fabs(dy) <= Tolerance) - { - return true; - } - - // check for horizontal lines - if (fabs(dx) <= Tolerance) - { - return true; - } - - // generate the equation of the line - double m = dy / dx; - double b = p1y - m*p1x; - - double pm = -1 * (dx / dy); - double pb = cy - pm*cx; - double mx = (pb - b) / (m - pm); - double my = m*mx + b; - - // test for inclusion mx/my - if (my <= b_maxY && - my >= b_minY && - mx <= b_maxX && - mx >= b_minX) - { - return true; - } - } - } - return false; + double cy = (b_minY + b_maxY) * .5; + double cx = (b_minX + b_maxX) * .5; + + int beg_part = 0; + int end_part = 0; + + bool selected = false; + long numparts = parts.size(); + size_t numpoints = xPts.size(); + + for (long j = 0; j < numparts && !selected; j++) + { + beg_part = parts[j]; + if (beg_part < 0) + beg_part = 0; + + if ((int)(parts.size() - 1) > j) + end_part = parts[j + 1]; + else + end_part = numpoints; + + for (long k = beg_part; k < end_part - 1; k++) + { + double p1x = xPts[k]; + double p1y = yPts[k]; + double p2x = xPts[k + 1]; + double p2y = yPts[k + 1]; + + // test for inclusion p1 + if (p1y <= b_maxY && + p1y >= b_minY && + p1x <= b_maxX && + p1x >= b_minX) + { + return true; + } + + // test the Y line + if ((p1y > b_maxY && p2y > b_maxY) || + (p1y < b_minY && p2y < b_minY)) + { + continue; + } + + // test the X line + if ((p1x > b_maxX && p2x > b_maxX) || + (p1x < b_minX && p2x < b_minX)) + { + continue; + } + + double dx = p2x - p1x; + double dy = p2y - p1y; + + // check for vertical lines + if (fabs(dy) <= Tolerance) + { + return true; + } + + // check for horizontal lines + if (fabs(dx) <= Tolerance) + { + return true; + } + + // generate the equation of the line + double m = dy / dx; + double b = p1y - m*p1x; + + double pm = -1 * (dx / dy); + double pb = cy - pm*cx; + double mx = (pb - b) / (m - pm); + double my = m*mx + b; + + // test for inclusion mx/my + if (my <= b_maxY && + my >= b_minY && + mx <= b_maxX && + mx >= b_minX) + { + return true; + } + } + } + return false; } /***********************************************************************/ @@ -188,10 +188,10 @@ bool SelectionHelper::PolygonIntersection(std::vector& xPts, std::vector /***********************************************************************/ bool SelectionHelper::SelectWithShapeBounds(IShapefile* sf, IShape* shp, vector& indices) { - if (!sf || !shp) return false; - CComPtr box = NULL; - shp->get_Extents(&box); - return SelectShapes(sf, Extent(box), SelectMode::INTERSECTION, indices); + if (!sf || !shp) return false; + CComPtr box = NULL; + shp->get_Extents(&box); + return SelectShapes(sf, Extent(box), SelectMode::INTERSECTION, indices); } /***********************************************************************/ @@ -199,7 +199,7 @@ bool SelectionHelper::SelectWithShapeBounds(IShapefile* sf, IShape* shp, vector< /***********************************************************************/ bool SelectionHelper::SelectShapes(IShapefile* sf, Extent& extents, SelectMode SelectMode, std::vector& selectResult) { - return ((CShapefile*)sf)->SelectShapesCore((extents), 0.0, SelectMode, selectResult, true); + return ((CShapefile*)sf)->SelectShapesCore((extents), 0.0, SelectMode, selectResult, true); } /***********************************************************************/ @@ -207,52 +207,64 @@ bool SelectionHelper::SelectShapes(IShapefile* sf, Extent& extents, SelectMode S /***********************************************************************/ bool SelectionHelper::SelectSingleShape(IShapefile* sf, Extent& box, long& shapeIndex) { - return SelectSingleShape(sf, box, SelectMode::INTERSECTION, shapeIndex); + return SelectSingleShape(sf, box, SelectMode::INTERSECTION, shapeIndex); } bool SelectionHelper::SelectSingleShape(IShapefile* sf, Extent& box, SelectMode mode, long& shapeIndex) { - vector results; - if (SelectShapes(sf, box, mode, results)) - { - for (int i = results.size() - 1; i >= 0; i--) - { - VARIANT_BOOL visible; - sf->get_ShapeRendered(results[i], &visible); - if (visible) - { - shapeIndex = results[i]; - return true; - } - } - } - return false; + vector results; + if (SelectShapes(sf, box, mode, results)) + { + for (int i = results.size() - 1; i >= 0; i--) + { + VARIANT_BOOL visible; + sf->get_ShapeRendered(results[i], &visible); + if (visible) + { + shapeIndex = results[i]; + return true; + } + } + } + return false; } /***********************************************************************/ /* SelectByPoint() /***********************************************************************/ -bool SelectionHelper::SelectByPoint(IShapefile* sf, Extent& box, bool clearPrevious) +bool SelectionHelper::SelectByPoint(IShapefile* sf, Extent& box, bool clearPrevious, bool singleShape) { - if (!sf) return false; - - bool result = false; - long numSelected = ShapefileHelper::GetNumSelected(sf); - - if (clearPrevious) { - sf->SelectNone(); - if (numSelected > 0) - result = true; - } - - long shapeIndex; - if (SelectSingleShape(sf, box, shapeIndex)) - { - // change the state of the top most shape - bool selected = ShapefileHelper::ShapeSelected(sf, shapeIndex); - sf->put_ShapeSelected(shapeIndex, (!selected) ? VARIANT_TRUE : VARIANT_FALSE); - result = true; - } - return result; + if (!sf) return false; + + bool result = false; + + if (singleShape) + { + // default behavior for single-layer selection (via ChooseLayer event) + long numSelected = ShapefileHelper::GetNumSelected(sf); + + if (clearPrevious) { + sf->SelectNone(); + if (numSelected > 0) + result = true; + } + + long shapeIndex; + // only a single (topmost) shape is selected + if (SelectSingleShape(sf, box, shapeIndex)) + { + // change the state of the top most shape + bool selected = ShapefileHelper::ShapeSelected(sf, shapeIndex); + sf->put_ShapeSelected(shapeIndex, (!selected) ? VARIANT_TRUE : VARIANT_FALSE); + result = true; + } + } + else + { + // default behavior for multi-layer selection (via 'Selectable' layers) + // allow for multiple overlapping shapes in each layer to be selected + result = SelectByRectangle(sf, box); + } + return result; } /***********************************************************************/ @@ -260,20 +272,20 @@ bool SelectionHelper::SelectByPoint(IShapefile* sf, Extent& box, bool clearPrevi /***********************************************************************/ bool SelectionHelper::SelectByRectangle(IShapefile* sf, Extent& box) { - if (!sf) return false; - - sf->SelectNone(); // TODO: make a property to control it - - vector results; - if (SelectShapes(sf, box, SelectMode::INTERSECTION, results)) - { - for (size_t i = 0; i < results.size(); i++) - { - sf->put_ShapeSelected(results[i], VARIANT_TRUE); - } - return true; - } - return false; + if (!sf) return false; + + sf->SelectNone(); // TODO: make a property to control it + + vector results; + if (SelectShapes(sf, box, SelectMode::INTERSECTION, results)) + { + for (size_t i = 0; i < results.size(); i++) + { + sf->put_ShapeSelected(results[i], VARIANT_TRUE); + } + return true; + } + return false; } /***********************************************************************/ @@ -281,48 +293,48 @@ bool SelectionHelper::SelectByRectangle(IShapefile* sf, Extent& box) /***********************************************************************/ int SelectionHelper::SelectByPolygon(IShapefile* sf, IShape* poly, int& errorCode) { - errorCode = tkNO_ERROR; - - if (!sf || !poly) { - errorCode = tkUNEXPECTED_NULL_PARAMETER; - return 0; - } - - if (ShapeHelper::GetShapeType2D(poly) != SHP_POLYGON) { - errorCode = tkUNEXPECTED_SHAPE_TYPE; - return 0; - } - - vector indices; - if (!SelectWithShapeBounds(sf, poly, indices)) - return 0; - - GEOSGeometry* g = GeosConverter::ShapeToGeom(poly); - if (!g) { - errorCode = tkCANT_CONVERT_SHAPE_GEOS; - return 0; - } - - sf->SelectNone(); // TODO: make a property to control it - - for (size_t i = 0; i < indices.size(); i++) - { - CComPtr shp = NULL; - sf->get_Shape(indices[i], &shp); - if (shp) - { - GEOSGeometry* g2 = GeosConverter::ShapeToGeom(shp); - if (g2) - { - if (GeosHelper::Intersects(g, g2)) { - sf->put_ShapeSelected(indices[i], VARIANT_TRUE); - } - GeosHelper::DestroyGeometry(g2); - } - } - } - - GeosHelper::DestroyGeometry(g); - return ShapefileHelper::GetNumSelected(sf); + errorCode = tkNO_ERROR; + + if (!sf || !poly) { + errorCode = tkUNEXPECTED_NULL_PARAMETER; + return 0; + } + + if (ShapeHelper::GetShapeType2D(poly) != SHP_POLYGON) { + errorCode = tkUNEXPECTED_SHAPE_TYPE; + return 0; + } + + vector indices; + if (!SelectWithShapeBounds(sf, poly, indices)) + return 0; + + GEOSGeometry* g = GeosConverter::ShapeToGeom(poly); + if (!g) { + errorCode = tkCANT_CONVERT_SHAPE_GEOS; + return 0; + } + + sf->SelectNone(); // TODO: make a property to control it + + for (size_t i = 0; i < indices.size(); i++) + { + CComPtr shp = NULL; + sf->get_Shape(indices[i], &shp); + if (shp) + { + GEOSGeometry* g2 = GeosConverter::ShapeToGeom(shp); + if (g2) + { + if (GeosHelper::Intersects(g, g2)) { + sf->put_ShapeSelected(indices[i], VARIANT_TRUE); + } + GeosHelper::DestroyGeometry(g2); + } + } + } + + GeosHelper::DestroyGeometry(g); + return ShapefileHelper::GetNumSelected(sf); } diff --git a/src/ComHelpers/SelectionHelper.h b/src/ComHelpers/SelectionHelper.h index c3d521c7..db42c42a 100644 --- a/src/ComHelpers/SelectionHelper.h +++ b/src/ComHelpers/SelectionHelper.h @@ -7,7 +7,7 @@ class SelectionHelper double& b_minX, double& b_maxX, double& b_minY, double& b_maxY, double& Tolerance); static bool SelectWithShapeBounds(IShapefile* sf, IShape* shp, vector& indices); static int SelectByPolygon(IShapefile* sf, IShape* polygon, int& errorCode); - static bool SelectByPoint(IShapefile* sf, Extent& box, bool clearPrevious); + static bool SelectByPoint(IShapefile* sf, Extent& box, bool clearPrevious, bool singleShape = true); static bool SelectByRectangle(IShapefile* sf, Extent& box); static bool SelectSingleShape(IShapefile* sf, Extent& box, long& shapeIndex); static bool SelectSingleShape(IShapefile* sf, Extent& box, SelectMode mode, long& shapeIndex); diff --git a/src/ComHelpers/ShapefileHelper.cpp b/src/ComHelpers/ShapefileHelper.cpp index cf511c36..cf59307c 100644 --- a/src/ComHelpers/ShapefileHelper.cpp +++ b/src/ComHelpers/ShapefileHelper.cpp @@ -1,7 +1,28 @@ -#include "stdafx.h" +//******************************************************************************************************** +//File name: ShapefileHelper.cpp +//Description: Implementation of the CShapefile (see other cpp files as well) +//******************************************************************************************************** +//The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); +//you may not use this file except in compliance with the License. You may obtain a copy of the License at +//http://www.mozilla.org/MPL/ +//Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF +//ANY KIND, either express or implied. See the License for the specific language governing rights and +//limitations under the License. +// +//The Original Code is MapWindow Open Source. +// +//The Initial Developer of this version of the Original Code is Daniel P. Ames using portions created by +//Utah State University and the Idaho National Engineering and Environmental Lab that were released as +//public domain in March 2004. +// +//Contributor(s): (Open source contributors should list themselves and their modifications here). +// ------------------------------------------------------------------------------------------------------- +// lsu 3-02-2011: split the initial Shapefile.cpp file to make entities of the reasonable size +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper + +#include "StdAfx.h" #include "ShapefileHelper.h" #include "Shapefile.h" -#include "FieldHelper.h" #include "TableClass.h" #include "TableHelper.h" @@ -10,10 +31,10 @@ // ***************************************************** long ShapefileHelper::GetMWShapeIdIndex(IShapefile* sf) { - long index = -1; - CComBSTR bstr("MWShapeID"); - sf->get_FieldIndexByName(bstr, &index); - return index; + long index = -1; + const CComBSTR bstr("MWShapeID"); + sf->get_FieldIndexByName(bstr, &index); + return index; } // ***************************************************** @@ -21,16 +42,16 @@ long ShapefileHelper::GetMWShapeIdIndex(IShapefile* sf) // ***************************************************** void ShapefileHelper::CopyAttributes(IShapefile* sf, long sourceIndex, long targetIndex, long skipFieldIndex) { - long numFields; - sf->get_NumFields(&numFields); - CComVariant var; - VARIANT_BOOL vb; - for (int i = 0; i < numFields; i++) - { - if (i == skipFieldIndex) continue; - sf->get_CellValue(i, sourceIndex, &var); - sf->EditCellValue(i, targetIndex, var, &vb); - } + long numFields; + sf->get_NumFields(&numFields); + CComVariant var; + VARIANT_BOOL vb; + for (int i = 0; i < numFields; i++) + { + if (i == skipFieldIndex) continue; + sf->get_CellValue(i, sourceIndex, &var); + sf->EditCellValue(i, targetIndex, var, &vb); + } } // *********************************************************************** @@ -38,121 +59,128 @@ void ShapefileHelper::CopyAttributes(IShapefile* sf, long sourceIndex, long targ // *********************************************************************** bool ShapefileHelper::CloneNoFields(IShapefile* sf, IShapefile** retVal, bool addShapeId) { - if (!sf) { - *retVal = NULL; - return false; - } - return CloneNoFields(sf, retVal, GetShapeType(sf), addShapeId); + if (!sf) + { + *retVal = nullptr; + return false; + } + return CloneNoFields(sf, retVal, GetShapeType(sf), addShapeId); } // *********************************************************************** // CloneNoFields() // *********************************************************************** -bool ShapefileHelper::CloneNoFields(IShapefile* sfSource, IShapefile** retVal, ShpfileType targetShapeType, bool addShapeId) +bool ShapefileHelper::CloneNoFields(IShapefile* sfSource, IShapefile** retVal, ShpfileType targetShapeType, + bool addShapeId) { - IShapefile* sf = NULL; - CoCreateInstance(CLSID_Shapefile, NULL, CLSCTX_INPROC_SERVER, IID_IShapefile, (void**)&sf); - - VARIANT_BOOL vb; - if (addShapeId) { - sf->CreateNewWithShapeID(m_globalSettings.emptyBstr, targetShapeType, &vb); - } - else { - sf->CreateNew(m_globalSettings.emptyBstr, targetShapeType, &vb); - } - - if (!vb) - { - // Pass error back to source: - *retVal = sf; - return false; - } - - CComPtr gpSource = NULL; - CComPtr gpTarget = NULL; - sfSource->get_GeoProjection(&gpSource); - sf->get_GeoProjection(&gpTarget); - - if (gpSource && gpTarget) { - gpTarget->CopyFrom(gpSource, &vb); - } - - ICallback* callback = NULL; - sfSource->get_GlobalCallback(&callback); - sf->put_GlobalCallback(callback); - - *retVal = sf; - return true; + IShapefile* sf = nullptr; + CoCreateInstance(CLSID_Shapefile, nullptr, CLSCTX_INPROC_SERVER, IID_IShapefile, (void**)&sf); + + VARIANT_BOOL vb; + if (addShapeId) + { + sf->CreateNewWithShapeID(m_globalSettings.emptyBstr, targetShapeType, &vb); + } + else + { + sf->CreateNew(m_globalSettings.emptyBstr, targetShapeType, &vb); + } + + if (!vb) + { + // Pass error back to source: + *retVal = sf; + return false; + } + + CComPtr gpSource = nullptr; + CComPtr gpTarget = nullptr; + sfSource->get_GeoProjection(&gpSource); + sf->get_GeoProjection(&gpTarget); + + if (gpSource && gpTarget) + { + gpTarget->CopyFrom(gpSource, &vb); + } + + ICallback* callback = nullptr; + sfSource->get_GlobalCallback(&callback); + sf->put_GlobalCallback(callback); + + *retVal = sf; + return true; } + // *********************************************************************** // CloneCore() // *********************************************************************** void ShapefileHelper::CloneCore(IShapefile* sfSource, IShapefile** retVal, ShpfileType shpType, bool addShapeId) { - CloneNoFields(sfSource, retVal, shpType, addShapeId); - VARIANT_BOOL vbretval; - - IShapefile* sf = *retVal; - - long numFields; - sfSource->get_NumFields(&numFields); - - for (long i = 0; i < numFields; i++) - { - IField * fld = NULL; - IField * fldNew = NULL; - sfSource->get_Field(i, &fld); - fld->Clone(&fldNew); - fld->Release(); - - sf->EditInsertField(fldNew, &i, NULL, &vbretval); - fldNew->Release(); - - if (!vbretval) - { - sf->Close(&vbretval); - sf->Release(); - sf = NULL; - break; - } - } + CloneNoFields(sfSource, retVal, shpType, addShapeId); + VARIANT_BOOL vbretval; + + IShapefile* sf = *retVal; + + long numFields; + sfSource->get_NumFields(&numFields); + + for (long i = 0; i < numFields; i++) + { + IField* fld = nullptr; + IField* fldNew = nullptr; + sfSource->get_Field(i, &fld); + fld->Clone(&fldNew); + fld->Release(); + + sf->EditInsertField(fldNew, &i, nullptr, &vbretval); + fldNew->Release(); + + if (!vbretval) + { + sf->Close(&vbretval); + sf->Release(); + sf = nullptr; + break; + } + } } // ***************************************************************** // GetSelectedExtents() // ***************************************************************** -bool ShapefileHelper::GetSelectedExtents(IShapefile* sf, double& xMinRef, double& yMinRef, double& xMaxRef, double& yMaxRef) +bool ShapefileHelper::GetSelectedExtents(IShapefile* sf, double& xMinRef, double& yMinRef, double& xMaxRef, + double& yMaxRef) { - double xMin, xMax, yMin, yMax; - bool found = false; - long numShapes; - sf->get_NumShapes(&numShapes); - VARIANT_BOOL vb; - - for (long i = 0; i < numShapes; i++) - { - sf->get_ShapeSelected(i, &vb); - if (vb) - { - if (((CShapefile*)sf)->QuickExtentsCore(i, &xMin, &yMin, &xMax, &yMax)) - { - if (!found) - { - xMinRef = xMin, xMaxRef = xMax; - yMinRef = yMin, yMaxRef = yMax; - found = true; - } - else - { - if (xMin < xMinRef) xMinRef = xMin; - if (xMax > xMaxRef) xMaxRef = xMax; - if (yMin < yMinRef) yMinRef = yMin; - if (yMax > yMaxRef) yMaxRef = yMax; - } - } - } - } - return found; + double xMin, xMax, yMin, yMax; + bool found = false; + long numShapes; + sf->get_NumShapes(&numShapes); + VARIANT_BOOL vb; + + for (long i = 0; i < numShapes; i++) + { + sf->get_ShapeSelected(i, &vb); + if (vb) + { + if (((CShapefile*)sf)->QuickExtentsCore(i, &xMin, &yMin, &xMax, &yMax)) + { + if (!found) + { + xMinRef = xMin, xMaxRef = xMax; + yMinRef = yMin, yMaxRef = yMax; + found = true; + } + else + { + if (xMin < xMinRef) xMinRef = xMin; + if (xMax > xMaxRef) xMaxRef = xMax; + if (yMin < yMinRef) yMinRef = yMin; + if (yMax > yMaxRef) yMaxRef = yMax; + } + } + } + } + return found; } // **************************************************************** @@ -160,29 +188,29 @@ bool ShapefileHelper::GetSelectedExtents(IShapefile* sf, double& xMinRef, double // **************************************************************** void ShapefileHelper::CopyFields(IShapefile* source, IShapefile* target) { - if (!source || !target) - return; + if (!source || !target) + return; - LONG numFields, position; - VARIANT_BOOL vbretval; - source->get_NumFields(&numFields); + LONG numFields, position; + VARIANT_BOOL vbretval; + source->get_NumFields(&numFields); - for (long i = 0; i < numFields; i++) - { - IField * field = NULL; - IField * fieldNew = NULL; - source->get_Field(i, &field); - field->Clone(&fieldNew); + for (long i = 0; i < numFields; i++) + { + IField* field = nullptr; + IField* fieldNew = nullptr; + source->get_Field(i, &field); + field->Clone(&fieldNew); - target->get_NumFields(&position); - target->EditInsertField(fieldNew, &position, NULL, &vbretval); + target->get_NumFields(&position); + target->EditInsertField(fieldNew, &position, nullptr, &vbretval); - field->Release(); - fieldNew->Release(); + field->Release(); + fieldNew->Release(); - if (!vbretval) - return; - } + if (!vbretval) + return; + } } // ***************************************************************** @@ -190,19 +218,20 @@ void ShapefileHelper::CopyFields(IShapefile* source, IShapefile* target) // ***************************************************************** vector* ShapefileHelper::GetSelectedIndices(IShapefile* sf) { - if (!sf) return NULL; - VARIANT_BOOL vb; - long numShapes; - sf->get_NumShapes(&numShapes); - vector* result = new vector(); - for (long i = 0; i < numShapes; i++) - { - sf->get_ShapeSelected(i, &vb); - if (vb) { - result->push_back(i); - } - } - return result; + if (!sf) return nullptr; + VARIANT_BOOL vb; + long numShapes; + sf->get_NumShapes(&numShapes); + auto* result = new vector(); + for (long i = 0; i < numShapes; i++) + { + sf->get_ShapeSelected(i, &vb); + if (vb) + { + result->push_back(i); + } + } + return result; } // ***************************************************************** @@ -210,10 +239,10 @@ vector* ShapefileHelper::GetSelectedIndices(IShapefile* sf) // ***************************************************************** long ShapefileHelper::GetNumShapes(IShapefile* sf) { - if (!sf) return 0; - long numShapes; - sf->get_NumShapes(&numShapes); - return numShapes; + if (!sf) return 0; + long numShapes; + sf->get_NumShapes(&numShapes); + return numShapes; } // ***************************************************************** @@ -221,10 +250,10 @@ long ShapefileHelper::GetNumShapes(IShapefile* sf) // ***************************************************************** long ShapefileHelper::GetNumFields(IShapefile* sf) { - if (!sf) return 0; - long numFields; - sf->get_NumFields(&numFields); - return numFields; + if (!sf) return 0; + long numFields; + sf->get_NumFields(&numFields); + return numFields; } // ***************************************************************** @@ -232,10 +261,10 @@ long ShapefileHelper::GetNumFields(IShapefile* sf) // ***************************************************************** long ShapefileHelper::GetNumSelected(IShapefile* sf) { - if (!sf) return 0; - long numSelected; - sf->get_NumSelected(&numSelected); - return numSelected; + if (!sf) return 0; + long numSelected; + sf->get_NumSelected(&numSelected); + return numSelected; } // ***************************************************************** @@ -243,10 +272,10 @@ long ShapefileHelper::GetNumSelected(IShapefile* sf) // ***************************************************************** bool ShapefileHelper::ShapeSelected(IShapefile* sf, int shapeIndex) { - if (!sf) return false; - VARIANT_BOOL vb; - sf->get_ShapeSelected(shapeIndex, &vb); - return vb ? true: false; + if (!sf) return false; + VARIANT_BOOL vb; + sf->get_ShapeSelected(shapeIndex, &vb); + return vb != 0; } // ***************************************************************** @@ -254,20 +283,21 @@ bool ShapefileHelper::ShapeSelected(IShapefile* sf, int shapeIndex) // ***************************************************************** void ShapefileHelper::Rotate(IShapefile* sf, double originX, double originY, double angleDegree) { - if (!sf) return; - tkShapefileSourceType sourceType; - sf->get_SourceType(&sourceType); - if (sourceType != sstInMemory) return; - - long numShapes = GetNumShapes(sf); - for (long i = 0; i < numShapes; i++) - { - CComPtr shp = NULL; - sf->get_Shape(i, &shp); - if (shp) { - shp->Rotate(originX, originY, angleDegree); - } - } + if (!sf) return; + tkShapefileSourceType sourceType; + sf->get_SourceType(&sourceType); + if (sourceType != sstInMemory) return; + + const long numShapes = GetNumShapes(sf); + for (long i = 0; i < numShapes; i++) + { + CComPtr shp = nullptr; + sf->get_Shape(i, &shp); + if (shp) + { + shp->Rotate(originX, originY, angleDegree); + } + } } // ***************************************************************** @@ -275,10 +305,10 @@ void ShapefileHelper::Rotate(IShapefile* sf, double originX, double originY, dou // ***************************************************************** ShpfileType ShapefileHelper::GetShapeType2D(IShapefile* sf) { - if (!sf) return SHP_NULLSHAPE; - ShpfileType shpType; - sf->get_ShapefileType2D(&shpType); - return shpType; + if (!sf) return SHP_NULLSHAPE; + ShpfileType shpType; + sf->get_ShapefileType2D(&shpType); + return shpType; } // ***************************************************************** @@ -286,10 +316,10 @@ ShpfileType ShapefileHelper::GetShapeType2D(IShapefile* sf) // ***************************************************************** ShpfileType ShapefileHelper::GetShapeType(IShapefile* sf) { - if (!sf) return SHP_NULLSHAPE; - ShpfileType shpType; - sf->get_ShapefileType(&shpType); - return shpType; + if (!sf) return SHP_NULLSHAPE; + ShpfileType shpType; + sf->get_ShapefileType(&shpType); + return shpType; } @@ -298,60 +328,62 @@ ShpfileType ShapefileHelper::GetShapeType(IShapefile* sf) // ***************************************************************** IShapefile* ShapefileHelper::CloneSelection(IShapefile* sf) { - IShapefile* sfNew = NULL; - sf->Clone(&sfNew); - long numShapes, shapeIndex; - sf->get_NumShapes(&numShapes); - for (long i = 0; i < numShapes; i++) - { - if (!ShapeSelected(sf, i)) continue; - CComPtr shp = NULL; - sf->get_Shape(i, &shp); - if (shp) { - sfNew->EditAddShape(shp, &shapeIndex); - } - } - return sfNew; + IShapefile* sfNew = nullptr; + sf->Clone(&sfNew); + long numShapes, shapeIndex; + sf->get_NumShapes(&numShapes); + for (long i = 0; i < numShapes; i++) + { + if (!ShapeSelected(sf, i)) continue; + CComPtr shp = nullptr; + sf->get_Shape(i, &shp); + if (shp) + { + sfNew->EditAddShape(shp, &shapeIndex); + } + } + return sfNew; } // ***************************************************************** // GetClosestPoint() // ***************************************************************** -bool ShapefileHelper::GetClosestPoint(IShapefile* sf, double x, double y, double maxDistance, std::vector& ids, long* shapeIndex, long* pointIndex, double& dist) +bool ShapefileHelper::GetClosestPoint(IShapefile* sf, double x, double y, double maxDistance, std::vector& ids, + long* shapeIndex, long* pointIndex, double& dist) { - if (!sf) return false; - - VARIANT_BOOL vb; - double minDist = DBL_MAX; - for (size_t i = 0; i < ids.size(); i++) - { - VARIANT_BOOL visible; - sf->get_ShapeVisible(ids[i], &visible); - if (!visible) continue; - - IShape* shp = NULL; - sf->get_Shape(ids[i], &shp); - if (shp) - { - long numPoints; - shp->get_NumPoints(&numPoints); - double xPnt, yPnt; - for (long j = 0; j < numPoints; j++) - { - shp->get_XY(j, &xPnt, &yPnt, &vb); - double dist = sqrt(pow(x - xPnt, 2.0) + pow(y - yPnt, 2.0)); - if (dist < minDist && dist < maxDistance) - { - minDist = dist; - *shapeIndex = ids[i]; - *pointIndex = j; - } - } - shp->Release(); - } - } - dist = minDist; - return minDist < maxDistance; + if (!sf) return false; + + VARIANT_BOOL vb; + double minDist = DBL_MAX; + for (long id : ids) + { + VARIANT_BOOL visible; + sf->get_ShapeVisible(id, &visible); + if (!visible) continue; + + IShape* shp = nullptr; + sf->get_Shape(id, &shp); + if (shp) + { + long numPoints; + shp->get_NumPoints(&numPoints); + double xPnt, yPnt; + for (long j = 0; j < numPoints; j++) + { + shp->get_XY(j, &xPnt, &yPnt, &vb); + const double distance = sqrt(pow(x - xPnt, 2.0) + pow(y - yPnt, 2.0)); + if (distance < minDist && distance < maxDistance) + { + minDist = distance; + *shapeIndex = id; + *pointIndex = j; + } + } + shp->Release(); + } + } + // dist = minDist; + return minDist < maxDistance; } // ******************************************************************** @@ -359,37 +391,38 @@ bool ShapefileHelper::GetClosestPoint(IShapefile* sf, double x, double y, double // ******************************************************************** bool ShapefileHelper::PointInPolygon(IShapefile* sf, long ShapeIndex, double x, double y) { - if (!sf) return false; - VARIANT_BOOL InPolygon; - if (ShapeIndex < 0) return FALSE; - sf->PointInShape(ShapeIndex, x, y, &InPolygon); - return InPolygon ? true : false; + if (!sf) return false; + VARIANT_BOOL InPolygon; + if (ShapeIndex < 0) return FALSE; + sf->PointInShape(ShapeIndex, x, y, &InPolygon); + return InPolygon != 0; } -/***********************************************************************/ -/* BoundsWithinPolygon() -/***********************************************************************/ -bool ShapefileHelper::BoundsWithinPolygon(IShapefile* sf, int shapeIndex, double b_minX, double b_minY, double b_maxX, double b_maxY) +// *********************************************************************** +// BoundsWithinPolygon() +// ********************************************************************** +bool ShapefileHelper::BoundsWithinPolygon(IShapefile* sf, int shapeIndex, double b_minX, double b_minY, double b_maxX, + double b_maxY) { - if (!sf) return false; - - if (ShapefileHelper::PointInPolygon(sf, shapeIndex, b_minX, b_minY)) - { - return true; - } - else if (ShapefileHelper::PointInPolygon(sf, shapeIndex, b_maxX, b_maxY)) - { - return true; - } - else if (ShapefileHelper::PointInPolygon(sf, shapeIndex, b_minX, b_maxY)) - { - return true; - } - else if (ShapefileHelper::PointInPolygon(sf, shapeIndex, b_maxX, b_minY)) - { - return true; - } - return false; + if (!sf) return false; + + if (PointInPolygon(sf, shapeIndex, b_minX, b_minY)) + { + return true; + } + if (PointInPolygon(sf, shapeIndex, b_maxX, b_maxY)) + { + return true; + } + if (PointInPolygon(sf, shapeIndex, b_minX, b_maxY)) + { + return true; + } + if (PointInPolygon(sf, shapeIndex, b_maxX, b_minY)) + { + return true; + } + return false; } // ******************************************************************** @@ -397,9 +430,9 @@ bool ShapefileHelper::BoundsWithinPolygon(IShapefile* sf, int shapeIndex, double // ******************************************************************** bool ShapefileHelper::ShapeTypeIsM(IShapefile* sf) { - if (!sf) return false; - ShpfileType shpType = GetShapeType(sf); - return ShapeUtility::IsM(shpType); + if (!sf) return false; + const ShpfileType shpType = GetShapeType(sf); + return ShapeUtility::IsM(shpType); } // ******************************************************************** @@ -407,17 +440,17 @@ bool ShapefileHelper::ShapeTypeIsM(IShapefile* sf) // ******************************************************************** CStringW ShapefileHelper::GetSymbologyFilename(IShapefile* sf) { - if (!sf) return L""; - CComBSTR bstr; - sf->get_Filename(&bstr); - CStringW name = OLE2W(bstr); - if (name.GetLength() > 0) - { - name += L".mwsymb"; - if (Utility::FileExistsUnicode(name)) - return name; - } - return L""; + if (!sf) return L""; + CComBSTR bstr; + sf->get_Filename(&bstr); + CStringW name = OLE2W(bstr); + if (name.GetLength() > 0) + { + name += L".mwsymb"; + if (Utility::FileExistsUnicode(name)) + return name; + } + return L""; } // ******************************************************************** @@ -425,7 +458,7 @@ CStringW ShapefileHelper::GetSymbologyFilename(IShapefile* sf) // ******************************************************************** CShapefile* ShapefileHelper::Cast(CComPtr& sf) { - return (CShapefile*)&(*sf); + return (CShapefile*)&*sf; } // ******************************************************************** @@ -433,10 +466,10 @@ CShapefile* ShapefileHelper::Cast(CComPtr& sf) // ******************************************************************** bool ShapefileHelper::InteractiveEditing(IShapefile* sf) { - if (!sf) return false; - VARIANT_BOOL editing; - sf->get_InteractiveEditing(&editing); - return editing ? true : false; + if (!sf) return false; + VARIANT_BOOL editing; + sf->get_InteractiveEditing(&editing); + return editing != 0; } // ******************************************************************** @@ -444,10 +477,10 @@ bool ShapefileHelper::InteractiveEditing(IShapefile* sf) // ******************************************************************** bool ShapefileHelper::IsVolatile(IShapefile* sf) { - if (!sf) return false; - VARIANT_BOOL isVolatile; - sf->get_Volatile(&isVolatile); - return isVolatile ? true : false; + if (!sf) return false; + VARIANT_BOOL isVolatile; + sf->get_Volatile(&isVolatile); + return isVolatile != 0; } // ******************************************************************** @@ -455,20 +488,20 @@ bool ShapefileHelper::IsVolatile(IShapefile* sf) // ******************************************************************** void ShapefileHelper::InsertMwShapeIdField(IShapefile* sf) { - if (!sf) return; + if (!sf) return; - CComPtr shapeIDField = NULL; - ComHelper::CreateInstance(idField, (IDispatch**)&shapeIDField); + CComPtr shapeIDField = nullptr; + ComHelper::CreateInstance(idField, (IDispatch**)&shapeIDField); - CComBSTR bstr("MWShapeID"); - shapeIDField->put_Name(bstr); - shapeIDField->put_Type(INTEGER_FIELD); - shapeIDField->put_Width(10); - shapeIDField->put_Precision(10); + const CComBSTR bstr("MWShapeID"); + shapeIDField->put_Name(bstr); + shapeIDField->put_Type(INTEGER_FIELD); + shapeIDField->put_Width(10); + shapeIDField->put_Precision(10); - long fldIndex = 0; - VARIANT_BOOL retVal; - sf->EditInsertField(shapeIDField, &fldIndex, NULL, &retVal); + long fldIndex = 0; + VARIANT_BOOL retVal; + sf->EditInsertField(shapeIDField, &fldIndex, nullptr, &retVal); } // ******************************************************************** @@ -476,10 +509,10 @@ void ShapefileHelper::InsertMwShapeIdField(IShapefile* sf) // ******************************************************************** tkShapefileSourceType ShapefileHelper::GetSourceType(IShapefile* sf) { - if (!sf) return sstUninitialized; - tkShapefileSourceType type; - sf->get_SourceType(&type); - return type; + if (!sf) return sstUninitialized; + tkShapefileSourceType type; + sf->get_SourceType(&type); + return type; } // ******************************************************************** @@ -487,22 +520,23 @@ tkShapefileSourceType ShapefileHelper::GetSourceType(IShapefile* sf) // ******************************************************************** int ShapefileHelper::GetModifiedCount(IShapefile* sf) { - if (!sf) return 0; - - long numShapes = GetNumShapes(sf); - - int count = 0; - for (long i = 0; i < numShapes; i++) - { - VARIANT_BOOL modified; - sf->get_ShapeModified(i, &modified); - - if (modified){ - count++; - } - } - - return count; + if (!sf) return 0; + + const long numShapes = GetNumShapes(sf); + + int count = 0; + for (long i = 0; i < numShapes; i++) + { + VARIANT_BOOL modified; + sf->get_ShapeModified(i, &modified); + + if (modified) + { + count++; + } + } + + return count; } // ****************************************************************** @@ -510,20 +544,20 @@ int ShapefileHelper::GetModifiedCount(IShapefile* sf) // ****************************************************************** void ShapefileHelper::ClearShapefileModifiedFlag(IShapefile* sf) { - if (!sf) return; + if (!sf) return; - long numShapes = 0; - sf->get_NumShapes(&numShapes); + long numShapes = 0; + sf->get_NumShapes(&numShapes); - CComPtr table = NULL; - sf->get_Table(&table); - CTableClass* tableInternal = TableHelper::Cast(table); + CComPtr table = nullptr; + sf->get_Table(&table); + CTableClass* tableInternal = TableHelper::Cast(table); - for (int i = 0; i < numShapes; i++) - { - sf->put_ShapeModified(i, VARIANT_FALSE); - tableInternal->MarkRowIsClean(i); - } + for (int i = 0; i < numShapes; i++) + { + sf->put_ShapeModified(i, VARIANT_FALSE); + tableInternal->MarkRowIsClean(i); + } } // ****************************************************************** @@ -531,20 +565,20 @@ void ShapefileHelper::ClearShapefileModifiedFlag(IShapefile* sf) // ****************************************************************** bool ShapefileHelper::Delete(CStringW filename) { - vector exts = { L"shp", L"shx", L"dbf", L"prj", L"lbl", L"chart", L"mwd", L"mwx", L"shp.mwsymb", L"mwsr" }; - - for (size_t i = 0; i < exts.size(); i++) - { - CStringW name = Utility::ChangeExtension(filename, exts[i]); - if (Utility::FileExistsW(name) && _wremove(name) != 0) - { - USES_CONVERSION; - CallbackHelper::ErrorMsg(Debug::Format("Failed to remove file: %s", W2A(name))); - return false; - } - } - - return true; + vector exts = {L"shp", L"shx", L"dbf", L"prj", L"lbl", L"chart", L"mwd", L"mwx", L"shp.mwsymb", L"mwsr"}; + + for (const auto& ext : exts) + { + const CStringW name = Utility::ChangeExtension(filename, ext); + if (Utility::FileExistsW(name) && _wremove(name) != 0) + { + USES_CONVERSION; + CallbackHelper::ErrorMsg(Debug::Format("Failed to remove file: %s", W2A(name))); + return false; + } + } + + return true; } // ************************************************** @@ -552,15 +586,15 @@ bool ShapefileHelper::Delete(CStringW filename) // ************************************************** void ShapefileHelper::MarkFieldsAreUnmodified(IShapefile* table) { - long numFields; - table->get_NumFields(&numFields); - - for (long i = 0; i < numFields; i++) - { - CComPtr field = NULL; - table->get_Field(i, &field); - field->put_Modified(VARIANT_FALSE); - } + long numFields; + table->get_NumFields(&numFields); + + for (long i = 0; i < numFields; i++) + { + CComPtr field = nullptr; + table->get_Field(i, &field); + field->put_Modified(VARIANT_FALSE); + } } // ************************************************** @@ -568,19 +602,20 @@ void ShapefileHelper::MarkFieldsAreUnmodified(IShapefile* table) // ************************************************** void ShapefileHelper::MarkShapeRecordIsUnmodified(IShapefile* sf, long shapeIndex) { - if (!sf) return; - - CComPtr table = NULL; - sf->get_Table(&table); - CTableClass* tableInternal = TableHelper::Cast(table); - - long numShapes = ShapefileHelper::GetNumShapes(sf); - if (shapeIndex >= 0 && shapeIndex < numShapes) - { - sf->put_ShapeModified(shapeIndex, VARIANT_FALSE); - tableInternal->MarkRowIsClean(shapeIndex); - } - else { - CallbackHelper::ErrorMsg("Failed to mark shape as clean. Invalid shape index."); - } + if (!sf) return; + + CComPtr table = nullptr; + sf->get_Table(&table); + CTableClass* tableInternal = TableHelper::Cast(table); + + const long numShapes = GetNumShapes(sf); + if (shapeIndex >= 0 && shapeIndex < numShapes) + { + sf->put_ShapeModified(shapeIndex, VARIANT_FALSE); + tableInternal->MarkRowIsClean(shapeIndex); + } + else + { + CallbackHelper::ErrorMsg("Failed to mark shape as clean. Invalid shape index."); + } } diff --git a/src/Control/GlobalSettingsInfo.h b/src/Control/GlobalSettingsInfo.h index 95f1c7e0..f04c1c5a 100644 --- a/src/Control/GlobalSettingsInfo.h +++ b/src/Control/GlobalSettingsInfo.h @@ -1,6 +1,4 @@ #pragma once -#include "resource.h" -#include "errorcodes.h" #include "MapWinGIS_i.h" #include @@ -8,269 +6,269 @@ struct GlobalSettingsInfo { - const double METERS_PER_DEGREE = 110899.999942; - tkProxyAuthentication proxyAuthentication; - bool forceHideLabels; - bool ogrLayerForceUpdateMode; - bool autoChooseRenderingHintForLabels; - long maxReprojectionShapeCount; - ICallback* callback; - double minPolygonArea; - double minAreaToPerimeterRatio; - double clipperGcsMultiplicationFactor; - bool shapefileFastMode; - double invalidShapesBufferDistance; - tkGridProxyMode gridProxyMode; - bool shapefileFastUnion; - CString gdalErrorMessage; - tkCompositingQuality labelsCompositingQuality; - tkSmoothingMode labelsSmoothingMode; - std::map shortUnitStrings; - bool zoomToFirstLayer; - tkCollisionMode labelsCollisionMode; - tkGridProxyFormat gridProxyFormat; - double MaxDirectGridSizeMb; - int maxUniqueValuesCount; - bool randomColorSchemeForGrids; - PredefinedColorScheme defaultColorSchemeForGrids; - tkShapeValidationMode inputValidation; - tkShapeValidationMode outputValidation; - tkGeometryEngine geometryEngine; - bool saveGridColorSchemeToFile; - int xmlFileVersion; - CString xmlFilenameEncoding; - tkTiffCompression tiffCompression; - tkRasterOverviewCreation rasterOverviewCreation; - int minOverviewWidth; - tkGDALResamplingMethod rasterOverviewResampling; - int tilesThreadPoolSize; - bool loadSymbologyOnAddLayer; - int tilesMaxZoomOnProjectionMismatch; - tkInterpolationMode imageUpsamplingMode; - tkInterpolationMode imageDownsamplingMode; - tkInterpolationMode gridUpsamplingMode; // currently not used - tkInterpolationMode gridDownsamplingMode; - tkOgrEncoding ogrEncoding; - int ogrLayerMaxFeatureCount; - bool autoChooseOgrLoadingMode; - bool useSchemesForStyles; - bool saveOgrLabels; - int getOgrMaxLabelCount() { return ogrLayerMaxFeatureCount; } - int ctorCount; - int dtorCount; - bool attachMapCallbackToLayers; - int hotTrackingMaxShapeCount; - OLE_COLOR identifierColor; - BSTR emptyBstr; - BSTR gdalBstr; - bool allowLayersWithoutProjection; - bool allowProjectionMismatch; - bool reprojectLayersOnAdding; - double mouseTolerance; - bool commonCollisionListForCharts; - bool commonCollisionListForLabels; - bool useShortUrlForTiles; - CString floatNumberFormat; - tkPixelOffsetMode pixelOffsetMode; - bool suppressGdalErrors; - bool forceReadOnlyModeForGdalRasters; - CString bingApiKey; - CString hereAppId; - CString hereAppCode; - bool gridFavorGreyScale; - bool gridUseHistogram; - bool overrideLocalCallback; - bool cacheDbfRecords; - bool cacheShapeRenderingData; - bool wmsDiskCaching; - tkCallbackVerbosity callbackVerbosity; - bool ogrShareConnection; - bool ogrListAllGeometryTypes; - bool ogrInterpretYNStringAsBoolean; + const double METERS_PER_DEGREE = 110899.999942; + tkProxyAuthentication proxyAuthentication; + bool forceHideLabels; + bool ogrLayerForceUpdateMode; + bool autoChooseRenderingHintForLabels; + long maxReprojectionShapeCount; + ICallback* callback; + double minPolygonArea; + double minAreaToPerimeterRatio; + double clipperGcsMultiplicationFactor; + bool shapefileFastMode; + double invalidShapesBufferDistance; + tkGridProxyMode gridProxyMode; + bool shapefileFastUnion; + CString gdalErrorMessage; + tkCompositingQuality labelsCompositingQuality; + tkSmoothingMode labelsSmoothingMode; + std::map shortUnitStrings; + bool zoomToFirstLayer; + tkCollisionMode labelsCollisionMode; + tkGridProxyFormat gridProxyFormat; + double MaxDirectGridSizeMb; + int maxUniqueValuesCount; + bool randomColorSchemeForGrids; + PredefinedColorScheme defaultColorSchemeForGrids; + tkShapeValidationMode inputValidation; + tkShapeValidationMode outputValidation; + tkGeometryEngine geometryEngine; + bool saveGridColorSchemeToFile; + int xmlFileVersion; + CString xmlFilenameEncoding; + tkTiffCompression tiffCompression; + tkRasterOverviewCreation rasterOverviewCreation; + int minOverviewWidth; + tkGDALResamplingMethod rasterOverviewResampling; + int tilesThreadPoolSize; + bool loadSymbologyOnAddLayer; + int tilesMaxZoomOnProjectionMismatch; + tkInterpolationMode imageUpsamplingMode; + tkInterpolationMode imageDownsamplingMode; + tkInterpolationMode gridUpsamplingMode; // currently not used + tkInterpolationMode gridDownsamplingMode; + tkOgrEncoding ogrEncoding; + int ogrLayerMaxFeatureCount; + bool autoChooseOgrLoadingMode; + bool useSchemesForStyles; + bool saveOgrLabels; + int getOgrMaxLabelCount() { return ogrLayerMaxFeatureCount; } + int ctorCount; + int dtorCount; + bool attachMapCallbackToLayers; + int hotTrackingMaxShapeCount; + OLE_COLOR identifierColor; + BSTR emptyBstr; + BSTR gdalBstr; + bool allowLayersWithoutProjection; + bool allowProjectionMismatch; + bool reprojectLayersOnAdding; + double mouseTolerance; + bool commonCollisionListForCharts; + bool commonCollisionListForLabels; + bool useShortUrlForTiles; + CString floatNumberFormat; + tkPixelOffsetMode pixelOffsetMode; + bool suppressGdalErrors; + bool forceReadOnlyModeForGdalRasters; + CString bingApiKey; + CString hereAppId; + CString hereAppCode; + bool gridFavorGreyScale; + bool gridUseHistogram; + bool overrideLocalCallback; + bool cacheDbfRecords; + bool cacheShapeRenderingData; + bool wmsDiskCaching; + tkCallbackVerbosity callbackVerbosity; + bool ogrShareConnection; + bool ogrListAllGeometryTypes; + bool ogrInterpretYNStringAsBoolean; - ~GlobalSettingsInfo() - { - SysFreeString(emptyBstr); - SysFreeString(gdalBstr); - if (callback) - callback->Release(); - } + ~GlobalSettingsInfo() + { + SysFreeString(emptyBstr); + SysFreeString(gdalBstr); + if (callback) + callback->Release(); + } - GlobalSettingsInfo::GlobalSettingsInfo() - { - ogrInterpretYNStringAsBoolean = true; - ogrListAllGeometryTypes = true; - ogrShareConnection = false; - callbackVerbosity = cvAll; - wmsDiskCaching = true; - cacheShapeRenderingData = false; - cacheDbfRecords = true; - overrideLocalCallback = true; - proxyAuthentication = asBasic; - hereAppId = ""; - hereAppCode = ""; - bingApiKey = ""; - forceReadOnlyModeForGdalRasters = false; - forceHideLabels = false; - ogrLayerForceUpdateMode = false; - autoChooseRenderingHintForLabels = true; - suppressGdalErrors = false; - pixelOffsetMode = pomDefault; - floatNumberFormat = "%g"; - maxReprojectionShapeCount = 50000; - useShortUrlForTiles = false; - callback = NULL; - mouseTolerance = 20; - allowLayersWithoutProjection = true; - allowProjectionMismatch = true; - reprojectLayersOnAdding = false; - emptyBstr = SysAllocString(L""); - gdalBstr = SysAllocString(L"GDAL"); - identifierColor = RGB(30, 144, 255); - hotTrackingMaxShapeCount = 200; - attachMapCallbackToLayers = true; - ctorCount = 0; - dtorCount = 0; - saveOgrLabels = false; - useSchemesForStyles = false; - autoChooseOgrLoadingMode = true; - ogrLayerMaxFeatureCount = 50000; - ogrEncoding = oseUtf8; - imageUpsamplingMode = imNone; - imageDownsamplingMode = imBilinear; - gridUpsamplingMode = imNone; - gridDownsamplingMode = imBilinear; - rasterOverviewResampling = tkGDALResamplingMethod::grmNearest; - minOverviewWidth = 512; - rasterOverviewCreation = tkRasterOverviewCreation::rocAuto; - tiffCompression = tkTiffCompression::tkmLZW; - labelsCollisionMode = tkCollisionMode::LocalList; - minPolygonArea = 1.0; - minAreaToPerimeterRatio = 0.0001; - clipperGcsMultiplicationFactor = 100000.0; - shapefileFastMode = false; - invalidShapesBufferDistance = 0.001; - shapefileFastUnion = true; - labelsCompositingQuality = HighQuality; - labelsSmoothingMode = HighQualityMode ; - zoomToFirstLayer = true; - gridProxyFormat = gpfBmpProxy; - MaxDirectGridSizeMb = 20.0; - gridProxyMode = gpmAuto; - maxUniqueValuesCount = 100; - randomColorSchemeForGrids = true; - defaultColorSchemeForGrids = SummerMountains; - inputValidation = tkShapeValidationMode::NoValidation; - outputValidation = tkShapeValidationMode::NoValidation; - geometryEngine = tkGeometryEngine::engineGeos; - saveGridColorSchemeToFile = true; - xmlFileVersion = 2; - xmlFilenameEncoding = "utf8"; - tilesThreadPoolSize = 5; - loadSymbologyOnAddLayer = true; - tilesMaxZoomOnProjectionMismatch = 6; - commonCollisionListForCharts = true; - commonCollisionListForLabels = true; - gridFavorGreyScale = true; - gridUseHistogram = true; + GlobalSettingsInfo::GlobalSettingsInfo() + { + ogrInterpretYNStringAsBoolean = true; + ogrListAllGeometryTypes = true; + ogrShareConnection = false; + callbackVerbosity = cvAll; + wmsDiskCaching = true; + cacheShapeRenderingData = false; + cacheDbfRecords = true; + overrideLocalCallback = true; + proxyAuthentication = asBasic; + hereAppId = ""; + hereAppCode = ""; + bingApiKey = ""; + forceReadOnlyModeForGdalRasters = false; + forceHideLabels = false; + ogrLayerForceUpdateMode = false; + autoChooseRenderingHintForLabels = true; + suppressGdalErrors = false; + pixelOffsetMode = pomDefault; + floatNumberFormat = "%g"; + maxReprojectionShapeCount = 50000; + useShortUrlForTiles = false; + callback = nullptr; + mouseTolerance = 20; + allowLayersWithoutProjection = true; + allowProjectionMismatch = true; + reprojectLayersOnAdding = false; + emptyBstr = SysAllocString(L""); + gdalBstr = SysAllocString(L"GDAL"); + identifierColor = RGB(30, 144, 255); + hotTrackingMaxShapeCount = 200; + attachMapCallbackToLayers = true; + ctorCount = 0; + dtorCount = 0; + saveOgrLabels = false; + useSchemesForStyles = false; + autoChooseOgrLoadingMode = true; + ogrLayerMaxFeatureCount = 50000; + ogrEncoding = oseUtf8; + imageUpsamplingMode = imNone; + imageDownsamplingMode = imBilinear; + gridUpsamplingMode = imNone; + gridDownsamplingMode = imBilinear; + rasterOverviewResampling = tkGDALResamplingMethod::grmNearest; + minOverviewWidth = 512; + rasterOverviewCreation = tkRasterOverviewCreation::rocAuto; + tiffCompression = tkTiffCompression::tkmLZW; + labelsCollisionMode = tkCollisionMode::LocalList; + minPolygonArea = 1.0; + minAreaToPerimeterRatio = 0.0001; + clipperGcsMultiplicationFactor = 100000.0; + shapefileFastMode = false; + invalidShapesBufferDistance = 0.001; + shapefileFastUnion = true; + labelsCompositingQuality = HighQuality; + labelsSmoothingMode = HighQualityMode; + zoomToFirstLayer = true; + gridProxyFormat = gpfBmpProxy; + MaxDirectGridSizeMb = 20.0; + gridProxyMode = gpmAuto; + maxUniqueValuesCount = 100; + randomColorSchemeForGrids = true; + defaultColorSchemeForGrids = SummerMountains; + inputValidation = tkShapeValidationMode::NoValidation; + outputValidation = tkShapeValidationMode::NoValidation; + geometryEngine = tkGeometryEngine::engineGeos; + saveGridColorSchemeToFile = true; + xmlFileVersion = 2; + xmlFilenameEncoding = "utf8"; + tilesThreadPoolSize = 5; + loadSymbologyOnAddLayer = true; + tilesMaxZoomOnProjectionMismatch = 6; + commonCollisionListForCharts = true; + commonCollisionListForLabels = true; + gridFavorGreyScale = true; + gridUseHistogram = true; - shortUnitStrings[lsHectars] = L"ha"; - shortUnitStrings[lsMeters] = L"m"; - shortUnitStrings[lsKilometers] = L"km"; - shortUnitStrings[lsSquareKilometers] = L"sq.km"; - shortUnitStrings[lsSquareMeters] = L"sq.m"; - shortUnitStrings[lsMapUnits] = L"mu"; - shortUnitStrings[lsSquareMapUnits] = L"sq.mu"; - shortUnitStrings[lsMiles] = L"mi"; - shortUnitStrings[lsFeet] = L"ft"; - shortUnitStrings[lsLatitude] = L"Lat"; - shortUnitStrings[lsLongitude] = L"Lng"; - shortUnitStrings[lsRadians] = L"rad"; - shortUnitStrings[lsNorthEast] = L"NE"; - shortUnitStrings[lsSouthEast] = L"SE"; - shortUnitStrings[lsSouthWest] = L"SW"; - shortUnitStrings[lsNorthWest] = L"NW"; - shortUnitStrings[lsNorth] = L"N"; - shortUnitStrings[lsEast] = L"E"; - shortUnitStrings[lsSouth] = L"S"; - shortUnitStrings[lsWest] = L"W"; - shortUnitStrings[lsSquareFeet] = L"sq.ft"; - shortUnitStrings[lsAcres] = L"ac"; - shortUnitStrings[lsSquareMiles] = L"sq.mi"; - } + shortUnitStrings[lsHectars] = L"ha"; + shortUnitStrings[lsMeters] = L"m"; + shortUnitStrings[lsKilometers] = L"km"; + shortUnitStrings[lsSquareKilometers] = L"sq.km"; + shortUnitStrings[lsSquareMeters] = L"sq.m"; + shortUnitStrings[lsMapUnits] = L"mu"; + shortUnitStrings[lsSquareMapUnits] = L"sq.mu"; + shortUnitStrings[lsMiles] = L"mi"; + shortUnitStrings[lsFeet] = L"ft"; + shortUnitStrings[lsLatitude] = L"Lat"; + shortUnitStrings[lsLongitude] = L"Lng"; + shortUnitStrings[lsRadians] = L"rad"; + shortUnitStrings[lsNorthEast] = L"NE"; + shortUnitStrings[lsSouthEast] = L"SE"; + shortUnitStrings[lsSouthWest] = L"SW"; + shortUnitStrings[lsNorthWest] = L"NW"; + shortUnitStrings[lsNorth] = L"N"; + shortUnitStrings[lsEast] = L"E"; + shortUnitStrings[lsSouth] = L"S"; + shortUnitStrings[lsWest] = L"W"; + shortUnitStrings[lsSquareFeet] = L"sq.ft"; + shortUnitStrings[lsAcres] = L"ac"; + shortUnitStrings[lsSquareMiles] = L"sq.mi"; + } - BSTR CreateEmptyBSTR() - { - USES_CONVERSION; - return A2BSTR(""); - } + BSTR CreateEmptyBSTR() + { + USES_CONVERSION; + return A2BSTR(""); + } - // ******************************************************* - // GetTiffCompression() - // ******************************************************* - CString GetTiffCompression() - { - return GdalHelper::TiffCompressionToString(tiffCompression); - } + // ******************************************************* + // GetTiffCompression() + // ******************************************************* + CString GetTiffCompression() + { + return GdalHelper::TiffCompressionToString(tiffCompression); + } - PredefinedColorScheme GetGridColorScheme() - { - PredefinedColorScheme coloring = defaultColorSchemeForGrids; - if (randomColorSchemeForGrids) - { - srand ((unsigned int)time(NULL)); - int r = rand(); - coloring = (PredefinedColorScheme)(r % 7); - } - return coloring; - } + PredefinedColorScheme GetGridColorScheme() + { + PredefinedColorScheme coloring = defaultColorSchemeForGrids; + if (randomColorSchemeForGrids) + { + srand((unsigned int)time(nullptr)); + int r = rand(); + coloring = (PredefinedColorScheme)(r % 7); + } + return coloring; + } - CStringW GetLocalizedString(tkLocalizedStrings s) - { - return shortUnitStrings.find(s) != shortUnitStrings.end() ? shortUnitStrings[s] : L""; - } + CStringW GetLocalizedString(tkLocalizedStrings s) + { + return shortUnitStrings.find(s) != shortUnitStrings.end() ? shortUnitStrings[s] : L""; + } - double GetMinPolygonArea(IGeoProjection* proj) - { - VARIANT_BOOL isGeographic; - if (proj == NULL) - return minPolygonArea; + double GetMinPolygonArea(IGeoProjection* proj) + { + VARIANT_BOOL isGeographic; + if (proj == nullptr) + return minPolygonArea; - proj->get_IsGeographic(&isGeographic); - return this->GetMinPolygonArea(isGeographic); - } + proj->get_IsGeographic(&isGeographic); + return this->GetMinPolygonArea(isGeographic); + } - double GetMinPolygonArea(VARIANT_BOOL isGeographic) - { - if (isGeographic) - { - return minPolygonArea / pow(METERS_PER_DEGREE, 2.0); // degrees to meters - } - else - { - return minPolygonArea; - } - } + double GetMinPolygonArea(const VARIANT_BOOL isGeographic) + { + if (isGeographic) + { + return minPolygonArea / pow(METERS_PER_DEGREE, 2.0); // degrees to meters + } - void SetGdalUtf8(bool turnon) - { - CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", turnon ? "YES" : "NO"); - } + return minPolygonArea; + } - int GetTilesThreadPoolSize() - { - int size = tilesThreadPoolSize > 20 ? 20 : tilesThreadPoolSize; - return size; - } + void SetGdalUtf8(bool turnon) + { + CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", turnon ? "YES" : "NO"); + } - double GetInvalidShapeBufferDistance(tkUnitsOfMeasure units) - { - double val = invalidShapesBufferDistance; - if (Utility::ConvertDistance(units, umMeters, val)) { - return invalidShapesBufferDistance / val; - } - else { - return invalidShapesBufferDistance; - } - } -}; \ No newline at end of file + int GetTilesThreadPoolSize() + { + int size = tilesThreadPoolSize > 20 ? 20 : tilesThreadPoolSize; + return size; + } + + double GetInvalidShapeBufferDistance(tkUnitsOfMeasure units) + { + double val = invalidShapesBufferDistance; + if (Utility::ConvertDistance(units, umMeters, val)) + { + return invalidShapesBufferDistance / val; + } + else + { + return invalidShapesBufferDistance; + } + } +}; diff --git a/src/Control/Map.cpp b/src/Control/Map.cpp index b4277eb6..ba20d4da 100644 --- a/src/Control/Map.cpp +++ b/src/Control/Map.cpp @@ -28,6 +28,7 @@ #include "Tiles.h" #include "ShapeEditor.h" #include "UndoList.h" +#include "curl.h" using namespace std; //disable some known warnings we don't care about @@ -302,6 +303,9 @@ void CMapView::Startup() GetMeasuringBase()->SetMapCallback(this, ShapeInputMode::simMeasuring); _shapeEditor->SetMapCallback(this); _geodesicShape->SetMapCallback(this); + + // initialize cURL + curl_global_init(CURL_GLOBAL_ALL); } // ********************************************************************** @@ -425,6 +429,9 @@ void CMapView::ReleaseTempObjects() // Must be called from destructor only void CMapView::Shutdown() { + // clean up cURL + curl_global_cleanup(); + Utility::ClosePointer(&_fontCourier); Utility::ClosePointer(&_fontCourierSmall); Utility::ClosePointer(&_fontCourierLink); diff --git a/src/Control/Map.h b/src/Control/Map.h index 535c9bc1..d07158a8 100644 --- a/src/Control/Map.h +++ b/src/Control/Map.h @@ -969,8 +969,17 @@ class CMapView : public COleControl, IMapViewCallback DOUBLE GetPixelsPerDegree(void); DOUBLE PixelsPerMapUnit(void); - inline void PixelToProjection( double piX, double piY, double & prX, double & prY ); - inline void ProjectionToPixel( double prX, double prY, double & piX, double & piY ); + inline void PixelToProjection(double piX, double piY, double & prX, double & prY) + { + prX = _extents.left + piX * _inversePixelPerProjectionX; + prY = _extents.top - piY * _inversePixelPerProjectionY; + } + + inline void ProjectionToPixel(double prX, double prY, double & piX, double & piY) + { + piX = (prX - _extents.left) * _pixelPerProjectionX; + piY = (_extents.top - prY) * _pixelPerProjectionY; + } // some simple encapsulation for code readability IGeoProjection* GetMapToWgs84Transform(); diff --git a/src/Control/Map_Cursors.cpp b/src/Control/Map_Cursors.cpp index e54a9730..8a2c946e 100644 --- a/src/Control/Map_Cursors.cpp +++ b/src/Control/Map_Cursors.cpp @@ -80,7 +80,8 @@ HCURSOR CMapView::GetCursorIcon() newCursor = (_useAlternatePanCursor == TRUE) ? _cursorAlternatePan : _cursorPan; break; - case cmSelection: + case cmSelection: + case cmSelectByPolygon: newCursor = _cursorSelect; break; diff --git a/src/Control/Map_Drawing.cpp b/src/Control/Map_Drawing.cpp index 7fe5fd2c..ce244dbe 100644 --- a/src/Control/Map_Drawing.cpp +++ b/src/Control/Map_Drawing.cpp @@ -880,7 +880,8 @@ void CMapView::DrawLayers(const CRect & rcBounds, Gdiplus::Graphics* graphics, b if (l->IsShapefile() && l->wasRendered) // if it's hidden don't clear every time { CComPtr sf = NULL; - if (l->QueryShapefile(&sf)) + // don't mark as 'undrawn' if we're not going to redraw it + if (l->QueryShapefile(&sf) && ShapefileHelper::IsVolatile(sf) != layerBuffer) { ShapefileHelper::Cast(sf)->MarkUndrawn(); } @@ -939,6 +940,7 @@ void CMapView::DrawLayers(const CRect & rcBounds, Gdiplus::Graphics* graphics, b continue; CComPtr sf = NULL; + // layerBuffer == true indicates we're drawing the non-Volatile layers if (l->QueryShapefile(&sf) && ShapefileHelper::IsVolatile(sf) == layerBuffer) continue; diff --git a/src/Control/Map_Edit.cpp b/src/Control/Map_Edit.cpp index 96abcbac..52a7995f 100644 --- a/src/Control/Map_Edit.cpp +++ b/src/Control/Map_Edit.cpp @@ -366,15 +366,35 @@ void CMapView::_UnboundShapeFinished(IShape* shp) _shapeEditor->StartUnboundShape((tkCursorMode)m_cursorMode); long layerHandle = -1; - FireChooseLayer(0, 0, &layerHandle); - if (layerHandle == -1) return; - - CComPtr sf = NULL; - sf.Attach(GetShapefile(layerHandle)); - if (!sf) { - ErrorMessage(tkINVALID_LAYER_HANDLE); - return; - } + bool selectingSelectable = false; + CComPtr sf = NULL; + + FireChooseLayer(0, 0, &layerHandle); + // if none specified, consider all 'selectable' layers + if (layerHandle == -1) + { + // only in 'selection' mode is -1 acceptable + if (m_cursorMode == cmSelectByPolygon) + { + // select based on all selectable layers + selectingSelectable = true; + } + else + { + // any other mode, layerHandle must be specified + return; + } + } + else + { + // single layer specified for selection + sf.Attach(GetShapefile(layerHandle)); + if (!sf) + { + ErrorMessage(tkINVALID_LAYER_HANDLE); + return; + } + } bool editing = m_cursorMode == cmSplitByPolyline || m_cursorMode == cmSplitByPolygon || @@ -390,28 +410,62 @@ void CMapView::_UnboundShapeFinished(IShape* shp) } } - ShpfileType shpType; - sf->get_ShapefileType2D(&shpType); - if (m_cursorMode == cmSplitByPolyline && shpType != SHP_POLYGON && shpType != SHP_POLYLINE) - { - ErrorMessage(tkUNEXPECTED_SHAPE_TYPE); - return; - } + if (m_cursorMode == cmSplitByPolyline) + { + ShpfileType shpType; + sf->get_ShapefileType2D(&shpType); + if (shpType != SHP_POLYGON && shpType != SHP_POLYLINE) + { + ErrorMessage(tkUNEXPECTED_SHAPE_TYPE); + return; + } + } bool redrawNeeded = false; int errorCode = tkNO_ERROR; if (m_cursorMode == cmSelectByPolygon) { - SelectionHelper::SelectByPolygon(sf, shp, errorCode); - FireSelectionChanged(layerHandle); - redrawNeeded = true; - } - else { + // if single layer selection + if (sf) + { + SelectionHelper::SelectByPolygon(sf, shp, errorCode); + // selection has (likely) changed for this layer + FireSelectionChanged(layerHandle); + redrawNeeded = true; + } + else if (selectingSelectable) + { + // iterate all layers + for (layerHandle = 0; layerHandle < GetNumLayers(); layerHandle++) + { + sf.Attach(GetShapefile(layerHandle)); + if (sf) + { + // is layer selectable? + VARIANT_BOOL isSelectable = VARIANT_FALSE; + sf->get_Selectable(&isSelectable); + if (isSelectable == VARIANT_TRUE) + { + int tempError = tkNO_ERROR; + SelectionHelper::SelectByPolygon(sf, shp, tempError); + // save error code if any layer returns an error + if (tempError != tkNO_ERROR) errorCode = tempError; + // selection has changed for this layer + FireSelectionChanged(layerHandle); + redrawNeeded = true; + } + } + } + } + } + else + { redrawNeeded = GroupOperation::Run((tkCursorMode)m_cursorMode, layerHandle, sf, shp, _undoList, errorCode); } - if (errorCode != tkNO_ERROR) { + if (errorCode != tkNO_ERROR) + { ErrorMessage(errorCode); } diff --git a/src/Control/Map_Events.cpp b/src/Control/Map_Events.cpp index f1901208..b3916db6 100644 --- a/src/Control/Map_Events.cpp +++ b/src/Control/Map_Events.cpp @@ -908,13 +908,23 @@ void CMapView::HandleLButtonUpZoomBox(long vbflags, long x, long y) { bool ctrl = vbflags & 2 ? true : false; long layerHandle = -1; + bool selectingSelectable = false; CComPtr sf = NULL; if (m_cursorMode == cmSelection) { tkMwBoolean cancel = blnFalse; + // see if single layer is specified to select FireChooseLayer(x, y, &layerHandle); - if (layerHandle != -1) { + // if none specified, consider all 'selectable' layers + if (layerHandle == -1) + { + // all selectable layers + selectingSelectable = true; + } + else + { + // single layer specified for selection sf.Attach(GetShapefile(layerHandle)); } } @@ -922,25 +932,56 @@ void CMapView::HandleLButtonUpZoomBox(long vbflags, long x, long y) _dragging.Operation = DragNone; if (!_dragging.HasRectangle()) { + // no drag rectangle, mouse click only switch (m_cursorMode) { case cmZoomIn: ZoomToCursorPosition(true); break; case cmSelection: - if (sf) + if (sf || selectingSelectable) { double xProj, yProj; PixelToProjection(x, y, xProj, yProj); - Extent box = GetPointSelectionBox(sf, xProj, yProj); - if (SelectionHelper::SelectByPoint(sf, box, !ctrl)) - { - FireSelectionChanged(layerHandle); - Redraw(); - return; - } + if (sf) + { + // single layer select + Extent box = GetPointSelectionBox(sf, xProj, yProj); + if (SelectionHelper::SelectByPoint(sf, box, !ctrl)) + { + FireSelectionChanged(layerHandle); + Redraw(); + return; + } + } + else if (selectingSelectable) + { + // iterate all layers + for (layerHandle = 0; layerHandle < GetNumLayers(); layerHandle++) + { + sf.Attach(GetShapefile(layerHandle)); + if (sf) + { + // is layer selectable? + VARIANT_BOOL isSelectable = VARIANT_FALSE; + sf->get_Selectable(&isSelectable); + if (isSelectable == VARIANT_TRUE) + { + Extent box = GetPointSelectionBox(sf, xProj, yProj); + // allow also for multiple overlapping shapes + if (SelectionHelper::SelectByPoint(sf, box, !ctrl, false)) + { + FireSelectionChanged(layerHandle); + } + } + } + } + Redraw(); + return; + } } - else if (m_sendMouseDown) { + else if (m_sendMouseDown) + { this->FireMouseDown(MK_LBUTTON, (short)vbflags, x, y); } break; @@ -948,6 +989,7 @@ void CMapView::HandleLButtonUpZoomBox(long vbflags, long x, long y) } else { + // get dragging rectangle CRect rect = _dragging.GetRectangle(); if (HasRotation()) @@ -969,13 +1011,39 @@ void CMapView::HandleLButtonUpZoomBox(long vbflags, long x, long y) SetNewExtentsWithForcedZooming(box, true); break; case cmSelection: - if (sf) { - if (SelectionHelper::SelectByRectangle(sf, box)) { + if (sf) + { + // single layer select + if (SelectionHelper::SelectByRectangle(sf, box)) + { FireSelectionChanged(layerHandle); Redraw(); return; } } + else if (selectingSelectable) + { + // iterate all layers + for (layerHandle = 0; layerHandle < GetNumLayers(); layerHandle++) + { + sf.Attach(GetShapefile(layerHandle)); + if (sf) + { + // select all selectable + VARIANT_BOOL isSelectable = VARIANT_FALSE; + sf->get_Selectable(&isSelectable); + if (isSelectable == VARIANT_TRUE) + { + if (SelectionHelper::SelectByRectangle(sf, box)) + { + FireSelectionChanged(layerHandle); + } + } + } + } + Redraw(); + return; + } break; } diff --git a/src/Control/Map_Layer.cpp b/src/Control/Map_Layer.cpp index 3c0f197b..7a432835 100644 --- a/src/Control/Map_Layer.cpp +++ b/src/Control/Map_Layer.cpp @@ -886,22 +886,25 @@ bool CMapView::ReprojectLayer(Layer* layer, int layerHandle) return false; } - // let's substitute original file with this one + // let's substitute original shapefile with the reprojected one // don't close the original shapefile; use may still want to interact with it - // release should be called twice, smart pointer won't allow it - ShapefileHelper::Cast(sf)->Release(); if (layer->get_LayerType() == OgrLayerSource) { CComPtr ogr = NULL; layer->QueryOgrLayer(&ogr); if (ogr) { + // don't need to Release original Shapefile reference here, + // since InjectShapefile Closes/Releases original reference OgrHelper::Cast(ogr)->InjectShapefile(sfNew); } } else { - layer->set_Object(sfNew); + // need to Release original reference for Shapefile here, + // since we are directly replacing old reference with new + ShapefileHelper::Cast(sf)->Release(); + layer->set_Object(sfNew); } layer->UpdateExtentsFromDatasource(); diff --git a/src/Control/Map_Scale.cpp b/src/Control/Map_Scale.cpp index 5bf0ae96..a34f55d3 100644 --- a/src/Control/Map_Scale.cpp +++ b/src/Control/Map_Scale.cpp @@ -749,18 +749,6 @@ void CMapView::PixelToProj(double pixelX, double pixelY, double FAR* projX, doub PixelToProjection(pixelX,pixelY,*projX,*projY); } -inline void CMapView::PixelToProjection( double piX, double piY, double & prX, double & prY ) -{ - prX = _extents.left + piX*_inversePixelPerProjectionX; - prY = _extents.top - piY*_inversePixelPerProjectionY; -} - -inline void CMapView::ProjectionToPixel( double prX, double prY, double & piX, double & piY ) -{ - piX = (prX - _extents.left)*_pixelPerProjectionX; - piY = (_extents.top - prY) * _pixelPerProjectionY; -} - // *********************************************************** // UnitsPerPixel // *********************************************************** diff --git a/src/CopyTamasFiles.bat b/src/CopyTamasFiles.bat index 2eaa3aa5..e4eaea96 100644 --- a/src/CopyTamasFiles.bat +++ b/src/CopyTamasFiles.bat @@ -1,23 +1,24 @@ @echo off -REM ***************************************************** -REM * Copy files from Tamas to bin folder of MapWinGIS * -REM * But only the needed files * -REM * * -REM * Usage CopyTamasFiles from_dir to_dir * -REM * * -REM * Paul Meems, March 2014 * -REM * Paul Meems, update for ecw dll, June 2015 * -REM * Paul Meems, update for ecw dll to v5.3, Aug 2017 * -REM * Usage to test: * -REM * CopyTamasFiles.bat D:\dev\MapwinGIS\GitHub\support\GDAL_SDK\v120\bin\win32 D:\dev\MapwinGIS\GitHub\src\bin\Win32\ -REM ***************************************************** +REM ************************************************************* +REM * Copy files from Tamas to bin folder of MapWinGIS * +REM * But only the needed files * +REM * * +REM * Usage CopyTamasFiles from_dir to_dir * +REM * * +REM * Paul Meems, March 2014 * +REM * Paul Meems, update for ecw dll, June 2015 * +REM * Paul Meems, update for ecw dll to v5.3, Aug 2017 * +REM * Paul Meems, update for xerces and lti_dsdk dll, Aug 2018 * +REM * Usage to test: * +REM * CopyTamasFiles.bat D:\dev\MapwinGIS\GitHub\support\GDAL_SDK\v140\bin\win32 D:\dev\MapwinGIS\GitHub\src\bin\Win32\ +REM ************************************************************* set _from_dir=%1 set _to_dir=%2 if '%_from_dir%'=='' if '%_to_dir%'=='' GOTO usage REM Copy gdal plugins: -FOR %%G IN (gdal_MrSID.dll gdal_netCDF.dll gdal_HDF5.dll gdal_HDF5Image.dll) DO ( +FOR %%G IN (gdal_MrSID.dll gdal_netCDF.dll gdal_HDF4.dll gdal_HDF5.dll gdal_HDF5Image.dll) DO ( IF EXIST %_from_dir%\gdal\plugins\%%G ( xcopy /v /c /r /y %_from_dir%\gdal\plugins\%%G %_to_dir%gdalplugins\ ) @@ -36,9 +37,9 @@ xcopy /v /c /r /y %_from_dir%\proj\SHARE\*.* %_to_dir%..\PROJ_NAD\ REM Copy needed Tamas binaries: FOR %%G IN (cfitsio.dll geos.dll geos_c.dll hdf5.dll hdf5_hl.dll hdf5_cpp.dll hdf5_hl_cpp.dll libcurl.dll - iconv.dll libeay32.dll libmysql.dll libpq.dll libxml2.dll lti_dsdk_9.1.dll - lti_lidar_dsdk_1.1.dll netcdf.dll openjp2.dll proj.dll spatialite.dll sqlite3.dll freexl.dll - ssleay32.dll szip.dll tbb.dll xdrdll.dll xerces-c_3_1.dll zlib1.dll libtiff.dll expat.dll NCSEcw.dll) DO ( + iconv.dll libeay32.dll libmysql.dll libpq.dll libxml2.dll lti_lidar_dsdk_1.1.dll netcdf.dll + openjp2.dll proj.dll spatialite.dll sqlite3.dll freexl.dll + ssleay32.dll szip.dll tbb.dll xdrdll.dll zlib1.dll libtiff.dll expat.dll NCSEcw.dll) DO ( IF EXIST %_from_dir%\%%G ( xcopy /v /c /r /y %_from_dir%\%%G %_to_dir% ) @@ -48,6 +49,14 @@ REM gdal contains the version number, so use a wildcard: del /f /q %_to_dir%\gdal*.dll xcopy /v /c /r /y %_from_dir%\gdal2*.dll %_to_dir% +REM xerces contains a version number, so use a wildcard: +del /f /q %_to_dir%\xerces-c*.dll +xcopy /v /c /r /y %_from_dir%\xerces-c*.dll %_to_dir% + +REM lti_dsdk contains a version number, so use a wildcard: +del /f /q %_to_dir%\lti_dsdk*.dll +xcopy /v /c /r /y %_from_dir%\lti_dsdk*.dll %_to_dir% + REM Copy licenses: xcopy /v /c /r /y %_from_dir%\..\..\..\licenses\*.rtf %_to_dir%..\Licenses\ diff --git a/src/Drawing/ChartDrawing.cpp b/src/Drawing/ChartDrawing.cpp index a8c4b2d5..218ae9d3 100644 --- a/src/Drawing/ChartDrawing.cpp +++ b/src/Drawing/ChartDrawing.cpp @@ -83,13 +83,13 @@ bool CChartDrawer::PrepareValues(IShapefile* sf, ICharts* charts, ChartOptions* sf->get_Table(&tbl); std::vector arrInit; - CString err; + CStringW err; bool useAll = true; if (SysStringLen(expr) > 0) { USES_CONVERSION; - if (TableHelper::Cast(tbl)->QueryCore(OLE2CA(expr), arrInit, err)) + if (TableHelper::Cast(tbl)->QueryCore(OLE2CW(expr), arrInit, err)) { useAll = false; } diff --git a/src/Drawing/GdiLabelDrawer.cpp b/src/Drawing/GdiLabelDrawer.cpp index 52031fdc..cb079115 100644 --- a/src/Drawing/GdiLabelDrawer.cpp +++ b/src/Drawing/GdiLabelDrawer.cpp @@ -147,11 +147,11 @@ CFont* GdiLabelDrawer::CreateFont(CLabelOptions* options, long fontSize, double lf.lfWidth = long((double)lf.lfWidth * scaleFactor); lf.lfHeight = long((double)lf.lfHeight * scaleFactor); } + + // MWGIS-121; rather than return null, enforce a minimum font size of 4 if (abs(lf.lfHeight) < 4) // changed 1 to 4; there is no way to read labels smaller than 4, but they slow down the performance { - fnt->DeleteObject(); - delete fnt; - return NULL; + lf.lfHeight = 4; } lf.lfItalic = options->fontStyle & fstItalic; diff --git a/src/Drawing/GdiPlusLabelDrawer.cpp b/src/Drawing/GdiPlusLabelDrawer.cpp index 8b145a47..89517a6c 100644 --- a/src/Drawing/GdiPlusLabelDrawer.cpp +++ b/src/Drawing/GdiPlusLabelDrawer.cpp @@ -283,8 +283,9 @@ Gdiplus::Font* GdiPlusLabelDrawer::CreateFont(CLabelOptions* options, double fon fontSize = fontSize * scaleFactor; } + // MWGIS-121; rather than return null, enforce a minimum font size of 4 if (fontSize < 4) { - return NULL; + fontSize = 4; } int style = FontStyleRegular; diff --git a/src/Drawing/LabelDrawing.cpp b/src/Drawing/LabelDrawing.cpp index 9cb8f047..7754801d 100644 --- a/src/Drawing/LabelDrawing.cpp +++ b/src/Drawing/LabelDrawing.cpp @@ -398,7 +398,7 @@ bool CLabelDrawer::GetExpressionFilter(ILabels* labels, IShapefile* sf, vectorget_VisibilityExpression(&expr); - CString err; + CStringW err; if (SysStringLen(expr) > 0) { @@ -408,7 +408,7 @@ bool CLabelDrawer::GetExpressionFilter(ILabels* labels, IShapefile* sf, vectorget_Table(&tbl); USES_CONVERSION; - if (TableHelper::Cast(tbl)->QueryCore(OLE2CA(expr), filter, err)) + if (TableHelper::Cast(tbl)->QueryCore(OLE2CW(expr), filter, err)) { return true; } diff --git a/src/Drawing/ShapefileDrawing.cpp b/src/Drawing/ShapefileDrawing.cpp index f00bffa1..87969cf5 100644 --- a/src/Drawing/ShapefileDrawing.cpp +++ b/src/Drawing/ShapefileDrawing.cpp @@ -226,7 +226,7 @@ bool CShapefileDrawer::Draw(const CRect & rcBounds, IShapefile* sf) // Analyzing visibility expression // -------------------------------------------------------------- std::vector arr; - CString err; + CStringW err; bool useAll = true; CComBSTR expr; @@ -238,7 +238,7 @@ bool CShapefileDrawer::Draw(const CRect & rcBounds, IShapefile* sf) _shapefile->get_Table(&tbl); USES_CONVERSION; - if (TableHelper::Cast(tbl)->QueryCore(OLE2CA(expr), arr, err)) + if (TableHelper::Cast(tbl)->QueryCore(OLE2CW(expr), arr, err)) { useAll = false; } @@ -1246,19 +1246,19 @@ void CShapefileDrawer::DrawPolyCategory( CDrawingOptionsEx* options, std::vector } else if ( options->drawingMode == vdmGDIMixed ) { - //m_hdc = _graphics->GetHDC(); + m_hdc = _graphics->GetHDC(); _dc->EndPath(); _dc->StrokePath(); - //_graphics->ReleaseHDC(m_hdc); + _graphics->ReleaseHDC(m_hdc); } } } if (drawingMode == vdmGDIMixed) { - //m_hdc = _graphics->GetHDC(); + m_hdc = _graphics->GetHDC(); options->ReleaseGdiBrushAndPen(_dc); - //_graphics->ReleaseHDC(m_hdc); + _graphics->ReleaseHDC(m_hdc); _dc = NULL; } diff --git a/src/Editor/Digitizer.cpp b/src/Editor/Digitizer.cpp index 191e0225..38a95aab 100644 --- a/src/Editor/Digitizer.cpp +++ b/src/Editor/Digitizer.cpp @@ -32,11 +32,6 @@ bool Digitizer::OnMouseDown(CShapeEditor* editor, double projX, double projY, bo { if (!editor) return false; - // an attempt to finish shape - if (ctrl) { - return editor->TryStop(); - } - // add another point editor->HandleProjPointAdd(projX, projY); editor->SetRedrawNeeded(rtShapeEditor); @@ -46,6 +41,11 @@ bool Digitizer::OnMouseDown(CShapeEditor* editor, double projX, double projY, bo { return editor->TryStop(); } + else if (ctrl) + { + // if an attempt to finish a multipoint shape + return editor->TryStop(); + } return true; } \ No newline at end of file diff --git a/src/InnoSetup/AxInterop.MapWinGIS.XML b/src/InnoSetup/AxInterop.MapWinGIS.XML index 50fb62e3..c916e7e6 100644 --- a/src/InnoSetup/AxInterop.MapWinGIS.XML +++ b/src/InnoSetup/AxInterop.MapWinGIS.XML @@ -1042,7 +1042,9 @@ \addtogroup map_drawing_layers Drawing layers - Here is a list of methods and properties to interact with the drawing layers of the map. This module is a part of the documentation of AxMap class. + Here is a list of methods and properties to interact with the drawing layers of the map. + The drawing layers are more transient that the standard layers, intended for fast drawing of temporary elements on top of the map. + This module is a part of the documentation of AxMap class. \dot digraph map_drawing_layers { splines = true; @@ -1071,18 +1073,21 @@ - Clears all the drawings on the drawing layer specified. + Clears all drawings on the specified drawing layer, and removes the drawing layer. The drawing handle will no longer be valid. + Call AxMap.NewDrawing again to create a new drawing layer to continue adding new elements. - Drawing handle of the drawing layer for which all drawings are to be cleared. + Drawing handle of the drawing layer to be cleared (and removed). - Clears all drawings on all drawing layers. This method is slower than using ClearDrawing on a specific layer + Clears all drawings on all drawing layers, and removes all drawing layers. + This method is slower than using ClearDrawing on a specific layer. + Call AxMap.NewDrawing again to create a new drawing layer to continue adding new elements. - Draws a circle on the last drawing layer created by NewDrawing + Draws a circle on the last drawing layer created by AxMap.NewDrawing Center x coordinate for the circle to be drawn. Center y coordinate for the circle to be drawn. @@ -1103,7 +1108,7 @@ - Draws a line on the last drawing layer created using NewDrawing. + Draws a line on the last drawing layer created using AxMap.NewDrawing. X coordinate of the first point used to draw the line Y coordinate of the first point used to draw the line. @@ -1126,7 +1131,7 @@ - Draws a point on the last drawing layer created by NewDrawing. + Draws a point on the last drawing layer created by AxMap.NewDrawing. The x coordinate of the point to draw The y coordinate of the point to draw. @@ -1145,7 +1150,7 @@ - Draws a polygon on the last drawing layer created using NewDrawing. + Draws a polygon on the last drawing layer created using AxMap.NewDrawing. An array containing x-coordinates for each point in the polygon. An array containing y-coordinates for each point in the polygon. @@ -1166,7 +1171,7 @@ - Draws a circle with custom outline width on the last drawing layer created by NewDrawing. + Draws a circle with custom outline width on the last drawing layer created by AxMap.NewDrawing. Center x coordinate for the circle to be drawn. Center y coordinate for the circle to be drawn. @@ -1189,7 +1194,7 @@ - Draws a polygon with custom width of outline on the last drawing layer created using NewDrawing. + Draws a polygon with custom width of outline on the last drawing layer created using AxMap.NewDrawing. An array containing x-coordinates for each point in the polygon. An array containing y-coordinates for each point in the polygon. @@ -1200,7 +1205,7 @@ - Draws a polygon with custom width of outline on the last drawing layer created using NewDrawing. + Draws a polygon with custom width of outline on the last drawing layer created using AxMap.NewDrawing. The handle of the drawing layer created with AxMap.NewDrawing call. An array containing x-coordinates for each point in the polygon. @@ -1214,8 +1219,8 @@ Creates a new drawing layer on the map returning its handle. - Sets the coordinate system to use for the new drawing layer to be created. (ScreenReferenced - uses pixels in screen coordinates. SpatiallyReferenced uses projected map units.) + Sets the coordinate system to use for the new drawing layer to be created. (tkDrawReferenceList.dlScreenReferencedList + uses pixels in screen coordinates. tkDrawReferenceList.dlSpatiallyReferencedList uses projected map units.) The handle for the new drawing layer in the map. diff --git a/src/InnoSetup/Interop.MapWinGIS.XML b/src/InnoSetup/Interop.MapWinGIS.XML index 894eb1e3..eb926127 100644 --- a/src/InnoSetup/Interop.MapWinGIS.XML +++ b/src/InnoSetup/Interop.MapWinGIS.XML @@ -702,6 +702,138 @@ The index of the break. The new colour to set. + + + Implementation of the GDAL v2 librified functions. + Not all functions are implemented yet. + + \new495 Added in version 4.9.5 + + + + Retrieves the last error generated in the object. + + \new495 Added in version 4.9.5 + + + + The global callback is the interface used by MapWinGIS to pass progress and error events to interested applications. + + \new495 Added in version 4.9.5 + + + + The key may be used by the programmer to store any string data associated with the object. + + \new495 Added in version 4.9.5 + + + + Gets the detailed error message. + + \new495 Added in version 4.9.5 + + + + Image reprojection and warping utility. + Implementing the librified function of GDAL's gdalwarp.exe tool + + The source filename. + The destination filename. + The options, as a string array + See GDAL's documentation here: http://www.gdal.org/gdalwarp.html + \new495 Added in version 4.9.5 + + \code + // Example of creating VRT file from TIFF file. More options are possible: + var output = Path.GetTempPath() + "GdalWarp.vrt"; + var options = new[] + { + "-of", "vrt", + "-overwrite" + }; + var gdalUtils = new GdalUtils(); + if (!gdalUtils.GDALWarp("test.tif", output, options)) + { + Debug.WriteLine("GdalWarp failed: " + gdalUtils.ErrorMsg[gdalUtils.LastErrorCode] + " Detailed error: " + gdalUtils.DetailedErrorMsg); + } + \endcode + + \code + // Example of cutting a TIFF file with a border file: + var output = Path.GetTempPath() + "GdalWarpCutline.vrt"; + const string border = @"test.shp"; + var options = new[] + { + "-of", "vrt", + "-overwrite", + "-crop_to_cutline", + "-cutline", border + }; + var gdalUtils = new GdalUtils(); + if (!gdalUtils.GDALWarp("test.tif", output, options)) + { + Debug.WriteLine("GdalWarp failed: " + gdalUtils.ErrorMsg[gdalUtils.LastErrorCode] + " Detailed error: " + gdalUtils.DetailedErrorMsg); + } + \endcode + + + + Converts simple features data between file formats. + Implementing the librified function of GDAL's ogr2ogr.exe tool + + The source filename. + The destination filename. + The options, as a string array + If set to true improves performance but also might make it instable. + See GDAL's documentation here: http://www.gdal.org/ogr2ogr.html + \new495 Added in version 4.9.5 + + \code + // Converting shapefile to gml: + var outputFilename = Path.Combine(Path.GetTempPath(), "translated.gml"); + var options = new[] + { + "-f", "GML" + }; + var gdalUtils = new GdalUtils(); + if (!gdalUtils.GdalVectorTranslate(inputFilename, outputFilename, options, true)) + { + Debug.WriteLine("GdalVectorTranslate failed: " + gdalUtils.ErrorMsg[gdalUtils.LastErrorCode] + " Detailed error: " + gdalUtils.DetailedErrorMsg); + } + \endcode + + + + Clips the vector with another vector. + + The subject filename. + The overlay filename. + The destination filename. + If set to true improves performance but also might make it instable. + Uses GdalUtils.GdalVectorTranslate under the hood. + \new495 Added in version 4.9.5 + + \code + // Clipping large shapefile with border file + const string subjectFilename = @"D:\dev\GIS-Data\Issues\MWGIS-78 Clipper\Fishnet.shp"; + const string borderFilename = @"D:\dev\GIS-Data\Issues\MWGIS-78 Clipper\border.shp"; + var outputFilename = Path.Combine(tempFolder, "GdalVectorTranslate.shp"); + var gdalUtils = new GdalUtils(); + if (!gdalUtils.ClipVectorWithVector("LargeFile.shp", "Border.shp", outputFilename)) + { + Debug.WriteLine("GdalVectorTranslate failed: " + gdalUtils.ErrorMsg[gdalUtils.LastErrorCode] + " Detailed error: " + gdalUtils.DetailedErrorMsg); + } + \endcode + + + + Retrieves the error message associated with the specified error code. + + The error code for which the error message is required. + The error message description for the specified error code. + \new495 Added in version 4.9.5 + Represents a layer downloading and displaying the data from the particular WMS Server. @@ -2269,7 +2401,7 @@ \note The fully working implementation of the shape editor can be examined in the the Demo application included in MapWinGIS installation (starting from v4.9.3). The source code for this application is available in - the repository. + the repository. \new493 Added in version 4.9.3 @@ -3835,6 +3967,16 @@ \see OgrLayer.MaxFeatureCount \new493 Added in version 4.9.3 + + + Applies to conversion of OGR Shapefile layers to MapWinGIS Shapefiles (via GetBuffer), since + OGR Shapefiles do not support Logical fields. Instead, DBF Logical fields are read as single- + character strings. This property Gets or Sets a value which indicates whether to interpret + single-character Ogr strings as Logical/Boolean values, as long as the character contained + in the string is one of valid DBF logical characters (e.g. Y, N, T, F). The default value is true. + + \new495 Added in version 4.9.5 + Gets or sets a value which indicates whether OgrLayer.DynamicLoading mode will @@ -3950,16 +4092,14 @@ - Gets or sets API key to access Bing maps. Without API key Bing Maps provider isn't available. See - details here. + Gets or sets API key to access Bing maps. Without API key Bing Maps provider isn't available. \new493 Added in version 4.9.3 Sets application credentials for Here Maps online tiles. - Without these credentials Here Maps providers are not available. See - details here. + Without these credentials Here Maps providers are not available. Application Id. Can be obtained by registering on the site of the service. Application code. Can be obtained by registering on the site of the service. @@ -8033,7 +8173,7 @@ - Imports shape data from WKT format. + Exports shape data to WKT format. String in WKT format with shape data. \new490 Added in version 4.9.0 @@ -8139,6 +8279,23 @@ \new493 Added in version 4.9.3 + + + Return a point at the specified distance (or percentage) along the specified line + + Starting point along this line (specify Point[0] for beginning of line) + Distance along this line (or percentage of line length; if a percentage, specify a number between 0.0 and 1.0) + Optional value; if FALSE, 'distance' is actual distance; if TRUE, distance is percentage of length; defaults to FALSE + Returns a Point class representing the point along the sourceLine that is the specified distance (or percentage) along the line. + + Only applies to a Polyline Shape. If 'distance' is greater than the the line length, the line's endpoint is returned. + + 'startPoint' does not have to be on the line. If not on the line, actual starting point will be the nearest point to 'startPoint' that is on the line. + + \see Utils.LineInterpolatePoint + + \new500 Added in version 5.0.0 + @@ -8947,125 +9104,6 @@ ShapefileCategory.Expression property will change it to cvExpression. \new493 Added in version 4.9.3 - - - Defines a part of shapefile color scheme and specifies how a certain region of a shapefile will be colored. - - \deprecated in v. 4.8. Use ShapefileCategories, ShapefileCategory, ColorScheme classes instead. - \removed493 Removed in 4.9.3 - - - - Gets or sets the caption of the shapefile color break. - - - - - Gets or sets the color which will be used for drawing objects characterized by ShapefileColorBreak.EndValue. - - - - - Gets or sets the value which represent the end of shapefile color break. - - - - - Gets or sets the color which will be used for drawing objects characterized by ShapefileColorBreak.StartValue. - - - - - Gets or sets the value which represent the start of shapefile color break. - - - - - Gets or sets a boolean value which indicates whether the objects defined by color break will be displayed. - - - - - A shapefile color scheme defines how a shapefile will be colored. - - A shapefile color scheme consists of ShapefileColorBreak objects. - \deprecated in v. 4.8. Use ShapefileCategories, ShapefileCategory, ColorScheme instead. - \removed493 Removed in 4.9.3 - - - - Adds a color break to the color scheme. - - - - - - - Gets or sets the index in the attribute table the color scheme is associated with. - - - - - Gets or sets callback object to return the information about the errors. - - \deprecated v4.9.3 Use GlobalSettings.ApplicationCallback instead. - - - - Insert a color break in the specified position of the scheme. - - The position to insert color break at. - The color break to insert. - The actual position color break was inserted at. - - - - Gets or sets text string associated with the object. - - - - - Returns the code of the last error which took place inside this instance of class. - - - - - Gets or sets the layer handle the color scheme is associated with. - - - - - Gets the number of color breaks in the color scheme. - - The number of breaks. - - - - Removes specific color break from the scheme. - - The index of the break to remove. - - - - Gets a color break from the color scheme. - - The index of the break. - The color break object or NULL reference on failure. - - - - Gets the description of the error code returned by ShapefileColorScheme.LastErrorCode. - - The code of error. - The description of error. - - - - Replaces a color break in the color scheme. - - The index of color break to replace. - The reference to the new color break. - Represents a network built from the polyline shapefile. @@ -9887,12 +9925,21 @@ Type of cache to be cleared. + + + Clears cache of the specified to type for a given provider ID and scales. + + Type of cache to be cleared. + Tile provider ID to be cleared. -1 will clear tiles for all providers. + Minimal scale (zoom) to clear tiles for. + Maximum scale (zoom) to clear tiles for. + Clears cache of the specified to type for a given provider and scales. Type of cache to be cleared. - Tile provider to be cleared. ProviderNone will clear tiles for all providers. + Tile provider to be cleared. ProviderNone will clear tiles for all providers. Minimal scale (zoom) to clear tiles for. Maximum scale (zoom) to clear tiles for. @@ -9904,6 +9951,11 @@ Therefore in most cases tiles of particular zoom will be somewhat additionally scaled to fit the map, i.e. their display size on map won't be equal to the original 256 pixels. + + + Gets or sets the delay request timeout. + + Restores the state of Tiles class from string. @@ -9919,7 +9971,7 @@ Gets the bounds of specific tile in decimal degrees (for inner use/debug purposes). - Id of the provider. + Id of the provider. Zoom level for a tile. X coordinate of the tile within zoom level. Y coordinate of the tile within zoom level. @@ -9931,7 +9983,7 @@ Bounds in decimal degrees. Zoom level. - Id of the provider. + Id of the provider. Extents object with tile bounds or null on failure. Can be used at the first step of prefetching operation. @@ -9962,7 +10014,7 @@ Minimal longitude to cache within. Maximum longitude to cache within. Zoom level. - Id of the provider. + Id of the provider. StopExecution interface implementation to stop the operation prematurely. The number of tiles scheduled for caching. The operation is executed asynchronously. To get the progress information use Tiles.GlobalCallback property. @@ -9977,7 +10029,7 @@ Minimum Y index of tile to be cached (in coordinates of tile zoom level). Maximum Y index of tile to be cached (in coordinates of tile zoom level). Zoom level to be cached. - Id of the provider. + Id of the provider. StopExecution interface implementation to stop the operation prematurely. Number of tiles scheduled for caching. The operation is executed asynchronously. See details in Tiles.Prefetch. @@ -9988,8 +10040,8 @@ Extents to cache within in decimal degrees. Zoom level. - Id of the provider. - Directory to save files into. Nested folders for zoom levels, + Id of the provider. + Directory to save files into (must exists). Nested folders for zoom levels, X/Y coordinates will be created automatically. File extension to store tiles with. StopExecution interface implementation to stop the operation prematurely. @@ -10021,6 +10073,31 @@ Gets list of the available default and custom tile providers. + + + Gets the current size of cache. + + The type of cache to return size for. + The size of cache in MB. + + + + Gets the current size of cache used for specific provider and zoom level. + + The type of cache to return size for. + Provider. ProviderNone will return size for all providers. + Scale (zoom) level. -1 will return size for all zoom levels. + The size of cache in MB. + + + + Gets the current size of cache used for specific provider and zoom level. + + Type of the cache. + The provider. -1 will return size for all providers. + The scale. -1 will return size for all zoom levels. + The size of cache in MB. + Gets proxy server settings for tiles class including IP and port, e.g. 192.168.0.1:80. @@ -10059,22 +10136,6 @@ When set to false tiles won't be requested either from server or cache. - - - Gets the current size of cache. - - The type of cache to return size for. - The size of cache in MB. - - - - Gets the current size of cache used for specific provider and zoom level. - - The type of cache to return size for. - Provider. ProviderNone will return size for all providers. - Scale (zoom) level. -1 will return size for all zoom levels. - The size of cache in MB. - Gets the value indicating whether tiles requested from server will be automatically cached. @@ -10135,21 +10196,6 @@ \new491 Added in version 4.9.1 - - - Starts logging HTTP requests for tile server. - - Filename to write log into. New file will be created any existing file - overwritten. - Indicate whether only unsuccessful requests should logged. - True if log was opened, and false on failure. - \new491 Added in version 4.9.1 - - - - Stops logging of HTTP requests to a file. - - \new491 Added in version 4.9.1 - Gets the number of unsuccessful HTTP requests during prefetching operation (Tiles.Prefetch and overloads). @@ -10172,7 +10218,7 @@ Gets number of tiles stored in disk cache for a given provider, zoom and region. - Id of provider. + Id of provider. Zoom level. Min X index of tile. Max X index of tile. @@ -10200,7 +10246,7 @@ Gets the description of the specific error code. - The error code returned by LastErrorCode property. + The error code returned by LastErrorCode property. String with the description. @@ -10222,6 +10268,20 @@ This diagnostic value indicates whether tiles will be rendered without scaling and distortions and if they will be rendered at all. \new491 Added in version 4.9.1 + + + Gets or sets the proxy authentication scheme. + + \new491 Added in version 4.9.1 + + + + Gets a value indicating whether [projection is spherical mercator]. + + + true if [projection is spherical mercator]; otherwise, false. + + Gets projection used by specific tile service. @@ -10230,11 +10290,11 @@ - Clears user name and password set by Tiles.SetProxyAuthorization method. + Clears user name and password set by Tiles.SetProxyAuthentication method. \new493 Added in version 4.9.3 - + Sets credentials for proxy authorization. @@ -10488,6 +10548,7 @@ A utils object provides access to a set of utility functions to perform a variety of tasks on other objects such as grids, images, points, shapes, shapefiles, tins, etc. + Starting at v4.9.5 some specific GDAL methods are moved to GdalUtils. @@ -10825,6 +10886,7 @@ See documentation here: http://www.gdal.org/gdalwarp.html \new490 Added in version 4.9.0 + \deprecated v4.9.5 Use GdalUtils.GDALWarp instead. @@ -10913,6 +10975,7 @@ See documentation here: http://www.gdal.org/ogr2ogr.html \new490 Added in version 4.9.0 + \deprecated v4.9.5 Use GdalUtils.GdalVectorTranslate instead. @@ -11131,6 +11194,41 @@ \new495 Added in version 4.9.5 + + + Calculates the angle defined by the two specified coordinates + + + + Geographic Angle (in degrees) of the vector measured from the first point to the second point + + The angle returned is the so-called Geographic angle, measured in a clockwise direction + from the positive Y-axis, as opposed to the Arithmetic (Cartesian) angle measured in a + counter-clockwise direction from the positive X-axis. The returned value can be used + as-is to specify Shape or Label Rotation, which expect the Geographic angle for input. + + \see Shapefile.set_ShapeRotation + + \new500 Added in version 5.0.0 + + + + Return a point at the specified distance (or percentage) along the specified line + + Polyline shape to traverse + Starting point along 'sourceLine' (specify Point[0] for beginning of line) + Distance along line (or percentage of line length; if a percentage, specify a number between 0.0 and 1.0) + Optional value; if FALSE, 'distance' is actual distance; if TRUE, distance is percentage of length; defaults to FALSE + Returns a Point class representing the point along the sourceLine that is the specified distance (or percentage) along the line. + + 'sourceLine' must be a Polyline Shape. If 'distance' is greater than the source line length, the line's endpoint is returned. + + 'startPoint' does not have to be on 'sourceLine'. If not on the line, actual starting point will be the nearest point to 'startPoint' that is on 'sourceline'. + + \see Shape.InterpolatePoint + + \new500 Added in version 5.0.0 + A vector object is used to represent the light source for a grid color scheme. @@ -11729,7 +11827,7 @@ The objects of the drawing layer are specified in screen coordinates and are not moved after the changes of the extents. - To update such layers the full redraw is not needed. Therefore use AxMap.Refresh rather than AxMap.Redraw. + To update such layers the full redraw is not needed. Therefore use AxMap.Redraw2 rather than AxMap.Redraw. @@ -12566,7 +12664,7 @@ Map will be rendered from the main buffer. Only measurements and coordinate display will be rendered anew. - Corresponds to AxMap.Refresh/AxMap.Invalidate. + Corresponds to AxMap.Redraw2/AxMap.Invalidate. @@ -14162,7 +14260,8 @@ \addtogroup shapefile_geoprocessing Shapefile geoprocessing Here is a list of methods to perform geoprocessing tasks using shapefile data. - This module is a part of the documentation of Shapefile class. + This module is a part of the documentation of Shapefile class.\n\n + Use GlobalSettings.MinAreaToPerimeterRatio and GlobalSettings.MinPolygonArea to tweak which polygon needs to be included in the output file. \dot digraph shapefile_geoprocessing { splines = true; @@ -14703,7 +14802,13 @@ \addtogroup shapefile_selection Shapefile selection Here is a list of properties and methods for managing shapefile selection. - This module is a part of the documentation of Shapefile class. + This module is a part of the documentation of the Shapefile class. + + Historically, using the cmSelection and cmSelectByPolygon tools, you would specify a LayerHandle in the ChooseLayer map event + to indicate which single layer you were selecting shapes from. Starting in v5.0, you can select from multiple layers concurrently + by setting the 'Selectable' property of each layer you would like to select shapes from. If the LayerHandle returned from the + ChooseLayer event is left unspecified (-1), then instead, all layers having the 'Selectable' property = TRUE + will be selectable by the tool. \dot digraph shapefile_selection { splines = true; @@ -14791,6 +14896,17 @@ \new48 Added in version 4.8 + + + Gets or sets a value indicating whether this shapefile will be selectable by the cmSelection and cmSelectByPolygon tool. + + + This property allows for multiple shapefiles to be concurrently selectable. + If only selecting from one layer, you can still use the ChooseLayer map event to specify the selectable layer. + + \see AxMap.CursorMode + \new500 Added in version 5.0.0 + Gets or sets the way shapefile selection will be displayed. diff --git a/src/InnoSetup/MapWinGIS-only.iss b/src/InnoSetup/MapWinGIS-only.iss index 84dae035..75995925 100644 --- a/src/InnoSetup/MapWinGIS-only.iss +++ b/src/InnoSetup/MapWinGIS-only.iss @@ -2,26 +2,25 @@ ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #define MyAppName "MapWinGIS" -#define MyAppVersion "4.9.6.0" +#define MyAppVersion "5.0.0.0" #define MyAppPublisher "MapWindow Open Source GIS Community" #define MyAppURL "http://www.mapwindow.org" #define SetupLocation "D:\dev\MapwinGIS\GitHub\src\InnoSetup" #define BinLocation "D:\dev\MapwinGIS\GitHub\src\bin" -#define x64BitVersion +;; #define x64BitVersion #ifdef x64BitVersion #define CPU "x64" - #define vcredist "vcredist_x64_2013.exe" + #define vcredist "vcredist_x64_2015.exe" #define MySourceDir BinLocation + "\x64\" #define SystemFlag "64bit" #else #define CPU "Win32" - #define vcredist "vcredist_x86-2013.exe" + #define vcredist "vcredist_x86-2015.exe" #define MySourceDir BinLocation + "\Win32\" #define SystemFlag "32bit" #endif - [Setup] ; NOTE: The value of AppId uniquely identifies this application. ; Do not use the same AppId value in installers for other applications. @@ -47,7 +46,7 @@ WizardImageFile={#SetupLocation}\WizImage-MW.bmp WizardSmallImageFile={#SetupLocation}\WizSmallImage-MW.bmp AppCopyright={#MyAppPublisher} PrivilegesRequired=admin -MinVersion=0,5.01sp3 +MinVersion=0,5.01sp3 ChangesEnvironment=yes AlwaysShowDirOnReadyPage=True EnableDirDoesntExistWarning=True @@ -62,9 +61,15 @@ VersionInfoCopyright=Mozilla Public License (MPL) 1.1 VersionInfoDescription=MapWindow Open Source GIS [www.mapwindow.org] VersionInfoProductName={#MyAppName} VersionInfoProductVersion={#MyAppVersion} +VersionInfoVersion={#MyAppVersion} #ifdef x64BitVersion ArchitecturesAllowed=x64 ArchitecturesInstallIn64BitMode=x64 +VersionInfoTextVersion={#MyAppVersion} 64Bit +VersionInfoProductTextVersion={#MyAppVersion} 64Bit +#else +VersionInfoTextVersion={#MyAppVersion} +VersionInfoProductTextVersion={#MyAppVersion} #endif [Files] @@ -73,6 +78,8 @@ Source: "{#MySourceDir}\MapWinGIS.ocx"; DestDir: "{app}"; Flags: ignoreversion { ;; IntelliSense: Source: "{#SetupLocation}\AxInterop.MapWinGIS.XML"; DestDir: "{app}"; Flags: ignoreversion; Components: MapWinGIS_Core Source: "{#SetupLocation}\Interop.MapWinGIS.XML"; DestDir: "{app}"; Flags: ignoreversion; Components: MapWinGIS_Core +;; Delphi TAB file +Source: "{#SetupLocation}\MapWinGIS_TLB.pas"; DestDir: "{app}"; Flags: ignoreversion; Components: MapWinGIS_Delphi ;; Licenses Source: "{#BinLocation}\Licenses\GDALLicense.rtf"; DestDir: "{app}\Licenses\"; Flags: ignoreversion; Components: MapWinGIS_Core Source: "{#BinLocation}\Licenses\GISInternalsLicense.rtf"; DestDir: "{app}\Licenses\"; Flags: ignoreversion; Components: MapWinGIS_Core @@ -119,9 +126,10 @@ Name: "ECW"; Description: "Add ECW & JPEG2000 support"; Types: full custom Name: "MrSID"; Description: "Add MrSID support"; Types: full custom ;; Name: "HDF4"; Description: "Add Hierarchical Data Format Release 4 support"; Types: full custom Name: "HDF5"; Description: "Add Hierarchical Data Format Release 5 support"; Types: full custom +Name: "MapWinGIS_Delphi"; Description: "Delphi Unit Source file (MapWinGIS_TLB.pas). If you don't know what it is you probably won't need it."; Types: full custom [Run] -; Install VC++ 2013 if needed: +; Install VC++ 2015 if needed: #ifdef x64BitVersion Filename: "{tmp}\{#vcredist}"; Parameters: "/quiet"; Flags: waituntilterminated; Check: VCRedistNeedsInstall_x64() #else @@ -129,9 +137,9 @@ Filename: "{tmp}\{#vcredist}"; Parameters: "/quiet"; Flags: waituntilterminated; #endif ;Run some command files: Filename: "{app}\regMapWinGIS.cmd"; WorkingDir: "{app}"; Flags: runhidden -Filename: "http://www.mapwindow.org/documentation/mapwingis4.9/getting_started.html?utm_source=MWv49&utm_medium=cpc&utm_campaign=MWGvInstaller-v{#MyAppVersion}"; Flags: shellexec runasoriginaluser postinstall nowait skipifsilent; Description: "Go to the online documentation" -Filename: "http://www.mapwindow.org/documentation/mapwingis4.9/MapWindow49.html?utm_source=MWv49&utm_medium=cpc&utm_campaign=MWGInstaller-v{#MyAppVersion}"; Flags: shellexec runasoriginaluser postinstall nowait skipifsilent; Description: "Read about the future of MapWinGIS/MapWindow v5" -Filename: "http://www.mapwindow.org/documentation/mapwingis4.9/version_history.html?utm_source=MWv49&utm_medium=cpc&utm_campaign=MWGInstaller-v{#MyAppVersion}"; Flags: shellexec runasoriginaluser postinstall nowait skipifsilent; Description: "Read about the changes in this version" +Filename: "https://www.mapwindow.org/documentation/mapwingis4.9/getting_started.html?utm_source=MWv50&utm_medium=cpc&utm_campaign=MWGvInstaller-v{#MyAppVersion}"; Flags: shellexec runasoriginaluser postinstall nowait skipifsilent; Description: "Go to the online documentation" +Filename: "https://www.mapwindow.org/documentation/mapwingis4.9/MapWindow49.html?utm_source=MWv50&utm_medium=cpc&utm_campaign=MWGInstaller-v{#MyAppVersion}"; Flags: shellexec runasoriginaluser postinstall nowait skipifsilent; Description: "Read about the future of MapWinGIS/MapWindow v5" +Filename: "https://www.mapwindow.org/documentation/mapwingis4.9/version_history.html?utm_source=MWv50&utm_medium=cpc&utm_campaign=MWGInstaller-v{#MyAppVersion}"; Flags: shellexec runasoriginaluser postinstall nowait skipifsilent; Description: "Read about the changes in this version" [UninstallRun] Filename: "{app}\unregMapWinGIS.cmd"; WorkingDir: "{app}"; Flags: runhidden @@ -183,6 +191,9 @@ const VC_2013_REDIST_X86 = '{13A4EE12-23EA-3371-91EE-EFB36DDFFF3E}'; //Microsoft.VS.VC_RuntimeMinimumVSU_x86,v12 VC_2013_REDIST_X64 = '{A749D8E6-B613-3BE3-8F5F-045C84EBA29B}'; //Microsoft.VS.VC_RuntimeMinimumVSU_amd64,v12 + VC_2015_REDIST_X86 = '{8F271F6C-6E7B-3D0A-951B-6E7B694D78BD}'; //Microsoft.VS.VC_RuntimeMinimumVSU_x86,v14 + VC_2015_REDIST_X64 = '{221D6DB4-46E2-333C-B09B-5F49351D0980}'; //Microsoft.VS.VC_RuntimeMinimumVSU_amd64,v14 + function MsiQueryProductState(szProduct: string): INSTALLSTATE; external 'MsiQueryProductState{#AW}@msi.dll stdcall'; @@ -198,7 +209,7 @@ begin // this statement, the following won't install your VC redist only when // the Visual C++ 2008 Redist (x86) and Visual C++ 2008 SP1 Redist(x86) // are installed for the current user - Result := not (VCVersionInstalled(VC_2013_REDIST_X86)); + Result := not (VCVersionInstalled(VC_2015_REDIST_X86)); end; function VCRedistNeedsInstall_x64(): Boolean; @@ -208,7 +219,7 @@ begin // this statement, the following won't install your VC redist only when // the Visual C++ 2008 Redist (x86) and Visual C++ 2008 SP1 Redist(x86) // are installed for the current user - Result := not (VCVersionInstalled(VC_2013_REDIST_X64)); + Result := not (VCVersionInstalled(VC_2015_REDIST_X64)); end; function NeedsAddPath(Param: string): boolean; diff --git a/src/InnoSetup/MapWinGIS_TLB.pas b/src/InnoSetup/MapWinGIS_TLB.pas index 6fa52c65..b2e350a7 100644 Binary files a/src/InnoSetup/MapWinGIS_TLB.pas and b/src/InnoSetup/MapWinGIS_TLB.pas differ diff --git a/src/InnoSetup/ReleaseNotes.rtf b/src/InnoSetup/ReleaseNotes.rtf index c80deadd..aabe4293 100644 Binary files a/src/InnoSetup/ReleaseNotes.rtf and b/src/InnoSetup/ReleaseNotes.rtf differ diff --git a/src/InnoSetup/vcredist_x64_2008_sp1.exe b/src/InnoSetup/vcredist_x64_2008_sp1.exe deleted file mode 100644 index e008d23b..00000000 Binary files a/src/InnoSetup/vcredist_x64_2008_sp1.exe and /dev/null differ diff --git a/src/InnoSetup/vcredist_x64_2010_sp1.exe b/src/InnoSetup/vcredist_x64_2010_sp1.exe deleted file mode 100644 index 834133f8..00000000 Binary files a/src/InnoSetup/vcredist_x64_2010_sp1.exe and /dev/null differ diff --git a/src/InnoSetup/vcredist_x64_2015.exe b/src/InnoSetup/vcredist_x64_2015.exe new file mode 100644 index 00000000..048fda2f Binary files /dev/null and b/src/InnoSetup/vcredist_x64_2015.exe differ diff --git a/src/InnoSetup/vcredist_x86-2015.exe b/src/InnoSetup/vcredist_x86-2015.exe new file mode 100644 index 00000000..caea04ac Binary files /dev/null and b/src/InnoSetup/vcredist_x86-2015.exe differ diff --git a/src/InnoSetup/vcredist_x86_2008_sp1.exe b/src/InnoSetup/vcredist_x86_2008_sp1.exe deleted file mode 100644 index 378e7f03..00000000 Binary files a/src/InnoSetup/vcredist_x86_2008_sp1.exe and /dev/null differ diff --git a/src/InnoSetup/vcredist_x86_2010_sp1.exe b/src/InnoSetup/vcredist_x86_2010_sp1.exe deleted file mode 100644 index d81f3f5a..00000000 Binary files a/src/InnoSetup/vcredist_x86_2010_sp1.exe and /dev/null differ diff --git a/src/MapWinGIS.cpp b/src/MapWinGIS.cpp index ef85ccae..b6190b90 100644 --- a/src/MapWinGIS.cpp +++ b/src/MapWinGIS.cpp @@ -16,6 +16,7 @@ #endif #include "MercatorProjection.h" #include "PrefetchManager.h" +#include "TileCacheManager.h" class CMapWinGISModule : public ATL::CAtlMfcModule @@ -29,8 +30,8 @@ class CMapWinGISModule : static char THIS_FILE[] = __FILE__; const GUID CDECL BASED_CODE _tlid = { 0xc368d713, 0xcc5f, 0x40ed, { 0x9f, 0x53, 0xf8, 0x4f, 0xe1, 0x97, 0xb9, 0x6a } }; -const WORD _wVerMajor = 4; -const WORD _wVerMinor = 9; +const WORD _wVerMajor = 5; +const WORD _wVerMinor = 0; CMapWinGISApp NEAR theApp; CMapWinGISModule _AtlModule; // this one is from ATL7 (used by all ATL co-classes) diff --git a/src/MapWinGIS.idl b/src/MapWinGIS.idl index 6480149e..ecb9fd6c 100644 --- a/src/MapWinGIS.idl +++ b/src/MapWinGIS.idl @@ -4167,6 +4167,9 @@ interface IShapefile : IDispatch{ [id(139)] HRESULT StartAppendMode([out, retval] VARIANT_BOOL* retVal); [id(140)] HRESULT StopAppendMode(); [propget, id(141)] HRESULT AppendMode([out, retval] VARIANT_BOOL* pVal); + [propget, id(142)] HRESULT IsGeographicProjection([out, retval] VARIANT_BOOL* retVal); + [propget, id(143)] HRESULT Selectable([out, retval] VARIANT_BOOL* retVal); + [propput, id(143)] HRESULT Selectable([in] VARIANT_BOOL newVal); }; /**************************** Shape Interface ***********************/ @@ -4265,6 +4268,7 @@ interface IShape : IDispatch{ [id(68)] HRESULT Clear(); [id(69)] HRESULT FixUp2([in] tkUnitsOfMeasure units, [out, retval] IShape** retVal); + [id(70), helpstring("method InterpolatePoint")] HRESULT InterpolatePoint([in] IPoint* startPoint, [in] double distance, [in, optional, defaultvalue(FALSE)] VARIANT_BOOL normalized, [out, retval] IPoint **retVal); }; /**************************** Extents Interface ***********************/ @@ -4596,6 +4600,8 @@ interface IUtils : IDispatch{ [id(61), helpstring("method GetWGS84ProjectionName")] HRESULT GetWGS84ProjectionName([in] tkWgs84Projection projectionID, [out, retval] BSTR* retVal); [id(62), helpstring("method GetProjectionNameByID")] HRESULT GetProjectionNameByID([in] int SRID, [out, retval] BSTR* retVal); [id(63), helpstring("method GetProjectionList")] HRESULT GetProjectionList([in] tkProjectionSet projectionSets, [in, out] VARIANT* list, [out, retval] VARIANT_BOOL* retVal); + [id(64), helpstring("method GetAngle")] HRESULT GetAngle([in] IPoint* firstPoint, [in] IPoint* secondPoint, [out, retval] double* retVal); + [id(65), helpstring("method LineInterpolatePoint")] HRESULT LineInterpolatePoint([in] IShape* sourceLine, [in] IPoint* startPoint, [in] double distance, [in, optional, defaultvalue(FALSE)] VARIANT_BOOL normalized, [out, retval] IPoint **retVal); }; /**************************** Vector Interface ***********************/ @@ -6006,7 +6012,8 @@ interface ITileProviders : IDispatch{ [in]BSTR UrlPattern, [in]tkTileProjection Projection, [in, optional, defaultvalue(0)]int MinZoom, - [in, optional, defaultvalue(17)]int MaxZoom, + [in, optional, defaultvalue(17)]int MaxZoom, + [in, optional, defaultvalue("")]BSTR Copyright, [out,retval] VARIANT_BOOL* retVal); [propget, id(5), helpstring("")] HRESULT Id([in] int Index, [out, retval] LONG* retVal); [propget, id(6), helpstring("")] HRESULT Name([in] int Index, [out, retval] BSTR* retVal); @@ -6619,7 +6626,7 @@ interface IGdalUtils : IDispatch{ } [ - uuid(C368D713-CC5F-40ED-9F53-F84FE197B96A), version(4.9), + uuid(C368D713-CC5F-40ED-9F53-F84FE197B96A), version(5.0), helpfile("MapWinGIS.chm"), helpstring("MapWinGIS Components"), control @@ -6641,7 +6648,7 @@ library MapWinGIS // Primary dispatch interface for CMap [ uuid(1D077739-E866-46A0-B256-8AECC04F2312), - helpstring("Dispatch interface for Map Control"),version(4.9), hidden + helpstring("Dispatch interface for Map Control"),version(5.0), hidden ] dispinterface _DMap { diff --git a/src/MapWinGIS.rc b/src/MapWinGIS.rc index 3dd2687a..9f80bf9c 100644 --- a/src/MapWinGIS.rc +++ b/src/MapWinGIS.rc @@ -112,8 +112,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,9,6,0 - PRODUCTVERSION 4,9,6,0 + FILEVERSION 5,0,0,0 + PRODUCTVERSION 5,0,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -131,13 +131,13 @@ BEGIN VALUE "Comments", "This control includes a mapping component and objects for reading and writing shapefiles and various triangulated irregular network and grid files. It also has extensive label and chart options and includes projection routines." VALUE "CompanyName", "MapWindow OSS Team - www.mapwindow.org" VALUE "FileDescription", "MapWinGIS ActiveX Control" - VALUE "FileVersion", "4.9.6.0" + VALUE "FileVersion", "5.0.0.0" VALUE "InternalName", "MapWinGIS ActiveX Control" - VALUE "LegalCopyright", "Copyright (C) 2004-2017 MapWindow OSS Team" - VALUE "LegalTrademarks", "MapWindow GIS is a trademark of Daniel P. Ames, 2005-2018" + VALUE "LegalCopyright", "Copyright (C) 2004-2019 MapWindow OSS Team" + VALUE "LegalTrademarks", "MapWindow GIS is a trademark of Daniel P. Ames, 2005-2019" VALUE "OriginalFilename", "MapWinGIS.ocx" VALUE "ProductName", "MapWinGIS ActiveX Control" - VALUE "ProductVersion", "4.9.6.0" + VALUE "ProductVersion", "5.0.0.0" END END BLOCK "VarFileInfo" diff --git a/src/MapWinGIS.sln.DotSettings b/src/MapWinGIS.sln.DotSettings new file mode 100644 index 00000000..1f61fb03 --- /dev/null +++ b/src/MapWinGIS.sln.DotSettings @@ -0,0 +1,5 @@ + + <NamingElement Priority="1"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="__interface" /><type Name="class" /><type Name="struct" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></NamingElement> + <NamingElement Priority="8"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="global function" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></NamingElement> + True + True \ No newline at end of file diff --git a/src/MapWinGIS.vcxproj b/src/MapWinGIS.vcxproj index 992c1a97..92893f5e 100644 --- a/src/MapWinGIS.vcxproj +++ b/src/MapWinGIS.vcxproj @@ -1,5 +1,5 @@  - + Release @@ -37,7 +37,7 @@ false false MultiByte - v120 + v140 @@ -92,13 +92,13 @@ Disabled false Default - true + false Speed true true - $(ProjectDir)..\Support\include;$(ProjectDir)..\Support\include\atlhttp;$(ProjectDir)..\support\include\ShapeLib;$(ProjectDir)..\Support\ERDAS-ECW-JPEG-2000-SDK-5.0\include;$(ProjectDir);$(ProjectDir)\COM classes;$(ProjectDir)\ComHelpers;$(ProjectDir)\Control;$(ProjectDir)\Drawing;$(ProjectDir)\Grid;$(ProjectDir)\Image;$(ProjectDir)\Processing;$(ProjectDir)\Shapefile;$(ProjectDir)\ShapeNetwork;$(ProjectDir)\Structures;$(ProjectDir)\Tin;$(ProjectDir)\Utilities;$(ProjectDir)\Grid\fip;$(ProjectDir)\Utilities\SpatialIndex;$(ProjectDir)\Tiles;$(ProjectDir)\Tiles\Providers;$(ProjectDir)\Tiles\Caching;$(ProjectDir)\Tiles\Loaders;$(ProjectDir)\Tiles\Projections;$(ProjectDir)\Tiles\Http;$(ProjectDir)\Utilities\SQLite;$(ProjectDir)..\Support\GDAL_SDK\$(PlatformToolset)\include\$(Platform);$(ProjectDir)\Ogr;$(ProjectDir)\Editor;%(AdditionalIncludeDirectories) + $(ProjectDir)..\Support\include;$(ProjectDir)..\Support\include\atlhttp;$(ProjectDir)..\support\include\ShapeLib;$(ProjectDir);$(ProjectDir)\COM classes;$(ProjectDir)\ComHelpers;$(ProjectDir)\Control;$(ProjectDir)\Drawing;$(ProjectDir)\Grid;$(ProjectDir)\Image;$(ProjectDir)\Processing;$(ProjectDir)\Shapefile;$(ProjectDir)\ShapeNetwork;$(ProjectDir)\Structures;$(ProjectDir)\Tin;$(ProjectDir)\Utilities;$(ProjectDir)\Grid\fip;$(ProjectDir)\Utilities\SpatialIndex;$(ProjectDir)\Tiles;$(ProjectDir)\Tiles\Providers;$(ProjectDir)\Tiles\Caching;$(ProjectDir)\Tiles\Loaders;$(ProjectDir)\Tiles\Projections;$(ProjectDir)\Tiles\Http;$(ProjectDir)\Utilities\SQLite;$(ProjectDir)..\Support\GDAL_SDK\$(PlatformToolset)\include\$(Platform);$(ProjectDir)..\Support\GDAL_SDK\$(PlatformToolset)\include\$(Platform)\curl;$(ProjectDir)\Ogr;$(ProjectDir)\Editor;%(AdditionalIncludeDirectories) _NDBGSYMBOLS;_WINDOWS;_USRDLL;_AFXDLL;_CRT_SECURE_NO_WARNINGS;GEOS_NEW;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) - true + false false Sync Default @@ -132,11 +132,11 @@ true /ignore:4099 /Ignore:4254 %(AdditionalOptions) - cqlib.lib;GdiPlus.lib;gdal_i.lib;version.lib;Crypt32.lib;Dbghelp.lib;SpatialIndex-mw.lib;geos_c.lib;libtiff_i.lib;ShapeLib.lib;%(AdditionalDependencies) + cqlib.lib;GdiPlus.lib;gdal_i.lib;version.lib;Crypt32.lib;Dbghelp.lib;SpatialIndex-mw.lib;geos_c.lib;libtiff_i.lib;ShapeLib.lib;libcurl_imp.lib;%(AdditionalDependencies) NotSet $(OutDir)MapWinGIS.ocx true - $(ProjectDir)..\Support\GDAL_SDK\$(PlatformToolset)\lib\$(Platform);$(ProjectDir)..\Support\lib\$(PlatformToolset)\$(Platform);$(ProjectDir)..\Support\ERDAS-ECW-JPEG-2000-SDK-5.0\lib\$(Platform);%(AdditionalLibraryDirectories) + $(ProjectDir)..\Support\GDAL_SDK\$(PlatformToolset)\lib\$(Platform);$(ProjectDir)..\Support\lib\$(PlatformToolset)\$(Platform)\Release;%(AdditionalLibraryDirectories) false LIBC.lib;%(IgnoreSpecificDefaultLibraries) @@ -168,6 +168,7 @@ if $(PlatformName) == x64 setx MapWinGISBin64 $(OutputPath) WIN32;%(PreprocessorDefinitions) + false /MACHINE:I386 %(AdditionalOptions) @@ -202,7 +203,7 @@ if $(PlatformName) == x64 setx MapWinGISBin64 $(OutputPath) Full false RELEASE_MODE;%(PreprocessorDefinitions) - true + false false diff --git a/src/Ogr/GeosConverter.cpp b/src/Ogr/GeosConverter.cpp index 4a6961fd..14a399b9 100644 --- a/src/Ogr/GeosConverter.cpp +++ b/src/Ogr/GeosConverter.cpp @@ -1,9 +1,29 @@ -#include "stdafx.h" +//******************************************************************************************************** +//File name: GeosConverter.cpp +//Description: +//******************************************************************************************************** +//The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); +//you may not use this file except in compliance with the License. You may obtain a copy of the License at +//http://www.mozilla.org/MPL/ +//Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF +//ANY KIND, either express or implied. See the License for the specific language governing rights and +//limitations under the License. +// +//The Original Code is MapWindow Open Source. +// +//The Initial Developer of this version of the Original Code is Daniel P. Ames using portions created by +//Utah State University and the Idaho National Engineering and Environmental Lab that were released as +//public domain in March 2004. +// +//Contributor(s): (Open source contributors should list themselves and their modifications here). +// ------------------------------------------------------------------------------------------------------- +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper + +#include "StdAfx.h" #include "GeosConverter.h" #include "GeosHelper.h" #include "OgrConverter.h" - // ********************************************************************* // DoBuffer() // ********************************************************************* @@ -15,7 +35,7 @@ GEOSGeometry* DoBuffer(DOUBLE distance, long nQuadSegments, GEOSGeometry* gsGeom } __except (1) { - return NULL; + return nullptr; } } @@ -24,8 +44,8 @@ GEOSGeometry* DoBuffer(DOUBLE distance, long nQuadSegments, GEOSGeometry* gsGeom // ********************************************************************* bool GeosConverter::GeomToShapes(GEOSGeom gsGeom, vector* vShapes, bool isM) { - bool substitute = false; - bool has25D = false; + auto substitute = false; + auto has25D = false; if (!GeosHelper::IsValid(gsGeom)) { @@ -36,7 +56,7 @@ bool GeosConverter::GeomToShapes(GEOSGeom gsGeom, vector* vShapes, bool // would be better to pass units explicitly // Fixing MWGIS-59: Fixup makes new shape larger: // GEOSGeometry* gsNew = DoBuffer(m_globalSettings.GetInvalidShapeBufferDistance(umMeters) / 1000.0, 30, gsGeom); - GEOSGeometry* gsNew = DoBuffer(0, 30, gsGeom); + const auto gsNew = DoBuffer(0, 30, gsGeom); if (gsNew && GeosHelper::IsValid(gsNew)) { gsGeom = gsNew; @@ -44,17 +64,18 @@ bool GeosConverter::GeomToShapes(GEOSGeom gsGeom, vector* vShapes, bool } } - bool result = false; - OGRGeometry* oGeom = GeosHelper::CreateFromGEOS(gsGeom); + auto result = false; + const auto oGeom = GeosHelper::CreateFromGEOS(gsGeom); if (oGeom) { - char* type = GeosHelper::GetGeometryType(gsGeom); - CString s = type; + const auto type = GeosHelper::GetGeometryType(gsGeom); + const CString s = type; GeosHelper::Free(type); - OGRwkbGeometryType oForceType = wkbNone; - if (s == "LinearRing" && oGeom->getGeometryType() != wkbLinearRing) - oForceType = wkbLinearRing; + // PM: It seems oForceType is never used: + // auto oForceType = wkbNone; + // if (s == "LinearRing" && oGeom->getGeometryType() != wkbLinearRing) + // oForceType = wkbLinearRing; result = OgrConverter::GeometryToShapes(oGeom, vShapes, isM, wkbNone, has25D); OGRGeometryFactory::destroyGeometry(oGeom); @@ -72,15 +93,14 @@ bool GeosConverter::GeomToShapes(GEOSGeom gsGeom, vector* vShapes, bool // Converts MapWinGis shape to GEOS geometry GEOSGeom GeosConverter::ShapeToGeom(IShape* shp) { - OGRGeometry* oGeom = OgrConverter::ShapeToGeometry(shp); - if (oGeom != NULL) + const auto oGeom = OgrConverter::ShapeToGeometry(shp); + if (oGeom != nullptr) { - GEOSGeometry* result = GeosHelper::ExportToGeos(oGeom); + const auto result = GeosHelper::ExportToGeos(oGeom); OGRGeometryFactory::destroyGeometry(oGeom); return result; } - else - return NULL; + return nullptr; } // ***************************************************** @@ -90,10 +110,11 @@ GEOSGeom GeosConverter::ShapeToGeom(IShape* shp) GEOSGeometry* GeosConverter::SimplifyPolygon(const GEOSGeometry *gsGeom, double tolerance) { const GEOSGeometry* gsRing = GeosHelper::GetExteriorRing(gsGeom); // no memory is allocated there - GEOSGeom gsPoly = GeosHelper::TopologyPreserveSimplify(gsRing, tolerance); // memory allocation + // ReSharper disable once CppLocalVariableMayBeConst + GEOSGeom gsPoly = GeosHelper::TopologyPreserveSimplify(gsRing, tolerance); // memory allocation if (!gsPoly) - return NULL; + return nullptr; std::vector holes; for (int n = 0; n < GeosHelper::GetNumInteriorRings(gsGeom); n++) @@ -105,7 +126,7 @@ GEOSGeometry* GeosConverter::SimplifyPolygon(const GEOSGeometry *gsGeom, double if (gsOut) { char* type = GeosHelper::GetGeometryType(gsOut); - CString s = type; + const CString s = type; GeosHelper::Free(type); if (s == "LinearRing") holes.push_back(gsOut); @@ -113,14 +134,14 @@ GEOSGeometry* GeosConverter::SimplifyPolygon(const GEOSGeometry *gsGeom, double } } - GEOSGeometry *gsNew = NULL; - if (holes.size() > 0) + GEOSGeometry *gsNew; + if (!holes.empty()) { - gsNew = GeosHelper::CreatePolygon(gsPoly, &(holes[0]), holes.size()); // memory allocation (should be released by caller) + gsNew = GeosHelper::CreatePolygon(gsPoly, &holes[0], holes.size()); // memory allocation (should be released by caller) } else { - gsNew = GeosHelper::CreatePolygon(gsPoly, NULL, 0); + gsNew = GeosHelper::CreatePolygon(gsPoly, nullptr, 0); } return gsNew; } @@ -132,7 +153,7 @@ void GeosConverter::NormalizeSplitResults(GEOSGeometry* result, GEOSGeometry* su { if (!result) return; - int numGeoms = GeosHelper::GetNumGeometries(result); + const int numGeoms = GeosHelper::GetNumGeometries(result); if (numGeoms > 1) { if (shpType == SHP_POLYGON) @@ -152,7 +173,7 @@ void GeosConverter::NormalizeSplitResults(GEOSGeometry* result, GEOSGeometry* su double polyArea; GeosHelper::Area(polygon, &polyArea); - double areaRatio = intersectArea / polyArea; + const double areaRatio = intersectArea / polyArea; if (areaRatio > 0.99 && areaRatio < 1.01) { GEOSGeometry* clone = GeosHelper::CloneGeometry(polygon); if (clone) { @@ -180,18 +201,18 @@ void GeosConverter::NormalizeSplitResults(GEOSGeometry* result, GEOSGeometry* su // Returns GEOS geometry which is result of the union operation for the geometries passed GEOSGeometry* GeosConverter::MergeGeometries(vector& data, ICallback* callback, bool deleteInput /*= true*/, bool displayProgress /*= true*/) { - if (data.size() == 0) - return NULL; + if (data.empty()) + return nullptr; USES_CONVERSION; - GEOSGeometry* g1 = NULL; - GEOSGeometry* g2 = NULL; + GEOSGeometry* g1 = nullptr; + GEOSGeometry* g2 = nullptr; bool stop = false; int count = 0; // number of union operation performed long percent = 0; - int size = data.size(); + const int size = data.size(); int depth = 0; if (size == 1) @@ -199,11 +220,8 @@ GEOSGeometry* GeosConverter::MergeGeometries(vector& data, ICallb // no need for calculation if (deleteInput) return data[0]; // no need to clone; it will be exactly the same - else - { - GEOSGeometry* geomTemp = GeosHelper::CloneGeometry(data[0]); - return geomTemp; - } + GEOSGeometry* geomTemp = GeosHelper::CloneGeometry(data[0]); + return geomTemp; } while (!stop) @@ -212,20 +230,20 @@ GEOSGeometry* GeosConverter::MergeGeometries(vector& data, ICallb for (int i = 0; i < size; i++) { - if (data[i] != NULL) + if (data[i] != nullptr) { if (!g1) { g1 = data[i]; - data[i] = NULL; + data[i] = nullptr; } else { g2 = data[i]; - data[i] = NULL; + data[i] = nullptr; } - if (g2 != NULL) + if (g2 != nullptr) { GEOSGeometry* geom = GeosHelper::Union(g1, g2); data[i] = geom; // placing the resulting geometry back for further processing @@ -238,8 +256,8 @@ GEOSGeometry* GeosConverter::MergeGeometries(vector& data, ICallb GeosHelper::DestroyGeometry(g2); } - g1 = NULL; - g2 = NULL; + g1 = nullptr; + g2 = nullptr; count++; stop = false; // in case there is at least one union occurred, we shall run once more @@ -248,7 +266,7 @@ GEOSGeometry* GeosConverter::MergeGeometries(vector& data, ICallb } // it is the last geometry, unpaired one, not the only one, it's the initial and must not be deleted - if (i == size - 1 && stop == false && g2 == NULL && g1 != NULL && depth == 0 && !deleteInput) + if (i == size - 1 && !stop && g2 == nullptr && g1 != nullptr && depth == 0 && !deleteInput) { // we need to clone it, to be able to apply unified memory management afterwards // when depth > 0 all interim geometries are deleted, while this one should be preserved diff --git a/src/Ogr/GeosHelper.h b/src/Ogr/GeosHelper.h index e33d22a7..f043ddd0 100644 --- a/src/Ogr/GeosHelper.h +++ b/src/Ogr/GeosHelper.h @@ -441,4 +441,28 @@ class GeosHelper return GEOSGeom_getCoordSeq(gs); #endif } + + static GEOSGeometry* Interpolate(GEOSGeometry* gs, double d, bool normalized = false) + { +#ifdef GEOS_NEW + if (normalized) + return GEOSInterpolateNormalized_r(getGeosHandle(), gs, d); + else + return GEOSInterpolate_r(getGeosHandle(), gs, d); +#else + if (normalized) + return GEOSInterpolateNormalized(gs, d); + else + return GEOSInterpolate(gs, d); +#endif + } + + static double Project(GEOSGeometry* g1, GEOSGeometry* g2) + { +#ifdef GEOS_NEW + return GEOSProject_r(getGeosHandle(), g1, g2); +#else + return GEOSProject(g1, g2); +#endif + } }; diff --git a/src/Ogr/Ogr2RawData.cpp b/src/Ogr/Ogr2RawData.cpp index eb4c4305..0a9875dc 100644 --- a/src/Ogr/Ogr2RawData.cpp +++ b/src/Ogr/Ogr2RawData.cpp @@ -86,7 +86,7 @@ bool Ogr2RawData::Layer2RawData(OGRLayer* layer, Extent* extents, OgrDynamicLoad break; } - vector fields; + vector fields; if ((generateLabels || categories.size() > 0) && (!loader->HaveWaitingTasks())) { if (!OgrHelper::GetFieldList(layer, fields)) { @@ -97,7 +97,7 @@ bool Ogr2RawData::Layer2RawData(OGRLayer* layer, Extent* extents, OgrDynamicLoad lock.Unlock(); if (generateLabels) { - CString error; + CStringW error; GenerateLabels(list, fields, loader->LabelExpression, error, loader); } @@ -131,7 +131,7 @@ bool Ogr2RawData::Layer2RawData(OGRLayer* layer, Extent* extents, OgrDynamicLoad // ************************************************************* // ApplyCategories() // ************************************************************* -void Ogr2RawData::ApplyCategories(vector& data, vector& fields, vector& categories, OgrDynamicLoader* loader) +void Ogr2RawData::ApplyCategories(vector& data, vector& fields, vector& categories, OgrDynamicLoader* loader) { for (size_t i = 0; i < categories.size(); i++) { @@ -177,7 +177,7 @@ void Ogr2RawData::ApplyCategories(vector& data, vector& data, vector& fields) +int Ogr2RawData::GetLabelFieldIndex(CStringW expression, vector& fields) { expression = expression.Trim(); if (expression.Mid(0, 1) == L"[" && expression.Mid(expression.GetLength() - 1, 1) == L"]") @@ -279,7 +279,7 @@ bool Ogr2RawData::PopulateExpressionFields(vector& data, int r // ************************************************************* // FieldsToShapeRecord() // ************************************************************* -bool Ogr2RawData::GenerateLabels(vector& data, vector& fields, CStringW expression, CString& error, OgrDynamicLoader* loader ) +bool Ogr2RawData::GenerateLabels(vector& data, vector& fields, CStringW expression, CStringW& error, OgrDynamicLoader* loader ) { if (expression.GetLength() == 0) return false; @@ -287,7 +287,7 @@ bool Ogr2RawData::GenerateLabels(vector& data, vector // maybe it's single field expression; no need for parsing then USES_CONVERSION; - int fieldIndex = GetLabelFieldIndex(W2A(expression), fields); + int fieldIndex = GetLabelFieldIndex(expression, fields); if (fieldIndex != -1) { for (size_t i = 0; i < data.size(); i++) @@ -319,7 +319,7 @@ bool Ogr2RawData::GenerateLabels(vector& data, vector CustomExpression expr; expr.SetFields(fields); - CString err; + CStringW err; if (expr.Parse(W2A(expression), true, error)) { diff --git a/src/Ogr/Ogr2RawData.h b/src/Ogr/Ogr2RawData.h index 8e7f56ad..4f174de3 100644 --- a/src/Ogr/Ogr2RawData.h +++ b/src/Ogr/Ogr2RawData.h @@ -11,10 +11,10 @@ class Ogr2RawData static bool Layer2RawData(OGRLayer* layer, Extent* extents, OgrDynamicLoader* loader, vector& categories, OgrLoadingTask* callback); private: static void FieldsToShapeRecord(OGRFeatureDefn* poFields, OGRFeature* poFeature, ShapeRecordData* data, bool hasFid, bool hasLabels, OgrLabelsHelper::LabelFields& labelFields); - static bool GenerateLabels(vector& data, vector& fields, CStringW expression, CString& error, OgrDynamicLoader* loader); + static bool GenerateLabels(vector& data, vector& fields, CStringW expression, CStringW& error, OgrDynamicLoader* loader); static void UpdateLabelsAndCategories(vector&data, OgrDynamicLoader* loader, bool hasLabels); - static int GetLabelFieldIndex(CString expression, vector& fields); - static void ApplyCategories(vector& data, vector& fields, vector& categories, OgrDynamicLoader* loader); + static int GetLabelFieldIndex(CStringW expression, vector& fields); + static void ApplyCategories(vector& data, vector& fields, vector& categories, OgrDynamicLoader* loader); static bool PopulateExpressionFields(vector& data, int rowIndex, CustomExpression& expr); }; diff --git a/src/Ogr/Ogr2Shape.cpp b/src/Ogr/Ogr2Shape.cpp index 443e2b05..307c70d0 100644 --- a/src/Ogr/Ogr2Shape.cpp +++ b/src/Ogr/Ogr2Shape.cpp @@ -262,15 +262,15 @@ bool Ogr2Shape::FillShapefile(OGRLayer* layer, IShapefile* sf, int maxFeatureCou } // is the specified character one of the valid XBase Logical characters -bool isXBaseLogicalChar(char c) +bool isXBaseLogicalChar(wchar_t c) { - return (c == 'Y' || c == 'N' || c == 'T' || c == 'F' || c == '?'); // || c == 'y' || c == 'n' || c == 't' || c == 'f'); + return (c == L'Y' || c == L'N' || c == L'T' || c == L'F' || c == L'?'); // || c == 'y' || c == 'n' || c == 't' || c == 'f'); } // is the specified character one of the valid XBase Logical characters indicating TRUE -bool isXBaseLogicalTrue(char c) +bool isXBaseLogicalTrue(wchar_t c) { - return (c == 'Y' || c == 'T'); // || c == 'y' || c == 't'); + return (c == L'Y' || c == L'T'); // || c == 'y' || c == 't'); } // ************************************************************* @@ -324,7 +324,8 @@ void Ogr2Shape::CopyValues(OGRFeatureDefn* poFields, OGRFeature* poFeature, ISha else if (type == OFTString) { // preview string - CString str(poFeature->GetFieldAsString(iFld)); + // NOTE that it is presumed that ALL strings coming from OGR can be interpreted as UTF-8 + CStringW str = Utility::ConvertFromUtf8(poFeature->GetFieldAsString(iFld)); // OGR does not currently support the Logical (boolean) field type. It is possible that they will exist // in the file, but OGR will interpret them as Strings. Since we support boolean field types, we want // to have a way of copying these particular string fields and interpreting them as booleans. We will @@ -344,7 +345,7 @@ void Ogr2Shape::CopyValues(OGRFeatureDefn* poFields, OGRFeature* poFeature, ISha { // else accept as a string var.vt = VT_BSTR; - var.bstrVal = A2BSTR(poFeature->GetFieldAsString(iFld)); // BSTR will be cleared by CComVariant destructor + var.bstrVal = W2BSTR(str); // BSTR will be cleared by CComVariant destructor } } } diff --git a/src/Ogr/OgrHelper.cpp b/src/Ogr/OgrHelper.cpp index 1a1146dd..2c092b8d 100644 --- a/src/Ogr/OgrHelper.cpp +++ b/src/Ogr/OgrHelper.cpp @@ -204,14 +204,14 @@ bool OgrHelper::IsMsSqlDatasource(GDALDataset* ds) // ************************************************************* // GetFieldList() // ************************************************************* -bool OgrHelper::GetFieldList(OGRLayer* layer, vector& fields) +bool OgrHelper::GetFieldList(OGRLayer* layer, vector& fields) { if (!layer) return false; fields.clear(); OGRFeatureDefn *poFields = layer->GetLayerDefn(); - CString s = layer->GetFIDColumn(); + CStringW s = layer->GetFIDColumn(); if (s.GetLength() > 0) { fields.push_back(s); } diff --git a/src/Ogr/OgrHelper.h b/src/Ogr/OgrHelper.h index bf518557..98013883 100644 --- a/src/Ogr/OgrHelper.h +++ b/src/Ogr/OgrHelper.h @@ -15,7 +15,7 @@ class OgrHelper static bool IsMsSqlDatasource(GDALDataset* ds); static OGRFieldType GetFieldType(IField* fld); static FieldType GetFieldType(OGRFieldType ogrType); - static bool GetFieldList(OGRLayer* layer, vector& fields); + static bool GetFieldList(OGRLayer* layer, vector& fields); static void GetFieldValues(OGRLayer* layer, int featureCount, OGRFieldType fieldType, vector& values, ICallback* cback); static COgrLayer* Cast(CComPtr& layer); static ShpfileType ShapeType2D(IOgrLayer* layer); diff --git a/src/Processing/CustomExpression.cpp b/src/Processing/CustomExpression.cpp index 0d99a89c..357e01ff 100644 --- a/src/Processing/CustomExpression.cpp +++ b/src/Processing/CustomExpression.cpp @@ -31,7 +31,7 @@ // ******************************************************************* // Calculate() // ******************************************************************* -CExpressionValue* CustomExpression::Calculate(CString& errorMessage) +CExpressionValue* CustomExpression::Calculate(CStringW& errorMessage) { Reset(); @@ -81,7 +81,7 @@ CExpressionValue* CustomExpression::Calculate(CString& errorMessage) // ******************************************************************* // EvaluatePart() // ******************************************************************* -bool CustomExpression::EvaluatePart(CExpressionPart* part, CString& errorMessage, int& operationCount) +bool CustomExpression::EvaluatePart(CExpressionPart* part, CStringW& errorMessage, int& operationCount) { do { @@ -150,7 +150,7 @@ bool CustomExpression::EvaluateFunction(CExpressionPart* part) // ******************************************************************* // CalculateNextOperationWithinPart() // ******************************************************************* -bool CustomExpression::CalculateNextOperationWithinPart(CExpressionPart* part, CString& errorMessage, int& operationCount) +bool CustomExpression::CalculateNextOperationWithinPart(CExpressionPart* part, CStringW& errorMessage, int& operationCount) { bool needReleaseOperation = false; COperation operation; @@ -754,7 +754,7 @@ bool CustomExpression::ReadFieldNames(ITable* tbl) USES_CONVERSION; CComBSTR bstr; fld->get_Name(&bstr); - CString str = OLE2CA(bstr); + CStringW str = OLE2CW(bstr); _fields.push_back(str.MakeLower()); fld->Release(); } @@ -766,7 +766,7 @@ bool CustomExpression::ReadFieldNames(ITable* tbl) // ************************************************************ // SetFields() //************************************************************ -void CustomExpression::SetFields(vector& fields) +void CustomExpression::SetFields(vector& fields) { _fields.clear(); _fields.insert(_fields.end(), fields.begin(), fields.end()); @@ -863,7 +863,7 @@ void CustomExpression::ReleaseArrays() // building list of operation; // UseFields: true - only fields form attribute table; // false - variables, the values of which must be set -bool CustomExpression::Parse(CString s, bool useFields, CString& errorMessage) +bool CustomExpression::Parse(CStringW s, bool useFields, CStringW& errorMessage) { Clear(); @@ -906,7 +906,7 @@ void CustomExpression::put_FieldValue(int FieldId, BSTR newVal) _variables[FieldId]->val->str(OLE2W(newVal)); } -void CustomExpression::put_FieldValue(int FieldId, CString newVal) { +void CustomExpression::put_FieldValue(int FieldId, CStringW newVal) { USES_CONVERSION; - _variables[FieldId]->val->str(A2W(newVal)); + _variables[FieldId]->val->str(newVal); } diff --git a/src/Processing/CustomExpression.h b/src/Processing/CustomExpression.h index a471010f..d803f360 100644 --- a/src/Processing/CustomExpression.h +++ b/src/Processing/CustomExpression.h @@ -45,12 +45,12 @@ class CustomExpression private: std::vector _parts; std::vector _variables; - std::vector _fields; + std::vector _fields; std::vector _operations; - std::vector _strings; + std::vector _strings; bool _useFields; bool _saveOperations; - CString _errorMessage; // the description of error + CStringW _errorMessage; // the description of error int _errorPosition; // the position of error CString _floatFormat; IShape* _shape; @@ -64,10 +64,10 @@ class CustomExpression bool EvaluateFunction(CExpressionPart* part); void EvaluatePart(); - bool EvaluatePart(CExpressionPart* part, CString& errorMessage, int& operationCount); + bool EvaluatePart(CExpressionPart* part, CStringW& errorMessage, int& operationCount); bool FinishPart(CExpressionPart* part); - bool CalculateNextOperationWithinPart(CExpressionPart* part, CString& errorMessage, int& operationCount); + bool CalculateNextOperationWithinPart(CExpressionPart* part, CStringW& errorMessage, int& operationCount); void SetFieldValues(ITable* tbl); void BuildFieldList(); @@ -78,37 +78,37 @@ class CustomExpression public: bool IsEmpty() { return _parts.size() == 0; } - bool Parse(CString s, bool useFields, CString& error); - CExpressionValue* Calculate(CString& errorMessage); + bool Parse(CStringW s, bool useFields, CStringW& error); + CExpressionValue* Calculate(CStringW& errorMessage); void Clear(); void ReleaseArrays(); bool ReadFieldNames(ITable* tbl); - void SetFields(vector& fields); + void SetFields(vector& fields); void AddPart(CExpressionPart* part) { _parts.push_back(part); } CString GetFloatFormat() { return _floatFormat; } void SetFloatFormat(CString value) { _floatFormat = value; } bool GetUseFields() { return _useFields; } - vector* GetStrings() { return &_strings; } - vector* GetFields() { return &_fields; } + vector* GetStrings() { return &_strings; } + vector* GetFields() { return &_fields; } // variable fields int get_NumFields() { return _variables.size(); } int get_FieldIndex(int FieldId) { return _variables[FieldId]->fieldIndex; } - CString get_FieldName(int FieldId) { return _variables[FieldId]->fieldName; } + CStringW get_FieldName(int FieldId) { return _variables[FieldId]->fieldName; } CExpressionValue* get_FieldValue(int FieldId) { return _variables[FieldId]->val;} void put_FieldValue(int FieldId, double newVal) { _variables[FieldId]->val->dbl(newVal); } void put_FieldValue(int FieldId, BSTR newVal); - void put_FieldValue(int FieldId, CString newVal); + void put_FieldValue(int FieldId, CStringW newVal); void put_FieldValue(int FieldId, bool newVal) { _variables[FieldId]->val->bln(newVal);} int get_PartCount() { return _parts.size(); } IShape* get_Shape(); void put_Shape(IShape* shape); - void SetErrorMessage(CString msg) { _errorMessage = msg; } + void SetErrorMessage(CStringW msg) { _errorMessage = msg; } void SetErrorPosition(int position){ _errorPosition = position; } void ClearOperations(); void CacheOperation(COperation& operation); diff --git a/src/Processing/ExpressionParser.cpp b/src/Processing/ExpressionParser.cpp index 6193abbc..5e7d3f1d 100644 --- a/src/Processing/ExpressionParser.cpp +++ b/src/Processing/ExpressionParser.cpp @@ -5,23 +5,23 @@ // ******************************************************************* // IsDecimal() // ******************************************************************* -bool ExpressionParser::IsDecimal(char chr, bool& exponential) +bool ExpressionParser::IsDecimal(wchar_t chr, bool& exponential) { - if (chr >= '0' && chr <= '9') + if (chr >= L'0' && chr <= L'9') return true; - if ((chr == '.') || (chr == ',')) // specify decimal separator explicitly + if ((chr == L'.') || (chr == L',')) // specify decimal separator explicitly { return true; } - if ((chr == 'e') || (chr == 'E')) + if ((chr == L'e') || (chr == L'E')) { exponential = true; return true; } - if (exponential && (chr == '+' || chr == '-')) + if (exponential && (chr == L'+' || chr == L'-')) { exponential = false; return true; @@ -34,14 +34,14 @@ bool ExpressionParser::IsDecimal(char chr, bool& exponential) // ******************************************************************* // IsDecimal() // ******************************************************************* -bool ExpressionParser::IsDecimal(CString& str) +bool ExpressionParser::IsDecimal(CStringW& str) { for (int i = 0; i < str.GetLength(); i++) { - if ((str[i] >= '0') && (str[i] < '9')) + if ((str[i] >= L'0') && (str[i] < L'9')) continue; - if ((str[i] == '.') || (str[i] == ',') || (str[i] == 'e') || (str[i] == 'E')) // specify decimal separator explicitly + if ((str[i] == L'.') || (str[i] == L',') || (str[i] == L'e') || (str[i] == L'E')) // specify decimal separator explicitly continue; return false; @@ -53,11 +53,11 @@ bool ExpressionParser::IsDecimal(CString& str) // ******************************************************************* // IsDecimalZero() // ******************************************************************* -bool ExpressionParser::IsDecimalZero(CString& str) +bool ExpressionParser::IsDecimalZero(CStringW& str) { for (int i = 0; i < str.GetLength(); i++) { - if ((str[i] != '0') && (str[i] != '.') && (str[i] != ',')) // TODO: check decimal separator in more general way + if ((str[i] != L'0') && (str[i] != L'.') && (str[i] != L',')) // TODO: check decimal separator in more general way return false; } return true; @@ -66,11 +66,11 @@ bool ExpressionParser::IsDecimalZero(CString& str) // ******************************************************************* // IsInteger() // ******************************************************************* -bool ExpressionParser::IsInteger(CString& str) +bool ExpressionParser::IsInteger(CStringW& str) { for (int i = 0; i < str.GetLength(); i++) { - if (str[i] < '0' || str[i] > '9') + if (str[i] < L'0' || str[i] > L'9') return false; } return true; @@ -79,20 +79,20 @@ bool ExpressionParser::IsInteger(CString& str) // ******************************************************************* // IsOperator() // ******************************************************************* -bool ExpressionParser::IsOperator(char s) +bool ExpressionParser::IsOperator(wchar_t s) { switch (s) { - case '<': - case '>' : - case '=' : - case '+' : - case '-' : - case '*' : - case '/' : - case '\\': - case '^': - case ':': + case L'<': + case L'>' : + case L'=' : + case L'+' : + case L'-' : + case L'*' : + case L'/' : + case L'\\': + case L'^': + case L':': return true; } @@ -102,15 +102,15 @@ bool ExpressionParser::IsOperator(char s) // ******************************************************************* // IsFunctionName() // ******************************************************************* -bool ExpressionParser::IsFunctionName(char s) +bool ExpressionParser::IsFunctionName(wchar_t s) { - return isalnum(s) || s == '$' || s == '_'; + return isalnum(s) || s == L'$' || s == L'_'; } // ******************************************************************* // IsOperator() // ******************************************************************* -bool ExpressionParser::IsOperator(CString s) +bool ExpressionParser::IsOperator(CStringW s) { if (s.GetLength() == 0) { @@ -120,10 +120,10 @@ bool ExpressionParser::IsOperator(CString s) { s = s.MakeUpper(); - if (s.Compare("AND") == 0 || - s.Compare("OR") == 0 || - s.Compare("XOR") == 0 || - s.Compare("MOD") == 0) + if (s.Compare(L"AND") == 0 || + s.Compare(L"OR") == 0 || + s.Compare(L"XOR") == 0 || + s.Compare(L"MOD") == 0) { return true; } @@ -137,7 +137,7 @@ bool ExpressionParser::IsOperator(CString s) // ***************************************************************** // building list of operation; UseFields: true - only fields form attribute table; // false - variables, the values of which must be set -bool ExpressionParser::Parse(CustomExpression* expression, CString s, bool useFields) +bool ExpressionParser::Parse(CustomExpression* expression, CStringW s, bool useFields) { _expression = expression; @@ -163,7 +163,7 @@ bool ExpressionParser::Parse(CustomExpression* expression, CString s, bool useFi return false; } - s.Replace(" ", ""); + s.Replace(L" ", L""); if (!ParseTree(s)) { @@ -176,10 +176,10 @@ bool ExpressionParser::Parse(CustomExpression* expression, CString s, bool useFi // ***************************************************************** // ParseTree() // ***************************************************************** -bool ExpressionParser::ParseTree(CString s) +bool ExpressionParser::ParseTree(CStringW s) { bool found = true; - CString temp; + CStringW temp; while (found) { @@ -212,7 +212,7 @@ bool ExpressionParser::ParseTree(CString s) } } - CString expression = found ? GetInnerString(s, begin, end) : s; + CStringW expression = found ? GetInnerString(s, begin, end) : s; CExpressionPart* part = ParseExpressionPart(expression); @@ -237,7 +237,7 @@ bool ExpressionParser::ParseTree(CString s) // ******************************************************************* // GetInnerString() // ******************************************************************* -CString ExpressionParser::GetInnerString(CString& s, int begin, int end) +CStringW ExpressionParser::GetInnerString(CStringW& s, int begin, int end) { return s.Mid(begin + 1, end - begin - 1); } @@ -246,7 +246,7 @@ CString ExpressionParser::GetInnerString(CString& s, int begin, int end) // GetBrackets() // ******************************************************************* // Returns positions of the first inner brackets -bool ExpressionParser::GetBrackets(CString expression, int& begin, int& end, CString openingSymbol, CString closingSymbol) +bool ExpressionParser::GetBrackets(CStringW expression, int& begin, int& end, CStringW openingSymbol, CStringW closingSymbol) { // closing bracket end = -1; @@ -279,11 +279,11 @@ bool ExpressionParser::GetBrackets(CString expression, int& begin, int& end, CSt // ParseFunction() //************************************************************ // Tries to parse function name from the position of the opening bracket going backwards -CustomFunction* ExpressionParser::ParseFunction(CString& s, int begin, int& fnBegin) +CustomFunction* ExpressionParser::ParseFunction(CStringW& s, int begin, int& fnBegin) { int i = begin; - CString sub; + CStringW sub; // reading function name from the end while (i >= 0) @@ -310,7 +310,7 @@ CustomFunction* ExpressionParser::ParseFunction(CString& s, int begin, int& fnBe // ************************************************************ // ParseArgumentList() //************************************************************ -bool ExpressionParser::ParseArgumentList(CString s, CustomFunction* fn) +bool ExpressionParser::ParseArgumentList(CStringW s, CustomFunction* fn) { if (!fn) return false; @@ -319,9 +319,9 @@ bool ExpressionParser::ParseArgumentList(CString s, CustomFunction* fn) part->function = fn; int pos = 0; - CString ct; + CStringW ct; - ct = s.Tokenize(";", pos); + ct = s.Tokenize(L";", pos); while (ct.GetLength() != 0) { @@ -343,10 +343,10 @@ bool ExpressionParser::ParseArgumentList(CString s, CustomFunction* fn) return false; } - ct = s.Tokenize(";", pos); + ct = s.Tokenize(L";", pos); }; - CString errorMessage; + CStringW errorMessage; if (!fn->CheckArguments(part->arguments.size(), errorMessage)) { SetErrorMessage(errorMessage); @@ -362,7 +362,7 @@ bool ExpressionParser::ParseArgumentList(CString s, CustomFunction* fn) // ParseExpressionPart // ***************************************************************** // Creates elements from the part of expression in the brackets -CExpressionPart* ExpressionParser::ParseExpressionPart(CString s) +CExpressionPart* ExpressionParser::ParseExpressionPart(CStringW s) { bool readVal = true; // true - reading values and unary operations; false - reading binary operations @@ -398,14 +398,14 @@ CExpressionPart* ExpressionParser::ParseExpressionPart(CString s) if (part->elements.size() == 0) { - SetErrorMessage("Expression part is empty"); + SetErrorMessage(L"Expression part is empty"); delete part; return NULL; } if (part->elements[part->elements.size() - 1]->type == etOperation) { - SetErrorMessage("Operator doesn't have right operand."); + SetErrorMessage(L"Operator doesn't have right operand."); delete part; return NULL; } @@ -417,15 +417,15 @@ CExpressionPart* ExpressionParser::ParseExpressionPart(CString s) // ReadValue() // ******************************************************************* // Parses string from the given position -bool ExpressionParser::ReadValue(CString s, int& position, CElement* element) +bool ExpressionParser::ReadValue(CStringW s, int& position, CElement* element) { USES_CONVERSION; - CString sub; // substring - char chr = s[position]; + CStringW sub; // substring + wchar_t chr = s[position]; switch (chr) { - case '{': + case L'{': { sub = ""; @@ -445,7 +445,7 @@ bool ExpressionParser::ReadValue(CString s, int& position, CElement* element) // writing the number of bracket if (IsInteger(sub)) { - unsigned int index = atoi(LPCTSTR(sub)); + unsigned int index = _wtoi((LPCWSTR)sub); sub = (*_expression->GetStrings())[index]; } else @@ -475,9 +475,9 @@ bool ExpressionParser::ReadValue(CString s, int& position, CElement* element) { // searching the field element->fieldIndex = -1; - CString str = sub.MakeLower(); + CStringW str = sub.MakeLower(); - vector* fields = _expression->GetFields(); + vector* fields = _expression->GetFields(); for (unsigned int i = 0; i < fields->size(); i++) { @@ -491,7 +491,7 @@ bool ExpressionParser::ReadValue(CString s, int& position, CElement* element) } if (element->fieldIndex == -1) { - SetErrorMessage("Field wasn't found: " + sub); + SetErrorMessage(L"Field wasn't found: " + sub); return false; } } @@ -504,13 +504,13 @@ bool ExpressionParser::ReadValue(CString s, int& position, CElement* element) } break; } - case '"': + case L'"': { sub = ""; position++; chr = s[position]; - while ((position < s.GetLength() - 1) && (chr != '"')) + while ((position < s.GetLength() - 1) && (chr != L'"')) { sub += chr; position++; @@ -519,20 +519,20 @@ bool ExpressionParser::ReadValue(CString s, int& position, CElement* element) chr = s[position]; } element->type = etValue; - element->val->str(A2W(sub)); + element->val->str(sub); break; } - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': + case L'0': + case L'1': + case L'2': + case L'3': + case L'4': + case L'5': + case L'6': + case L'7': + case L'8': + case L'9': { sub = ""; bool exponential = false; @@ -546,7 +546,7 @@ bool ExpressionParser::ReadValue(CString s, int& position, CElement* element) } position--; - double val = Utility::atof_custom((LPCTSTR)sub); + double val = Utility::wtof_custom((LPCWSTR)sub); if (val != 0.0) { element->type = etValue; @@ -561,32 +561,32 @@ bool ExpressionParser::ReadValue(CString s, int& position, CElement* element) } else { - SetErrorMessage("The value is not a number: " + sub); + SetErrorMessage(L"The value is not a number: " + sub); } } break; } - case 'T': - case 't': - if (s.Mid(position, 4).MakeLower() == "true") + case L'T': + case L't': + if (s.Mid(position, 4).MakeLower() == L"true") { position += 3; element->val->bln(true); element->type = etValue; } break; - case 'F': - case 'f': - if (s.Mid(position, 5).MakeLower() == "false") + case L'F': + case L'f': + if (s.Mid(position, 5).MakeLower() == L"false") { position += 4; element->val->bln(false); element->type = etValue; } break; - case 'N': - case 'n': - if (s.Mid(position, 3).MakeLower() == "not") + case L'N': + case L'n': + if (s.Mid(position, 3).MakeLower() == L"not") { position += 2; element->type = etOperation; @@ -594,13 +594,13 @@ bool ExpressionParser::ReadValue(CString s, int& position, CElement* element) element->operation = operNOT; } break; - case '-': + case L'-': element->type = etOperation; element->priority = 3; element->operation = operChangeSign; element->type = etOperation; break; - case '&': + case L'&': position++; chr = s[position]; @@ -618,10 +618,10 @@ bool ExpressionParser::ReadValue(CString s, int& position, CElement* element) if (IsInteger(sub)) { element->type = etValue; - unsigned int index = atoi(LPCTSTR(sub)); + unsigned int index = _wtoi(LPCWSTR(sub)); - vector* strings = _expression->GetStrings(); - element->val->str(index < strings->size() ? A2W((*strings)[index]) : L""); + vector* strings = _expression->GetStrings(); + element->val->str(index < strings->size() ? (*strings)[index] : L""); } else { @@ -630,7 +630,7 @@ bool ExpressionParser::ReadValue(CString s, int& position, CElement* element) break; - case '#': + case L'#': sub = ""; position++; chr = s[position]; @@ -648,7 +648,7 @@ bool ExpressionParser::ReadValue(CString s, int& position, CElement* element) // writing the number of bracket if (IsInteger(sub)) { - element->partIndex = atoi(LPCTSTR(sub)); + element->partIndex = _wtoi(LPCWSTR(sub)); element->type = etPart; } else @@ -658,7 +658,7 @@ bool ExpressionParser::ReadValue(CString s, int& position, CElement* element) break; default: - SetErrorMessage("Operand expected. The character found: " + s.Mid(position, 1)); + SetErrorMessage(L"Operand expected. The character found: " + s.Mid(position, 1)); SetErrorPosition(position); return false; } @@ -669,21 +669,21 @@ bool ExpressionParser::ReadValue(CString s, int& position, CElement* element) // ReadOperation() // ******************************************************************* // Reading the operation from the given position -bool ExpressionParser::ReadOperation(CString s, int& position, CElement& element) +bool ExpressionParser::ReadOperation(CStringW s, int& position, CElement& element) { - char chr = s[position]; + wchar_t chr = s[position]; switch (chr) { - case '<': // <, <>, <, "<:>" - if (s.Mid(position, 2) == "<=") + case L'<': // <, <>, <, "<:>" + if (s.Mid(position, 2) == L"<=") { element.type = etOperation; element.priority = 4; element.operation = operLessEqual; position++; } - else if (s.Mid(position, 2) == "<>") + else if (s.Mid(position, 2) == L"<>") { element.type = etOperation; element.priority = 4; @@ -698,9 +698,9 @@ bool ExpressionParser::ReadOperation(CString s, int& position, CElement& element } break; - case '>': // >, >= + case L'>': // >, >= - if (s.Mid(position, 2) == ">=") + if (s.Mid(position, 2) == L">=") { element.type = etOperation; element.priority = 4; @@ -714,65 +714,65 @@ bool ExpressionParser::ReadOperation(CString s, int& position, CElement& element element.operation = operGreater; } break; - case '=': + case L'=': { element.type = etOperation; element.priority = 4; element.operation = operEqual; break; } - case '+': + case L'+': { element.type = etOperation; element.priority = 3; element.operation = operPlus; break; } - case '-': + case L'-': { element.type = etOperation; element.priority = 3; element.operation = operMinus; break; } - case '*': + case L'*': { element.type = etOperation; element.priority = 2; element.operation = operMult; break; } - case '/': + case L'/': { element.type = etOperation; element.priority = 2; element.operation = operDiv; break; } - case '\\': + case L'\\': { element.type = etOperation; element.priority = 2; element.operation = operDivInt; break; } - case '^': + case L'^': { element.type = etOperation; element.priority = 1; element.operation = operExpon; break; } - case ':': + case L':': { element.type = etOperation; element.priority = 7; element.operation = operCONSEQ; break; } - case 'l': - case 'L': - if (s.Mid(position, 4).MakeUpper() == "LIKE") + case L'l': + case L'L': + if (s.Mid(position, 4).MakeUpper() == L"LIKE") { element.type = etOperation; element.priority = 3; @@ -780,9 +780,9 @@ bool ExpressionParser::ReadOperation(CString s, int& position, CElement& element position += 3; } break; - case 'm': - case 'M': - if (s.Mid(position, 3).MakeUpper() == "MOD") + case L'm': + case L'M': + if (s.Mid(position, 3).MakeUpper() == L"MOD") { element.type = etOperation; element.priority = 3; @@ -790,9 +790,9 @@ bool ExpressionParser::ReadOperation(CString s, int& position, CElement& element position += 2; } break; - case 'a': - case 'A': - if (s.Mid(position, 3).MakeUpper() == "AND") + case L'a': + case L'A': + if (s.Mid(position, 3).MakeUpper() == L"AND") { element.type = etOperation; element.priority = 5; @@ -800,9 +800,9 @@ bool ExpressionParser::ReadOperation(CString s, int& position, CElement& element position += 2; } break; - case 'o': - case 'O': - if (s.Mid(position, 2).MakeUpper() == "OR") + case L'o': + case L'O': + if (s.Mid(position, 2).MakeUpper() == L"OR") { element.type = etOperation; element.priority = 6; @@ -810,9 +810,9 @@ bool ExpressionParser::ReadOperation(CString s, int& position, CElement& element position++; } break; - case 'x': - case 'X': - if (s.Mid(position, 3).MakeUpper() == "XOR") + case L'x': + case L'X': + if (s.Mid(position, 3).MakeUpper() == L"XOR") { element.type = etOperation; element.priority = 6; @@ -822,7 +822,7 @@ bool ExpressionParser::ReadOperation(CString s, int& position, CElement& element break; default: { - SetErrorMessage("Operator expected. The character found: " + s.Mid(position, 1)); + SetErrorMessage(L"Operator expected. The character found: " + s.Mid(position, 1)); SetErrorPosition(position); return false; } @@ -833,7 +833,7 @@ bool ExpressionParser::ReadOperation(CString s, int& position, CElement& element // ***************************************************************** // ReplaceStringConstants() // ***************************************************************** -bool ExpressionParser::ReplaceStringLiterals(CString& s, int& count) +bool ExpressionParser::ReplaceStringLiterals(CStringW& s, int& count) { bool found = true; @@ -855,8 +855,8 @@ bool ExpressionParser::ReplaceStringLiterals(CString& s, int& count) if (i > begin + 1) // at least one character { _expression->GetStrings()->push_back(s.Mid(begin + 1, i - begin - 1)); - CString strReplace; - strReplace.Format("&%i", count); + CStringW strReplace; + strReplace.Format(L"&%i", count); ReplaceSubString(s, begin, i - begin + 1, strReplace); count++; begin = -1; @@ -884,7 +884,7 @@ bool ExpressionParser::ReplaceStringLiterals(CString& s, int& count) // ***************************************************************** // ReplaceFieldNames() // ***************************************************************** -bool ExpressionParser::ReplaceFieldNames(CString& s, int& count) +bool ExpressionParser::ReplaceFieldNames(CStringW& s, int& count) { bool found = true; @@ -919,8 +919,8 @@ bool ExpressionParser::ReplaceFieldNames(CString& s, int& count) if (i > begin + 1) // at least one character { _expression->GetStrings()->push_back(s.Mid(begin + 1, i - begin - 1)); - CString strReplace; - strReplace.Format("{%i}", count); + CStringW strReplace; + strReplace.Format(L"{%i}", count); ReplaceSubString(s, begin, i - begin + 1, strReplace); count++; begin = -1; @@ -949,20 +949,20 @@ bool ExpressionParser::ReplaceFieldNames(CString& s, int& count) // ***************************************************************** // ReplacePart() // ***************************************************************** -void ExpressionParser::ReplacePart(CString& s, int begin, int end) +void ExpressionParser::ReplacePart(CStringW& s, int begin, int end) { int partCount = _expression->get_PartCount(); - CString strReplace; - strReplace.Format("#%i", partCount - 1); // refer to previously added part + CStringW strReplace; + strReplace.Format(L"#%i", partCount - 1); // refer to previously added part ReplaceSubString(s, begin, end - begin + 1, strReplace); } // ************************************************************ // ReplaceInPosition() //************************************************************ -void ExpressionParser::ReplaceSubString(CString& s, int begin, int length, CString replacement) +void ExpressionParser::ReplaceSubString(CStringW& s, int begin, int length, CStringW replacement) { - CString part1, part2; + CStringW part1, part2; if (begin > 0) part1 = s.Left(begin); @@ -976,13 +976,13 @@ void ExpressionParser::ReplaceSubString(CString& s, int begin, int length, CStri // ************************************************************ // ReplaceParameterlessFunctions() //************************************************************ -bool ExpressionParser::ReplaceParameterlessFunctions(CString& s) +bool ExpressionParser::ReplaceParameterlessFunctions(CStringW& s) { - int pos = s.Find("$"); + int pos = s.Find(L"$"); while (pos != -1) { - CString sub; + CStringW sub; int i; for (i = pos; i < s.GetLength(); i++) { @@ -1010,11 +1010,11 @@ bool ExpressionParser::ReplaceParameterlessFunctions(CString& s) } else { - SetErrorMessage(Debug::Format("Invalid function name: %s", sub)); + SetErrorMessage(CStringW(Debug::Format("Invalid function name: %s", CString(sub)))); return false; } - pos = s.Find("$"); + pos = s.Find(L"$"); } return true; diff --git a/src/Processing/ExpressionParser.h b/src/Processing/ExpressionParser.h index da8d30ac..e3bdbc53 100644 --- a/src/Processing/ExpressionParser.h +++ b/src/Processing/ExpressionParser.h @@ -15,35 +15,35 @@ class ExpressionParser CustomExpression* _expression; private: - bool IsDecimal(CString& str); - bool IsDecimal(char chr, bool& exponential); - bool IsDecimalZero(CString& str); - bool IsInteger(CString& str); - bool IsOperator(char s); - bool IsOperator(CString s); - bool IsFunctionName(char s); - - bool ParseTree(CString s); - CustomFunction* ParseFunction(CString& s, int begin, int& fnBegin); - bool ParseArgumentList(CString s, CustomFunction* fn); - - bool GetBrackets(CString expression, int& begin, int& end, CString openingSymbol = "(", CString closingSymbol = ")"); - CString GetInnerString(CString& s, int begin, int end); - - CExpressionPart* ParseExpressionPart(CString s); - bool ReadValue(CString s, int& position, CElement* element); - bool ReadOperation(CString s, int& position, CElement& element); + bool IsDecimal(CStringW& str); + bool IsDecimal(wchar_t chr, bool& exponential); + bool IsDecimalZero(CStringW& str); + bool IsInteger(CStringW& str); + bool IsOperator(wchar_t s); + bool IsOperator(CStringW s); + bool IsFunctionName(wchar_t s); + + bool ParseTree(CStringW s); + CustomFunction* ParseFunction(CStringW& s, int begin, int& fnBegin); + bool ParseArgumentList(CStringW s, CustomFunction* fn); + + bool GetBrackets(CStringW expression, int& begin, int& end, CStringW openingSymbol = "(", CStringW closingSymbol = ")"); + CStringW GetInnerString(CStringW& s, int begin, int end); + + CExpressionPart* ParseExpressionPart(CStringW s); + bool ReadValue(CStringW s, int& position, CElement* element); + bool ReadOperation(CStringW s, int& position, CElement& element); - void ReplacePart(CString& s, int begin, int end); - bool ReplaceStringLiterals(CString& s, int& count); - bool ReplaceFieldNames(CString& s, int& count); - void ReplaceSubString(CString& s, int begin, int length, CString replacement); + void ReplacePart(CStringW& s, int begin, int end); + bool ReplaceStringLiterals(CStringW& s, int& count); + bool ReplaceFieldNames(CStringW& s, int& count); + void ReplaceSubString(CStringW& s, int begin, int length, CStringW replacement); - void SetErrorMessage(CString msg) { _expression->SetErrorMessage(msg); } + void SetErrorMessage(CStringW msg) { _expression->SetErrorMessage(msg); } void SetErrorPosition(int position) { _expression->SetErrorPosition(position); } public: - bool Parse(CustomExpression* expression, CString s, bool useFields); - bool ReplaceParameterlessFunctions(CString& s); + bool Parse(CustomExpression* expression, CStringW s, bool useFields); + bool ReplaceParameterlessFunctions(CStringW& s); }; diff --git a/src/Processing/ExpressionParts.cpp b/src/Processing/ExpressionParts.cpp index b5420ce2..4fa8ff4a 100644 --- a/src/Processing/ExpressionParts.cpp +++ b/src/Processing/ExpressionParts.cpp @@ -15,17 +15,17 @@ void CustomFunction::Clear() // ************************************************************ // ParseName() //************************************************************ -void CustomFunction::ParseName(CString name) +void CustomFunction::ParseName(CStringW name) { _aliases.clear(); int position = 0; - CString part = name.Tokenize(";", position); + CStringW part = name.Tokenize(L";", position); while (part.GetLength() > 0) { _aliases.push_back(part); - part = name.Tokenize(";", position); + part = name.Tokenize(L";", position); } if (_aliases.size() == 0) @@ -56,7 +56,7 @@ bool CustomFunction::CheckNumParams() // ************************************************************ // CheckArguments() //************************************************************ -bool CustomFunction::CheckArguments(int argSize, CString& errorMessage) +bool CustomFunction::CheckArguments(int argSize, CStringW& errorMessage) { if (numParams() != argSize && _numParams != -1) { @@ -303,7 +303,7 @@ void CustomFunction::InitOverloads() CStringW CustomFunction::GetSignature() { USES_CONVERSION; - CStringW name = A2W(GetName()); + CStringW name = GetName(); if (name.Left(1) == "$") { diff --git a/src/Processing/ExpressionParts.h b/src/Processing/ExpressionParts.h index d3d82164..d9daa949 100644 --- a/src/Processing/ExpressionParts.h +++ b/src/Processing/ExpressionParts.h @@ -207,7 +207,7 @@ class CElement delete calcVal; } - CString fieldName; // name of field (in [square brackets]) + CStringW fieldName; // name of field (in [square brackets]) tkElementType type; // type of element tkOperation operation; // type of operation CExpressionValue* val; // initial value @@ -252,13 +252,13 @@ class CustomFunction bool _useGeometry; tkFunctionGroup _group; ExpressionFunction _fn; - vector _aliases; + vector _aliases; CStringW _description; FunctionId _fnId; vector _params; public: - CustomFunction(FunctionId fnId, CString name, int numParams, ExpressionFunction function, tkFunctionGroup group, bool useGeometry = false) + CustomFunction(FunctionId fnId, CStringW name, int numParams, ExpressionFunction function, tkFunctionGroup group, bool useGeometry = false) : _fnId(fnId), _useGeometry(useGeometry), _group(group), _fn(function) { _numParams = numParams; @@ -281,11 +281,11 @@ class CustomFunction void InitOverloads(); - void ParseName(CString name); + void ParseName(CStringW name); - vector* getAliases() { return &_aliases; } + vector* getAliases() { return &_aliases; } - CString GetName() { return _aliases[0]; } + CStringW GetName() { return _aliases[0]; } int numParams() { return _params.size(); } @@ -302,7 +302,7 @@ class CustomFunction // optionally type of parameters can be checked as well, // provided we define all the types in CustomFunction.InitOverloads - bool CheckArguments(int argSize, CString& errorMessage); + bool CheckArguments(int argSize, CStringW& errorMessage); void AddParameter(CStringW name, CStringW description) { @@ -333,7 +333,7 @@ class CustomFunction class CExpressionPart { public: - CString expression; // for debugging only + CStringW expression; // for debugging only bool isArgument; // for debugging only vector elements; // fields, operators, literals vector arguments; // references to other parts diff --git a/src/Processing/Functions.cpp b/src/Processing/Functions.cpp index d0232d8c..c9444674 100644 --- a/src/Processing/Functions.cpp +++ b/src/Processing/Functions.cpp @@ -7,7 +7,7 @@ namespace parser { std::vector functions; - map fnMap; + map fnMap; #pragma region Math functions @@ -754,15 +754,15 @@ namespace parser for (size_t i = 0; i < functions.size(); i++) { CustomFunction* fn = functions[i]; - vector* aliases = fn->getAliases(); + vector* aliases = fn->getAliases(); for (size_t j = 0; j < aliases->size(); j++) { - CString key = (*aliases)[j].MakeLower(); + CStringW key = (*aliases)[j].MakeLower(); if (fnMap.find(key) != fnMap.end()) { - CallbackHelper::AssertionFailed("Duplicate name of the function: " + key); + CallbackHelper::AssertionFailed("Duplicate name of the function: " + CString(key)); } else { @@ -800,9 +800,9 @@ namespace parser // ***************************************************************** // GetFunction() // ***************************************************************** - CustomFunction* GetFunction(CString name) + CustomFunction* GetFunction(CStringW name) { - map::iterator it = fnMap.find(name.MakeLower()); + map::iterator it = fnMap.find(name.MakeLower()); return it != fnMap.end() ? it->second : NULL; } } \ No newline at end of file diff --git a/src/Processing/Functions.h b/src/Processing/Functions.h index 5ce16ad8..d9c61498 100644 --- a/src/Processing/Functions.h +++ b/src/Processing/Functions.h @@ -5,7 +5,7 @@ namespace parser { extern std::vector functions; void InitializeFunctions(); - CustomFunction* GetFunction(CString name); + CustomFunction* GetFunction(CStringW name); void ReleaseFunctions(); }; diff --git a/src/Structures/Structures.h b/src/Structures/Structures.h index 15238786..2966361d 100644 --- a/src/Structures/Structures.h +++ b/src/Structures/Structures.h @@ -102,8 +102,8 @@ struct CategoriesData public: CComVariant minValue; CComVariant maxValue; - CString expression; - CString name; + CStringW expression; + CStringW name; tkCategoryValue valueType; int classificationField; bool skip; diff --git a/src/Tiles/Caching/DiskCache.cpp b/src/Tiles/Caching/DiskCache.cpp index 8b51738d..c074f963 100644 --- a/src/Tiles/Caching/DiskCache.cpp +++ b/src/Tiles/Caching/DiskCache.cpp @@ -17,7 +17,9 @@ ************************************************************************************** * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ -#include "stdafx.h" +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper + +#include "StdAfx.h" #include "DiskCache.h" // ********************************************************* @@ -27,7 +29,7 @@ void DiskCache::InitEncoder() { if (_ext.GetLength() >= 4) { - CStringW s = _ext.Mid(0, 4).MakeLower(); // try to guess it from input + const CStringW s = _ext.Mid(0, 4).MakeLower(); // try to guess it from input if (s == L".png") { @@ -45,7 +47,7 @@ void DiskCache::InitEncoder() // ********************************************************* bool DiskCache::get_TileExists(BaseProvider* provider, int zoom, int x, int y) { - return (Utility::FileExistsW(get_TilePath(zoom, x, y)) == TRUE); + return Utility::FileExistsW(get_TilePath(zoom, x, y)) == TRUE; } // ********************************************************* @@ -78,7 +80,7 @@ void DiskCache::CreateFolder(int zoom, TilePoint* pnt) { CStringW name; name.Format(L"\\%d\\", zoom); // _mkdir can't create folders recursively - int val = _wmkdir(_rootPath + name); + _wmkdir(_rootPath + name); name.Format(L"\\%d\\%d\\", zoom, pnt->x); _wmkdir(_rootPath + name); } @@ -97,12 +99,12 @@ void DiskCache::AddTile(TileCore* tile) } // TODO: better to get it from provider (as tile size is not necessarily 256 by 256 pixels) - Gdiplus::Bitmap* bmp = new Gdiplus::Bitmap(256, 256); + auto* bmp = new Gdiplus::Bitmap(256, 256); Gdiplus::Graphics* g = Gdiplus::Graphics::FromImage(bmp); for (size_t i = 0; i < tile->Overlays.size(); i++) { - Gdiplus::Bitmap* bmp = tile->get_Bitmap(i)->m_bitmap; + bmp = tile->get_Bitmap(i)->m_bitmap; if (bmp) { g->DrawImage(bmp, 0.0f, 0.0f); @@ -110,8 +112,8 @@ void DiskCache::AddTile(TileCore* tile) } USES_CONVERSION; - CStringW path = tile->get_Path(_rootPath, _ext); - bmp->Save(path, &_pngClsid, NULL); + const CStringW path = tile->GetPath(_rootPath, _ext); + bmp->Save(path, &_pngClsid, nullptr); delete g; delete bmp; diff --git a/src/Tiles/Caching/ITileCache.h b/src/Tiles/Caching/ITileCache.h index 7566ae37..0e3eaebd 100644 --- a/src/Tiles/Caching/ITileCache.h +++ b/src/Tiles/Caching/ITileCache.h @@ -17,12 +17,15 @@ ************************************************************************************** * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ - #pragma once +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper + +#pragma once #include "TileCore.h" #include "BaseProvider.h" #define TILE_CACHE_SIZE_TO_CLEAR (20 * 0x1 << 20) // 20 MB; when overall size of tiles exceeds maximum, tiles will be removed - // starting from the oldest until this amount of memory is freed + +// starting from the oldest until this amount of memory is freed // ******************************************************** // ITileCache() @@ -31,29 +34,29 @@ class ITileCache { protected: - double _maxSize; + double _maxSize; public: - virtual void AddTile(TileCore* tile) = 0; - virtual void Close() = 0; - virtual TileCore* get_Tile(BaseProvider* provider, int scale, int x, int y) = 0; - virtual bool get_TileExists(BaseProvider* provider, int scale, int x, int y) = 0; - virtual bool IsLocked() = 0; - virtual CacheType get_CacheType() = 0; - virtual void Clear(int providerId = -1, int fromScale = 0, int toScale = 100) = 0; - virtual double get_SizeMB() = 0; - virtual double get_SizeMB(int providerId, int scale) = 0; - virtual CStringW get_Filename() = 0; - virtual void set_Filename(CStringW name) = 0; - virtual void Initialize(bool canUseCache, bool canDoCaching) = 0; - virtual void Lock() = 0; - virtual void Unlock() = 0; - virtual void InitBulkDownload(int zoom, vector& points) = 0; - virtual long get_TileCount(int providerId, int zoom, CRect indices) = 0; + virtual void AddTile(TileCore* tile) = 0; + virtual void Close() = 0; + virtual TileCore* get_Tile(BaseProvider* provider, int scale, int x, int y) = 0; + virtual bool get_TileExists(BaseProvider* provider, int scale, int x, int y) = 0; + virtual bool IsLocked() = 0; + virtual CacheType get_CacheType() = 0; + virtual void Clear(int providerId = -1, int fromScale = 0, int toScale = 100) = 0; + virtual double get_SizeMB() = 0; + virtual double get_SizeMB(int providerId, int scale) = 0; + virtual CStringW get_Filename() = 0; + virtual void set_Filename(CStringW name) = 0; + virtual void Initialize(bool canUseCache, bool canDoCaching) = 0; + virtual void Lock() = 0; + virtual void Unlock() = 0; + virtual void InitBulkDownload(int zoom, vector& points) = 0; + virtual long get_TileCount(int providerId, int zoom, CRect indices) = 0; public: - double get_MaxSize() { return _maxSize; } - void set_MaxSize(double value) { _maxSize = value; } + double get_MaxSize() { return _maxSize; } + void set_MaxSize(double value) { _maxSize = value; } }; // ******************************************************** @@ -64,13 +67,13 @@ class ITileCache // we shall use this wrapper to store permissions. struct TileCacheInfo { - TileCacheInfo() - { - useCache = true; - doCaching = true; - } + TileCacheInfo(): cache(nullptr) + { + useCache = true; + doCaching = true; + } - ITileCache* cache; - bool useCache; - bool doCaching; -}; \ No newline at end of file + ITileCache* cache; + bool useCache; + bool doCaching; +}; diff --git a/src/Tiles/Caching/PrefetchManager.cpp b/src/Tiles/Caching/PrefetchManager.cpp index a9924808..bb23fd29 100644 --- a/src/Tiles/Caching/PrefetchManager.cpp +++ b/src/Tiles/Caching/PrefetchManager.cpp @@ -17,12 +17,12 @@ ************************************************************************************** * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper -#include "stdafx.h" +#include "StdAfx.h" #include "PrefetchManager.h" -#include "HttpProxyHelper.h" -::CCriticalSection PrefetchManagerFactory::_lock; +CCriticalSection PrefetchManagerFactory::_lock; vector PrefetchManagerFactory::_managers; // ******************************************************* @@ -30,15 +30,15 @@ vector PrefetchManagerFactory::_managers; // ******************************************************* PrefetchManager* PrefetchManagerFactory::Create(ITileCache* cache) { - if (!cache) return NULL; + if (!cache) return nullptr; - PrefetchManager* manager = new PrefetchManager(cache); + auto* manager = new PrefetchManager(cache); - _lock.Lock(); - _managers.push_back(manager); - _lock.Unlock(); + _lock.Lock(); + _managers.push_back(manager); + _lock.Unlock(); - return manager; + return manager; } // ******************************************************* @@ -46,16 +46,16 @@ PrefetchManager* PrefetchManagerFactory::Create(ITileCache* cache) // ******************************************************* void PrefetchManagerFactory::Clear() { - _lock.Lock(); + _lock.Lock(); - for (size_t i = 0; i < _managers.size(); i++) - { - delete _managers[i]; - } + for (size_t i = 0; i < _managers.size(); i++) + { + delete _managers[i]; + } - _managers.clear(); + _managers.clear(); - _lock.Unlock(); + _lock.Unlock(); } // ********************************************************* @@ -63,83 +63,85 @@ void PrefetchManagerFactory::Clear() // ********************************************************* void PrefetchManager::BuildDownloadList(BaseProvider* provider, int zoom, CRect indices, vector& points) { - CSize size1, size2; - provider->get_Projection()->GetTileMatrixMinXY(zoom, size1); - provider->get_Projection()->GetTileMatrixMaxXY(zoom, size2); - - int minX = (int)BaseProjection::Clip(indices.left, size1.cx, size2.cx); - int maxX = (int)BaseProjection::Clip(indices.right, size1.cy, size2.cy); - int minY = (int)BaseProjection::Clip(MIN(indices.top, indices.bottom), size1.cx, size2.cx); - int maxY = (int)BaseProjection::Clip(MAX(indices.top, indices.bottom), size1.cy, size2.cy); - - int centX = (maxX + minX) / 2; - int centY = (maxY + minY) / 2; - - USES_CONVERSION; - - ITileCache* cache = _loader.get_Cache(); - - for (int x = minX; x <= maxX; x++) - { - for (int y = minY; y <= maxY; y++) - { - if (!cache->get_TileExists(provider, zoom, x, y)) - { - TilePoint* pnt = new TilePoint(x, y); - pnt->dist = sqrt(pow((pnt->x - centX), 2.0) + pow((pnt->y - centY), 2.0)); - points.push_back(pnt); - } - } - } + CSize size1, size2; + provider->get_Projection()->GetTileMatrixMinXY(zoom, size1); + provider->get_Projection()->GetTileMatrixMaxXY(zoom, size2); + + const auto minX = (int)BaseProjection::Clip(indices.left, size1.cx, size2.cx); + const auto maxX = (int)BaseProjection::Clip(indices.right, size1.cy, size2.cy); + const auto minY = (int)BaseProjection::Clip(MIN(indices.top, indices.bottom), size1.cx, size2.cx); + const auto maxY = (int)BaseProjection::Clip(MAX(indices.top, indices.bottom), size1.cy, size2.cy); + + const int centX = (maxX + minX) / 2; + const int centY = (maxY + minY) / 2; + + USES_CONVERSION; + + ITileCache* cache = _loader.get_Cache(); + + for (int x = minX; x <= maxX; x++) + { + for (int y = minY; y <= maxY; y++) + { + if (!cache->get_TileExists(provider, zoom, x, y)) + { + auto* pnt = new TilePoint(x, y); + pnt->dist = sqrt(pow(pnt->x - centX, 2.0) + pow(pnt->y - centY, 2.0)); + points.push_back(pnt); + } + } + } } // ********************************************************* // PrefetchCore // ********************************************************* -long PrefetchManager::Prefetch(BaseProvider* provider, CRect indices, int zoom, ICallback* callback, IStopExecution* stop) +long PrefetchManager::Prefetch(BaseProvider* provider, CRect indices, int zoom, ICallback* callback, + IStopExecution* stop) { - if (!provider) - { - CallbackHelper::ErrorMsg("PrefetchManager", NULL, "", "Invalid provider."); - return -1; - } + if (!provider) + { + CallbackHelper::ErrorMsg("PrefetchManager", nullptr, "", "Invalid provider."); + return -1; + } - LogBulkDownloadStarted(zoom); + LogBulkDownloadStarted(zoom); - if (provider->get_MaxZoom() < zoom) - { - CallbackHelper::ErrorMsg("PrefetchManager", NULL, "", "Invalid zoom level for tile provider: %s, %d", provider->Name, zoom); - return 0; - } + if (provider->get_MaxZoom() < zoom) + { + CallbackHelper::ErrorMsg("PrefetchManager", nullptr, "", "Invalid zoom level for tile provider: %s, %d", + provider->Name, zoom); + return 0; + } - vector points; - BuildDownloadList(provider, zoom, indices, points); + vector points; + BuildDownloadList(provider, zoom, indices, points); - if (points.size() == 0) - { - if (tilesLogger.IsOpened()) - { - tilesLogger.out() << "\n"; - tilesLogger.out() << "Nothing to fetch\n"; - tilesLogger.out() << "---------------------" << endl; - } - return 0; - } + if (points.size() == 0) + { + if (tilesLogger.IsOpened()) + { + tilesLogger.out() << "\n"; + tilesLogger.out() << "Nothing to fetch\n"; + tilesLogger.out() << "---------------------" << endl; + } + return 0; + } - _loader.set_StopCallback(stop); - _loader.set_Callback(callback); - _loader.get_Cache()->InitBulkDownload(zoom, points); + _loader.set_StopCallback(stop); + _loader.set_Callback(callback); + _loader.get_Cache()->InitBulkDownload(zoom, points); - TileRequestInfo* info = _loader.CreateNextRequest("", false); + TileRequestInfo* info = _loader.CreateNextRequest("", false); - // actual call to do the job - _loader.Load(points, provider, zoom, info); + // actual call to do the job + _loader.Load(points, provider, zoom, info); - long size = points.size(); + const long size = points.size(); - TilePoint::ReleaseMemory(points); + TilePoint::ReleaseMemory(points); - return size; + return size; } // ********************************************************* @@ -147,11 +149,11 @@ long PrefetchManager::Prefetch(BaseProvider* provider, CRect indices, int zoom, // ********************************************************* void PrefetchManager::LogBulkDownloadStarted(int zoom) { - if (tilesLogger.IsOpened()) - { - tilesLogger.out() << "\n"; - tilesLogger.out() << "PREFETCHING TILES:\n"; - tilesLogger.out() << "ZOOM " << zoom << endl; - tilesLogger.out() << "---------------------" << endl; - } + if (tilesLogger.IsOpened()) + { + tilesLogger.out() << "\n"; + tilesLogger.out() << "PREFETCHING TILES:\n"; + tilesLogger.out() << "ZOOM " << zoom << endl; + tilesLogger.out() << "---------------------" << endl; + } } diff --git a/src/Tiles/Caching/PrefetchManager.h b/src/Tiles/Caching/PrefetchManager.h index a4ffc5c2..4c83f66a 100644 --- a/src/Tiles/Caching/PrefetchManager.h +++ b/src/Tiles/Caching/PrefetchManager.h @@ -17,6 +17,8 @@ ************************************************************************************** * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper + #pragma once #include "TileBulkLoader.h" @@ -28,11 +30,11 @@ class PrefetchManager; class PrefetchManagerFactory { private: - static vector _managers; - static ::CCriticalSection _lock; + static vector _managers; + static CCriticalSection _lock; public: - static PrefetchManager* Create(ITileCache* cache); - static void Clear(); + static PrefetchManager* Create(ITileCache* cache); + static void Clear(); }; // ****************************************************** @@ -41,25 +43,23 @@ class PrefetchManagerFactory class PrefetchManager { public: - PrefetchManager(ITileCache* cache) - : _loader(cache) - { - - } + PrefetchManager(ITileCache* cache) + : _loader(cache) + { + } private: - TileBulkLoader _loader; + TileBulkLoader _loader; private: - // methods - void BuildDownloadList(BaseProvider* provider, int zoom, CRect indices, vector& points); -public: - // properties - TileBulkLoader* get_Loader() { return &_loader; } + // methods + void BuildDownloadList(BaseProvider* provider, int zoom, CRect indices, vector& points); +public: + // properties + TileBulkLoader* get_Loader() { return &_loader; } public: - // methods - long Prefetch(BaseProvider* provider, CRect indices, int zoom, ICallback* callback, IStopExecution* stop); - void LogBulkDownloadStarted(int zoom); - + // methods + long Prefetch(BaseProvider* provider, CRect indices, int zoom, ICallback* callback, IStopExecution* stop); + static void LogBulkDownloadStarted(int zoom); }; diff --git a/src/Tiles/Caching/RamCache.cpp b/src/Tiles/Caching/RamCache.cpp index ae52e504..eb999c15 100644 --- a/src/Tiles/Caching/RamCache.cpp +++ b/src/Tiles/Caching/RamCache.cpp @@ -17,7 +17,9 @@ ************************************************************************************** * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ - #include "stdafx.h" +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper + +#include "StdAfx.h" #include "RamCache.h" // ********************************************************* @@ -25,34 +27,35 @@ // ********************************************************* void RamCache::Close() { - // release tiles first - Clear(); - - // then release dynamically allocated containers - for( TilesCache::iterator it = _tilesMap.begin(); it != _tilesMap.end(); ++it ) - { - vector* list = it->second; - if (list) - { - for (size_t i = 0; i< (int)list->size(); i++) - { - TilePoints* points = (*list)[i]; - if (points) - { - TilePoints::iterator it2 = points->begin(); - while (it2 != points->end()) - { - TilePositions* tiles = it2->second; - if (tiles) { - delete tiles; - } - ++it2; - } - delete points; - } - } - delete list; - } + // release tiles first + Clear(); + + // then release dynamically allocated containers + for (TilesCache::iterator it = _tilesMap.begin(); it != _tilesMap.end(); ++it) + { + vector* list = it->second; + if (list) + { + for (size_t i = 0; i < (int)list->size(); i++) + { + TilePoints* points = (*list)[i]; + if (points) + { + TilePoints::iterator it2 = points->begin(); + while (it2 != points->end()) + { + TilePositions* tiles = it2->second; + if (tiles) + { + delete tiles; + } + ++it2; + } + delete points; + } + } + delete list; + } } } @@ -61,60 +64,61 @@ void RamCache::Close() // ********************************************************* void RamCache::AddTile(TileCore* tile) { - if (tile->zoom() < 0 || tile->zoom() >= 25) { - return; - } - - _section.Lock(); - - // seeking provider - std::vector* list = NULL; - if (_tilesMap.find(tile->get_ProviderId()) != _tilesMap.end()) - { - list = _tilesMap[tile->get_ProviderId()]; - } - else - { - list = new std::vector(); - list->resize(25, NULL); - _tilesMap[tile->get_ProviderId()] = list; - } - - // seeking scale - TilePoints* points = (*list)[tile->zoom()]; - if (!points) - { - points = new TilePoints(); - (*list)[tile->zoom()] = points; - } - - // seeking X - std::map* map = NULL; - if (points->find(tile->tileX()) != points->end()) - { - map = (*points)[tile->tileX()]; - } - else - { - map = new std::map(); - (*points)[tile->tileX()] = map; - } - - // seeking Y - if (map->find(tile->tileY()) == map->end()) - { - tile->AddRef(); - (*map)[tile->tileY()] = tile; - _size += tile->get_ByteSize(); - _list.push_back(tile); - } - _section.Unlock(); - - // automatically clear the cache if it exceeds the maximum size - if ((double)_size/(double)(0x1 << 20) > _maxSize) - { - ClearOldest(TILE_CACHE_SIZE_TO_CLEAR); - } + if (tile->zoom() < 0 || tile->zoom() >= 25) + { + return; + } + + _section.Lock(); + + // seeking provider + std::vector* list; + if (_tilesMap.find(tile->get_ProviderId()) != _tilesMap.end()) + { + list = _tilesMap[tile->get_ProviderId()]; + } + else + { + list = new std::vector(); + list->resize(25, nullptr); + _tilesMap[tile->get_ProviderId()] = list; + } + + // seeking scale + TilePoints* points = (*list)[tile->zoom()]; + if (!points) + { + points = new TilePoints(); + (*list)[tile->zoom()] = points; + } + + // seeking X + std::map* map; + if (points->find(tile->tileX()) != points->end()) + { + map = (*points)[tile->tileX()]; + } + else + { + map = new std::map(); + (*points)[tile->tileX()] = map; + } + + // seeking Y + if (map->find(tile->tileY()) == map->end()) + { + tile->AddRef(); + (*map)[tile->tileY()] = tile; + _size += tile->get_ByteSize(); + _list.push_back(tile); + } + _section.Unlock(); + + // automatically clear the cache if it exceeds the maximum size + if ((double)_size / (double)(0x1 << 20) > _maxSize) + { + ClearOldest(TILE_CACHE_SIZE_TO_CLEAR); + } } // ********************************************************* @@ -122,21 +126,21 @@ void RamCache::AddTile(TileCore* tile) // ********************************************************* void RamCache::ClearOldest(int sizeToClearBytes) { - int size = 0; + int size = 0; - for(size_t i = 0; i < _list.size(); i++) - { - if (!_list[i]->inBuffer()) - { - _list[i]->toDelete(true); + for (size_t i = 0; i < _list.size(); i++) + { + if (!_list[i]->inBuffer()) + { + _list[i]->toDelete(true); - size += _list[i]->get_ByteSize(); - if (size > TILE_CACHE_SIZE_TO_CLEAR) - break; - } - } + size += _list[i]->get_ByteSize(); + if (size > TILE_CACHE_SIZE_TO_CLEAR) + break; + } + } - DeleteMarkedTiles(); + DeleteMarkedTiles(); } // ********************************************************** @@ -145,77 +149,77 @@ void RamCache::ClearOldest(int sizeToClearBytes) // Removes tiles of the specified provider void RamCache::Clear(int provider, int fromScale, int toScale) { - _section.Lock(); - for( TilesCache::iterator it = _tilesMap.begin(); it != _tilesMap.end(); ++it ) - { - // if provider is equal -1, then all tiles are to be removed - if (provider != (int)tkTileProvider::ProviderNone && provider != it->first) - continue; - - vector* list = it->second; - if (list) - { - for (int i = 0; i< (int)list->size(); i++) - { - if (i < fromScale || i > toScale) - continue; - - TilePoints* points = (*list)[i]; - if (points) - { - TilePoints::iterator it2 = points->begin(); - while (it2 != points->end()) - { - TilePositions* tiles = it2->second; - TilePositions::iterator it3 = tiles->begin(); - - while(it3 != tiles->end()) - { - TileCore* tile = it3->second; - - if (tile->inBuffer()) - { - ++it3; // it doesn't make sense to delete tiles which are - // currently displayed; as they will be requested again on simple refresh of the map - } - else - { - tiles->erase(it3++); - _size -= tile->get_ByteSize(); - - // marks it to be removed from chronological vector - tile->toDelete(true); - } - } - - // if all the Y's for given X are removed - delete the container - if (tiles->empty()) - { - points->erase(it2++); - delete tiles; - } - else - ++it2; - } - - // if all the X's for a given scale are removed - delete the container, - // but don't delete scale entry - 25 scales must preserved as they are accessed by index - if (points->empty()) - { - delete points; - (*list)[i] = NULL; - } - } - } - } + _section.Lock(); + for (TilesCache::iterator it = _tilesMap.begin(); it != _tilesMap.end(); ++it) + { + // if provider is equal -1, then all tiles are to be removed + if (provider != (int)tkTileProvider::ProviderNone && provider != it->first) + continue; + + vector* list = it->second; + if (list) + { + for (int i = 0; i < (int)list->size(); i++) + { + if (i < fromScale || i > toScale) + continue; + + TilePoints* points = (*list)[i]; + if (points) + { + TilePoints::iterator it2 = points->begin(); + while (it2 != points->end()) + { + TilePositions* tiles = it2->second; + TilePositions::iterator it3 = tiles->begin(); + + while (it3 != tiles->end()) + { + TileCore* tile = it3->second; + + if (tile->inBuffer()) + { + ++it3; // it doesn't make sense to delete tiles which are + // currently displayed; as they will be requested again on simple refresh of the map + } + else + { + tiles->erase(it3++); + _size -= tile->get_ByteSize(); + + // marks it to be removed from chronological vector + tile->toDelete(true); + } + } + + // if all the Y's for given X are removed - delete the container + if (tiles->empty()) + { + points->erase(it2++); + delete tiles; + } + else + ++it2; + } + + // if all the X's for a given scale are removed - delete the container, + // but don't delete scale entry - 25 scales must preserved as they are accessed by index + if (points->empty()) + { + delete points; + (*list)[i] = nullptr; + } + } + } + } } - _tilesMap.clear(); + _tilesMap.clear(); + + // now remove tiles from chronological vector + DeleteMarkedTiles(); - // now remove tiles from chronological vector - DeleteMarkedTiles(); - - _section.Unlock(); + _section.Unlock(); } // ********************************************************* @@ -227,20 +231,20 @@ void RamCache::Clear(int provider, int fromScale, int toScale) // It's responsibility of caller to lock the the cache to make it thread safe void RamCache::DeleteMarkedTiles() { - vector::iterator it = _list.begin(); - while (it != _list.end()) - { - TileCore* tile = *it; - if (tile->toDelete()) - { - tile->Release(); - it = _list.erase(it); - } - else - { - ++it; - } - } + vector::iterator it = _list.begin(); + while (it != _list.end()) + { + TileCore* tile = *it; + if (tile->toDelete()) + { + tile->Release(); + it = _list.erase(it); + } + else + { + ++it; + } + } } // ******************************************************** @@ -249,17 +253,18 @@ void RamCache::DeleteMarkedTiles() // Extracts a single tile from the cache TileCore* RamCache::get_Tile(BaseProvider* provider, int scale, int tileX, int tileY) { - if (!provider) return NULL; - - TileCore* tile = get_TileCore(provider->Id, scale, tileX, tileY); + if (!provider) return nullptr; + + TileCore* tile = get_TileCore(provider->Id, scale, tileX, tileY); - if (tile) { - // original instance of projection might be closed if WMS layer was reopened; - // so get_Projection() pointer is invalid, assign a new one - tile->set_Projection(provider->get_Projection()); - } + if (tile) + { + // original instance of projection might be closed if WMS layer was reopened; + // so get_Projection() pointer is invalid, assign a new one + tile->set_Projection(provider->get_Projection()); + } - return tile; + return tile; } // ******************************************************** @@ -267,36 +272,37 @@ TileCore* RamCache::get_Tile(BaseProvider* provider, int scale, int tileX, int t // ******************************************************** void RamCache::OnProviderClosed(int providerId) { - // set projection for all tiles of this provider to NULL - if (_tilesMap.find(providerId) != _tilesMap.end()) - { - std::vector* scales = _tilesMap[providerId]; - for (size_t i = 0; i < scales->size(); i++) - { - TilePoints* points = (*scales)[i]; - if (points) - { - TilePoints::iterator it = points->begin(); - while (it != points->end()) - { - TilePositions* positions = it->second; - if (positions) - { - TilePositions::iterator it2 = positions->begin(); - while (it2 != positions->end()) - { - TileCore* tile = it2->second; - if (tile) { - tile->set_Projection(NULL); - } - ++it2; - } - } - ++it; - } - } - } - } + // set projection for all tiles of this provider to NULL + if (_tilesMap.find(providerId) != _tilesMap.end()) + { + std::vector* scales = _tilesMap[providerId]; + for (size_t i = 0; i < scales->size(); i++) + { + TilePoints* points = (*scales)[i]; + if (points) + { + TilePoints::iterator it = points->begin(); + while (it != points->end()) + { + TilePositions* positions = it->second; + if (positions) + { + TilePositions::iterator it2 = positions->begin(); + while (it2 != positions->end()) + { + TileCore* tile = it2->second; + if (tile) + { + tile->set_Projection(nullptr); + } + ++it2; + } + } + ++it; + } + } + } + } } // ******************************************************** @@ -304,32 +310,32 @@ void RamCache::OnProviderClosed(int providerId) // ******************************************************** TileCore* RamCache::get_TileCore(int providerId, int scale, int tileX, int tileY) { - _section.Lock(); - - TileCore* tile = NULL; - - if (_tilesMap.find(providerId) != _tilesMap.end()) - { - std::vector* scales = _tilesMap[providerId]; - if (scale >= 0 && scale < (int)scales->size()) - { - TilePoints* points = (*scales)[scale]; - if (points != NULL) - { - if (points->find(tileX) != points->end()) - { - std::map* map = (*points)[tileX]; - if (map->find(tileY) != map->end()) - { - tile = ((*map)[tileY]); - } - } - } - } - } - - _section.Unlock(); - return tile; + _section.Lock(); + + TileCore* tile = nullptr; + + if (_tilesMap.find(providerId) != _tilesMap.end()) + { + std::vector* scales = _tilesMap[providerId]; + if (scale >= 0 && scale < (int)scales->size()) + { + TilePoints* points = (*scales)[scale]; + if (points != nullptr) + { + if (points->find(tileX) != points->end()) + { + std::map* map = (*points)[tileX]; + if (map->find(tileY) != map->end()) + { + tile = (*map)[tileY]; + } + } + } + } + } + + _section.Unlock(); + return tile; } // *********************************************************** @@ -338,39 +344,39 @@ TileCore* RamCache::get_TileCore(int providerId, int scale, int tileX, int tileY // Use -1 for provider and scale to retrieve all the values double RamCache::get_SizeMB(int provider, int scale) { - _section.Lock(); - int sum = 0; - for( TilesCache::iterator it = _tilesMap.begin(); it != _tilesMap.end(); ++it ) - { - if (provider != (int)tkTileProvider::ProviderNone && provider != it->first) - continue; - - std::vector* scales = it->second; - for (size_t i = 0; i < scales->size(); i++) - { - if (scale != -1 && i != scale) - continue; - - TilePoints* points = (*scales)[i]; - if (points) - { - for( TilePoints::iterator it2 = points->begin(); it2 != points->end(); ++it2 ) - { - TilePositions* tiles = it2->second; - if (tiles) - { - for( TilePositions::iterator it3 = tiles->begin(); it3 != tiles->end(); ++it3 ) - { - TileCore* tile = it3->second; - sum += tile->get_ByteSize(); - } - } - } - } - } - } - _section.Unlock(); - return (double)sum/(double)(0x1 << 20); + _section.Lock(); + int sum = 0; + for (TilesCache::iterator it = _tilesMap.begin(); it != _tilesMap.end(); ++it) + { + if (provider != (int)tkTileProvider::ProviderNone && provider != it->first) + continue; + + std::vector* scales = it->second; + for (size_t i = 0; i < scales->size(); i++) + { + if (scale != -1 && i != scale) + continue; + + TilePoints* points = (*scales)[i]; + if (points) + { + for (TilePoints::iterator it2 = points->begin(); it2 != points->end(); ++it2) + { + TilePositions* tiles = it2->second; + if (tiles) + { + for (TilePositions::iterator it3 = tiles->begin(); it3 != tiles->end(); ++it3) + { + TileCore* tile = it3->second; + sum += tile->get_ByteSize(); + } + } + } + } + } + } + _section.Unlock(); + return (double)sum / (double)(0x1 << 20); } // *********************************************************** @@ -378,7 +384,7 @@ double RamCache::get_SizeMB(int provider, int scale) // *********************************************************** bool RamCache::get_TileExists(BaseProvider* provider, int scale, int x, int y) { - return get_Tile(provider, scale, x, y) != NULL; + return get_Tile(provider, scale, x, y) != nullptr; } // *********************************************************** @@ -386,18 +392,19 @@ bool RamCache::get_TileExists(BaseProvider* provider, int scale, int x, int y) // *********************************************************** long RamCache::get_TileCount(int providerId, int zoom, CRect indices) { - long count = 0; - - for (int x = indices.left; x <= indices.right; x++) - { - for (int y = indices.bottom; y <= indices.top; y++) - { - TileCore* tile = get_TileCore(providerId, zoom, x, y); - if (tile) { - count++; - } - } - } - - return count; -} \ No newline at end of file + long count = 0; + + for (int x = indices.left; x <= indices.right; x++) + { + for (int y = indices.bottom; y <= indices.top; y++) + { + TileCore* tile = get_TileCore(providerId, zoom, x, y); + if (tile) + { + count++; + } + } + } + + return count; +} diff --git a/src/Tiles/Caching/RamCache.h b/src/Tiles/Caching/RamCache.h index 66b24509..9a31d0ba 100644 --- a/src/Tiles/Caching/RamCache.h +++ b/src/Tiles/Caching/RamCache.h @@ -17,58 +17,75 @@ ************************************************************************************** * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper + #pragma once #include "ITileCache.h" using namespace std; // provider id -> zoom -> x -> y -typedef std::map TilePositions; // indexed by y -typedef std::map TilePoints; // indexed by x -typedef std::map*> TilesCache; // indexed by provider; indices in vector - by zoom +typedef std::map TilePositions; // indexed by y +typedef std::map TilePoints; // indexed by x +typedef std::map*> TilesCache; // indexed by provider; indices in vector - by zoom // Provides storage for map tiles in RAM -class RamCache: public ITileCache +class RamCache : public ITileCache { public: - RamCache() - : _size(0) - { - _maxSize = 100.0; - } + RamCache() + : _size(0) + { + _maxSize = 100.0; + } -private: - ::CCriticalSection _section; - vector _list; // the list of tiles in the chronological order - TilesCache _tilesMap; // the main storage - int _size; // size of cache in bytes +private: + CCriticalSection _section; + vector _list; // the list of tiles in the chronological order + TilesCache _tilesMap; // the main storage + int _size; // size of cache in bytes private: - void DeleteMarkedTiles(); // deletes tiles from chronological vector - TileCore* get_TileCore(int providerId, int zoom, int tileX, int tileY); + void DeleteMarkedTiles(); // deletes tiles from chronological vector + TileCore* get_TileCore(int providerId, int zoom, int tileX, int tileY); public: - // interface - void Close(); - void AddTile(TileCore* tile); - TileCore* get_Tile(BaseProvider* provider, int scale, int tileX, int tileY); - CacheType get_CacheType() { return tctRamCache; } - void Clear(int provider = -1, int fromScale = 0, int toScale = 100); - double get_SizeMB() { return get_SizeMB(-1, -1); } - double get_SizeMB(int provider, int scale); - bool get_TileExists(BaseProvider* provider, int scale, int x, int y); - bool IsLocked() { return false; } - CStringW get_Filename() { return L""; } - void set_Filename(CStringW name) { } - virtual void Initialize(bool canUseCache, bool canDoCaching) { } - void Lock() { } - void Unlock() { } - void InitBulkDownload(int zoom, vector& points) { } - long get_TileCount(int providerId, int zoom, CRect indices); + // interface + void Close(); + void AddTile(TileCore* tile); + TileCore* get_Tile(BaseProvider* provider, int scale, int tileX, int tileY); + CacheType get_CacheType() { return tctRamCache; } + void Clear(int provider = -1, int fromScale = 0, int toScale = 100); + double get_SizeMB() { return get_SizeMB(-1, -1); } + double get_SizeMB(int provider, int scale); + bool get_TileExists(BaseProvider* provider, int scale, int x, int y); + bool IsLocked() { return false; } + CStringW get_Filename() { return L""; } -public: - // methods - void ClearAll(int fromScale, int toScale) { Clear(-1, fromScale, toScale); } - void ClearOldest(int sizeToClearBytes); - void OnProviderClosed(int providerId); + void set_Filename(CStringW name) + { + } + + virtual void Initialize(bool canUseCache, bool canDoCaching) + { + } + + void Lock() + { + } + void Unlock() + { + } + + void InitBulkDownload(int zoom, vector& points) + { + } + + long get_TileCount(int providerId, int zoom, CRect indices); + +public: + // methods + void ClearAll(int fromScale, int toScale) { Clear(-1, fromScale, toScale); } + void ClearOldest(int sizeToClearBytes); + void OnProviderClosed(int providerId); }; diff --git a/src/Tiles/Caching/TileCacheManager.cpp b/src/Tiles/Caching/TileCacheManager.cpp index d6fe21c2..92f51c1f 100644 --- a/src/Tiles/Caching/TileCacheManager.cpp +++ b/src/Tiles/Caching/TileCacheManager.cpp @@ -17,41 +17,46 @@ ************************************************************************************** * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ -#include "stdafx.h" +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper + +#include "StdAfx.h" #include "TileCacheManager.h" #include "DiskCache.h" #include "RamCache.h" #include "SQLiteCache.h" -ITileCache* TileCacheManager::_ramCache = NULL; -ITileCache* TileCacheManager::_sqlLiteCache = NULL; -ITileCache* TileCacheManager::_diskCache = NULL; -::CCriticalSection TileCacheManager::_lock; +ITileCache* TileCacheManager::_ramCache = nullptr; +ITileCache* TileCacheManager::_sqlLiteCache = nullptr; +ITileCache* TileCacheManager::_diskCache = nullptr; +CCriticalSection TileCacheManager::_lock; // *********************************************************** // get_Cache() // *********************************************************** ITileCache* TileCacheManager::get_Cache(CacheType type) { - CSingleLock lock(&_lock, TRUE); + CSingleLock lock(&_lock, TRUE); - switch (type) { - case tctDiskCache: - // we can't create an instance without path - return NULL; - case tctRamCache: - if (!_ramCache) { - _ramCache = new RamCache(); - } - return _ramCache; - case tctSqliteCache: - if (!_sqlLiteCache) { - _sqlLiteCache = new SQLiteCache(); - } - return _sqlLiteCache; - } + switch (type) + { + case tctDiskCache: + // we can't create an instance without path + return nullptr; + case tctRamCache: + if (!_ramCache) + { + _ramCache = new RamCache(); + } + return _ramCache; + case tctSqliteCache: + if (!_sqlLiteCache) + { + _sqlLiteCache = new SQLiteCache(); + } + return _sqlLiteCache; + } - return NULL; + return nullptr; } // *********************************************************** @@ -59,15 +64,18 @@ ITileCache* TileCacheManager::get_Cache(CacheType type) // *********************************************************** void TileCacheManager::CloseAll() { - if (_ramCache) { - _ramCache->Close(); - } + if (_ramCache) + { + _ramCache->Close(); + } - if (_ramCache) { - _ramCache->Close(); - } + if (_ramCache) + { + _ramCache->Close(); + } - if (_diskCache) { - _diskCache->Close(); - } + if (_diskCache) + { + _diskCache->Close(); + } } diff --git a/src/Tiles/Caching/TileCacheManager.h b/src/Tiles/Caching/TileCacheManager.h index 04883162..12b4e00f 100644 --- a/src/Tiles/Caching/TileCacheManager.h +++ b/src/Tiles/Caching/TileCacheManager.h @@ -17,19 +17,21 @@ ************************************************************************************** * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ - #pragma once +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper + +#pragma once #include "ITileCache.h" #include class TileCacheManager { private: - static ITileCache* _ramCache; - static ITileCache* _diskCache; - static ITileCache* _sqlLiteCache; - static ::CCriticalSection _lock; + static ITileCache* _ramCache; + static ITileCache* _diskCache; + static ITileCache* _sqlLiteCache; + static CCriticalSection _lock; public: - static ITileCache* get_Cache(CacheType type); - static void CloseAll(); + static ITileCache* get_Cache(CacheType type); + static void CloseAll(); }; diff --git a/src/Tiles/Http/SecureHttpClient.cpp b/src/Tiles/Http/SecureHttpClient.cpp index d8bb2fbd..b30b026b 100644 --- a/src/Tiles/Http/SecureHttpClient.cpp +++ b/src/Tiles/Http/SecureHttpClient.cpp @@ -17,84 +17,164 @@ ************************************************************************************** * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper -#include "stdafx.h" +#include "StdAfx.h" #include "SecureHttpClient.h" #include "HttpProxyHelper.h" #include "TileCore.h" +SecureHttpClient::SecureHttpClient(): file(nullptr) +{ + // create CURL handle + curl = curl_easy_init(); + + // set up write buffer + chunk.memory = (char *)malloc(1); /* will be grown as needed */ + chunk.size = 0; /* no data yet */ + + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); +} + +size_t SecureHttpClient::WriteMemoryCallback(void* contents, size_t size, size_t nmemb, void* userp) +{ + const size_t realsize = size * nmemb; + auto* mem = (struct MemoryStruct *)userp; + + mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1); + if (mem->memory == nullptr) + { + /* out of memory! */ + CallbackHelper::ErrorMsg("not enough memory (realloc returned NULL)"); + return 0; + } + + memcpy(&mem->memory[mem->size], contents, realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + + return realsize; +} +SecureHttpClient::~SecureHttpClient() +{ + free(chunk.memory); + // CURL cleanup + curl_easy_cleanup(curl); +} // ************************************************************ // SetAuthentication() // ************************************************************ // passing authorization object in such way it ugly, but allocating them dynamically might // give even more troubles -bool SecureHttpClient::SetProxyAndAuthentication(CString userName, CString password, CString domain) +bool SecureHttpClient::SetProxyAndAuthentication(const CString& userName, const CString& password, + const CString& domain) const +{ + // validate handle + if (!curl) return false; + + CURLcode curlCode; + //curlCode = curl_easy_setopt(curl, CURLOPT_SUPPRESS_CONNECT_HEADERS, 1L); + //if (curlCode != CURLcode::CURLE_OK) + //{ + // /* what to do */ + //} + + if (HttpProxyHelper::m_proxyAddress.GetLength() > 0) + { + if (!SetProxy((LPCTSTR)HttpProxyHelper::m_proxyAddress, (long)HttpProxyHelper::m_proxyPort)) + return false; + } + + //curlCode = curl_easy_setopt(curl, CURLOPT_PROXY, (LPCTSTR)domain); + curlCode = curl_easy_setopt(curl, CURLOPT_USERNAME, (LPCTSTR)userName); + curlCode = curl_easy_setopt(curl, CURLOPT_PASSWORD, (LPCTSTR)password); + + // TODO: this needs to be expanded to have more options, e.g. hidden auth is now impossible + if (m_globalSettings.proxyAuthentication == tkProxyAuthentication::asNtlm) + { + return (curl_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_NTLM) == CURLcode::CURLE_OK); + } + // Let CURL decide: + return (curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY) == CURLcode::CURLE_OK); +} + +// ************************************************************* +// GetBodyLength() +// ************************************************************* +int SecureHttpClient::GetBodyLength() const { - // otherwise it will query zone settings in IE for that URL - // if the setting is anything other than URLPOLICY_CREDENTIALS_SILENT_LOGON_OK it will fail - SetSilentLogonOk(true); - - if (HttpProxyHelper::m_proxyAddress.GetLength() > 0) - { - if (!SetProxy(HttpProxyHelper::m_proxyAddress, HttpProxyHelper::m_proxyPort)) - return false; - } - - basicAuth.SetCredentials(userName, password, domain); - - if (m_globalSettings.proxyAuthentication == tkProxyAuthentication::asNtlm) - { - // We're using the standard CNLMAuthObject class here because it automatically uses the current user's - // credentials if no IAuthInfo implementation is given. - return AddAuthObj(_T("NTLM"), &ntlmAuth); - } - - return AddAuthObj("BASIC", &basicAuth, &basicAuth); + return chunk.size; +} + +// ************************************************************* +// GetBody() +// ************************************************************* +BYTE* SecureHttpClient::GetBody() const +{ + return (BYTE*)chunk.memory; } // ************************************************************* // ReadBody() // ************************************************************* -bool SecureHttpClient::ReadBody(char** body, int& length) +bool SecureHttpClient::ReadBody(char** body, int& length) const { - length = GetBodyLength(); + length = GetBodyLength(); - if (length > 0) - { - *body = new char[length + 1]; - memcpy(*body, GetBody(), length); - (*body)[length] = 0; - return true; - } + if (length > 0) + { + *body = new char[length + 1]; + memcpy(*body, GetBody(), length); + (*body)[length] = 0; + return true; + } - return false; + return false; } // ************************************************************* // get_ContentType() // ************************************************************* -TileHttpContentType SecureHttpClient::get_ContentType(int providerId) +TileHttpContentType SecureHttpClient::get_ContentType(int providerId) const { - CString contentType; - GetHeaderValue(_T("Content-Type"), contentType); + char* szContentType; + + curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &szContentType); + CString contentType = szContentType; - if (contentType.Left(5).CompareNoCase(_T("image")) == 0) - { - return httpImage; - } + if (contentType.Left(5).CompareNoCase(_T("image")) == 0) + { + return httpImage; + } - if (contentType.MakeLower().Find("xml") != -1) - { - return httpXml; - } + if (contentType.MakeLower().Find("xml") != -1) + { + return httpXml; + } - if (providerId == (int)tkTileProvider::Rosreestr) { // ad-hoc fix - return httpImage; - } + if (providerId == (int)tkTileProvider::Rosreestr) + { + // ad-hoc fix + return httpImage; + } - return httpUndefined; + return httpUndefined; +} + +// get request status +long SecureHttpClient::GetStatus() const +{ + long responseCode = -1; + if (curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode) != CURLcode::CURLE_OK) + responseCode = -1; + // + return responseCode; } // ************************************************************ @@ -102,67 +182,79 @@ TileHttpContentType SecureHttpClient::get_ContentType(int providerId) // ************************************************************ void SecureHttpClient::LogHttpError() { - int status = GetStatus(); - DWORD socketError = GetLastError(); - - if (status == -1 && socketError == 0) - { - CallbackHelper::ErrorMsg("Failed to retrieve tile: undefined error."); - return; - } - - if (status == -1) - { - CString msg = Utility::GetSocketErrorMessage(socketError); - CallbackHelper::ErrorMsg(Debug::Format("Failed to retrieve tile. WSA error: %d. %s", socketError, msg)); - } - else - { - CallbackHelper::ErrorMsg(Debug::Format("Failed to retrieve tile. HTTP status: %d", status)); - } + // last error message should be in buffer + CallbackHelper::ErrorMsg(Debug::Format("Failed to retrieve tile. cURL error: %s", errorString)); } // ************************************************************* // LogRequest() // ************************************************************* -void SecureHttpClient::LogRequest(int bodyLen, CString shortUrl, CString url) +void SecureHttpClient::LogRequest(int bodyLen, CString shortUrl, CString url) const { - if (tilesLogger.IsOpened() || Debug::LogTiles()) - { - bool hasError = GetStatus() != 200; - bool useShortUrl = false; + if (tilesLogger.IsOpened() || Debug::LogTiles()) + { + const bool hasError = GetStatus() != 200; + // Not used: bool useShortUrl = false; + + if (!tilesLogger.errorsOnly || hasError) + { + CString err; + err.Format("ERROR: %d; ", GetLastError()); + + const CString tempUrl = m_globalSettings.useShortUrlForTiles ? shortUrl : url; - if (!tilesLogger.errorsOnly || hasError) - { - CString err; - err.Format("ERROR: %d; ", GetLastError()); + CString s; + s.Format("%sstatus %d size %6d b %s", !hasError ? "" : err, GetStatus(), bodyLen, tempUrl); - CString tempUrl = m_globalSettings.useShortUrlForTiles ? shortUrl : url; + tilesLogger.Log(s); + } + } +} - CString s; - s.Format("%sstatus %d size %6d b %s", (!hasError ? "" : err), GetStatus(), bodyLen, tempUrl); +// ************************************************************ +// SetProxy() +// ************************************************************ +bool SecureHttpClient::SetProxy(LPCTSTR address, long port) const +{ + return curl_easy_setopt(curl, CURLOPT_PROXY, address) == CURLcode::CURLE_OK && + curl_easy_setopt(curl, CURLOPT_PROXYPORT, port) == CURLcode::CURLE_OK; +} - tilesLogger.Log(s); - } - } +// ************************************************************ +// Navigate() +// ************************************************************ +bool SecureHttpClient::Navigate(LPCTSTR url) const +{ + if (curl_easy_setopt(curl, CURLOPT_URL, url) == CURLcode::CURLE_OK) + { + // the following call is currently required to avoid error CURLE_SSL_CACERT + // not sure whether or not this is appropriate solution + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE); + + // perform the operation + const CURLcode result = curl_easy_perform(curl); + return result == CURLcode::CURLE_OK; + } + // something went wrong + return false; } // ************************************************************ // CheckConnection() // ************************************************************ -bool SecureHttpClient::CheckConnection(CString url) +bool SecureHttpClient::CheckConnection(const CString url) { - CAtlHttpClient* httpClient = new CAtlHttpClient(); - CAtlNavigateData navData; + auto* httpClient = new SecureHttpClient(); - if (HttpProxyHelper::m_proxyAddress.GetLength() > 0) - { - httpClient->SetProxy(HttpProxyHelper::m_proxyAddress, HttpProxyHelper::m_proxyPort); - } + if (HttpProxyHelper::m_proxyAddress.GetLength() > 0) + { + httpClient->SetProxy(HttpProxyHelper::m_proxyAddress, (long)HttpProxyHelper::m_proxyPort); + } - bool result = httpClient->Navigate(url); - httpClient->Close(); - delete httpClient; + const bool result = httpClient->Navigate((LPCTSTR)url); - return result; -} \ No newline at end of file + //httpClient->Close(); + delete httpClient; + + return result; +} diff --git a/src/Tiles/Http/SecureHttpClient.h b/src/Tiles/Http/SecureHttpClient.h index 32fd8569..79826d3c 100644 --- a/src/Tiles/Http/SecureHttpClient.h +++ b/src/Tiles/Http/SecureHttpClient.h @@ -17,26 +17,44 @@ ************************************************************************************** * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ +// jf, 8/2018: Replace ATL Http library usage with libCurl so as to support SSL/HTTPS + #pragma once -#include "BasicAuth.h" -//#include "SecureSocket.h" +#include "curl.h" + +struct MemoryStruct +{ + char *memory; + size_t size; +}; -class SecureHttpClient : public CAtlHttpClient - //public CAtlHttpClientT +class SecureHttpClient { +public: + SecureHttpClient(); + ~SecureHttpClient(); + private: - BasicAuth basicAuth; - CNTLMAuthObject ntlmAuth; + CURL *curl; + FILE *file; + struct MemoryStruct chunk{}; + char errorString[CURL_ERROR_SIZE]{}; + static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp); public: - void LogRequest(int bodyLen, CString shortUrl, CString url); + long GetStatus() const; + void LogRequest(int bodyLen, CString shortUrl, CString url) const; void LogHttpError(); - TileHttpContentType get_ContentType(int providerId); - bool ReadBody(char** body, int& length); + bool SetProxy(LPCTSTR address, long port) const; + bool Navigate(LPCTSTR url) const; + TileHttpContentType get_ContentType(int providerId) const; + int GetBodyLength() const; + BYTE *GetBody() const; + bool ReadBody(char** body, int& length) const; public: // methods - bool SetProxyAndAuthentication(CString userName, CString password, CString domain); + bool SetProxyAndAuthentication(const CString& userName, const CString& password, const CString& domain) const; public: // static methods diff --git a/src/Tiles/Loaders/ITileLoader.cpp b/src/Tiles/Loaders/ITileLoader.cpp index 65204440..8611df78 100644 --- a/src/Tiles/Loaders/ITileLoader.cpp +++ b/src/Tiles/Loaders/ITileLoader.cpp @@ -17,38 +17,38 @@ ************************************************************************************** * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ -#include "stdafx.h" +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper + +#include "StdAfx.h" #include "ITileLoader.h" #include "ILoadingTask.h" -#include "TileBulkLoader.h" -#include "TileMapLoader.h" // ******************************************************* // InitPools() // ******************************************************* bool ITileLoader::InitPools() { - _stopped = false; - - int size = m_globalSettings.GetTilesThreadPoolSize(); - - if (!_pool) - { - _pool = new CThreadPool(); - HRESULT hr = _pool->Initialize(NULL, size); - if (!SUCCEEDED( hr )) - CallbackHelper::ErrorMsg("Failed to initialize thread pool (1) for tile loading."); - } - - if (!_pool2) - { - _pool2 = new CThreadPool(); - HRESULT hr = _pool2->Initialize(NULL, size); - if(!SUCCEEDED( hr )) - CallbackHelper::ErrorMsg("Failed to initialize thread pool (2) for tile loading."); - } - - return _pool && _pool2; + _stopped = false; + + const int size = m_globalSettings.GetTilesThreadPoolSize(); + + if (!_pool) + { + _pool = new CThreadPool(); + const HRESULT hr = _pool->Initialize(nullptr, size); + if (!SUCCEEDED( hr )) + CallbackHelper::ErrorMsg("Failed to initialize thread pool (1) for tile loading."); + } + + if (!_pool2) + { + _pool2 = new CThreadPool(); + const HRESULT hr = _pool2->Initialize(nullptr, size); + if (!SUCCEEDED( hr )) + CallbackHelper::ErrorMsg("Failed to initialize thread pool (2) for tile loading."); + } + + return _pool && _pool2; } // ******************************************************* @@ -56,22 +56,22 @@ bool ITileLoader::InitPools() // ******************************************************* CThreadPool* ITileLoader::PreparePool() { - if (!InitPools()) - return NULL; + if (!InitPools()) + return nullptr; - CThreadPool* pool = (_lastGeneration % 2 == 0) ? _pool : _pool2; + CThreadPool* pool = _lastGeneration % 2 == 0 ? _pool : _pool2; - pool->SetSize(m_globalSettings.GetTilesThreadPoolSize()); + pool->SetSize(m_globalSettings.GetTilesThreadPoolSize()); - pool->SetTimeout(100000); // 100 seconds (lower rate limit may be set) + pool->SetTimeout(100000); // 100 seconds (lower rate limit may be set) - tilesLogger.WriteLine("Tiles requested; generation = %d", _lastGeneration); + tilesLogger.WriteLine("Tiles requested; generation = %d", _lastGeneration); - CleanTasks(); + CleanTasks(); - CleanRequests(); + CleanRequests(); - return pool; + return pool; } // ******************************************************* @@ -79,33 +79,33 @@ CThreadPool* ITileLoader::PreparePool() // ******************************************************* unsigned int ITileLoader::RegisterTile(int generation) { - TileRequestInfo* info = FindRequest(generation); - if (info) - { - return InterlockedIncrement(&info->count); - } + TileRequestInfo* info = FindRequest(generation); + if (info) + { + return InterlockedIncrement(&info->count); + } - return 0; + return 0; } // ******************************************************* // Load() // ******************************************************* // Creates tasks for tile points and enqueues them in thread pool -void ITileLoader::Load(std::vector &points, BaseProvider* provider, int zoom, TileRequestInfo* requestInfo) +void ITileLoader::Load(std::vector& points, BaseProvider* provider, int zoom, TileRequestInfo* requestInfo) { - CThreadPool* pool = PreparePool(); - if (!pool) return; - - sort(points.begin(), points.end(), [](TilePoint* p1, TilePoint* p2) { return p1->dist < p2->dist; } ); - - for ( size_t i = 0; i < points.size(); i++ ) - { - ILoadingTask* task = CreateTask(points[i]->x, points[i]->y, zoom, provider, _lastGeneration); - task->set_Loader(this); - _tasks.push_back(task); - pool->QueueRequest( (ThreadWorker::RequestType) task ); - } + CThreadPool* pool = PreparePool(); + if (!pool) return; + + sort(points.begin(), points.end(), [](TilePoint* p1, TilePoint* p2) { return p1->dist < p2->dist; }); + + for (size_t i = 0; i < points.size(); i++) + { + ILoadingTask* task = CreateTask(points[i]->x, points[i]->y, zoom, provider, _lastGeneration); + task->set_Loader(this); + _tasks.push_back(task); + pool->QueueRequest((ThreadWorker::RequestType)task); + } } // ******************************************************* @@ -113,14 +113,14 @@ void ITileLoader::Load(std::vector &points, BaseProvider* provider, // ******************************************************* void ITileLoader::Stop() { - // This one is called from map destructor. Don't wait for tasks to finish - // as it deters the closing of form or application. - // Don't bother with releasing memory for unfinished tasks as - // they use around 30 bytes of memory, so just let it leak. + // This one is called from map destructor. Don't wait for tasks to finish + // as it deters the closing of form or application. + // Don't bother with releasing memory for unfinished tasks as + // they use around 30 bytes of memory, so just let it leak. - _stopped = true; + _stopped = true; - CleanTasks(); + CleanTasks(); } // ******************************************************* @@ -129,20 +129,20 @@ void ITileLoader::Stop() // Cleans completed tasks void ITileLoader::CleanTasks() { - list::iterator it = _tasks.begin(); - while (it != _tasks.end()) - { - ILoadingTask* task = (ILoadingTask*)*it; - if (task->completed()) - { - delete task; - it = _tasks.erase(it); - } - else - { - ++it; - } - } + list::iterator it = _tasks.begin(); + while (it != _tasks.end()) + { + ILoadingTask* task = (ILoadingTask*)*it; + if (task->completed()) + { + delete task; + it = _tasks.erase(it); + } + else + { + ++it; + } + } } // ******************************************************* @@ -150,25 +150,25 @@ void ITileLoader::CleanTasks() // ******************************************************* void ITileLoader::CleanRequests() { - CSingleLock lock(&_requestLock, TRUE); - - int size = static_cast(_requests.size()); - - // remove all requests but the last one - list::iterator it = _requests.begin(); - while (it != _requests.end()) - { - TileRequestInfo* info = *it; - if (info->generation < _lastGeneration) - { - delete info; - it = _requests.erase(it); - } - else - { - ++it; - } - } + CSingleLock lock(&_requestLock, TRUE); + + // Never used: int size = static_cast(_requests.size()); + + // remove all requests but the last one + list::iterator it = _requests.begin(); + while (it != _requests.end()) + { + TileRequestInfo* info = *it; + if (info->generation < _lastGeneration) + { + delete info; + it = _requests.erase(it); + } + else + { + ++it; + } + } } // ******************************************************* @@ -176,30 +176,33 @@ void ITileLoader::CleanRequests() // ******************************************************* TileRequestInfo* ITileLoader::FindRequest(int generation) { - CSingleLock lock(&_requestLock, TRUE); + CSingleLock lock(&_requestLock, TRUE); - list::iterator it = find_if(_requests.begin(), _requests.end(), - [&](TileRequestInfo* info){ return info->generation == generation; }); + const list::iterator it = find_if(_requests.begin(), _requests.end(), + [&](TileRequestInfo* info) + { + return info->generation == generation; + }); - if (it != _requests.end()) - { - return *it; - } + if (it != _requests.end()) + { + return *it; + } - return NULL; + return nullptr; } // ******************************************************* // CreateNextRequest() // ******************************************************* -TileRequestInfo* ITileLoader::CreateNextRequest(CString key, bool isSnapshot) +TileRequestInfo* ITileLoader::CreateNextRequest(const CString& key, bool isSnapshot) { - TileRequestInfo* info = new TileRequestInfo(key, isSnapshot); - info->generation = ++_lastGeneration; - - _requestLock.Lock(); - _requests.push_back(info); - _requestLock.Unlock(); - - return info; + TileRequestInfo* info = new TileRequestInfo(key, isSnapshot); + info->generation = ++_lastGeneration; + + _requestLock.Lock(); + _requests.push_back(info); + _requestLock.Unlock(); + + return info; } diff --git a/src/Tiles/Loaders/ITileLoader.h b/src/Tiles/Loaders/ITileLoader.h index f4f08331..d84b0b78 100644 --- a/src/Tiles/Loaders/ITileLoader.h +++ b/src/Tiles/Loaders/ITileLoader.h @@ -17,12 +17,12 @@ ************************************************************************************** * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ - #pragma once +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper + +#pragma once #include -#include -#include "baseprovider.h" +#include "BaseProvider.h" #include "tilecacher.h" -#include "TileCacheManager.h" #include "ITileCache.h" #include "ILoadingTask.h" @@ -38,62 +38,63 @@ class ILoadingTask; class ITileLoader { public: - ITileLoader() - : _pool(NULL), _pool2(NULL), _stopped(false) - { - _delayRequestTimeout = 0; - _lastGeneration = 0; - } + ITileLoader() + : _pool(nullptr), _pool2(nullptr), _stopped(false) + { + _delayRequestTimeout = 0; + _lastGeneration = 0; + } - virtual ~ITileLoader(void) - { - CleanTasks(); + virtual ~ITileLoader(void) + { + CleanTasks(); - if (_pool != NULL) { - _pool->Shutdown(50); // will result in TerminateThread call after 50ms delay - delete _pool; - } + if (_pool != nullptr) + { + _pool->Shutdown(50); // will result in TerminateThread call after 50ms delay + delete _pool; + } - if (_pool2 != NULL) { - _pool2->Shutdown(50); // will result in TerminateThread call after 50ms delay - delete _pool2; - } - } + if (_pool2 != nullptr) + { + _pool2->Shutdown(50); // will result in TerminateThread call after 50ms delay + delete _pool2; + } + } protected: - // 2 pools will be used in alternate fashion, - // as it's problematic to terminate threads correctly until their current task is finished - // but it's quite desirable to start the processing of new requests ASAP - CThreadPool* _pool; - CThreadPool* _pool2; - list _tasks; // all the scheduled tasks - list _requests; - ::CCriticalSection _requestLock; - long _delayRequestTimeout; - bool _stopped; - int _lastGeneration; // the most recent generation + // 2 pools will be used in alternate fashion, + // as it's problematic to terminate threads correctly until their current task is finished + // but it's quite desirable to start the processing of new requests ASAP + CThreadPool* _pool; + CThreadPool* _pool2; + list _tasks; // all the scheduled tasks + list _requests; + CCriticalSection _requestLock; + long _delayRequestTimeout; + bool _stopped; + int _lastGeneration; // the most recent generation private: - void CleanTasks(); - bool InitPools(); - CThreadPool* PreparePool(); - void CleanRequests(); + void CleanTasks(); + bool InitPools(); + CThreadPool* PreparePool(); + void CleanRequests(); public: - // properties - virtual bool IsOutdated(int generation) { return _stopped; } - bool isStopped() { return _stopped; } - int get_LastGeneration() { return _lastGeneration; } - TileRequestInfo* CreateNextRequest(CString key, bool isSnapshot); - long get_DelayRequestTimeout() { return _delayRequestTimeout; } - void set_DelayRequestTimeout(long value) { _delayRequestTimeout = value; } + // properties + virtual bool IsOutdated(int generation) { return _stopped; } + bool isStopped() { return _stopped; } + int get_LastGeneration() { return _lastGeneration; } + TileRequestInfo* CreateNextRequest(const CString& key, bool isSnapshot); + long get_DelayRequestTimeout() { return _delayRequestTimeout; } + void set_DelayRequestTimeout(long value) { _delayRequestTimeout = value; } public: - //methods - virtual ILoadingTask* CreateTask(int x, int y, int zoom, BaseProvider* provider, int generation) = 0; - virtual void Stop(); - TileRequestInfo* FindRequest(int generation); - void Load(vector &points, BaseProvider* provider, int zoom, TileRequestInfo* info ); - unsigned int RegisterTile(int generation); + //methods + virtual ILoadingTask* CreateTask(int x, int y, int zoom, BaseProvider* provider, int generation) = 0; + virtual void Stop(); + TileRequestInfo* FindRequest(int generation); + void Load(vector& points, BaseProvider* provider, int zoom, TileRequestInfo* info); + unsigned int RegisterTile(int generation); }; - diff --git a/src/Tiles/Loaders/TileBulkLoader.h b/src/Tiles/Loaders/TileBulkLoader.h index 1abafc46..f744cb9d 100644 --- a/src/Tiles/Loaders/TileBulkLoader.h +++ b/src/Tiles/Loaders/TileBulkLoader.h @@ -17,50 +17,56 @@ ************************************************************************************** * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ - #pragma once +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper + +#pragma once #include "ITileLoader.h" // ****************************************************** // TileBulkLoader() // ****************************************************** // Handles the loading queue of map tiles, schedules the loaded tiles for caching -class TileBulkLoader: public ITileLoader +class TileBulkLoader : public ITileLoader { public: - TileBulkLoader(ITileCache* cache) - : _cache(cache) - { - _errorCount = 0; - _sumCount = 0; - } + TileBulkLoader(ITileCache* cache) + : _cache(cache), _callback(nullptr), _stopCallback(nullptr) + { + _errorCount = 0; + _sumCount = 0; + } - virtual ~TileBulkLoader(void) - { - } + virtual ~TileBulkLoader() + = default; private: - // caching only - ITileCache* _cache; - ICallback* _callback; // to report progress to clients via COM interface - IStopExecution* _stopCallback; // to stop execution by clients via COM interface - int _errorCount; - int _sumCount; // sums all requests even if generation doesn't match + // caching only + ITileCache* _cache; + ICallback* _callback; // to report progress to clients via COM interface + IStopExecution* _stopCallback; // to stop execution by clients via COM interface + int _errorCount; + int _sumCount; // sums all requests even if generation doesn't match private: - void CleanTasks(); + void CleanTasks(); public: - // properties - void set_Callback(ICallback* callback) { _callback = callback; } - void set_StopCallback(IStopExecution* callback) { _stopCallback = callback; } - int get_ErrorCount() { return _errorCount; } - int get_SumCount() { return _sumCount; } - ITileCache* get_Cache() { return _cache; } + // properties + void set_Callback(ICallback* callback) { _callback = callback; } + void set_StopCallback(IStopExecution* callback) { _stopCallback = callback; } + int get_ErrorCount() { return _errorCount; } + int get_SumCount() { return _sumCount; } + ITileCache* get_Cache() { return _cache; } public: - //methods - void TileLoaded(TileCore* tile, int generation); - void ResetErrorCount() { _errorCount = 0; _sumCount = 0; } - ILoadingTask* CreateTask(int x, int y, int zoom, BaseProvider* provider, int generation); -}; + //methods + void TileLoaded(TileCore* tile, int generation); + void ResetErrorCount() + { + _errorCount = 0; + _sumCount = 0; + } + + ILoadingTask* CreateTask(int x, int y, int zoom, BaseProvider* provider, int generation); +}; diff --git a/src/Tiles/Loaders/TileMapLoader.cpp b/src/Tiles/Loaders/TileMapLoader.cpp index ab616537..885f88aa 100644 --- a/src/Tiles/Loaders/TileMapLoader.cpp +++ b/src/Tiles/Loaders/TileMapLoader.cpp @@ -17,7 +17,9 @@ ************************************************************************************** * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ -#include "stdafx.h" +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper + +#include "StdAfx.h" #include "TileMapLoader.h" #include "MapLoadingTask.h" @@ -26,7 +28,7 @@ // ******************************************************* ILoadingTask* TileMapLoader::CreateTask(int x, int y, int zoom, BaseProvider* provider, int generation) { - return new MapLoadingTask(x, y, zoom, provider, generation); + return new MapLoadingTask(x, y, zoom, provider, generation); } // ******************************************************* @@ -34,10 +36,10 @@ ILoadingTask* TileMapLoader::CreateTask(int x, int y, int zoom, BaseProvider* pr // ******************************************************* void TileMapLoader::LockActiveTasks(bool lock) { - if (lock) - _activeTasksLock.Lock(); - else - _activeTasksLock.Unlock(); + if (lock) + _activeTasksLock.Lock(); + else + _activeTasksLock.Unlock(); } // ******************************************************* @@ -45,9 +47,9 @@ void TileMapLoader::LockActiveTasks(bool lock) // ******************************************************* void TileMapLoader::AddActiveTask(void* task) { - _activeTasksLock.Lock(); - _activeTasks.push_back(task); - _activeTasksLock.Unlock(); + _activeTasksLock.Lock(); + _activeTasks.push_back(task); + _activeTasksLock.Unlock(); } // ******************************************************* @@ -55,23 +57,23 @@ void TileMapLoader::AddActiveTask(void* task) // ******************************************************* void TileMapLoader::RemoveActiveTask(void* t) { - _activeTasksLock.Lock(); - - ILoadingTask* task = (ILoadingTask*)t; - list::iterator it = _activeTasks.begin(); - - while (it != _activeTasks.end()) - { - ILoadingTask* item = (ILoadingTask*)*it; - if (item->Compare(task)) - { - _activeTasks.remove(item); - break; - } - ++it; - } - - _activeTasksLock.Unlock(); + _activeTasksLock.Lock(); + + auto* task = (ILoadingTask*)t; + list::iterator it = _activeTasks.begin(); + + while (it != _activeTasks.end()) + { + ILoadingTask* item = (ILoadingTask*)*it; + if (item->Compare(task)) + { + _activeTasks.remove(item); + break; + } + ++it; + } + + _activeTasksLock.Unlock(); } // ******************************************************* @@ -79,26 +81,26 @@ void TileMapLoader::RemoveActiveTask(void* t) // ******************************************************* bool TileMapLoader::HasActiveTask(void* t) { - _activeTasksLock.Lock(); - - ILoadingTask* task = (ILoadingTask*)t; - list::iterator it = _activeTasks.begin(); - bool found = false; - - while (it != _activeTasks.end()) - { - ILoadingTask* item = (ILoadingTask*)*it; - if (item->Compare(task)) - { - found = true; - break; - } - ++it; - } - - _activeTasksLock.Unlock(); - - return found; + _activeTasksLock.Lock(); + + auto* task = (ILoadingTask*)t; + list::iterator it = _activeTasks.begin(); + bool found = false; + + while (it != _activeTasks.end()) + { + ILoadingTask* item = (ILoadingTask*)*it; + if (item->Compare(task)) + { + found = true; + break; + } + ++it; + } + + _activeTasksLock.Unlock(); + + return found; } // ******************************************************* @@ -106,7 +108,7 @@ bool TileMapLoader::HasActiveTask(void* t) // ******************************************************* void TileMapLoader::StopCaching() { - _cacher->Stop(); + _cacher->Stop(); } // ******************************************************* @@ -114,11 +116,11 @@ void TileMapLoader::StopCaching() // ******************************************************* void TileMapLoader::ScheduleForCaching(TileCore* tile) { - if (tile) - { - tile->AddRef(); - _cacher->Enqueue(tile); - } + if (tile) + { + tile->AddRef(); + _cacher->Enqueue(tile); + } } // ******************************************************* @@ -126,7 +128,7 @@ void TileMapLoader::ScheduleForCaching(TileCore* tile) // ******************************************************* void TileMapLoader::RunCaching() { - _cacher->Run(); + _cacher->Run(); } // ******************************************************* @@ -134,8 +136,7 @@ void TileMapLoader::RunCaching() // ******************************************************* void TileMapLoader::Stop() { - ITileLoader::Stop(); + ITileLoader::Stop(); - StopCaching(); + StopCaching(); } - diff --git a/src/Tiles/Loaders/TileMapLoader.h b/src/Tiles/Loaders/TileMapLoader.h index 6ed52d84..e822b9ae 100644 --- a/src/Tiles/Loaders/TileMapLoader.h +++ b/src/Tiles/Loaders/TileMapLoader.h @@ -17,8 +17,11 @@ ************************************************************************************** * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ - #pragma once +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper + +#pragma once #include "ITileLoader.h" +#include "TileCacheManager.h" // ****************************************************** // TileMapLoader() @@ -28,37 +31,36 @@ class TileMapLoader : public ITileLoader { public: - TileMapLoader(CacheType cacheType) - { - ITileCache* cache = TileCacheManager::get_Cache(cacheType); - _cacher = new TileCacher(cache); - } + TileMapLoader(CacheType cacheType) + { + ITileCache* cache = TileCacheManager::get_Cache(cacheType); + _cacher = new TileCacher(cache); + } - virtual ~TileMapLoader(void) - { - delete _cacher; - } + virtual ~TileMapLoader(void) + { + delete _cacher; + } protected: - list _activeTasks; // HTTP requests being currently performed - ::CCriticalSection _activeTasksLock; - TileCacher* _cacher; + list _activeTasks; // HTTP requests being currently performed + CCriticalSection _activeTasksLock; + TileCacher* _cacher; public: - // properties - list& get_ActiveTasks() { return _activeTasks; } - bool IsOutdated(int generation) { return _stopped || generation < _lastGeneration; } + // properties + list& get_ActiveTasks() { return _activeTasks; } + bool IsOutdated(int generation) { return _stopped || generation < _lastGeneration; } public: - //methods - ILoadingTask* CreateTask(int x, int y, int zoom, BaseProvider* provider, int generation); - void LockActiveTasks(bool lock); - void AddActiveTask(void* task); - void RemoveActiveTask(void* task); - bool HasActiveTask(void* task); - void StopCaching(); - void ScheduleForCaching(TileCore* tile); - void RunCaching(); - void Stop(); + //methods + ILoadingTask* CreateTask(int x, int y, int zoom, BaseProvider* provider, int generation); + void LockActiveTasks(bool lock); + void AddActiveTask(void* task); + void RemoveActiveTask(void* task); + bool HasActiveTask(void* task); + void StopCaching(); + void ScheduleForCaching(TileCore* tile); + void RunCaching(); + void Stop(); }; - diff --git a/src/Tiles/Providers/BaseProvider.cpp b/src/Tiles/Providers/BaseProvider.cpp index 77b57f21..ef54456e 100644 --- a/src/Tiles/Providers/BaseProvider.cpp +++ b/src/Tiles/Providers/BaseProvider.cpp @@ -17,38 +17,39 @@ ************************************************************************************** * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper -#include "stdafx.h" -#include "Wininet.h" +#include "StdAfx.h" +//#include "Wininet.h" #include "BaseProvider.h" #include "SecureHttpClient.h" CString BaseProvider::_proxyUsername = ""; CString BaseProvider::_proxyPassword = ""; CString BaseProvider::_proxyDomain = ""; -::CCriticalSection BaseProvider::_clientLock; +CCriticalSection BaseProvider::_clientLock; // ************************************************************ // GetTileImage() // ************************************************************ -TileCore* BaseProvider::GetTileImage(CPoint &pos, int zoom) +TileCore* BaseProvider::GetTileImage(CPoint& pos, const int zoom) { - TileCore* tile = new TileCore(this->Id, zoom, pos, this->_projection); - - for (size_t i = 0; i < _subProviders.size(); i++) - { - CMemoryBitmap* bmp = _subProviders[i]->DownloadBitmap(pos, zoom); - if (bmp) - { - tile->AddOverlay(bmp); - } - else - { - tile->hasErrors(true); - } - } - - return tile; + auto* tile = new TileCore(this->Id, zoom, pos, this->_projection); + + for (size_t i = 0; i < _subProviders.size(); i++) + { + CMemoryBitmap* bmp = _subProviders[i]->DownloadBitmap(pos, zoom); + if (bmp) + { + tile->AddOverlay(bmp); + } + else + { + tile->hasErrors(true); + } + } + + return tile; } // ************************************************************ @@ -56,110 +57,121 @@ TileCore* BaseProvider::GetTileImage(CPoint &pos, int zoom) // ************************************************************ CMemoryBitmap* BaseProvider::GetTileHttpData(CString url, CString shortUrl, bool recursive) { - SecureHttpClient client; - CAtlNavigateData navData; - navData.dwReadBlockSize = 262144; + // jf: I don't think we need a mutex here + //CSingleLock lock(&_clientLock, TRUE); - client.SetProxyAndAuthentication(_proxyUsername, _proxyPassword, _proxyDomain); + // stack-based instance + SecureHttpClient client; - PreventParallelExecution(); + client.SetProxyAndAuthentication(_proxyUsername, _proxyPassword, _proxyDomain); - bool success = client.Navigate(url, &navData); + // jf: Likewise, don't think we need this work-around. + // if we observe multiple retransmissions, we can reconsider. + //PreventParallelExecution(); - CMemoryBitmap* bmp = ProcessHttpRequest(reinterpret_cast(&client), url, shortUrl, success); + const bool success = client.Navigate(url); + if (!success) + { + client.LogHttpError(); + return nullptr; + } - if (!success && !recursive && client.GetStatus() == -1) - { - // it's a socket error, so let's try one more time - Sleep(20); - tilesLogger.Log("Reloading attempt: " + m_globalSettings.useShortUrlForTiles ? shortUrl : url); - bmp = GetTileHttpData(url, shortUrl, true); - } + CMemoryBitmap* bmp = ProcessHttpRequest(reinterpret_cast(&client), url, shortUrl, success); - return bmp; + // this is a leftover from the Atl Http code; it remains to be seen if it is necessary + if (!success && !recursive && client.GetStatus() == -1) + { + // it's a socket error, so let's try one more time + Sleep(20); + tilesLogger.Log("Reloading attempt: " + m_globalSettings.useShortUrlForTiles ? shortUrl : url); + bmp = GetTileHttpData(url, shortUrl, true); + } + + return bmp; } // ************************************************************ // DownloadBitmap() // ************************************************************ -CMemoryBitmap* BaseProvider::DownloadBitmap(CPoint &pos, int zoom) +CMemoryBitmap* BaseProvider::DownloadBitmap(CPoint& pos, int zoom) { - CString url = MakeTileImageUrl(pos, zoom); - CString shortUrl; + const CString url = MakeTileImageUrl(pos, zoom); + CString shortUrl; - shortUrl.Format("\\zoom=%d\\x=%d\\y=%d", zoom, pos.x, pos.y); + shortUrl.Format(R"(\zoom=%d\x=%d\y=%d)", zoom, pos.x, pos.y); - CMemoryBitmap* bmp = GetTileHttpData(url, shortUrl); + CMemoryBitmap* bmp = GetTileHttpData(url, shortUrl); - return bmp; + return bmp; } // ************************************************************ // ProcessResults() // ************************************************************ -CMemoryBitmap* BaseProvider::ProcessHttpRequest(void* secureHttpClient, CString url, CString shortUrl, bool success) +CMemoryBitmap* BaseProvider::ProcessHttpRequest(void* secureHttpClient, const CString& url, const CString& shortUrl, + bool success) { - SecureHttpClient* client = reinterpret_cast(secureHttpClient); - - if (_isStopped) return NULL; - - if (!success) client->LogHttpError(); - - TileHttpContentType contentType = client->get_ContentType(Id); - - char* body = NULL; - int length = 0; - - if (success && client->GetStatus() == 200) - { - client->ReadBody(&body, length); - } - - client->LogRequest(length, shortUrl, url); - - CMemoryBitmap* bmp = NULL; - switch (contentType) - { - case httpImage: - bmp = ReadBitmap(body, length); - break; - case httpXml: - if (IsWms()) - { - CString s(body); - ParseServerException(s); - } - break; - default: - break; - } - - if (body) { - delete[] body; - } - - return bmp; + auto* client = reinterpret_cast(secureHttpClient); + + if (_isStopped) return nullptr; + + if (!success) client->LogHttpError(); + + const TileHttpContentType contentType = client->get_ContentType(Id); + + char* body = nullptr; + int length = 0; + + if (success && client->GetStatus() == 200) + { + client->ReadBody(&body, length); + } + + client->LogRequest(length, shortUrl, url); + + CMemoryBitmap* bmp = nullptr; + switch (contentType) + { + case httpImage: + bmp = ReadBitmap(body, length); + break; + case httpXml: + if (IsWms()) + { + const CString s(body); + ParseServerException(s); + } + break; + default: + break; + } + + delete[] body; + + return bmp; } // ************************************************************ // ParseServerException() // ************************************************************ -void BaseProvider::ParseServerException(CString s) +void BaseProvider::ParseServerException(const CString& s) const { - CPLXMLNode* node = CPLParseXMLString(s); - if (node) - { - while (node) { - CPLXMLNode* nodeException = CPLGetXMLNode(node, "ServiceException"); - if (nodeException) { - CString msg = CPLGetXMLValue(nodeException, "", ""); - CallbackHelper::ErrorMsg(Debug::Format("WMS Server exception (%s): %s", Name, msg)); - } - node = node->psNext; - } - - CPLDestroyXMLNode(node); - } + CPLXMLNode* node = CPLParseXMLString(s); + if (node) + { + while (node) + { + CPLXMLNode* nodeException = CPLGetXMLNode(node, "ServiceException"); + if (nodeException) + { + const CString msg = CPLGetXMLValue(nodeException, "", ""); + CallbackHelper::ErrorMsg(Debug::Format("WMS Server exception (%s): %s", Name, msg)); + } + node = node->psNext; + } + + CPLDestroyXMLNode(node); + } } // ************************************************************ @@ -167,40 +179,40 @@ void BaseProvider::ParseServerException(CString s) // ************************************************************ void BaseProvider::PreventParallelExecution() { - CSingleLock lock(&_clientLock, TRUE); + CSingleLock lock(&_clientLock, TRUE); - // there is info that ARP might have problems with simultaneous connections, - // discarding the next connection if the previous one is under resolution; - // since we experience periodic rertransmissions let's introduce - // a small delay between connections http://stackoverflow.com/questions/1875151/delay-in-multiple-tcp-connections-from-java-to-the-same-machine - Sleep(5); + // there is info that ARP might have problems with simultaneous connections, + // discarding the next connection if the previous one is under resolution; + // since we experience periodic rertransmissions let's introduce + // a small delay between connections http://stackoverflow.com/questions/1875151/delay-in-multiple-tcp-connections-from-java-to-the-same-machine + Sleep(5); } // ************************************************************* // ReadBitmap() // ************************************************************* -CMemoryBitmap* BaseProvider::ReadBitmap(char* body, int bodyLen) +CMemoryBitmap* BaseProvider::ReadBitmap(char* body, int bodyLen) const { - CMemoryBitmap* bmp = new CMemoryBitmap(); - bmp->LoadFromRawData(body, bodyLen); - bmp->Provider = this->Id; - return bmp; + auto* bmp = new CMemoryBitmap(); + bmp->LoadFromRawData(body, bodyLen); + bmp->Provider = this->Id; + return bmp; } // ************************************************************* // SetProxyAuthorization() // ************************************************************* -bool BaseProvider::SetAuthorization(CString username, CString password, CString domain) +bool BaseProvider::SetAuthorization(const CString& username, const CString& password, const CString& domain) { - CString oldProxy = _proxyUsername; - CString oldDomain = _proxyDomain; - CString oldPassword = _proxyPassword; + CString oldProxy = _proxyUsername; + CString oldDomain = _proxyDomain; + CString oldPassword = _proxyPassword; - _proxyUsername = username; - _proxyDomain = domain; - _proxyPassword = password; + _proxyUsername = username; + _proxyDomain = domain; + _proxyPassword = password; - return true; + return true; } // ************************************************************* @@ -208,9 +220,9 @@ bool BaseProvider::SetAuthorization(CString username, CString password, CString // ************************************************************* void BaseProvider::ClearAuthorization() { - _proxyUsername = ""; - _proxyDomain = ""; - _proxyPassword = ""; + _proxyUsername = ""; + _proxyDomain = ""; + _proxyPassword = ""; } // ************************************************************* @@ -218,11 +230,12 @@ void BaseProvider::ClearAuthorization() // ************************************************************* void BaseProvider::AddDynamicOverlay(BaseProvider* p) { - if (p) { - p->_dynamicOverlay = true; - } + if (p) + { + p->_dynamicOverlay = true; + } - _subProviders.push_back(p); + _subProviders.push_back(p); } // ************************************************************* @@ -230,10 +243,10 @@ void BaseProvider::AddDynamicOverlay(BaseProvider* p) // ************************************************************* void BaseProvider::ClearSubProviders() { - for (size_t i = 0; i < _subProviders.size(); i++) - { - if (_subProviders[i]->_dynamicOverlay) - delete _subProviders[i]; - } - _subProviders.clear(); -} \ No newline at end of file + for (size_t i = 0; i < _subProviders.size(); i++) + { + if (_subProviders[i]->_dynamicOverlay) + delete _subProviders[i]; + } + _subProviders.clear(); +} diff --git a/src/Tiles/Providers/BaseProvider.h b/src/Tiles/Providers/BaseProvider.h index 5f234d1f..58b4180b 100644 --- a/src/Tiles/Providers/BaseProvider.h +++ b/src/Tiles/Providers/BaseProvider.h @@ -17,10 +17,11 @@ ************************************************************************************** * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper +// jf, 8/2018: Replace ATL Http library usage with libCurl so as to support SSL/HTTPS #pragma once -#include "geopoint.h" -#include "baseprojection.h" +#include "BaseProjection.h" #include "TileCore.h" #include "afxmt.h" @@ -31,84 +32,83 @@ class BaseProvider { public: - BaseProvider() - : Id(-1), _minZoom(1), _maxZoom(18), - _manager(NULL), _projection(NULL), _isStopped(false), - _dynamicOverlay(false), _initAttemptCount(0) - { - _licenseUrl = "https://mapwingis.codeplex.com/wikipage?title=tiles"; - LanguageStr = "en"; - } + BaseProvider() + : _dynamicOverlay(false), _isStopped(false), _manager(nullptr), + _projection(nullptr), _initAttemptCount(0), _minZoom(1), + _maxZoom(18), Id(-1) + { + _licenseUrl = "https://mapwingis.codeplex.com/wikipage?title=tiles"; + LanguageStr = "en"; + } - virtual ~BaseProvider(void) - { - if (_projection) { - delete _projection; - } - }; + virtual ~BaseProvider(void) + { + delete _projection; + } private: - bool _dynamicOverlay; - bool _isStopped; - void* _manager; + bool _dynamicOverlay; + bool _isStopped; + void* _manager; protected: - static ::CCriticalSection _clientLock; - static CString _proxyUsername; - static CString _proxyPassword; - static CString _proxyDomain; + static CCriticalSection _clientLock; + static CString _proxyUsername; + static CString _proxyPassword; + static CString _proxyDomain; protected: - vector _subProviders; // for complex providers with more than one source bitmap per tile - BaseProjection* _projection; - CStringW _copyright; - CString _refererUrl; - CString _licenseUrl; - CString _urlFormat; - int _initAttemptCount; - CString _serverLetters; - int _minZoom; - int _maxZoom; + vector _subProviders{}; // for complex providers with more than one source bitmap per tile + BaseProjection* _projection; + CStringW _copyright; + CString _refererUrl; + CString _licenseUrl; + CString _urlFormat; + int _initAttemptCount; + CString _serverLetters; + int _minZoom; + int _maxZoom; public: - int Id; - CString Name; - CString Version; - CString LanguageStr; + int Id; + CString Name; + CString Version; + CString LanguageStr; private: - CMemoryBitmap* GetTileHttpData(CString urlStr, CString shortUrl, bool recursive = false); - void PreventParallelExecution(); - CMemoryBitmap* ProcessHttpRequest(void* client, CString url, CString shortUrl, bool success); - CMemoryBitmap* DownloadBitmap(CPoint &pos, int zoom); - void ParseServerException(CString s); + CMemoryBitmap* GetTileHttpData(CString url, CString shortUrl, bool recursive = false); + static void PreventParallelExecution(); + CMemoryBitmap* ProcessHttpRequest(void* secureHttpClient, const CString& url, const CString& shortUrl, + bool success); + CMemoryBitmap* DownloadBitmap(CPoint& pos, int zoom); + void ParseServerException(const CString& s) const; protected: - virtual CString MakeTileImageUrl(CPoint &pos, int zoom) = 0; - int GetServerNum(CPoint &pos, int max) { return (pos.x + 2 * pos.y) % max; } - CMemoryBitmap* ReadBitmap(char* body, int bodyLen); + virtual CString MakeTileImageUrl(CPoint& pos, int zoom) = 0; + int GetServerNum(CPoint& pos, int max) { return (pos.x + 2 * pos.y) % max; } + CMemoryBitmap* ReadBitmap(char* body, int bodyLen) const; public: - // properties - virtual bool IsWms() { return false; } - virtual CStringW get_Copyright() { return _copyright; } - virtual bool Initialize() { return true; }; + // properties + virtual bool IsWms() { return false; } + virtual CStringW get_Copyright() { return _copyright; } + virtual bool Initialize() { return true; } - int get_MinZoom() { return _minZoom; } - int get_MaxZoom() { return _maxZoom; } - void* get_Manager() { return _manager; } - void put_Manager(void* value) { _manager = value; } - vector* get_SubProviders() { return &_subProviders; } - CString get_LicenseUrl() { return _licenseUrl; } - BaseProjection* get_Projection() { return _projection; } - CString get_UrlFormat() { return _urlFormat; } + int get_MinZoom() { return _minZoom; } + int get_MaxZoom() { return _maxZoom; } + void* get_Manager() { return _manager; } + void put_Manager(void* value) { _manager = value; } + vector* get_SubProviders() { return &_subProviders; } + CString get_LicenseUrl() { return _licenseUrl; } + BaseProjection* get_Projection() { return _projection; } + CString get_UrlFormat() { return _urlFormat; } public: - // methods - bool SetAuthorization(CString username, CString password, CString domain); - void ClearAuthorization(); + // methods + static bool SetAuthorization(const CString& username, const CString& password, const CString& domain); + static void ClearAuthorization(); - void AddDynamicOverlay(BaseProvider* p); - void ClearSubProviders(); - TileCore* GetTileImage(CPoint &pos, int zoom); + void AddDynamicOverlay(BaseProvider* p); + void ClearSubProviders(); + TileCore* GetTileImage(CPoint& pos, int zoom); }; diff --git a/src/Tiles/Providers/BingMapProvider.cpp b/src/Tiles/Providers/BingMapProvider.cpp index eb25ec8f..d57814ca 100644 --- a/src/Tiles/Providers/BingMapProvider.cpp +++ b/src/Tiles/Providers/BingMapProvider.cpp @@ -1,4 +1,25 @@ -#include "stdafx.h" +/************************************************************************************** + * Project: MapWindow Open Source (MapWinGis ActiveX control) + ************************************************************************************** + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at http://www.mozilla.org/mpl/ + * See the License for the specific language governing rights and limitations + * under the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ************************************************************************************** + * Contributor(s): + * (Open source contributors should list themselves and their modifications here). */ +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper + +#include "StdAfx.h" #include "BingMapProvider.h" #include "SecureHttpClient.h" @@ -8,38 +29,39 @@ // Runs imagery metadata request: http://msdn.microsoft.com/en-us/library/ff701716.aspx bool BingBaseProvider::Initialize() { - if (_urlFormat.GetLength() > 0) return true; - - if (m_globalSettings.bingApiKey.GetLength() == 0) - { - CallbackHelper::ErrorMsg("No Bing Maps API key was provided. See GlobalSettings.BingApiKey."); - return false; - } - - _initAttemptCount++; - if (_initAttemptCount > 3) - { - CallbackHelper::ErrorMsg("Number of initialization attempts for Bing Maps provider was exceeded (3)."); - return false; - } - - SecureHttpClient client; - client.SetProxyAndAuthentication(_proxyUsername, _proxyPassword, _proxyDomain); - - CAtlNavigateData navData; - - CString url; - url.Format("http://dev.virtualearth.net/REST/v1/Imagery/Metadata/%s?key=%s&o=xml", _imagerySet, m_globalSettings.bingApiKey); - - bool result = false; - - if (!client.Navigate(url, &navData) || client.GetStatus() != 200) - { - CallbackHelper::ErrorMsg(Debug::Format("Failed to perform imagery metadata request. URL: ", url)); - return false; - } - - return ParseUrlFormat(reinterpret_cast(&client)); + if (_urlFormat.GetLength() > 0) return true; + + if (m_globalSettings.bingApiKey.GetLength() == 0) + { + CallbackHelper::ErrorMsg("No Bing Maps API key was provided. See GlobalSettings.BingApiKey."); + return false; + } + + _initAttemptCount++; + if (_initAttemptCount > 3) + { + CallbackHelper::ErrorMsg("Number of initialization attempts for Bing Maps provider was exceeded (3)."); + return false; + } + + // jf: SecureHttpClient is now based on libCurl instead of AtlHttp. + // this affected the Navigate call, removing the navigation data. + SecureHttpClient client; + client.SetProxyAndAuthentication(_proxyUsername, _proxyPassword, _proxyDomain); + + CString url; + url.Format("http://dev.virtualearth.net/REST/v1/Imagery/Metadata/%s?key=%s&o=xml", _imagerySet, + m_globalSettings.bingApiKey); + + // Not used: bool result = false; + + if (!client.Navigate(url) || client.GetStatus() != 200) + { + CallbackHelper::ErrorMsg(Debug::Format("Failed to perform imagery metadata request. URL: ", url)); + return false; + } + + return ParseUrlFormat(reinterpret_cast(&client)); } // ****************************************************** @@ -47,30 +69,30 @@ bool BingBaseProvider::Initialize() // ****************************************************** bool BingBaseProvider::ParseUrlFormat(void* secureHttpClient) { - SecureHttpClient* client = reinterpret_cast(secureHttpClient); - - int bodyLen = client->GetBodyLength(); - if (bodyLen > 0) - { - char* body = new char[bodyLen + 1]; - memcpy(body, client->GetBody(), bodyLen); - CString s = body; - delete[] body; - s = s.MakeLower(); - - int pos = s.Find(""); - int pos2 = s.Find(""); - s = s.Mid(pos + 10, pos2 - pos - 10); - s.Replace("&", "&"); - - if (s.GetLength() == 0) - return false; - - _urlFormat = s; - return true; - } - - return false; + auto* client = reinterpret_cast(secureHttpClient); + + const int bodyLen = client->GetBodyLength(); + if (bodyLen > 0) + { + auto* body = new char[bodyLen + 1]; + memcpy(body, client->GetBody(), bodyLen); + CString s = body; + delete[] body; + s = s.MakeLower(); + + const int pos = s.Find(""); + const int pos2 = s.Find(""); + s = s.Mid(pos + 10, pos2 - pos - 10); + s.Replace("&", "&"); + + if (s.GetLength() == 0) + return false; + + _urlFormat = s; + return true; + } + + return false; } // ****************************************************** @@ -80,42 +102,43 @@ bool BingBaseProvider::ParseUrlFormat(void* secureHttpClient) // LevelOfDetail: Level of detail, from 1 (lowest detail) to 23 (highest detail). CString BingBaseProvider::TileXYToQuadKey(int tileX, int tileY, int levelOfDetail) { - CString s; - for (int i = levelOfDetail; i > 0; i--) - { - char digit = '0'; - int mask = 1 << (i - 1); - if ((tileX & mask) != 0) - { - digit++; - } - if ((tileY & mask) != 0) - { - digit++; - digit++; - } - - s.AppendChar(digit); - } - return s; + CString s; + for (int i = levelOfDetail; i > 0; i--) + { + char digit = '0'; + // ReSharper disable once CppRedundantParentheses + const int mask = 1 << (i - 1); + if ((tileX & mask) != 0) + { + digit++; + } + if ((tileY & mask) != 0) + { + digit++; + digit++; + } + + s.AppendChar(digit); + } + return s; } // ****************************************************** // MakeTileImageUrl() // ****************************************************** -CString BingBaseProvider::MakeTileImageUrl(CPoint &pos, int zoom) +CString BingBaseProvider::MakeTileImageUrl(CPoint& pos, int zoom) { - // http://ecn.{subdomain}.tiles.virtualearth.net/tiles/r{quadkey}.jpeg?g=3179&mkt={culture}&shading=hill - CString key = TileXYToQuadKey(pos.x, pos.y, zoom); - CString subDomain; - subDomain.Format("t%d", GetServerNum(pos, 4)); + // http://ecn.{subdomain}.tiles.virtualearth.net/tiles/r{quadkey}.jpeg?g=3179&mkt={culture}&shading=hill + const CString key = TileXYToQuadKey(pos.x, pos.y, zoom); + CString subDomain; + subDomain.Format("t%d", GetServerNum(pos, 4)); - CString temp = _urlFormat; - temp.Replace("{quadkey}", key); - temp.Replace("{culture}", LanguageStr); - temp.Replace("{subdomain}", subDomain); + CString temp = _urlFormat; + temp.Replace("{quadkey}", key); + temp.Replace("{culture}", LanguageStr); + temp.Replace("{subdomain}", subDomain); - return temp; + return temp; } // ****************************************************** @@ -123,10 +146,10 @@ CString BingBaseProvider::MakeTileImageUrl(CPoint &pos, int zoom) // ****************************************************** CStringW BingBaseProvider::GetCopyright() { - if (_urlFormat.GetLength() == 0) { - return "INVALID BING MAPS API KEY"; - } - else { - return _copyright; - } -} \ No newline at end of file + if (_urlFormat.GetLength() == 0) + { + return "INVALID BING MAPS API KEY"; + } + + return _copyright; +} diff --git a/src/Tiles/Providers/BingMapProvider.h b/src/Tiles/Providers/BingMapProvider.h index d473b5d9..7138a3d5 100644 --- a/src/Tiles/Providers/BingMapProvider.h +++ b/src/Tiles/Providers/BingMapProvider.h @@ -39,7 +39,7 @@ class BingBaseProvider : public BaseProvider } bool Initialize(); - CString TileXYToQuadKey(int tileX, int tileY, int levelOfDetail); + static CString TileXYToQuadKey(int tileX, int tileY, int levelOfDetail); bool ParseUrlFormat(void* secureHttpClient); CString MakeTileImageUrl(CPoint &pos, int zoom); virtual CStringW GetCopyright(); diff --git a/src/Tiles/Providers/CustomTileProvider.h b/src/Tiles/Providers/CustomTileProvider.h index 1f381a2f..ecf1a2ee 100644 --- a/src/Tiles/Providers/CustomTileProvider.h +++ b/src/Tiles/Providers/CustomTileProvider.h @@ -30,12 +30,13 @@ class CustomTileProvider : public BaseProvider { public: - CustomTileProvider(int id, CString name, CString urlPattern, tkTileProjection projection, int minZoom, int maxZoom) throw(...) + CustomTileProvider(int id, CString name, CString urlPattern, tkTileProjection projection, int minZoom, int maxZoom, CString Copyright) throw(...) { this->Id = (tkTileProvider)id; this->Name = name; this->_minZoom = minZoom; this->_maxZoom = maxZoom; + this->_copyright = Copyright; _projectionId = projection; _subProviders.push_back(this); diff --git a/src/Tiles/Providers/GoogleMapProvider.h b/src/Tiles/Providers/GoogleMapProvider.h index 3a9e5cce..23966654 100644 --- a/src/Tiles/Providers/GoogleMapProvider.h +++ b/src/Tiles/Providers/GoogleMapProvider.h @@ -17,6 +17,7 @@ ************************************************************************************** * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper #pragma once #include "BaseProvider.h" @@ -27,47 +28,48 @@ class GoogleBaseProvider : public BaseProvider { protected: - CString server; - CString UrlFormatServer; - CString UrlFormatRequest; - CString SecureWord; - CString Sec1; + CString server; + CString UrlFormatServer; + CString UrlFormatRequest; + CString SecureWord; + CString Sec1; public: - GoogleBaseProvider() - { - SecureWord = "Galileo"; - Sec1 = "&s="; - server = "google.com"; - _refererUrl.Format("http://maps.%s/", server); - _licenseUrl = "https://mapwingis.codeplex.com/wikipage?title=googletiles"; - int year = Utility::GetCurrentYear(); - _copyright.Format(L"©%d Google (FOR PRIVATE USE ONLY)", year); - this->_projection = new MercatorProjection(); - this->_maxZoom = 20; - } + GoogleBaseProvider() + { + SecureWord = "Galileo"; + Sec1 = "&s="; + server = "google.com"; + _refererUrl.Format("http://maps.%s/", server); + _licenseUrl = "https://mapwingis.codeplex.com/wikipage?title=googletiles"; + const int year = Utility::GetCurrentYear(); + _copyright.Format(L"©%d Google (FOR PRIVATE USE ONLY)", year); + this->_projection = new MercatorProjection(); + this->_maxZoom = 20; + } - CString MakeTileImageUrl(CPoint &pos, int zoom) - { - CString sec1 = ""; - CString sec2 = ""; - GetSecureWords(pos, sec1, sec2); + CString MakeTileImageUrl(CPoint& pos, int zoom) + { + CString sec1 = ""; + CString sec2 = ""; + GetSecureWords(pos, sec1, sec2); - CString s; - s.Format(_urlFormat, UrlFormatServer, GetServerNum(pos, 4), server, UrlFormatRequest, Version, LanguageStr, pos.x, sec1, pos.y, zoom, sec2); - return s; - } + CString s; + s.Format(_urlFormat, UrlFormatServer, GetServerNum(pos, 4), server, UrlFormatRequest, Version, LanguageStr, + pos.x, sec1, pos.y, zoom, sec2); + return s; + } - void GetSecureWords(CPoint &pos, CString &sec1, CString &sec2) - { - sec1 = ""; - sec2 = ""; - int seclen = ((pos.x * 3) + pos.y) % 8; - sec2 = SecureWord.Left(seclen); - if (pos.y >= 10000 && pos.y < 100000) - { - sec1 = Sec1; - } - } + void GetSecureWords(CPoint& pos, CString& sec1, CString& sec2) const + { + sec1 = ""; + sec2 = ""; + const int seclen = (pos.x * 3 + pos.y) % 8; + sec2 = SecureWord.Left(seclen); + if (pos.y >= 10000 && pos.y < 100000) + { + sec1 = Sec1; + } + } }; // ******************************************************* @@ -76,16 +78,16 @@ class GoogleBaseProvider : public BaseProvider class GoogleMapProvider : public GoogleBaseProvider { public: - GoogleMapProvider() - { - UrlFormatServer = "mt"; - UrlFormatRequest = "vt"; - Version = "m@285000000"; - Id = tkTileProvider::GoogleMaps; - Name = "GoogleMaps"; - _urlFormat = "http://%s%d.%s/%s/lyrs=%s&hl=%s&x=%d%s&y=%d&z=%d&s=%s"; - _subProviders.push_back(this); - } + GoogleMapProvider() + { + UrlFormatServer = "mt"; + UrlFormatRequest = "vt"; + Version = "m@285000000"; + Id = tkTileProvider::GoogleMaps; + Name = "GoogleMaps"; + _urlFormat = "http://%s%d.%s/%s/lyrs=%s&hl=%s&x=%d%s&y=%d&z=%d&s=%s"; + _subProviders.push_back(this); + } }; // ******************************************************* @@ -94,16 +96,16 @@ class GoogleMapProvider : public GoogleBaseProvider class GoogleSatelliteProvider : public GoogleBaseProvider { public: - GoogleSatelliteProvider() - { - UrlFormatServer = "khms"; - UrlFormatRequest = "kh"; - Version = "164"; - Id = tkTileProvider::GoogleSatellite; - Name = "GoogleSatellite"; - _urlFormat = "http://%s%d.%s/%s/v=%s&hl=%s&x=%d%s&y=%d&z=%d&s=%s"; - _subProviders.push_back(this); - } + GoogleSatelliteProvider() + { + UrlFormatServer = "khms"; + UrlFormatRequest = "kh"; + Version = "164"; + Id = tkTileProvider::GoogleSatellite; + Name = "GoogleSatellite"; + _urlFormat = "http://%s%d.%s/%s/v=%s&hl=%s&x=%d%s&y=%d&z=%d&s=%s"; + _subProviders.push_back(this); + } }; // ******************************************************* @@ -112,17 +114,17 @@ class GoogleSatelliteProvider : public GoogleBaseProvider class GoogleHybridProvider : public GoogleBaseProvider { public: - GoogleHybridProvider(CTileProviders* list) - { - UrlFormatServer = "mt"; - UrlFormatRequest = "vt"; - Version = "h@285000000"; - Id = tkTileProvider::GoogleHybrid; - Name = "GoogleHybrid"; - _urlFormat = "http://%s%d.%s/%s/lyrs=%s&hl=%s&x=%d%s&y=%d&z=%d&s=%s"; - _subProviders.push_back(list->get_Provider(tkTileProvider::GoogleSatellite)); - _subProviders.push_back(this); - } + GoogleHybridProvider(CTileProviders* list) + { + UrlFormatServer = "mt"; + UrlFormatRequest = "vt"; + Version = "h@285000000"; + Id = tkTileProvider::GoogleHybrid; + Name = "GoogleHybrid"; + _urlFormat = "http://%s%d.%s/%s/lyrs=%s&hl=%s&x=%d%s&y=%d&z=%d&s=%s"; + _subProviders.push_back(list->get_Provider(tkTileProvider::GoogleSatellite)); + _subProviders.push_back(this); + } }; // ******************************************************* @@ -131,14 +133,14 @@ class GoogleHybridProvider : public GoogleBaseProvider class GoogleTerrainProvider : public GoogleBaseProvider { public: - GoogleTerrainProvider() - { - UrlFormatServer = "mt"; - UrlFormatRequest = "vt"; - Version = "t@164,r@285000000"; - Id = tkTileProvider::GoogleTerrain; - Name = "GoogleTerrain"; - _urlFormat = "http://%s%d.%s/%s/v=%s&hl=%s&x=%d%s&y=%d&z=%d&s=%s"; - _subProviders.push_back(this); - } + GoogleTerrainProvider() + { + UrlFormatServer = "mt"; + UrlFormatRequest = "vt"; + Version = "t@164,r@285000000"; + Id = tkTileProvider::GoogleTerrain; + Name = "GoogleTerrain"; + _urlFormat = "http://%s%d.%s/%s/v=%s&hl=%s&x=%d%s&y=%d&z=%d&s=%s"; + _subProviders.push_back(this); + } }; diff --git a/src/Tiles/Providers/OpenStreetMapProvider.h b/src/Tiles/Providers/OpenStreetMapProvider.h index 07869af4..3a9d3217 100644 --- a/src/Tiles/Providers/OpenStreetMapProvider.h +++ b/src/Tiles/Providers/OpenStreetMapProvider.h @@ -41,7 +41,7 @@ class OpenStreetMapProvider : public BaseProvider _refererUrl = "https://www.openstreetmap.org/"; _urlFormat = "https://%c.tile.openstreetmap.org/%d/%d/%d.png"; _maxZoom = 19; - _licenseUrl = "https://wiki.openstreetmap.org/wiki/Tile_usage_policy"; + _licenseUrl = "https://operations.osmfoundation.org/policies/tiles/"; } CString MakeTileImageUrl(CPoint &pos, int zoom) @@ -63,7 +63,7 @@ class OpenCycleMapProvider : public OpenStreetMapProvider Name = "OpenCycleMap"; _refererUrl = "http://www.opencyclemap.org/"; _licenseUrl = "http://www.opencyclemap.org/docs/"; - _urlFormat = "http://%c.tile.opencyclemap.org/cycle/%d/%d/%d.png"; + _urlFormat = "https://%c.tile.opencyclemap.org/cycle/%d/%d/%d.png"; } }; @@ -75,7 +75,7 @@ class OpenTransportMapProvider : public OpenStreetMapProvider Id = tkTileProvider::OpenTransportMap; Name = "OpenTransportMap"; _refererUrl = "http://www.opencyclemap.org/"; - _urlFormat = "http://%c.tile2.opencyclemap.org/transport/%d/%d/%d.png"; + _urlFormat = "https://%c.tile2.opencyclemap.org/transport/%d/%d/%d.png"; } }; @@ -88,7 +88,7 @@ class OpenHumanitarianMapProvider : public OpenStreetMapProvider Id = tkTileProvider::OpenHumanitarianMap; Name = "OpenHumanitarianMap"; _refererUrl = "https://www.openstreetmap.org/"; - _urlFormat = "http://%c.tile.openstreetmap.fr/hot/%d/%d/%d.png"; + _urlFormat = "https://%c.tile.openstreetmap.fr/hot/%d/%d/%d.png"; } }; diff --git a/src/Tiles/TileCore.cpp b/src/Tiles/TileCore.cpp index a2578a5e..30a83ab2 100644 --- a/src/Tiles/TileCore.cpp +++ b/src/Tiles/TileCore.cpp @@ -17,8 +17,9 @@ ************************************************************************************** * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper -#include "stdafx.h" +#include "StdAfx.h" #include "TileCore.h" Debug::Logger tilesLogger; @@ -27,11 +28,11 @@ Debug::Logger tilesLogger; // TileCore // ****************************************************** // gets relative path of tile disk storage -CStringW TileCore::get_Path(CStringW root, CStringW ext) +CStringW TileCore::GetPath(const CStringW root, const CStringW ext) const { - CStringW path; - path.Format(L"%s%d\\%d\\%d%s", root, this->_scale, this->_tileX, this->_tileY, ext); - return path; + CStringW path; + path.Format(L"%s%d\\%d\\%d%s", root, this->_scale, this->_tileX, this->_tileY, ext); + return path; } // ****************************************************** @@ -39,11 +40,11 @@ CStringW TileCore::get_Path(CStringW root, CStringW ext) // ****************************************************** void TileCore::ClearOverlays() { - for (size_t i = 0; i < Overlays.size(); i++) - { - delete Overlays[i]; - } - Overlays.clear(); + for (size_t i = 0; i < Overlays.size(); i++) + { + delete Overlays[i]; + } + Overlays.clear(); } // ****************************************************** @@ -52,12 +53,12 @@ void TileCore::ClearOverlays() // returns combined size of bitmaps for all overlays int TileCore::get_ByteSize() { - int size = 0; - for (size_t i = 0; i < Overlays.size(); i++) - { - size += Overlays[i]->get_Size(); - } - return size; + int size = 0; + for (size_t i = 0; i < Overlays.size(); i++) + { + size += Overlays[i]->get_Size(); + } + return size; } // ****************************************************** @@ -65,8 +66,8 @@ int TileCore::get_ByteSize() // ****************************************************** long TileCore::AddRef() { - InterlockedIncrement(&_refCount); - return _refCount; + InterlockedIncrement(&_refCount); + return _refCount; } // ****************************************************** @@ -75,36 +76,34 @@ long TileCore::AddRef() // Attention: object is deleted automatically when reference count is equal to 0 long TileCore::Release() { - InterlockedDecrement(&_refCount); + InterlockedDecrement(&_refCount); - if (_refCount < 0) - CallbackHelper::AssertionFailed("Invalid reference count for a tile."); + if (_refCount < 0) + CallbackHelper::AssertionFailed("Invalid reference count for a tile."); - if (this->_refCount == 0) - { - delete this; - return 0; - } - else { - return _refCount; - } + if (this->_refCount == 0) + { + delete this; + return 0; + } + return _refCount; } // ****************************************************** // operator== // ****************************************************** -bool TileCore::operator==(const TileCore &t2) +bool TileCore::operator==(const TileCore& t2) const { - return (this->_tileX == t2._tileX && - this->_tileY == t2._tileY && - this->_scale == t2._scale && - this->_providerId == t2._providerId); + return this->_tileX == t2._tileX && + this->_tileY == t2._tileY && + this->_scale == t2._scale && + this->_providerId == t2._providerId; } // ****************************************************** // getBitmap // ****************************************************** CMemoryBitmap* TileCore::get_Bitmap(int index) -{ - return (index >= 0 && index < (int)Overlays.size()) ? Overlays[index] : NULL; -} \ No newline at end of file +{ + return index >= 0 && index < (int)Overlays.size() ? Overlays[index] : nullptr; +} diff --git a/src/Tiles/TileCore.h b/src/Tiles/TileCore.h index e400ea24..5d3417d0 100644 --- a/src/Tiles/TileCore.h +++ b/src/Tiles/TileCore.h @@ -17,6 +17,8 @@ ************************************************************************************** * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper + #pragma once #include "InMemoryBitmap.h" #include "GeoPoint.h" @@ -31,16 +33,16 @@ extern Debug::Logger tilesLogger; // to the caller via callback struct TileRequestInfo { - TileRequestInfo(CString key, bool isSnapshot) - : count(0), totalCount(10000), key(key), isSnapshot(isSnapshot), generation(-1) - { - } + TileRequestInfo(CString key, bool isSnapshot) + : key(key), isSnapshot(isSnapshot), totalCount(10000), count(0), generation(-1) + { + } - CString key; - bool isSnapshot; - unsigned int totalCount; - unsigned int count; - int generation; + CString key; + bool isSnapshot; + unsigned int totalCount; + unsigned int count; + int generation; }; // ****************************************************** @@ -50,21 +52,22 @@ struct TileRequestInfo class TilePoint : public CPoint { public: - TilePoint(int x, int y) - : CPoint(x, y), dist(0.0) - { - } + TilePoint(int x, int y) + : CPoint(x, y), dist(0.0) + { + } public: - double dist; + double dist; public: - static void ReleaseMemory(vector& points) - { - for (size_t i = 0; i < points.size(); i++) { - delete points[i]; - } - } + static void ReleaseMemory(vector& points) + { + for (size_t i = 0; i < points.size(); i++) + { + delete points[i]; + } + } }; // ****************************************************** @@ -75,85 +78,87 @@ class TilePoint : public CPoint class TileCore { public: - TileCore(int providerId, int zoom, CPoint& pnt, BaseProjection* projection) - : _scale(zoom), _tileX(pnt.x), _tileY(pnt.y), _providerId(providerId), _projection(projection) - { - _refCount = 0; - _projectionChangeCount = -1; - _hasErrors = false; - _drawn = false; - _toDelete = false; - _inBuffer = false; - _geogBounds = projection->CalculateGeogBounds(pnt, zoom); - } + TileCore(int providerId, int zoom, CPoint& pnt, BaseProjection* projection) + : _scale(zoom), _tileX(pnt.x), _tileY(pnt.y), _projection(projection), _providerId(providerId) + { + _refCount = 0; + _projectionChangeCount = -1; + _hasErrors = false; + _drawn = false; + _toDelete = false; + _inBuffer = false; + _geogBounds = projection->CalculateGeogBounds(pnt, zoom); + } - virtual ~TileCore() - { - ClearOverlays(); - } + virtual ~TileCore() + { + ClearOverlays(); + } private: - // a tile can be deleted while: - // 1) clearing drawing buffer: Tiles::Clear() - // 2) clearing RAM cache: RAMCache::Clear() - // 3) after disk caching: SQLiteCache::DoCaching() - long _refCount; // number of references (it can be used in drawing, RAM cache or be scheduled for disk caching) - bool _hasErrors; // there were errors during download, one of several layers weren't loaded - long _scale; - long _tileX; - long _tileY; - int _projectionChangeCount; // position of tile was recalculated using the current projection; number of projection changes - BaseProjection* _projection; - RectLatLng _projectedBounds; - RectLatLng _geogBounds; - bool _drawn; // it's drawn on screen - bool _toDelete; // for inner working of RAM cache - bool _inBuffer; // it's currently displayed or scheduled to be displayed; it must not be destroyed while cleaning the cache - int _providerId; + // a tile can be deleted while: + // 1) clearing drawing buffer: Tiles::Clear() + // 2) clearing RAM cache: RAMCache::Clear() + // 3) after disk caching: SQLiteCache::DoCaching() + long _refCount; // number of references (it can be used in drawing, RAM cache or be scheduled for disk caching) + bool _hasErrors; // there were errors during download, one of several layers weren't loaded + long _scale; + long _tileX; + long _tileY; + int _projectionChangeCount; + // position of tile was recalculated using the current projection; number of projection changes + BaseProjection* _projection; + RectLatLng _projectedBounds; + RectLatLng _geogBounds; + bool _drawn; // it's drawn on screen + bool _toDelete; // for inner working of RAM cache + bool _inBuffer; + // it's currently displayed or scheduled to be displayed; it must not be destroyed while cleaning the cache + int _providerId; public: - // a tile may be comprised of several semi-transparent bitmaps (e.g. satellite image and labels above it) - vector Overlays; + // a tile may be comprised of several semi-transparent bitmaps (e.g. satellite image and labels above it) + vector Overlays; public: - // properties - int get_ProjectionChangeCount() { return _projectionChangeCount; } - RectLatLng* get_ProjectedBounds() { return &_projectedBounds; } - RectLatLng* get_GeographicBounds() { return &_geogBounds; } - BaseProjection* get_Projection() { return _projection; } - void set_Projection(BaseProjection* projection) { _projection = projection; } - CStringW get_Path(CStringW root, CStringW ext); - CMemoryBitmap* get_Bitmap(int overlayIndex); - int get_ByteSize(); - bool IsEmpty() { return Overlays.size() == 0; } - bool hasErrors() { return _hasErrors; } - void hasErrors(bool value) { _hasErrors = true; } - int tileX() { return _tileX; } - int tileY() { return _tileY; } - int zoom() { return _scale; } - int get_ProviderId() { return _providerId; } - void set_ProviderId(int value) { _providerId = value; } - bool inBuffer() { return _inBuffer; } - void inBuffer(bool value) { _inBuffer = value; } - bool isDrawn() { return _drawn; } - void isDrawn(bool value) { _drawn = value; } - bool toDelete() { return _toDelete; } - void toDelete(bool value) { _toDelete = value; } + // properties + int get_ProjectionChangeCount() { return _projectionChangeCount; } + RectLatLng* get_ProjectedBounds() { return &_projectedBounds; } + RectLatLng* get_GeographicBounds() { return &_geogBounds; } + BaseProjection* get_Projection() { return _projection; } + void set_Projection(BaseProjection* projection) { _projection = projection; } + CStringW GetPath(CStringW root, CStringW ext) const; + CMemoryBitmap* get_Bitmap(int overlayIndex); + int get_ByteSize(); + bool IsEmpty() { return Overlays.size() == 0; } + bool hasErrors() { return _hasErrors; } + void hasErrors(bool value) { _hasErrors = true; } + int tileX() { return _tileX; } + int tileY() { return _tileY; } + int zoom() { return _scale; } + int get_ProviderId() { return _providerId; } + void set_ProviderId(int value) { _providerId = value; } + bool inBuffer() { return _inBuffer; } + void inBuffer(bool value) { _inBuffer = value; } + bool isDrawn() { return _drawn; } + void isDrawn(bool value) { _drawn = value; } + bool toDelete() { return _toDelete; } + void toDelete(bool value) { _toDelete = value; } public: - //methods - void AddOverlay(CMemoryBitmap* bitmap) { Overlays.push_back(bitmap); } - void ClearOverlays(); - long AddRef(); - long Release(); + //methods + void AddOverlay(CMemoryBitmap* bitmap) { Overlays.push_back(bitmap); } + void ClearOverlays(); + long AddRef(); + long Release(); - void UpdateProjectedBounds(RectLatLng bounds, int projectionChangeCount) { - _projectedBounds = bounds; - _projectionChangeCount = projectionChangeCount; - } + void UpdateProjectedBounds(RectLatLng bounds, int projectionChangeCount) + { + _projectedBounds = bounds; + _projectionChangeCount = projectionChangeCount; + } public: - // operators - bool TileCore::operator==(const TileCore &t2); + // operators + bool TileCore::operator==(const TileCore& t2) const; }; - diff --git a/src/Tiles/TileManager.cpp b/src/Tiles/TileManager.cpp index f120c50a..d8a4201e 100644 --- a/src/Tiles/TileManager.cpp +++ b/src/Tiles/TileManager.cpp @@ -17,22 +17,25 @@ ************************************************************************************** * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ -#include "stdafx.h" +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper + +#include "StdAfx.h" #include "TileManager.h" +#include "TileCacheManager.h" // ************************************************************ // set_MapCallback() // ************************************************************ void TileManager::set_MapCallback(IMapViewCallback* map) { - if (_map && map) - { - // don't allow to use the same manager for 2 map controls, - // sicne we have only one buffer for tiles; - return; - } - - _map = map; + if (_map && map) + { + // don't allow to use the same manager for 2 map controls, + // sicne we have only one buffer for tiles; + return; + } + + _map = map; } // ************************************************************ @@ -40,93 +43,95 @@ void TileManager::set_MapCallback(IMapViewCallback* map) // ************************************************************ void TileManager::InitCaches() { - _ramCache.doCaching = true; - _ramCache.useCache = true; - _ramCache.cache = TileCacheManager::get_Cache(tctRamCache); - _caches.push_back(&_ramCache); - - _diskCache.doCaching = m_globalSettings.wmsDiskCaching; - _diskCache.useCache = true; - _diskCache.cache = TileCacheManager::get_Cache(tctSqliteCache); - _caches.push_back(&_diskCache); + _ramCache.doCaching = true; + _ramCache.useCache = true; + _ramCache.cache = TileCacheManager::get_Cache(tctRamCache); + _caches.push_back(&_ramCache); + + _diskCache.doCaching = m_globalSettings.wmsDiskCaching; + _diskCache.useCache = true; + _diskCache.cache = TileCacheManager::get_Cache(tctSqliteCache); + _caches.push_back(&_diskCache); } // ********************************************************* // LoadTiles() // ********************************************************* -void TileManager::LoadTiles(BaseProvider* provider, bool isSnapshot, CString key) +void TileManager::LoadTiles(BaseProvider* provider, bool isSnapshot, const CString& key) { - if (!provider) return; + if (!provider) return; - CRect indices; - int zoom; + CRect indices; + int zoom; - if (!GetTileIndices(provider, indices, zoom, isSnapshot)) { - return; - } + if (!GetTileIndices(provider, indices, zoom, isSnapshot)) + { + return; + } - // it's not a duplicated request, something has changed - ClearBuffer(); + // it's not a duplicated request, something has changed + ClearBuffer(); - tilesLogger.WriteLine("\nLOAD TILES: xMin=%d; xMax=%d; yMin=%d; yMax=%d; zoom =%d", indices.left, indices.right, indices.bottom, indices.top, zoom); + tilesLogger.WriteLine("\nLOAD TILES: xMin=%d; xMax=%d; yMin=%d; yMax=%d; zoom =%d", indices.left, indices.right, + indices.bottom, indices.top, zoom); - provider->put_Manager(this); + provider->put_Manager(this); - InitializeDiskCache(); + InitializeDiskCache(); - // it's pure snapshot call, so we shall only grab only those tiles - // the are already in the cache - bool cacheOnly = isSnapshot && key.GetLength() == 0; + // it's pure snapshot call, so we shall only grab only those tiles + // the are already in the cache + const bool cacheOnly = isSnapshot && key.GetLength() == 0; - // check which ones we already have, and which ones are to be loaded - std::vector activeTasks; + // check which ones we already have, and which ones are to be loaded + std::vector activeTasks; - TileRequestInfo* requestInfo = NULL; + TileRequestInfo* requestInfo = nullptr; - if (!cacheOnly) - { - // all incoming tasks will be discarded - requestInfo = _loader.CreateNextRequest(key, isSnapshot); + if (!cacheOnly) + { + // all incoming tasks will be discarded + requestInfo = _loader.CreateNextRequest(key, isSnapshot); - GetActiveTasks(activeTasks, provider->Id, zoom, requestInfo->generation, indices); - } + GetActiveTasks(activeTasks, provider->Id, zoom, requestInfo->generation, indices); + } - // loads tiles available in the cache to the buffer - // builds list of tiles to be loaded from server - std::vector points; - BuildLoadingList(provider, indices, zoom, activeTasks, points); + // loads tiles available in the cache to the buffer + // builds list of tiles to be loaded from server + std::vector points; + BuildLoadingList(provider, indices, zoom, activeTasks, points); - // it will be considered completed when this amount of tiles is loaded - if (!cacheOnly) - { - requestInfo->totalCount = activeTasks.size() + points.size(); - } + // it will be considered completed when this amount of tiles is loaded + if (!cacheOnly) + { + requestInfo->totalCount = activeTasks.size() + points.size(); + } - // delete unused tiles from the screen buffer - DeleteMarkedTilesFromBuffer(); + // delete unused tiles from the screen buffer + DeleteMarkedTilesFromBuffer(); - UnlockDiskCache(); + UnlockDiskCache(); - _loader.RunCaching(); + _loader.RunCaching(); - _provider = provider; + _provider = provider; - if (points.size() > 0) - { - tilesLogger.WriteLine("Queued to load from server: %d", points.size()); + if (points.size() > 0) + { + tilesLogger.WriteLine("Queued to load from server: %d", points.size()); - // zoom can change in the process, so we use the calculated version and not the one current for provider - _loader.Load(points, provider, zoom, requestInfo); - } - else - { - // tilesLogger.WriteLine("Tiles loaded event; Were loaded from server (y/n): %d", !nothingToLoad); - FireTilesLoaded(isSnapshot, key, true); - } + // zoom can change in the process, so we use the calculated version and not the one current for provider + _loader.Load(points, provider, zoom, requestInfo); + } + else + { + // tilesLogger.WriteLine("Tiles loaded event; Were loaded from server (y/n): %d", !nothingToLoad); + FireTilesLoaded(isSnapshot, key, true); + } - TilePoint::ReleaseMemory(activeTasks); - TilePoint::ReleaseMemory(points); + TilePoint::ReleaseMemory(activeTasks); + TilePoint::ReleaseMemory(points); } // ********************************************************* @@ -134,113 +139,118 @@ void TileManager::LoadTiles(BaseProvider* provider, bool isSnapshot, CString key // ********************************************************* bool TileManager::GetTileIndices(BaseProvider* provider, CRect& indices, int& zoom, bool isSnapshot) { - Extent* mapExtents = _map->_GetExtents(); - - bool boundsChanged = provider->get_Projection()->get_ClipBoundsChanged(); - - if (!isSnapshot && _lastMapExtents == *mapExtents && _lastProvider == provider->Id && !boundsChanged) - { - // no need to clear buffer - tilesLogger.WriteLine("Duplicate request is dropped."); - return false; - } - - if (!_map->_GetTilesForMap(provider, _scalingRatio, indices, zoom)) - { - Clear(); - return false; - } - - if (!IsNewRequest(*mapExtents, indices, provider->Id, zoom)) - { - return false; - } - - // to apply API key for example - if (!provider->Initialize()) - { - Clear(); - return false; - } - - return true; + Extent* mapExtents = _map->_GetExtents(); + + const bool boundsChanged = provider->get_Projection()->get_ClipBoundsChanged(); + + if (!isSnapshot && _lastMapExtents == *mapExtents && _lastProvider == provider->Id && !boundsChanged) + { + // no need to clear buffer + tilesLogger.WriteLine("Duplicate request is dropped."); + return false; + } + + if (!_map->_GetTilesForMap(provider, _scalingRatio, indices, zoom)) + { + Clear(); + return false; + } + + if (!IsNewRequest(*mapExtents, indices, provider->Id, zoom)) + { + return false; + } + + // to apply API key for example + if (!provider->Initialize()) + { + Clear(); + return false; + } + + return true; } // ********************************************************* // BuildLoadingList() // ********************************************************* -void TileManager::BuildLoadingList(BaseProvider* provider, CRect indices, int zoom, vector& activeTasks, vector& points) +void TileManager::BuildLoadingList(BaseProvider* provider, CRect indices, int zoom, vector& activeTasks, + vector& points) { - CPoint center = indices.CenterPoint(); - - for (int x = indices.left; x <= indices.right; x++) - { - for (int y = indices.bottom; y <= indices.top; y++) - { - // was it already reassigned? - bool found = false; - for (size_t i = 0; i < activeTasks.size(); i++) - { - if (activeTasks[i]->x == x && activeTasks[i]->y == y) - { - found = true; - break; - } - } - - if (found) { - continue; - } - - // check maybe the tile is already in the buffer - _tilesBufferLock.Lock(); - - for (size_t i = 0; i < _tiles.size(); i++) - { - TileCore* tile = _tiles[i]; - if (tile->tileX() == x && tile->tileY() == y && tile->zoom() == zoom && tile->get_ProviderId() == provider->Id) - { - tile->toDelete(false); - tile->inBuffer(true); - found = true; - break; - } - } - - _tilesBufferLock.Unlock(); - - if (found) { - continue; - } - - // seeking through available caches - for (size_t i = 0; i < _caches.size(); i++) - { - if (_caches[i]->useCache) - { - TileCore* tile = _caches[i]->cache->get_Tile(provider, zoom, x, y); - if (tile) - { - AddTileNoCaching(tile); - found = true; - break; - } - } - } - - if (found) { - continue; - } - - // if the tile isn't present in both caches, request it from the server - if (_useServer) - { - TilePoint* pnt = new TilePoint(x, y); - pnt->dist = sqrt(pow((pnt->x - center.x), 2.0) + pow((pnt->y - center.y), 2.0)); - points.push_back(pnt); - } - } - } + const CPoint center = indices.CenterPoint(); + + for (int x = indices.left; x <= indices.right; x++) + { + for (int y = indices.bottom; y <= indices.top; y++) + { + // was it already reassigned? + bool found = false; + for (size_t i = 0; i < activeTasks.size(); i++) + { + if (activeTasks[i]->x == x && activeTasks[i]->y == y) + { + found = true; + break; + } + } + + if (found) + { + continue; + } + + // check maybe the tile is already in the buffer + _tilesBufferLock.Lock(); + + for (size_t i = 0; i < _tiles.size(); i++) + { + TileCore* tile = _tiles[i]; + if (tile->tileX() == x && tile->tileY() == y && tile->zoom() == zoom && tile->get_ProviderId() == + provider->Id) + { + tile->toDelete(false); + tile->inBuffer(true); + found = true; + break; + } + } + + _tilesBufferLock.Unlock(); + + if (found) + { + continue; + } + + // seeking through available caches + for (size_t i = 0; i < _caches.size(); i++) + { + if (_caches[i]->useCache) + { + TileCore* tile = _caches[i]->cache->get_Tile(provider, zoom, x, y); + if (tile) + { + AddTileNoCaching(tile); + found = true; + break; + } + } + } + + if (found) + { + continue; + } + + // if the tile isn't present in both caches, request it from the server + if (_useServer) + { + auto* pnt = new TilePoint(x, y); + pnt->dist = sqrt(pow(pnt->x - center.x, 2.0) + pow(pnt->y - center.y, 2.0)); + points.push_back(pnt); + } + } + } } // ********************************************************* @@ -248,19 +258,19 @@ void TileManager::BuildLoadingList(BaseProvider* provider, CRect indices, int zo // ********************************************************* void TileManager::Clear() { - ClearBuffer(); + ClearBuffer(); - DeleteMarkedTilesFromBuffer(); + DeleteMarkedTilesFromBuffer(); - _lastMapExtents.left = 0; - _lastMapExtents.right = 0; - _lastMapExtents.top = 0; - _lastMapExtents.bottom = 0; + _lastMapExtents.left = 0; + _lastMapExtents.right = 0; + _lastMapExtents.top = 0; + _lastMapExtents.bottom = 0; - _lastTileExtents.left = 0; - _lastTileExtents.right = 0; - _lastTileExtents.top = 0; - _lastTileExtents.bottom = 0; + _lastTileExtents.left = 0; + _lastTileExtents.right = 0; + _lastTileExtents.top = 0; + _lastTileExtents.bottom = 0; } // ********************************************************* @@ -268,11 +278,11 @@ void TileManager::Clear() // ********************************************************* void TileManager::UnlockDiskCache() { - if (_diskCache.useCache) - { - // caching will be stopped while loading tiles to avoid locking the database and speed up things - _diskCache.cache->Unlock(); - } + if (_diskCache.useCache) + { + // caching will be stopped while loading tiles to avoid locking the database and speed up things + _diskCache.cache->Unlock(); + } } // ********************************************************* @@ -280,11 +290,11 @@ void TileManager::UnlockDiskCache() // ********************************************************* void TileManager::InitializeDiskCache() { - _diskCache.cache->Initialize(_diskCache.useCache, _diskCache.doCaching); - if (_diskCache.useCache) - { - _diskCache.cache->Lock(); // caching will be stopped while loading tiles to avoid locking the database - } + _diskCache.cache->Initialize(_diskCache.useCache, _diskCache.doCaching); + if (_diskCache.useCache) + { + _diskCache.cache->Lock(); // caching will be stopped while loading tiles to avoid locking the database + } } // ********************************************************* @@ -292,25 +302,25 @@ void TileManager::InitializeDiskCache() // ********************************************************* void TileManager::DeleteMarkedTilesFromBuffer() { - _tilesBufferLock.Lock(); - - std::vector::iterator it = _tiles.begin(); - - while (it < _tiles.end()) - { - TileCore* tile = (*it); - if (tile->toDelete()) - { - tile->Release(); - it = _tiles.erase(it); - } - else - { - ++it; - } - } - - _tilesBufferLock.Unlock(); + _tilesBufferLock.Lock(); + + std::vector::iterator it = _tiles.begin(); + + while (it < _tiles.end()) + { + TileCore* tile = *it; + if (tile->toDelete()) + { + tile->Release(); + it = _tiles.erase(it); + } + else + { + ++it; + } + } + + _tilesBufferLock.Unlock(); } // ********************************************************* @@ -320,18 +330,18 @@ void TileManager::DeleteMarkedTilesFromBuffer() // This is made for not searching tiles in the cache again. void TileManager::ClearBuffer() { - _tilesBufferLock.Lock(); + _tilesBufferLock.Lock(); - for (size_t i = 0; i < _tiles.size(); i++) - { - _tiles[i]->isDrawn(false); - _tiles[i]->inBuffer(false); - _tiles[i]->toDelete(true); - } + for (size_t i = 0; i < _tiles.size(); i++) + { + _tiles[i]->isDrawn(false); + _tiles[i]->inBuffer(false); + _tiles[i]->toDelete(true); + } - _tilesBufferLock.Unlock(); + _tilesBufferLock.Unlock(); - UpdateScreenBuffer(); + UpdateScreenBuffer(); } // ********************************************************* @@ -339,57 +349,58 @@ void TileManager::ClearBuffer() // ********************************************************* bool TileManager::IsNewRequest(Extent& mapExtents, CRect indices, int providerId, int zoom) { - if (indices == _lastTileExtents && - _lastProvider == providerId && - _lastZoom == zoom) - { - // map extents has changed but the list of tiles to be displayed is the same - tilesLogger.WriteLine("The same list of tiles can be used."); + if (indices == _lastTileExtents && + _lastProvider == providerId && + _lastZoom == zoom) + { + // map extents has changed but the list of tiles to be displayed is the same + tilesLogger.WriteLine("The same list of tiles can be used."); - UpdateScreenBuffer(); + UpdateScreenBuffer(); - return false; - } + return false; + } - _lastMapExtents = mapExtents; - _lastTileExtents = indices; - _lastProvider = providerId; - _lastZoom = zoom; + _lastMapExtents = mapExtents; + _lastTileExtents = indices; + _lastProvider = providerId; + _lastZoom = zoom; - return true; + return true; } // ********************************************************* // GetActiveTasks() // ********************************************************* -void TileManager::GetActiveTasks(vector& activeTasks, int providerId, int zoom, int newGeneration, CRect indices) +void TileManager::GetActiveTasks(vector& activeTasks, int providerId, int zoom, int newGeneration, + CRect indices) { - // lock it, so active task can't be removed while we analyze it here - _loader.LockActiveTasks(true); - - std::list list = _loader.get_ActiveTasks(); - std::list::iterator it = list.begin(); - - while (it != list.end()) - { - ILoadingTask* task = (ILoadingTask*)*it; - if (task->get_Provider()->Id == providerId && - task->zoom() == zoom && - task->x() >= indices.left && - task->x() <= indices.right && - task->y() >= indices.bottom && - task->y() <= indices.top) - { - tilesLogger.WriteLine("Tile reassigned to current generation: %d\\%d\\%d", zoom, task->x(), task->y()); - - task->generation(newGeneration); // reassign it to current generation - activeTasks.push_back(new TilePoint(task->x(), task->y())); // don't include in current list of requests - } - - ++it; - } - - _loader.LockActiveTasks(false); + // lock it, so active task can't be removed while we analyze it here + _loader.LockActiveTasks(true); + + std::list list = _loader.get_ActiveTasks(); + std::list::iterator it = list.begin(); + + while (it != list.end()) + { + ILoadingTask* task = (ILoadingTask*)*it; + if (task->get_Provider()->Id == providerId && + task->zoom() == zoom && + task->x() >= indices.left && + task->x() <= indices.right && + task->y() >= indices.bottom && + task->y() <= indices.top) + { + tilesLogger.WriteLine("Tile reassigned to current generation: %d\\%d\\%d", zoom, task->x(), task->y()); + + task->generation(newGeneration); // reassign it to current generation + activeTasks.push_back(new TilePoint(task->x(), task->y())); // don't include in current list of requests + } + + ++it; + } + + _loader.LockActiveTasks(false); } // ********************************************************* @@ -397,8 +408,8 @@ void TileManager::GetActiveTasks(vector& activeTasks, int providerId // ********************************************************* void TileManager::AddTileWithCaching(TileCore* tile) { - AddTileNoCaching(tile); - AddTileOnlyCaching(tile); + AddTileNoCaching(tile); + AddTileOnlyCaching(tile); } // ********************************************************* @@ -406,13 +417,13 @@ void TileManager::AddTileWithCaching(TileCore* tile) // ********************************************************* void TileManager::AddTileNoCaching(TileCore* tile) { - tile->inBuffer(true); - tile->toDelete(false); - tile->AddRef(); + tile->inBuffer(true); + tile->toDelete(false); + tile->AddRef(); - _tilesBufferLock.Lock(); - _tiles.push_back(tile); - _tilesBufferLock.Unlock(); + _tilesBufferLock.Lock(); + _tiles.push_back(tile); + _tilesBufferLock.Unlock(); } // ********************************************************* @@ -420,20 +431,21 @@ void TileManager::AddTileNoCaching(TileCore* tile) // ********************************************************* void TileManager::AddTileOnlyCaching(TileCore* tile) { - if (_diskCache.doCaching) { - _loader.ScheduleForCaching(tile); - } + if (_diskCache.doCaching) + { + _loader.ScheduleForCaching(tile); + } } // ********************************************************* // AddTileToRamCache() // ********************************************************* -void TileManager::AddTileToRamCache(TileCore* tile) +void TileManager::AddTileToRamCache(TileCore* tile) { - if (_ramCache.doCaching) - { - _ramCache.cache->AddTile(tile); - } + if (_ramCache.doCaching) + { + _ramCache.cache->AddTile(tile); + } } // ********************************************************* @@ -441,20 +453,20 @@ void TileManager::AddTileToRamCache(TileCore* tile) // ********************************************************* bool TileManager::TileIsInBuffer(int providerId, int zoom, int x, int y) { - CSingleLock lock(&_tilesBufferLock, TRUE); - - for (size_t i = 0; i < _tiles.size(); i++) - { - if (_tiles[i]->tileX() == x && - _tiles[i]->tileY() == y && - _tiles[i]->zoom() == zoom && - _tiles[i]->get_ProviderId() == providerId) - { - return true; - } - } - - return false; + CSingleLock lock(&_tilesBufferLock, TRUE); + + for (size_t i = 0; i < _tiles.size(); i++) + { + if (_tiles[i]->tileX() == x && + _tiles[i]->tileY() == y && + _tiles[i]->zoom() == zoom && + _tiles[i]->get_ProviderId() == providerId) + { + return true; + } + } + + return false; } // ************************************************************ @@ -462,14 +474,14 @@ bool TileManager::TileIsInBuffer(int providerId, int zoom, int x, int y) // ************************************************************ void TileManager::MarkUndrawn() { - _tilesBufferLock.Lock(); + _tilesBufferLock.Lock(); - for (size_t i = 0; i < _tiles.size(); i++) - { - _tiles[i]->isDrawn(false); - } + for (size_t i = 0; i < _tiles.size(); i++) + { + _tiles[i]->isDrawn(false); + } - _tilesBufferLock.Unlock(); + _tilesBufferLock.Unlock(); } // ************************************************************ @@ -478,16 +490,17 @@ void TileManager::MarkUndrawn() // Returns true if at least one not drawn tile exists bool TileManager::UndrawnTilesExist() { - CSingleLock lock(&_tilesBufferLock, TRUE); + CSingleLock lock(&_tilesBufferLock, TRUE); - for (size_t i = 0; i < _tiles.size(); i++) - { - if (!_tiles[i]->isDrawn()) { - return true; - } - } + for (size_t i = 0; i < _tiles.size(); i++) + { + if (!_tiles[i]->isDrawn()) + { + return true; + } + } - return false; + return false; } // ************************************************************ @@ -496,16 +509,17 @@ bool TileManager::UndrawnTilesExist() // Returns true if at least one drawn tile exists bool TileManager::DrawnTilesExist() { - CSingleLock lock(&_tilesBufferLock, TRUE); + CSingleLock lock(&_tilesBufferLock, TRUE); - for (size_t i = 0; i < _tiles.size(); i++) - { - if (_tiles[i]->isDrawn()) { - return true; - } - } + for (size_t i = 0; i < _tiles.size(); i++) + { + if (_tiles[i]->isDrawn()) + { + return true; + } + } - return false; + return false; } // ************************************************************ @@ -513,12 +527,12 @@ bool TileManager::DrawnTilesExist() // ************************************************************ void TileManager::CopyBuffer(vector& buffer) { - _tilesBufferLock.Lock(); + _tilesBufferLock.Lock(); - buffer.reserve(_tiles.size()); - copy(_tiles.begin(), _tiles.end(), inserter(buffer, buffer.end())); + buffer.reserve(_tiles.size()); + copy(_tiles.begin(), _tiles.end(), inserter(buffer, buffer.end())); - _tilesBufferLock.Unlock(); + _tilesBufferLock.Unlock(); } // ************************************************************ @@ -526,12 +540,14 @@ void TileManager::CopyBuffer(vector& buffer) // ************************************************************ void TileManager::UpdateScreenBuffer() { - if (_isBackground) { - _map->_MarkTileBufferChanged(); - } - else { - _screenBufferChanged = true; - } + if (_isBackground) + { + _map->_MarkTileBufferChanged(); + } + else + { + _screenBufferChanged = true; + } } // ************************************************************ @@ -539,9 +555,9 @@ void TileManager::UpdateScreenBuffer() // ************************************************************ bool TileManager::get_ScreenBufferChanged() { - bool val = _screenBufferChanged; - _screenBufferChanged = false; - return val; + const bool val = _screenBufferChanged; + _screenBufferChanged = false; + return val; } // ********************************************************* @@ -550,44 +566,47 @@ bool TileManager::get_ScreenBufferChanged() // checks whether all the tiles are present in cache bool TileManager::TilesAreInCache(BaseProvider* provider) { - // if false is return the client code will try to load them - // but there is no provider to do it - if (!provider) return true; - - CRect indices; - int zoom; - - if (!_map->_GetTilesForMap(_provider, scalingRatio(), indices, zoom)) { - return true; - } - - for (int x = indices.left; x <= indices.right; x++) - { - for (int y = indices.bottom; y <= indices.top; y++) - { - if (TileIsInBuffer(provider->Id, zoom, x, y)) { - continue; - } - - bool found = false; - - vector& caches = get_AllCaches(); - for (size_t i = 0; i < caches.size(); i++) - { - if (caches[i]->useCache) - { - if (caches[i]->cache->get_TileExists(provider, zoom, x, y)) - { - found = true; - } - } - } - - if (!found) { - return false; - } - } - } - - return true; + // if false is return the client code will try to load them + // but there is no provider to do it + if (!provider) return true; + + CRect indices; + int zoom; + + if (!_map->_GetTilesForMap(_provider, scalingRatio(), indices, zoom)) + { + return true; + } + + for (int x = indices.left; x <= indices.right; x++) + { + for (int y = indices.bottom; y <= indices.top; y++) + { + if (TileIsInBuffer(provider->Id, zoom, x, y)) + { + continue; + } + + bool found = false; + + vector& caches = get_AllCaches(); + for (size_t i = 0; i < caches.size(); i++) + { + if (caches[i]->useCache) + { + if (caches[i]->cache->get_TileExists(provider, zoom, x, y)) + { + found = true; + } + } + } + + if (!found) + { + return false; + } + } + } + + return true; } diff --git a/src/Tiles/TileManager.h b/src/Tiles/TileManager.h index 30d32f31..3b15b32e 100644 --- a/src/Tiles/TileManager.h +++ b/src/Tiles/TileManager.h @@ -17,118 +17,127 @@ ************************************************************************************** * Contributor(s): * (Open source contributors should list themselves and their modifications here). */ - #pragma once +// Paul Meems August 2018: Modernized the code as suggested by CLang and ReSharper + +#pragma once #include "ITileCache.h" #include "TileMapLoader.h" class TileManager { public: - TileManager(bool isBackground) - : _map(NULL), _lastZoom(-1), _lastProvider(-1), _isBackground(isBackground), - _loader(tctSqliteCache), _gridLinesVisible(false), _provider(NULL), _screenBufferChanged(true) - { - _useServer = true; - _lastZoom = -1; - _lastProvider = tkTileProvider::ProviderNone; - _scalingRatio = 1.0; - - _alpha = 255; - brightness = 0.0f; - contrast = 1.0f; - saturation = 1.0f; - hue = 0.0f; - gamma = 1.0f; - useTransparentColor = false; - transparentColor = RGB(255, 255, 255); - - InitCaches(); - } + TileManager(bool isBackground) + : _provider(nullptr), _loader(tctSqliteCache), _map(nullptr), _isBackground(isBackground), + _screenBufferChanged(true), _lastZoom(-1), _lastProvider(-1), _gridLinesVisible(false) + { + _useServer = true; + _lastZoom = -1; + _lastProvider = tkTileProvider::ProviderNone; + _scalingRatio = 1.0; + + _alpha = 255; + brightness = 0.0f; + contrast = 1.0f; + saturation = 1.0f; + hue = 0.0f; + gamma = 1.0f; + useTransparentColor = false; + transparentColor = RGB(255, 255, 255); + + InitCaches(); + } private: - BaseProvider* _provider; - TileMapLoader _loader; - IMapViewCallback* _map; - double _scalingRatio; - bool _isBackground; // this is background TMS layer associated with ITiles (screen buffer can be scaled on zooming) - TileCacheInfo _diskCache; - TileCacheInfo _ramCache; - vector _caches; - bool _screenBufferChanged; - - // can be wrapped in a separate class - ::CCriticalSection _tilesBufferLock; - vector _tiles; - - Extent _projExtents; // extents of the world under current projection; in WGS84 it'll be (-180, 180, -90, 90) - bool _projExtentsNeedUpdate; // do we need to update bounds in m_projExtents on the next request? - - CRect _lastTileExtents; // in tile coordinates - Extent _lastMapExtents; - int _lastZoom; - int _lastProvider; - bool _useServer; - bool _gridLinesVisible; - byte _alpha; - -public: - float brightness; // -1, 1 - float contrast; // 0, 4 - float saturation; // 0, 3 - float hue; // -180, 180 - float gamma; // 0, 4 - OLE_COLOR transparentColor; - bool useTransparentColor; + BaseProvider* _provider; + TileMapLoader _loader; + IMapViewCallback* _map; + double _scalingRatio; + bool _isBackground; // this is background TMS layer associated with ITiles (screen buffer can be scaled on zooming) + TileCacheInfo _diskCache; + TileCacheInfo _ramCache; + vector _caches; + bool _screenBufferChanged; + + // can be wrapped in a separate class + CCriticalSection _tilesBufferLock; + vector _tiles; + + Extent _projExtents; // extents of the world under current projection; in WGS84 it'll be (-180, 180, -90, 90) + bool _projExtentsNeedUpdate; // do we need to update bounds in m_projExtents on the next request? + + CRect _lastTileExtents; // in tile coordinates + Extent _lastMapExtents; + int _lastZoom; + int _lastProvider; + bool _useServer; + bool _gridLinesVisible; + byte _alpha; + +public: + float brightness; // -1, 1 + float contrast; // 0, 4 + float saturation; // 0, 3 + float hue; // -180, 180 + float gamma; // 0, 4 + OLE_COLOR transparentColor; + bool useTransparentColor; private: - void InitCaches(); - void UpdateScreenBuffer(); - void BuildLoadingList(BaseProvider* provider, CRect indices, int zoom, vector& activeTasks, vector& points); - void GetActiveTasks(std::vector& activeTasks, int providerId, int zoom, int newGeneration, CRect indices); - bool IsNewRequest(Extent& mapExtents, CRect indices, int providerId, int zoom); - void DeleteMarkedTilesFromBuffer(); - void InitializeDiskCache(); - void UnlockDiskCache(); - bool GetTileIndices(BaseProvider* provider, CRect& indices, int& zoom, bool isSnapshot); + void InitCaches(); + void UpdateScreenBuffer(); + void BuildLoadingList(BaseProvider* provider, CRect indices, int zoom, vector& activeTasks, + vector& points); + void GetActiveTasks(std::vector& activeTasks, int providerId, int zoom, int newGeneration, + CRect indices); + bool IsNewRequest(Extent& mapExtents, CRect indices, int providerId, int zoom); + void DeleteMarkedTilesFromBuffer(); + void InitializeDiskCache(); + void UnlockDiskCache(); + bool GetTileIndices(BaseProvider* provider, CRect& indices, int& zoom, bool isSnapshot); public: - // properties - bool IsBackground() { return _isBackground; } - void set_MapCallback(IMapViewCallback* map); - IMapViewCallback* get_MapCallback() { return _map; } - bool TileIsInBuffer(int providerId, int zoom, int x, int y); - bool useServer() { return _useServer; } - void useServer(bool value) { _useServer = value; } - double scalingRatio() { return _scalingRatio; } - void scalingRatio(double value) { _scalingRatio = value; } - ITileLoader* get_Loader() { return &_loader; } - void CopyBuffer(vector& buffer); - TileCacheInfo* get_DiskCache() { return &_diskCache; } - TileCacheInfo* get_RamCache() { return &_ramCache; } - TileCacheInfo* get_Cache(tkCacheType type) { return type == Disk ? &_diskCache : &_ramCache; } - vector& get_AllCaches() { return _caches; } - bool get_GridLinesVisible() { return _gridLinesVisible; } - void set_GridLinesVisible(bool value) { _gridLinesVisible = value; } - BaseProvider* get_Provider() { return _provider; } - bool get_ScreenBufferChanged(); - byte get_Alpha() { return _alpha; } - void set_Alpha(byte value) { _alpha = value; } + // properties + bool IsBackground() { return _isBackground; } + void set_MapCallback(IMapViewCallback* map); + IMapViewCallback* get_MapCallback() { return _map; } + bool TileIsInBuffer(int providerId, int zoom, int x, int y); + bool useServer() { return _useServer; } + void useServer(bool value) { _useServer = value; } + double scalingRatio() { return _scalingRatio; } + void scalingRatio(double value) { _scalingRatio = value; } + ITileLoader* get_Loader() { return &_loader; } + void CopyBuffer(vector& buffer); + TileCacheInfo* get_DiskCache() { return &_diskCache; } + TileCacheInfo* get_RamCache() { return &_ramCache; } + TileCacheInfo* get_Cache(tkCacheType type) { return type == Disk ? &_diskCache : &_ramCache; } + vector& get_AllCaches() { return _caches; } + bool get_GridLinesVisible() { return _gridLinesVisible; } + void set_GridLinesVisible(bool value) { _gridLinesVisible = value; } + BaseProvider* get_Provider() { return _provider; } + bool get_ScreenBufferChanged(); + byte get_Alpha() { return _alpha; } + void set_Alpha(byte value) { _alpha = value; } public: - // methods - void Clear(); - void LoadTiles(BaseProvider* provider, bool isSnapshot, CString key); - void MarkUndrawn(); - bool UndrawnTilesExist(); - bool DrawnTilesExist(); - - void AddTileToRamCache(TileCore* tile); - void AddTileWithCaching(TileCore* tile); - void AddTileNoCaching(TileCore* tile); - void AddTileOnlyCaching(TileCore* tile); - - void TriggerMapRedraw() { _map->_Redraw(tkRedrawType::RedrawSkipDataLayers, false, true); }; - void FireTilesLoaded(bool isSnapshot, CString key, bool fromCache) { _map->_FireTilesLoaded(isSnapshot, key, fromCache); } - bool TilesAreInCache(BaseProvider* provider); - void ClearBuffer(); -}; \ No newline at end of file + // methods + void Clear(); + void LoadTiles(BaseProvider* provider, bool isSnapshot, const CString& key); + void MarkUndrawn(); + bool UndrawnTilesExist(); + bool DrawnTilesExist(); + + void AddTileToRamCache(TileCore* tile); + void AddTileWithCaching(TileCore* tile); + void AddTileNoCaching(TileCore* tile); + void AddTileOnlyCaching(TileCore* tile); + + void TriggerMapRedraw() { _map->_Redraw(tkRedrawType::RedrawSkipDataLayers, false, true); } + + void FireTilesLoaded(bool isSnapshot, CString key, bool fromCache) + { + _map->_FireTilesLoaded(isSnapshot, key, fromCache); + } + + bool TilesAreInCache(BaseProvider* provider); + void ClearBuffer(); +}; diff --git a/src/Utilities/FieldClassification.cpp b/src/Utilities/FieldClassification.cpp index c97e706b..2d8e33d3 100644 --- a/src/Utilities/FieldClassification.cpp +++ b/src/Utilities/FieldClassification.cpp @@ -31,7 +31,7 @@ void FieldClassification::GetMinValue(vector& srcValues, CComVariant& // ***************************************************************** // GenerateCategories() // ***************************************************************** -vector* FieldClassification::GenerateCategories(CString fieldName, FieldType fieldType, +vector* FieldClassification::GenerateCategories(CStringW fieldName, FieldType fieldType, vector& srcValues, tkClassificationType ClassificationType, long numClasses, long& errorCode) { @@ -290,22 +290,22 @@ vector* FieldClassification::GenerateCategories(CString fieldNam for (int i = 0; i < (int)result->size(); i++) { //CString strExpression; - CString strValue; + CStringW strValue; CComVariant* val = &(*result)[i].minValue; switch (val->vt) { case VT_BSTR: - strValue = OLE2CA(val->bstrVal); + strValue = OLE2CW(val->bstrVal); (*result)[i].name = strValue; (*result)[i].expression = "[" + fieldName + "] = \"" + strValue + "\""; break; case VT_R8: - strValue.Format("%g", val->dblVal); + strValue.Format(L"%g", val->dblVal); (*result)[i].name = strValue; (*result)[i].expression = "[" + fieldName + "] = " + strValue; break; case VT_I4: - strValue.Format("%i", val->lVal); + strValue.Format(L"%i", val->lVal); (*result)[i].name = strValue; (*result)[i].expression = "[" + fieldName + "] = " + strValue; break; @@ -315,7 +315,7 @@ vector* FieldClassification::GenerateCategories(CString fieldNam else //if (ClassificationType == ctEqualIntervals || ClassificationType == ctEqualCount) { // in case % is present, we need to put to double it for proper formatting - fieldName.Replace("%", "%%"); + fieldName.Replace(L"%", L"%%"); for (int i = 0; i < (int)result->size(); i++) { @@ -332,7 +332,7 @@ vector* FieldClassification::GenerateCategories(CString fieldNam data->maxValue.dblVal = ceil(data->maxValue.dblVal); } - CString upperBound = (i == result->size() - 1) ? "<=" : "<"; + CStringW upperBound = (i == result->size() - 1) ? L"<=" : L"<"; switch (data->minValue.vt) { diff --git a/src/Utilities/FieldClassification.h b/src/Utilities/FieldClassification.h index 2d399f74..f88f0d6d 100644 --- a/src/Utilities/FieldClassification.h +++ b/src/Utilities/FieldClassification.h @@ -3,7 +3,7 @@ class FieldClassification { public: - static vector* GenerateCategories(CString fieldName, FieldType fieldType, vector& srcValues, + static vector* GenerateCategories(CStringW fieldName, FieldType fieldType, vector& srcValues, tkClassificationType ClassificationType, long numClasses, long& errorCode); static void GetMinValue(vector& srcValues, CComVariant& result, bool min); diff --git a/src/Utilities/UtilityFunctions.cpp b/src/Utilities/UtilityFunctions.cpp index c55fa722..d1c9e5ec 100644 --- a/src/Utilities/UtilityFunctions.cpp +++ b/src/Utilities/UtilityFunctions.cpp @@ -530,12 +530,18 @@ namespace Utility GetTempPath(MAX_PATH, tmppath); - tmpnam(tmpfname); - - strcat(tmppath, tmpfname); - strcat(tmppath, extensionWithLeadingPoint); - - CString result = tmppath; + //tmpnam(tmpfname); + // replacing tmpnam with the Windows call GetTempFileName + // because, at least under certain circumstances, tmpnam was + // returning a name including a path, which when concatenated + // with tmppath, resulted in an invalid filename. + ::GetTempFileName(tmppath, "", 0, tmpfname); + + //strcat(tmppath, tmpfname); + //strcat(tmppath, extensionWithLeadingPoint); + + CString result = tmpfname; + result.MakeLower().Replace(".tmp", extensionWithLeadingPoint); delete[] tmpfname; delete[] tmppath; diff --git a/src/changingVersionNumbers.txt b/src/changingVersionNumbers.txt index 5a8f9246..9cffc1a6 100644 --- a/src/changingVersionNumbers.txt +++ b/src/changingVersionNumbers.txt @@ -1,8 +1,8 @@ When releasing a new version version numbers need to be updated on several locations: -MapWinGIS.rc lines: 114, 115, 133, 139 -MapWinGIS.cpp lines: 32, 33 -MapWinGIS.idl line: 6622 (helpfile("MapWinGIS.chm")) - , 6644 (helpstring("Dispatch interface for Map Control")) +MapWinGIS.rc lines: 114, 115, 133, 139 [5.0.0.0] +MapWinGIS.cpp lines: 32, 33 [5.0] +MapWinGIS.idl line: 6629 (helpfile("MapWinGIS.chm")) [5.0] + , 6651 (helpstring("Dispatch interface for Map Control")) [5.0] If you want to update AxInterop.MapWinGIS.dll and Interop.MapWinGIS.dll you can run the InteropCreator project. It is a C# project which will create the interop dlls for you. diff --git a/support/GDAL_SDK/v140/bin/win32/!!!VC2015 win32 binaries should be here!!!.txt b/support/GDAL_SDK/v140/bin/win32/!!!VC2015 win32 binaries should be here!!!.txt new file mode 100644 index 00000000..0519ecba --- /dev/null +++ b/support/GDAL_SDK/v140/bin/win32/!!!VC2015 win32 binaries should be here!!!.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/support/GDAL_SDK/v140/bin/x64/!!!VC2015 x64 binaries should be here!!!.txt b/support/GDAL_SDK/v140/bin/x64/!!!VC2015 x64 binaries should be here!!!.txt new file mode 100644 index 00000000..0519ecba --- /dev/null +++ b/support/GDAL_SDK/v140/bin/x64/!!!VC2015 x64 binaries should be here!!!.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/support/GDAL_SDK/v140/include/win32/!!!VC2015 win32 headers should be here!!!.txt b/support/GDAL_SDK/v140/include/win32/!!!VC2015 win32 headers should be here!!!.txt new file mode 100644 index 00000000..0519ecba --- /dev/null +++ b/support/GDAL_SDK/v140/include/win32/!!!VC2015 win32 headers should be here!!!.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/support/GDAL_SDK/v140/include/x64/!!!VC2015 x64 headers should be here!!!.txt b/support/GDAL_SDK/v140/include/x64/!!!VC2015 x64 headers should be here!!!.txt new file mode 100644 index 00000000..0519ecba --- /dev/null +++ b/support/GDAL_SDK/v140/include/x64/!!!VC2015 x64 headers should be here!!!.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/support/GDAL_SDK/v140/lib/win32/!!!VC2015 win32 libs should be here!!!.txt b/support/GDAL_SDK/v140/lib/win32/!!!VC2015 win32 libs should be here!!!.txt new file mode 100644 index 00000000..0519ecba --- /dev/null +++ b/support/GDAL_SDK/v140/lib/win32/!!!VC2015 win32 libs should be here!!!.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/support/GDAL_SDK/v140/lib/x64/!!!VC2015 x64 libs should be here!!!.txt b/support/GDAL_SDK/v140/lib/x64/!!!VC2015 x64 libs should be here!!!.txt new file mode 100644 index 00000000..0519ecba --- /dev/null +++ b/support/GDAL_SDK/v140/lib/x64/!!!VC2015 x64 libs should be here!!!.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/support/ShapeLib/ShapeLib.vcxproj b/support/ShapeLib/ShapeLib.vcxproj index ccea5049..38de1485 100644 --- a/support/ShapeLib/ShapeLib.vcxproj +++ b/support/ShapeLib/ShapeLib.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -22,31 +22,32 @@ {A0B00502-605B-4382-B313-5983F6BC8E71} Win32Proj ShapeLib + 8.1 StaticLibrary true - v120 + v140 Unicode StaticLibrary true - v120 + v140 Unicode StaticLibrary false - v120 + v140 true Unicode StaticLibrary false - v120 + v140 true Unicode @@ -67,19 +68,19 @@ - $(SolutionDir)lib\$(PlatformToolset)\$(Platform)\ + $(SolutionDir)lib\$(PlatformToolset)\$(Platform)\$(Configuration)\ $(ProjectDir)$(Platform)\$(Configuration)\ - $(SolutionDir)lib\$(PlatformToolset)\$(Platform)\ + $(SolutionDir)lib\$(PlatformToolset)\$(Platform)\$(Configuration)\ $(ProjectDir)$(Platform)\$(Configuration)\ - $(SolutionDir)lib\$(PlatformToolset)\$(Platform)\ + $(SolutionDir)lib\$(PlatformToolset)\$(Platform)\$(Configuration)\ $(ProjectDir)$(Platform)\$(Configuration)\ - $(SolutionDir)lib\$(PlatformToolset)\$(Platform)\ + $(SolutionDir)lib\$(PlatformToolset)\$(Platform)\$(Configuration)\ $(ProjectDir)$(Platform)\$(Configuration)\ @@ -92,6 +93,8 @@ %(AdditionalIncludeDirectories) OldStyle false + MultiThreadedDLL + Default Windows @@ -114,6 +117,8 @@ %(AdditionalIncludeDirectories) OldStyle false + MultiThreadedDLL + Default Windows @@ -132,12 +137,14 @@ Full - true - true + + + false WIN32;_CRT_SECURE_NO_WARNINGS;SHAPELIB_DLLEXPORT;NDEBUG;_LIB;%(PreprocessorDefinitions) %(AdditionalIncludeDirectories) None false + true Windows @@ -158,12 +165,14 @@ Full - true + + true WIN32;_CRT_SECURE_NO_WARNINGS;SHAPELIB_DLLEXPORT;NDEBUG;_LIB;%(PreprocessorDefinitions) %(AdditionalIncludeDirectories) None false + true Windows diff --git a/support/cqlib/cqlib.vcxproj b/support/cqlib/cqlib.vcxproj index 1e4cae4d..4742d5e5 100644 --- a/support/cqlib/cqlib.vcxproj +++ b/support/cqlib/cqlib.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -21,29 +21,30 @@ {E81699D6-81BA-41BF-A3A9-DD4CEF0B4E95} cqlib + 8.1 StaticLibrary - v120 + v140 false MultiByte StaticLibrary - v120 + v140 false MultiByte StaticLibrary - v120 + v140 false MultiByte StaticLibrary - v120 + v140 false MultiByte @@ -71,27 +72,27 @@ <_ProjectFileVersion>12.0.21005.1 - $(SolutionDir)lib\$(PlatformToolset)\$(Platform)\ + $(SolutionDir)lib\$(PlatformToolset)\$(Platform)\$(Configuration)\ $(ProjectDir)$(Platform)\$(Configuration)\ - $(SolutionDir)lib\$(PlatformToolset)\$(Platform)\ + $(SolutionDir)lib\$(PlatformToolset)\$(Platform)\$(Configuration)\ $(ProjectDir)$(Platform)\$(Configuration)\ - $(SolutionDir)lib\$(PlatformToolset)\$(Platform)\ + $(SolutionDir)lib\$(PlatformToolset)\$(Platform)\$(Configuration)\ $(ProjectDir)$(Platform)\$(Configuration)\ - $(SolutionDir)lib\$(PlatformToolset)\$(Platform)\ + $(SolutionDir)lib\$(PlatformToolset)\$(Platform)\$(Configuration)\ $(ProjectDir)$(Platform)\$(Configuration)\ Disabled WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebugDLL + Default + MultiThreadedDLL .\Debug/cqlib.pch .\Debug/ @@ -106,7 +107,7 @@ 0x0409 - ..\tkImage\cqlib.lib + $(OutDir)cqlib.lib true @@ -116,9 +117,9 @@ Disabled - WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebugDLL + WIN32;WIN64;_DEBUG;_LIB;%(PreprocessorDefinitions) + Default + MultiThreadedDLL .\Debug/cqlib.pch .\Debug/ @@ -133,18 +134,19 @@ 0x0409 - ..\tkImage\cqlib.lib + $(OutDir)$(TargetName)$(TargetExt) true - MaxSpeed - OnlyExplicitInline + Full + Default WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) true MultiThreadedDLL - true + + $(IntDir)cqlib.pch $(IntDir) @@ -173,12 +175,13 @@ X64 - MaxSpeed - OnlyExplicitInline + Full + Default WIN32;WIN64;NDEBUG;_LIB;%(PreprocessorDefinitions) true MultiThreadedDLL - true + + $(IntDir)cqlib.pch $(IntDir) @@ -194,6 +197,7 @@ $(OutDir)cqlib.lib true + true $(OutDir)$(ProjectName).bsc diff --git a/support/lib/v120/Win32/cqlib.lib b/support/lib/v120/Win32/cqlib.lib deleted file mode 100644 index b8883f43..00000000 Binary files a/support/lib/v120/Win32/cqlib.lib and /dev/null differ diff --git a/support/lib/v120/Win32/shapelib.lib b/support/lib/v120/Win32/shapelib.lib deleted file mode 100644 index 9c91524f..00000000 Binary files a/support/lib/v120/Win32/shapelib.lib and /dev/null differ diff --git a/support/lib/v120/Win32/spatialindex-mw.lib b/support/lib/v120/Win32/spatialindex-mw.lib deleted file mode 100644 index 3f8b9f45..00000000 Binary files a/support/lib/v120/Win32/spatialindex-mw.lib and /dev/null differ diff --git a/support/lib/v120/x64/cqlib.lib b/support/lib/v120/x64/cqlib.lib deleted file mode 100644 index b638e913..00000000 Binary files a/support/lib/v120/x64/cqlib.lib and /dev/null differ diff --git a/support/lib/v140/Win32/Release/ShapeLib.lib b/support/lib/v140/Win32/Release/ShapeLib.lib new file mode 100644 index 00000000..ffbab143 Binary files /dev/null and b/support/lib/v140/Win32/Release/ShapeLib.lib differ diff --git a/support/lib/v140/Win32/Release/cqlib.lib b/support/lib/v140/Win32/Release/cqlib.lib new file mode 100644 index 00000000..ea37dd6c Binary files /dev/null and b/support/lib/v140/Win32/Release/cqlib.lib differ diff --git a/support/lib/v140/Win32/Release/spatialindex-mw.lib b/support/lib/v140/Win32/Release/spatialindex-mw.lib new file mode 100644 index 00000000..d3089687 Binary files /dev/null and b/support/lib/v140/Win32/Release/spatialindex-mw.lib differ diff --git a/support/lib/v140/x64/Debug/ShapeLib.lib b/support/lib/v140/x64/Debug/ShapeLib.lib new file mode 100644 index 00000000..fa40247c Binary files /dev/null and b/support/lib/v140/x64/Debug/ShapeLib.lib differ diff --git a/support/lib/v140/x64/Debug/cqlib.lib b/support/lib/v140/x64/Debug/cqlib.lib new file mode 100644 index 00000000..e6b2db6d Binary files /dev/null and b/support/lib/v140/x64/Debug/cqlib.lib differ diff --git a/support/lib/v140/x64/Debug/spatialindex-mw.lib b/support/lib/v140/x64/Debug/spatialindex-mw.lib new file mode 100644 index 00000000..13355c5c Binary files /dev/null and b/support/lib/v140/x64/Debug/spatialindex-mw.lib differ diff --git a/support/lib/v140/x64/Debug/spatialindex-mw.pdb b/support/lib/v140/x64/Debug/spatialindex-mw.pdb new file mode 100644 index 00000000..00971b93 Binary files /dev/null and b/support/lib/v140/x64/Debug/spatialindex-mw.pdb differ diff --git a/support/lib/v140/x64/Release/ShapeLib.lib b/support/lib/v140/x64/Release/ShapeLib.lib new file mode 100644 index 00000000..5457ec85 Binary files /dev/null and b/support/lib/v140/x64/Release/ShapeLib.lib differ diff --git a/support/lib/v140/x64/Release/cqlib.lib b/support/lib/v140/x64/Release/cqlib.lib new file mode 100644 index 00000000..cef71edc Binary files /dev/null and b/support/lib/v140/x64/Release/cqlib.lib differ diff --git a/support/lib/v140/x64/Release/spatialindex-mw.lib b/support/lib/v140/x64/Release/spatialindex-mw.lib new file mode 100644 index 00000000..03dcfd8a Binary files /dev/null and b/support/lib/v140/x64/Release/spatialindex-mw.lib differ diff --git a/support/spatialindex/spatialindex-vc/spatialindex.vcxproj b/support/spatialindex/spatialindex-vc/spatialindex.vcxproj index 45e57277..865ae764 100644 --- a/support/spatialindex/spatialindex-vc/spatialindex.vcxproj +++ b/support/spatialindex/spatialindex-vc/spatialindex.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -23,31 +23,32 @@ {38FBBD59-8344-4D8E-B728-3D51763B6A70} spatialindex ManagedCProj + 8.1 StaticLibrary - v120 + v140 Unicode false true StaticLibrary - v120 + v140 Unicode false StaticLibrary - v120 + v140 Unicode false true StaticLibrary - v120 + v140 Unicode false @@ -71,19 +72,19 @@ <_ProjectFileVersion>12.0.21005.1 - $(SolutionDir)lib\$(PlatformToolset)\$(Platform)\ + $(SolutionDir)lib\$(PlatformToolset)\$(Platform)\$(Configuration)\ $(ProjectDir)$(Platform)\$(Configuration)\ - $(SolutionDir)lib\$(PlatformToolset)\$(Platform)\ + $(SolutionDir)lib\$(PlatformToolset)\$(Platform)\$(Configuration)\ $(ProjectDir)$(Platform)\$(Configuration)\ - $(SolutionDir)lib\$(PlatformToolset)\$(Platform)\ + $(SolutionDir)lib\$(PlatformToolset)\$(Platform)\$(Configuration)\ $(ProjectDir)$(Platform)\$(Configuration)\ - $(SolutionDir)lib\$(PlatformToolset)\$(Platform)\ + $(SolutionDir)lib\$(PlatformToolset)\$(Platform)\$(Configuration)\ $(ProjectDir)$(Platform)\$(Configuration)\ @@ -94,10 +95,11 @@ Disabled $(SolutionDir)include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;SPATIALINDEX_CREATE_DLL;%(PreprocessorDefinitions) - MultiThreadedDebugDLL + MultiThreadedDLL Level3 OldStyle + Default @@ -108,7 +110,7 @@ Disabled $(SolutionDir)include;%(AdditionalIncludeDirectories) WIN64;_DEBUG;SPATIALINDEX_CREATE_DLL;%(PreprocessorDefinitions) - MultiThreadedDebugDLL + MultiThreadedDLL Level3 ProgramDatabase @@ -119,7 +121,7 @@ X64 - Disabled + Full $(SolutionDir)include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;%(PreprocessorDefinitions) MultiThreadedDLL @@ -127,6 +129,7 @@ Level3 false + true @@ -141,6 +144,8 @@ Level3 + false + true diff --git a/unittests/MapWinGISTests/Helper.cs b/unittests/MapWinGISTests/Helper.cs index b10c87fb..5097dbb2 100644 --- a/unittests/MapWinGISTests/Helper.cs +++ b/unittests/MapWinGISTests/Helper.cs @@ -26,6 +26,8 @@ public static void PrintExtents(Extents extents) public static void DeleteShapefile(string filename) { + if (string.IsNullOrEmpty(filename)) return; + var folder = Path.GetDirectoryName(filename); if (folder == null) return; var filenameBody = Path.GetFileNameWithoutExtension(filename); @@ -135,6 +137,15 @@ public static Shapefile CreateSfFromWkt(string wkt, int epsgCode) return sf; } + public static Shapefile CreateSf(ShpfileType shpfileType) + { + var sf = new Shapefile(); + if (!sf.CreateNewWithShapeID("", shpfileType)) + throw new Exception("Can't create shapefile. Error: " + sf.ErrorMsg[sf.LastErrorCode]); + + return sf; + } + public static Shapefile OpenShapefile(string fileLocation, bool checkInvalidShapes = true, ICallback callback = null) { if (!File.Exists(fileLocation)) @@ -159,6 +170,12 @@ public static Shapefile OpenShapefile(string fileLocation, bool checkInvalidShap return sf; } + public static Point MakePoint(double x, double y) + { + var point = new Point(); + point.Set(x, y); + return point; + } public static void CheckValidity(IShapefile sf) { @@ -193,6 +210,144 @@ public static void CheckValidity(IShapefile sf) DebugMsg("Logging invalid shapes using partioner took: " + stopwatch.Elapsed); } + public static bool GetInfoShapefile(ref Shapefile sf) + { + if (sf == null) + throw new ArgumentNullException(nameof(sf)); + + bool retVal; + + try + { + Console.WriteLine("Working with " + sf.Filename); + + Console.WriteLine("ShapefileType: " + sf.ShapefileType); + Console.WriteLine("ShapefileType2D: " + sf.ShapefileType2D); + Console.WriteLine("Num fields: " + sf.NumFields); + Console.WriteLine("Num shapes: " + sf.NumShapes); + Console.WriteLine("HasSpatialIndex: " + sf.HasSpatialIndex); + Console.WriteLine("Projection: " + sf.Projection); + if (sf.NumShapes < 100) + { + Console.WriteLine("HasInvalidShapes: " + sf.HasInvalidShapes()); + } + else + { + Console.WriteLine("Warning. Large number of shapes. Skipping HasInvalidShapes check."); + } + + if (!sf.GeoProjection.IsEmpty) + { + int epsgCode; + sf.GeoProjection.TryAutoDetectEpsg(out epsgCode); + Console.WriteLine("epsgCode: " + epsgCode); + } + + if (sf.NumShapes <= 0) + throw new Exception("Shapefile has no shapes"); + + var shp = sf.Shape[0]; + if (shp == null) + throw new NullReferenceException("Cannot get shape: " + sf.ErrorMsg[sf.LastErrorCode]); + + Console.WriteLine("numPoints: " + shp.numPoints); + Console.WriteLine("NumParts: " + shp.NumParts); + Console.WriteLine("ShapeType: " + shp.ShapeType); + Console.WriteLine("ShapeType2D: " + shp.ShapeType2D); + var isValid = shp.IsValid; + Console.WriteLine("IsValid: " + isValid); + if (!isValid) + { + Console.WriteLine("IsValidReason: " + shp.IsValidReason); + } + + if (sf.ShapefileType != shp.ShapeType) + Console.WriteLine("Warning shape(file) type mismatch"); + + double x = 0d, y = 0d; + double z, m; + if (!shp.XY[0, ref x, ref y]) + throw new Exception("Cannot get XY from shape: " + shp.ErrorMsg[shp.LastErrorCode]); + Console.WriteLine("First point X: " + x); + Console.WriteLine("First point Y: " + y); + + switch (shp.ShapeType) + { + case ShpfileType.SHP_NULLSHAPE: + throw new Exception("Shape is a NULLSHAPE"); + case ShpfileType.SHP_POINT: + break; + case ShpfileType.SHP_POLYLINE: + Console.WriteLine("First shape length: " + shp.Length); + break; + case ShpfileType.SHP_POLYGON: + Console.WriteLine("First shape area: " + shp.Area); + Console.WriteLine("First shape perimeter: " + shp.Perimeter); + break; + case ShpfileType.SHP_MULTIPOINT: + break; + case ShpfileType.SHP_POINTZ: + if (!shp.get_Z(0, out z)) + throw new Exception("Cannot get Z from first shape: " + shp.ErrorMsg[shp.LastErrorCode]); + Console.WriteLine("First shape, first point Z: " + z); + break; + case ShpfileType.SHP_POLYLINEZ: + if (!shp.get_Z(0, out z)) + throw new Exception("Cannot get Z from first shape: " + shp.ErrorMsg[shp.LastErrorCode]); + Console.WriteLine("First shape, first point Z: " + z); + Console.WriteLine("First shape length: " + shp.Length); + break; + case ShpfileType.SHP_POLYGONZ: + if (!shp.get_Z(0, out z)) + throw new Exception("Cannot get Z from first shape: " + shp.ErrorMsg[shp.LastErrorCode]); + Console.WriteLine("First shape, first point Z: " + z); + Console.WriteLine("First shape area: " + shp.Area); + Console.WriteLine("First shape perimeter: " + shp.Perimeter); + break; + case ShpfileType.SHP_MULTIPOINTZ: + break; + case ShpfileType.SHP_POINTM: + if (!shp.get_M(0, out m)) + throw new Exception("Cannot get M from first shape: " + shp.ErrorMsg[shp.LastErrorCode]); + Console.WriteLine("First shape, first point M: " + m); + break; + case ShpfileType.SHP_POLYLINEM: + if (!shp.get_M(0, out m)) + throw new Exception("Cannot get M from first shape: " + shp.ErrorMsg[shp.LastErrorCode]); + Console.WriteLine("First shape, first point M: " + m); + Console.WriteLine("First shape length: " + shp.Length); + break; + case ShpfileType.SHP_POLYGONM: + if (!shp.get_M(0, out m)) + throw new Exception("Cannot get M from first shape: " + shp.ErrorMsg[shp.LastErrorCode]); + Console.WriteLine("First shape, first point M: " + m); + Console.WriteLine("First shape area: " + shp.Area); + Console.WriteLine("First shape perimeter: " + shp.Perimeter); + break; + case ShpfileType.SHP_MULTIPOINTM: + if (!shp.get_M(0, out m)) + throw new Exception("Cannot get M from first shape: " + shp.ErrorMsg[shp.LastErrorCode]); + Console.WriteLine("First shape, first point M: " + m); + break; + case ShpfileType.SHP_MULTIPATCH: + break; + default: + throw new ArgumentOutOfRangeException(); + } + + retVal = true; + } + catch (Exception e) + { + Console.WriteLine(e.Message); + retVal = false; + } + + return retVal; + } + + + public static void DebugMsg(string msg) { Console.WriteLine(msg); @@ -234,7 +389,7 @@ public static string SaveSnapshot2(AxMap axMap1, string baseName, bool shouldFai Application.DoEvents(); var filename = Path.Combine(Path.GetTempPath(), baseName); DeleteFile(filename); - + var img = axMap1.SnapShot2(0, axMap1.CurrentZoom + 10, 1000); if (img == null) throw new NullReferenceException("Snapshot is null"); @@ -258,13 +413,12 @@ public static string SaveSnapshot2(AxMap axMap1, string baseName, bool shouldFai public static string SaveSnapshot(AxMap axMap1, string baseName, IExtents boundBox, double extentEnlarger = 1d) { Application.DoEvents(); - var filename = Path.Combine(Path.GetTempPath(), baseName); + var filename = Path.Combine(WorkingFolder("mw5-snapshots"), baseName); DeleteFile(filename); - if ((boundBox.Width * boundBox.Height).Equals(0)) + if ((boundBox.Width * boundBox.Height).Equals(0) || extentEnlarger > 1) { - double xmin, ymin, xmax, ymax, zmin, zmax; - boundBox.GetBounds(out xmin, out ymin, out zmin, out xmax, out ymax, out zmax); + boundBox.GetBounds(out var xmin, out var ymin, out var zmin, out var xmax, out var ymax, out var zmax); boundBox.SetBounds(xmin - extentEnlarger, ymin - extentEnlarger, zmin, xmax + extentEnlarger, ymax + extentEnlarger, zmax); } @@ -272,10 +426,16 @@ public static string SaveSnapshot(AxMap axMap1, string baseName, IExtents boundB if (img == null) throw new NullReferenceException("Snapshot is null: " + axMap1.get_ErrorMsg(axMap1.LastErrorCode)); - var retVal = img.Save(filename); - img.Close(); - img = null; - if (!retVal) throw new Exception("Snapshot could not be saved: " + img.ErrorMsg[img.LastErrorCode]); + try + { + var retVal = img.Save(filename); + if (!retVal) throw new Exception("Snapshot could not be saved: " + img.ErrorMsg[img.LastErrorCode]); + } + finally + { + img.Close(); + } + if (!File.Exists(filename)) throw new FileNotFoundException("The file doesn't exists.", filename); DebugMsg(filename); @@ -319,5 +479,47 @@ public static Dictionary GetColorsFromBitmap(string fileLocation) } return retVal; } + + public static void AddPointToPointSf(Shapefile sfPoints, Point point) + { + // Save first point to result shapefile: + var shp = new Shape(); + if (!shp.Create(ShpfileType.SHP_POINT)) + throw new Exception("Error in creating shape. Error: " + shp.ErrorMsg[shp.LastErrorCode]); + var index = 0; + if (!shp.InsertPoint(point, ref index)) + throw new Exception("Error in inserting point. Error: " + shp.ErrorMsg[shp.LastErrorCode]); + if (sfPoints.EditAddShape(shp) < 0) + throw new Exception("Error in adding shape. Error: " + sfPoints.ErrorMsg[sfPoints.LastErrorCode]); + } + + public static Coordinate PointToCoordinate(Point point) + { + return new Coordinate(point.x, point.y); + } + + public struct Coordinate + { + public double X { get; set; } + public double Y { get; set; } + + public Coordinate(double x, double y) + { + X = x; + Y = y; + } + + public override string ToString() + { + return $"X: {X}, Y: {Y}"; + } + } + + public static Point CoordinateToPoint(Coordinate coordinate) + { + var pnt = new Point(); + pnt.Set(coordinate.X, coordinate.Y); + return pnt; + } } } diff --git a/unittests/MapWinGISTests/MapWinGISTests.csproj b/unittests/MapWinGISTests/MapWinGISTests.csproj index b6018862..fd32cd6b 100644 --- a/unittests/MapWinGISTests/MapWinGISTests.csproj +++ b/unittests/MapWinGISTests/MapWinGISTests.csproj @@ -108,6 +108,7 @@ + @@ -120,6 +121,7 @@ + @@ -134,10 +136,42 @@ - PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -186,6 +220,30 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -225,6 +283,18 @@ Always + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -237,6 +307,30 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -273,6 +367,30 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -375,6 +493,18 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest diff --git a/unittests/MapWinGISTests/NewSfMethodsTests.cs b/unittests/MapWinGISTests/NewSfMethodsTests.cs new file mode 100644 index 00000000..60d75af0 --- /dev/null +++ b/unittests/MapWinGISTests/NewSfMethodsTests.cs @@ -0,0 +1,412 @@ +using System; +using System.Diagnostics; +using System.IO; +using MapWinGIS; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace MapWinGISTests +{ + [TestClass] + [DeploymentItem("Testdata")] + public class NewSfMethodsTests + { + [TestInitialize] + public void Start() + { + var utils = new Utils(); + var version = utils.GDALInfo("", "--version"); + Debug.WriteLine(version); + } + + [TestMethod] + public void CreateAnglePolylines() + { + var tempFolder = Path.GetTempPath(); + var sfLineInput = Helper.OpenShapefile(Path.Combine(@"sf", "Tracks.shp")); + var utils = new Utils(); + + // Create new shapefile with polylines: + var sfNew = Helper.CreateSf(ShpfileType.SHP_POLYLINE); + sfNew.GeoProjection = sfLineInput.GeoProjection.Clone(); + + var numShapesInput = sfLineInput.NumShapes; + for (var i = 0; i < numShapesInput; i++) + { + var shape = sfLineInput.Shape[i]; + var numPoints = shape.numPoints; + const int distance = 20; + + // Skip the first point: + for (var j = 0; j < numPoints; j++) + { + var firstPoint = shape.Point[j]; + var secondPoint = shape.Point[j + 1]; + if (j == numPoints - 1) secondPoint = shape.Point[j - 1]; + + var angle = GetAngle(utils, firstPoint, secondPoint); + + var newLineShp = new Shape(); + newLineShp.Create(ShpfileType.SHP_POLYLINE); + newLineShp.AddPoint(firstPoint.x, firstPoint.y); + newLineShp.AddPoint(Math.Cos(angle) * distance + firstPoint.x, + Math.Sin(angle) * distance + firstPoint.y); + sfNew.EditAddShape(newLineShp); + // Add line to other side: + newLineShp = new Shape(); + newLineShp.Create(ShpfileType.SHP_POLYLINE); + newLineShp.AddPoint(firstPoint.x, firstPoint.y); + newLineShp.AddPoint(Math.Cos(angle) * -1 * distance + firstPoint.x, + Math.Sin(angle) * -1 * distance + firstPoint.y); + sfNew.EditAddShape(newLineShp); + } + } + + // Save new shapefile: + Helper.SaveAsShapefile(sfNew, Path.Combine(tempFolder, "AngledLines.shp")); + sfNew.Close(); + + var retVal = sfLineInput.Close(); + Assert.IsTrue(retVal, "Cannot close line shapefile: " + sfLineInput.ErrorMsg[sfLineInput.LastErrorCode]); + } + + + [TestMethod] + public void InterpolateTest() + { + var sfTracks = Helper.OpenShapefile(Path.Combine(@"sf", "Tracks.shp")); + var sfPoints = Helper.CreateSf(ShpfileType.SHP_POINT); + // Set projection: + sfPoints.GeoProjection = sfTracks.GeoProjection.Clone(); + + // Get the first shape: + var shp = sfTracks.Shape[0]; + var trackLength = shp.Length; + // Get the first point: + var firstPoint = shp.Point[0]; + // Save first point to result shapefile: + Helper.AddPointToPointSf(sfPoints, firstPoint); + var segmentLength = 0d; + const int distance = 10; + while (trackLength > segmentLength) + { + var lastShape = sfPoints.Shape[sfPoints.NumShapes - 1]; + var nextPoint = shp.InterpolatePoint(lastShape.Point[0], distance); + Helper.AddPointToPointSf(sfPoints, nextPoint); + segmentLength += distance; + } + + sfTracks.Close(); + Helper.SaveAsShapefile(sfPoints, Path.Combine(Path.GetTempPath(), "InterpolatedPoints.shp")); + } + + [TestMethod] + public void GetIntersectionTest() + { + var result = GetIntersection(new Helper.Coordinate(1, 1), new Helper.Coordinate(5, 7), + new Helper.Coordinate(1, 6), new Helper.Coordinate(2, 5)); + Debug.WriteLine(result); + Assert.AreEqual(3, result.X); + Assert.AreEqual(4, result.Y); + } + + + [TestMethod] + public void DifferenceTest1() + { + // 145, 154 + GetDifference("POLYGON ((259449.071118579 543868.469050065,259460.460699968 543878.23006619,259463.245769758 543873.768560297,259455.578068317 543860.876455946,259449.071118579 543868.469050065))", + "POLYGON ((259463.243735243 543856.317227775,259458.0148573 543864.973555723,259462.906856719 543873.198728218,259471.741696109 543861.450443661,259463.243735243 543856.317227775))"); + } + + [TestMethod] + public void DifferenceTest2() + { + // 24, 32 + GetDifference("POLYGON ((259073.014081524 544320.092508488,259085.267633063 544321.677731439,259085.942168897 544320.308101652,259074.192475622 544310.983687036,259073.014081524 544320.092508488))", + "POLYGON ((259087.677538158 544333.393525226,259097.260135269 544321.853428493,259090.546766392 544316.354731347,259079.984140336 544327.005127152,259087.677538158 544333.393525226))"); + } + + private static void GetDifference(string wktShp1, string wktShp2) + { + var sf = Helper.CreateSf(ShpfileType.SHP_POLYGON); + var shp = new Shape(); + shp.ImportFromWKT(wktShp1); + Assert.IsTrue(shp.IsValid); + var shpIndex = sf.EditAddShape(shp); + Assert.IsTrue(shpIndex >= 0); + + var shp2 = new Shape(); + shp2.ImportFromWKT(wktShp2); + Assert.IsTrue(shp2.IsValid); + shpIndex = sf.EditAddShape(shp2); + Assert.IsTrue(shpIndex >= 0); + + if (shp.Overlaps(shp2)) + { + Debug.WriteLine("Shapes overlap and touches: " + shp.Touches(shp2)); + + var shp3 = shp2.Clip(shp, tkClipOperation.clDifference); + Debug.WriteLine(shp3.ExportToWKT()); + Assert.IsTrue(shp3.IsValid); + Assert.IsTrue(shp3.PartIsClockWise[0]); + Assert.IsFalse(shp.Overlaps(shp3), "New shape still overlaps"); + // sf.EditDeleteShape(1); // Remove shp2 + shpIndex = sf.EditAddShape(shp3); + Assert.IsTrue(shpIndex >= 0); + Debug.WriteLine($"Clipped shape overlaps: {shp.Overlaps(shp3)} and touches: {shp.Touches(shp3)}"); + Debug.WriteLine($"shp2.Perimeter: {shp2.Perimeter} shp3.Perimeter: {shp3.Perimeter}"); + Debug.WriteLine($"shp2.Area: {shp2.Area} shp3.Area: {shp3.Area}"); + Assert.IsFalse(Math.Abs(shp2.Area - shp3.Area) < double.Epsilon, "Area are still the same"); + } + + Helper.SaveAsShapefile(sf, Path.Combine(Path.GetTempPath(), "TestClippingMethods.shp")); + } + + [TestMethod] + public void CreateSprayAreas() + { + var sfTracks = Helper.OpenShapefile(Path.Combine(@"sf", "Tracks.shp")); + var sfPoints = Helper.CreateSf(ShpfileType.SHP_POINT); + var sfLines = Helper.CreateSf(ShpfileType.SHP_POLYLINE); + var sfPolygons = Helper.CreateSf(ShpfileType.SHP_POLYGON); + + // Set projection: + sfPoints.GeoProjection = sfTracks.GeoProjection.Clone(); + sfLines.GeoProjection = sfTracks.GeoProjection.Clone(); + sfPolygons.GeoProjection = sfTracks.GeoProjection.Clone(); + + var tempPath = Path.GetTempPath(); + var utils = new Utils(); + + // Debug first track point: + var debugPoint = sfTracks.Shape[0].Point[0]; + Debug.WriteLine(debugPoint.x); + Debug.WriteLine(debugPoint.y); + + // Get the first shape: + var shp = sfTracks.Shape[0]; + var trackLength = shp.Length; + // Get the first point: + var firstPoint = shp.Point[0]; + // Save first point to result shapefile: + Helper.AddPointToPointSf(sfPoints, firstPoint); + var segmentLength = 0d; // Total lenght between the points + const int distance = 10; // Distance between points + const int length = 15; // Length of perpendicular lines + var skip = 1; + while (trackLength > segmentLength) + { + // Create next point on line: + var lastShape = sfPoints.Shape[sfPoints.NumShapes - 1]; + var previousPoint = lastShape.Point[0]; + var nextPoint = shp.InterpolatePoint(previousPoint, distance * skip); + + Helper.AddPointToPointSf(sfPoints, nextPoint); + skip = 1; // reset + + // Create perpendicular line + var angle = GetAngle(utils, previousPoint, nextPoint); + var previousPointCoordinate = new Helper.Coordinate(previousPoint.x, previousPoint.y); + var perpendicularCoordinate1 = new Helper.Coordinate(Math.Cos(angle) * length + previousPoint.x, + Math.Sin(angle) * length + previousPoint.y); + AddCoordinatesToLineSf(sfLines, previousPointCoordinate, perpendicularCoordinate1); + // Add line to other side: + var perpendicularCoordinate2 = new Helper.Coordinate(Math.Cos(angle) * -1 * length + previousPoint.x, + Math.Sin(angle) * -1 * length + previousPoint.y); + AddCoordinatesToLineSf(sfLines, previousPointCoordinate, perpendicularCoordinate2); + + // Create polygon from two perpendicular lines: + var numShapes = sfLines.NumShapes; + if (numShapes >= 4) + { + Debug.WriteLine("numShapes: " + numShapes); + // Get first perpendicular line: + var shpLine1 = sfLines.Shape[numShapes - 4]; + var point1_1 = shpLine1.Point[0]; + var point1_2 = shpLine1.Point[1]; + var shpLine3 = sfLines.Shape[numShapes - 2]; + var point3_1 = shpLine3.Point[0]; + var point3_2 = shpLine3.Point[1]; + + AddToPolygonSf(sfPolygons, + new Helper.Coordinate(point1_1.x, point1_1.y), + new Helper.Coordinate(point1_2.x, point1_2.y), + new Helper.Coordinate(point3_2.x, point3_2.y), + new Helper.Coordinate(point3_1.x, point3_1.y)); + + // Other side: + var shpLine2 = sfLines.Shape[numShapes - 3]; + var point2_1 = shpLine2.Point[0]; + var point2_2 = shpLine2.Point[1]; + var shpLine4 = sfLines.Shape[numShapes - 1]; + var point4_1 = shpLine4.Point[0]; + var point4_2 = shpLine4.Point[1]; + AddToPolygonSf(sfPolygons, + new Helper.Coordinate(point2_1.x, point2_1.y), + new Helper.Coordinate(point2_2.x, point2_2.y), + new Helper.Coordinate(point4_2.x, point4_2.y), + new Helper.Coordinate(point4_1.x, point4_1.y)); + } + + segmentLength += distance; + + // Debug: + if (sfPolygons.NumShapes >= 33) break; + } + + // TODO: Check last point + + sfTracks.Close(); + Helper.SaveAsShapefile(sfPoints, Path.Combine(tempPath, "InterpolatedPoints.shp")); + Helper.SaveAsShapefile(sfLines, Path.Combine(tempPath, "PerpendicularLines.shp")); + Helper.SaveAsShapefile(sfPolygons, Path.Combine(tempPath, "SprayAreas.shp")); + } + + private static Extents PointToExtent(IPoint nextPoint, double enlargeValue) + { + var extent = new Extents(); + extent.SetBounds(nextPoint.x - enlargeValue, nextPoint.y - enlargeValue, 0, + nextPoint.x + enlargeValue, nextPoint.y + enlargeValue, 0); + return extent; + } + + private static double GetAngle(IUtils utils, Point firstPoint, Point secondPoint) + { + var angle = utils.GetAngle(firstPoint, secondPoint); + // convert to proper Cartesian angle of rotation + angle = (450 - angle) % 360; + // convert to Radians + angle = angle * Math.PI / 180.0; + + // Get perpendicular angle: + angle += 90 * Math.PI / 180.0; + if (angle > 2 * Math.PI) angle -= 2 * Math.PI; + + return angle; + } + + public static void AddCoordinatesToLineSf(Shapefile sfLines, Helper.Coordinate coordinate1, + Helper.Coordinate coordinate2) + { + var shp = new Shape(); + if (!shp.Create(ShpfileType.SHP_POLYLINE)) + throw new Exception("Error in creating shape. Error: " + shp.ErrorMsg[shp.LastErrorCode]); + if (shp.AddPoint(coordinate1.X, coordinate1.Y) < 0) + throw new Exception("Error in adding point. Error: " + shp.ErrorMsg[shp.LastErrorCode]); + if (shp.AddPoint(coordinate2.X, coordinate2.Y) < 0) + throw new Exception("Error in adding point. Error: " + shp.ErrorMsg[shp.LastErrorCode]); + + // Check if this line intersects other lines, if so shorten to intersection point: + var numShapes = sfLines.NumShapes; + for (var i = 0; i < numShapes; i++) + { + var shpTesting = sfLines.Shape[i]; + if (!shpTesting.Crosses(shp)) continue; + + var newCoordinate = GetIntersection(Helper.PointToCoordinate(shp.Point[0]), + Helper.PointToCoordinate(shp.Point[1]), Helper.PointToCoordinate(shpTesting.Point[0]), + Helper.PointToCoordinate(shpTesting.Point[1])); + // Replace point: + shp.Point[1] = Helper.CoordinateToPoint(newCoordinate); + } + + if (sfLines.EditAddShape(shp) < 0) + throw new Exception("Error in adding shape. Error: " + sfLines.ErrorMsg[sfLines.LastErrorCode]); + } + + private static void AddToPolygonSf(IShapefile sfPolygons, Helper.Coordinate coordinate1_1, + Helper.Coordinate coordinate1_2, Helper.Coordinate coordinate2_1, Helper.Coordinate coordinate2_2) + { + var shp = new Shape(); + if (!shp.Create(ShpfileType.SHP_POLYGON)) + throw new Exception("Error in creating shape. Error: " + shp.ErrorMsg[shp.LastErrorCode]); + if (shp.AddPoint(coordinate1_1.X, coordinate1_1.Y) < 0) + throw new Exception("Error in adding point. Error: " + shp.ErrorMsg[shp.LastErrorCode]); + if (shp.AddPoint(coordinate1_2.X, coordinate1_2.Y) < 0) + throw new Exception("Error in adding point. Error: " + shp.ErrorMsg[shp.LastErrorCode]); + if (shp.AddPoint(coordinate2_1.X, coordinate2_1.Y) < 0) + throw new Exception("Error in adding point. Error: " + shp.ErrorMsg[shp.LastErrorCode]); + if (shp.AddPoint(coordinate2_2.X, coordinate2_2.Y) < 0) + throw new Exception("Error in adding point. Error: " + shp.ErrorMsg[shp.LastErrorCode]); + // Closing: + if (shp.AddPoint(coordinate1_1.X, coordinate1_1.Y) < 0) + throw new Exception("Error in adding point. Error: " + shp.ErrorMsg[shp.LastErrorCode]); + if (!shp.PartIsClockWise[0]) shp.ReversePointsOrder(0); + if (!shp.IsValid) + { + shp = shp.FixUp2(tkUnitsOfMeasure.umMeters); + if (shp == null) return; + if (!shp.IsValid) + throw new Exception("Error: shape is not valid. " + shp.IsValidReason); + } + + // Check if this new shape is overlapping other shapes, + // if so clip and add that version instead: + var numShapes = sfPolygons.NumShapes; + for (var i = 0; i < numShapes; i++) + { + var shpTesting = sfPolygons.Shape[i]; + // If within, don't add again: + if (shp.Within(shpTesting)) + { + Debug.WriteLine("Shape is within " + i); + return; + } + // If overlaps, add only new part: + if (shp.Overlaps(shpTesting)) + { + Debug.WriteLine(i + " overlaps. Touches: " + shp.Touches(shpTesting)); + // TODO: Returns wrong part: + //shp = shpTesting.Clip(shp, tkClipOperation.clDifference); + //if (shp == null) return; + } + } + + if (shp.ShapeType2D != ShpfileType.SHP_POLYGON) return; + + if (!shp.PartIsClockWise[0]) shp.ReversePointsOrder(0); + if (!shp.IsValid) + { + shp = shp.FixUp2(tkUnitsOfMeasure.umMeters); + if (shp == null) return; + if (!shp.IsValid) + throw new Exception("Error: shape is not valid. " + shp.IsValidReason); + } + + + if (shp.ShapeType2D != ShpfileType.SHP_POLYGON) return; + if (sfPolygons.EditAddShape(shp) < 0) + throw new Exception("Error in adding shape. Error: " + sfPolygons.ErrorMsg[sfPolygons.LastErrorCode]); + + } + + private static Helper.Coordinate GetIntersection( + Helper.Coordinate p1, + Helper.Coordinate p2, + Helper.Coordinate p3, + Helper.Coordinate p4) + { + // http://csharphelper.com/blog/2014/08/determine-where-two-lines-intersect-in-c/ + + // Get the segments' parameters. + var dx12 = p2.X - p1.X; + var dy12 = p2.Y - p1.Y; + var dx34 = p4.X - p3.X; + var dy34 = p4.Y - p3.Y; + + // Solve for t1 and t2 + var denominator = dy12 * dx34 - dx12 * dy34; + + var t1 = ((p1.X - p3.X) * dy34 + (p3.Y - p1.Y) * dx34) / denominator; + + if (double.IsInfinity(t1)) + { + // The lines are parallel (or close enough to it). + throw new ArgumentException("Lines are parallel"); + } + + // Find the point of intersection. + return new Helper.Coordinate(p1.X + dx12 * t1, p1.Y + dy12 * t1); + } + } +} diff --git a/unittests/MapWinGISTests/PointInPolygonTests.cs b/unittests/MapWinGISTests/PointInPolygonTests.cs new file mode 100644 index 00000000..44b078b3 --- /dev/null +++ b/unittests/MapWinGISTests/PointInPolygonTests.cs @@ -0,0 +1,370 @@ +using System.Diagnostics; +using System.IO; +using MapWinGIS; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace MapWinGISTests +{ + [TestClass] + [DeploymentItem("Testdata")] + public class PointInPolygonTests + { + private static readonly GlobalSettings _settings = new GlobalSettings(); + + [TestMethod] + public void TimePointLoop() + { + bool retVal; + // Open shapefile: + var fileName = Path.Combine(@"sf", "Reach2_simplify100_smooth.shp"); + var sfLine = Helper.OpenShapefile(fileName); + + var numShapes = sfLine.NumShapes; + Debug.WriteLine("numShapes: " + numShapes); + + var stopwatch = new Stopwatch(); + stopwatch.Start(); + for (var i = 0; i < numShapes; i++) + { + var shape = sfLine.Shape[i]; + var numPoints = shape.numPoints; + Debug.WriteLine(""); + Debug.WriteLine("***************"); + Debug.WriteLine($"Shape {i} has {numPoints} points"); + for (var j = 0; j < numPoints; j++) + { + var x = 0d; + var y = 0d; + retVal = shape.XY[j, ref x, ref y]; + Assert.IsTrue(retVal, "Cannot get XY: " + shape.ErrorMsg[shape.LastErrorCode]); + Debug.WriteLine($"X: {x} Y: {y}"); + } + } + + retVal = sfLine.Close(); + Assert.IsTrue(retVal, "Cannot close shapefile: " + sfLine.ErrorMsg[sfLine.LastErrorCode]); + stopwatch.Stop(); + Debug.WriteLine("Looping shapes " + stopwatch.Elapsed); + } + + [TestMethod] + public void TimePointInShapefile() + { + // Open shapefiles: + var sfLine = Helper.OpenShapefile(Path.Combine(@"sf", "Reach2_simplify100_smooth.shp")); + var sfPolygon = Helper.OpenShapefile(Path.Combine(@"sf", "embankments_buffered_split.shp")); + + var stopwatch = new Stopwatch(); + stopwatch.Start(); + var retVal = sfPolygon.BeginPointInShapefile(); + Assert.IsTrue(retVal, "Error in BeginPointInShapefile: " + sfPolygon.ErrorMsg[sfPolygon.LastErrorCode]); + stopwatch.Stop(); + Debug.WriteLine("BeginPointInShapefile took " + stopwatch.Elapsed); + + var numShapes = sfLine.NumShapes; + Debug.WriteLine("numShapes: " + numShapes); + stopwatch.Restart(); + + // Get long line: + var shape = sfLine.Shape[16]; + var numPoints = shape.numPoints; + Debug.WriteLine($"Shape 16 has {numPoints} points"); + for (var j = 0; j < numPoints; j++) + { + var x = 0d; + var y = 0d; + retVal = shape.XY[j, ref x, ref y]; + Assert.IsTrue(retVal, "Cannot get XY: " + shape.ErrorMsg[shape.LastErrorCode]); + var selectedShape = sfPolygon.PointInShapefile(x, y); + Debug.WriteLine($"point (X: {x} Y: {y}) is in shape {selectedShape}"); + } + + stopwatch.Stop(); + Debug.WriteLine("PointInShapefile took " + stopwatch.Elapsed); + + sfPolygon.EndPointInShapefile(); + retVal = sfPolygon.Close(); + Assert.IsTrue(retVal, "Cannot close polygon shapefile: " + sfPolygon.ErrorMsg[sfPolygon.LastErrorCode]); + + retVal = sfLine.Close(); + Assert.IsTrue(retVal, "Cannot close line shapefile: " + sfLine.ErrorMsg[sfLine.LastErrorCode]); + } + + [TestMethod] + public void TestPointInSimplePolygon() + { + RunPointInPolygonTests(Path.Combine(@"sf", "SHP_POLYGON.shp"), + Helper.MakePoint(6.37464034466582, 52.4102411020024), + Helper.MakePoint(6.37448647420611, 52.410194349055)); + } + + [TestMethod] + public void TestPointInPolygonZ() + { + RunPointInPolygonTests(Path.Combine(@"sf", "SHP_POLYGONZ.shp"), + Helper.MakePoint(-102.519749544454, 46.8429408895592), + Helper.MakePoint(-102.51917417091, 46.8431631610016)); + } + + [TestMethod] + public void TestPointInPolygonM() + { + RunPointInPolygonTests(Path.Combine(@"sf", "SHP_POLYGONM.shp"), + Helper.MakePoint(-51.9355346789247, -23.4136868836559), + Helper.MakePoint(-51.9147302015054, -23.4096941051613)); + } + + [TestMethod] + public void TestPointInLotsOfPolygons() + { + RunPointInPolygonTests(Path.Combine(@"sf", "LotsOfPolygons.shp"), + Helper.MakePoint(321273.225450182, 5857301.96614617), + Helper.MakePoint(321451.30034346, 5857310.8255936)); + } + + [TestMethod] + public void TestPointInPolygonWithHole() + { + RunPointInPolygonTests(Path.Combine(@"sf", "polygonWithHole.shp"), + Helper.MakePoint(265273.647149635, 500006.809897344), + Helper.MakePoint(265559.247149635, 500252.089897344)); + } + + [TestMethod] + public void TestPointInMultiPolygon() + { + RunPointInPolygonTests(Path.Combine(@"sf", "MultiPolygon.shp"), + Helper.MakePoint(268484.127149635, 499948.009897344), + Helper.MakePoint(267684.447149635, 500418.409897344)); + } + + + private static void RunPointInPolygonTests(string sfPolygonFilename, Point pointInside, Point pointOutside) + { + // Shapefile.PointInShape, Shapefile.PointInShapefile, Utils.PointInPolygon, and Shapefile.SelectByShapefile (using srContains or srWithin). + var sfPolygon = Helper.OpenShapefile(sfPolygonFilename); + + var stopwatch = new Stopwatch(); + stopwatch.Start(); + var numErrors = UsePointInShapefile(sfPolygon, pointInside, pointOutside); + stopwatch.Stop(); + Debug.WriteLine("UsePointInShapefile took " + stopwatch.Elapsed); + + stopwatch.Restart(); + numErrors += UsePointInShape(sfPolygon, pointInside, pointOutside); + stopwatch.Stop(); + Debug.WriteLine("UsePointInShape took " + stopwatch.Elapsed); + + stopwatch.Restart(); + numErrors += UsePointInPolygon(sfPolygon, pointInside, pointOutside); + stopwatch.Stop(); + Debug.WriteLine("UsePointInPolygon took " + stopwatch.Elapsed); + + stopwatch.Restart(); + numErrors += UseGetRelatedShapes2(sfPolygon, pointInside, pointOutside); + stopwatch.Stop(); + Debug.WriteLine("UseGetRelatedShapes2 took " + stopwatch.Elapsed); + + stopwatch.Restart(); + numErrors += UseSelectByShapefile(sfPolygon, pointInside, pointOutside); + stopwatch.Stop(); + Debug.WriteLine("UseSelectByShapefile took " + stopwatch.Elapsed); + + if (!sfPolygon.Close()) + { + Debug.WriteLine("Error! Cannot close polygon shapefile: " + sfPolygon.ErrorMsg[sfPolygon.LastErrorCode]); + } + + Assert.IsTrue(numErrors == 0, "Errors were found"); + } + + private static int UseSelectByShapefile(IShapefile sfPolygon, IPoint pointInside, IPoint pointOutside) + { + var numErrors = 0; + object resultArray = null; + var sfInside = new Shapefile(); + sfInside.CreateNewWithShapeID("", ShpfileType.SHP_POINT); + var shpInside = new Shape(); + shpInside.Create(ShpfileType.SHP_POINT); + shpInside.AddPoint(pointInside.x, pointInside.y); + sfInside.EditAddShape(shpInside); + + if (!sfPolygon.SelectByShapefile(sfInside, tkSpatialRelation.srContains, false, ref resultArray)) + { + Debug.WriteLine("Error! No shapes found"); + numErrors++; + } + + var sfOutside = new Shapefile(); + sfOutside.CreateNewWithShapeID("", ShpfileType.SHP_POINT); + var shpOutside = new Shape(); + shpOutside.Create(ShpfileType.SHP_POINT); + shpOutside.AddPoint(pointOutside.x, pointOutside.y); + sfOutside.EditAddShape(shpOutside); + + if (sfPolygon.SelectByShapefile(sfOutside, tkSpatialRelation.srContains, false, ref resultArray)) + { + Debug.WriteLine("Error! Shapes found, this is unexpected"); + numErrors++; + } + + return numErrors; + } + + private static int UseGetRelatedShapes2(IShapefile sfPolygon, IPoint pointInside, IPoint pointOutside) + { + var numErrors = 0; + object resultArray = null; + + var shpInside = new Shape(); + shpInside.Create(ShpfileType.SHP_POINT); + shpInside.AddPoint(pointInside.x, pointInside.y); + if (!sfPolygon.GetRelatedShapes2(shpInside, tkSpatialRelation.srIntersects, ref resultArray)) + { + Debug.WriteLine("Error! No shapes found"); + numErrors++; + } + + var shpOutside = new Shape(); + shpOutside.Create(ShpfileType.SHP_POINT); + shpOutside.AddPoint(pointOutside.x, pointOutside.y); + if (sfPolygon.GetRelatedShapes2(shpOutside, tkSpatialRelation.srIntersects, ref resultArray)) + { + Debug.WriteLine("Error! Shapes found, this is unexpected"); + numErrors++; + } + + return numErrors; + } + + private static int UsePointInPolygon(IShapefile sfPolygon, Point pointInside, Point pointOutside) + { + Debug.WriteLine("Testing UsePointInPolygon"); + var numErrors = 0; + var numShapes = sfPolygon.NumShapes; + var foundInside = false; + var utils = new Utils(); + var stopwatch = new Stopwatch(); + stopwatch.Start(); + + for (var i = 0; i < numShapes; i++) + { + var shp = sfPolygon.Shape[i]; + if (!utils.PointInPolygon(shp, pointInside)) continue; + + foundInside = true; + Debug.WriteLine("Inside point is in shape " + i); + break; + } + stopwatch.Stop(); + Debug.WriteLine("Searching for inside point took " + stopwatch.Elapsed); + + if (!foundInside) + { + Debug.WriteLine("Error! Inside point is not in any shape"); + numErrors++; + } + + stopwatch.Restart(); + for (var i = 0; i < numShapes; i++) + { + var shp = sfPolygon.Shape[i]; + if (!utils.PointInPolygon(shp, pointOutside)) continue; + + Debug.WriteLine("Error! Outside point is in shape " + i); + numErrors++; + break; + } + stopwatch.Stop(); + Debug.WriteLine("Searching for outside point took " + stopwatch.Elapsed); + + return numErrors; + } + + private static int UsePointInShapefile(IShapefile sfPolygon, IPoint pointInside, IPoint pointOutside) + { + Debug.WriteLine("Testing PointInShapefile"); + var numErrors = 0; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + if (!sfPolygon.BeginPointInShapefile()) + { + Debug.WriteLine("Error in BeginPointInShapefile: " + sfPolygon.ErrorMsg[sfPolygon.LastErrorCode]); + numErrors++; + } + + stopwatch.Stop(); + Debug.WriteLine("BeginPointInShapefile took " + stopwatch.Elapsed); + + stopwatch.Restart(); + var selectedShapeInside = sfPolygon.PointInShapefile(pointInside.x, pointInside.y); + stopwatch.Stop(); + Debug.WriteLine("PointInShapefile inside point took " + stopwatch.Elapsed); + if (selectedShapeInside == -1) + { + Debug.WriteLine("Error! No shape found with inside point"); + numErrors++; + } + else + { + Debug.WriteLine("Point is in shape " + selectedShapeInside); + } + + stopwatch.Restart(); + var selectedShapeOutside = sfPolygon.PointInShapefile(pointOutside.x, pointOutside.y); + stopwatch.Stop(); + Debug.WriteLine("PointInShapefile outside point took " + stopwatch.Elapsed); + if (selectedShapeOutside != -1) + { + Debug.WriteLine("Error! Shape found with outside point, this is unexpexted"); + numErrors++; + } + + sfPolygon.EndPointInShapefile(); + + return numErrors; + } + + private static int UsePointInShape(IShapefile sfPolygon, IPoint pointInside, IPoint pointOutside) + { + Debug.WriteLine("Testing PointInShape"); + var numErrors = 0; + var numShapes = sfPolygon.NumShapes; + var foundInside = false; + var stopwatch = new Stopwatch(); + stopwatch.Start(); + + for (var i = 0; i < numShapes; i++) + { + if (!sfPolygon.PointInShape(i, pointInside.x, pointInside.y)) continue; + + foundInside = true; + Debug.WriteLine("Inside point is in shape " + i); + break; + } + stopwatch.Stop(); + Debug.WriteLine("Searching for inside point took " + stopwatch.Elapsed); + + if (!foundInside) + { + Debug.WriteLine("Error! Inside point is not in any shape"); + numErrors++; + } + + stopwatch.Restart(); + for (var i = 0; i < numShapes; i++) + { + if (!sfPolygon.PointInShape(i, pointOutside.x, pointOutside.y)) continue; + + Debug.WriteLine("Error! Outside point is in shape " + i); + numErrors++; + break; + } + stopwatch.Stop(); + Debug.WriteLine("Searching for outside point took " + stopwatch.Elapsed); + + return numErrors; + } + + } +} \ No newline at end of file diff --git a/unittests/MapWinGISTests/ShapefileTests.cs b/unittests/MapWinGISTests/ShapefileTests.cs index 8516d8fb..7576f9d3 100644 --- a/unittests/MapWinGISTests/ShapefileTests.cs +++ b/unittests/MapWinGISTests/ShapefileTests.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Globalization; using System.IO; using System.Threading; using AxMapWinGIS; @@ -140,9 +141,7 @@ public void CreateShapefileTest() [TestMethod] public void ShapefileDataTest() { - var tempFolder = Path.GetTempPath(); - var tempFilename = Path.Combine(tempFolder, "ShapefileDataTest.shp"); - var esriShapefilePath = @"C:\dev\MapWinGIS\unittests\MapWinGISTests\Testdata\Issues\MWGIS-48-68\EsriShapefile.shp"; + var tempFilename = Path.Combine(Helper.WorkingFolder("ShapefileDataTest"), "ShapefileDataTest.shp"); Helper.DeleteShapefile(tempFilename); bool result; @@ -184,7 +183,7 @@ public void ShapefileDataTest() // Add data: result = sf.EditCellValue(sf.FieldIndexByName["intField"], idx, 99); Assert.IsTrue(result, "Could not edit intField"); - DateTime dt = System.DateTime.Now; + var dt = DateTime.Now; result = sf.EditCellValue(sf.FieldIndexByName["dateField"], idx, dt); Assert.IsTrue(result, "Could not edit dateField"); result = sf.EditCellValue(sf.FieldIndexByName["boolField"], idx, true); @@ -196,11 +195,11 @@ public void ShapefileDataTest() // Read back data: for (idx = 0; idx < sf.NumShapes; idx++) { - int iField = (int)sf.CellValue[sf.FieldIndexByName["intField"], idx]; + var iField = (int)sf.CellValue[sf.FieldIndexByName["intField"], idx]; Assert.AreEqual(iField, 99, "intField value of 99 was not returned"); - DateTime dField = (DateTime)sf.CellValue[sf.FieldIndexByName["dateField"], idx]; - Assert.IsTrue(DateTime.Now.DayOfYear.Equals(((DateTime)dField).DayOfYear), "dateField value of Now was not returned"); - bool bField = (bool)sf.CellValue[sf.FieldIndexByName["boolField"], idx]; + var dField = (DateTime)sf.CellValue[sf.FieldIndexByName["dateField"], idx]; + Assert.IsTrue(DateTime.Now.DayOfYear.Equals(dField.DayOfYear), "dateField value of Now was not returned"); + var bField = (bool)sf.CellValue[sf.FieldIndexByName["boolField"], idx]; Assert.AreEqual(bField, true, "boolField value of True was not returned"); } } @@ -212,25 +211,24 @@ public void ShapefileDataTest() } // although the default setting, indicate intent to interpret Y/N OGR String fields as Boolean - GlobalSettings gs = new GlobalSettings(); + var gs = new GlobalSettings(); gs.OgrInterpretYNStringAsBoolean = true; // setting to false results in exception reading boolField below // open as OGRLayer - OgrDatasource _datasource = new OgrDatasource(); - _datasource.GlobalCallback = this; + var _datasource = new OgrDatasource { GlobalCallback = this }; if (_datasource.Open(tempFilename)) // "ESRI Shapefile:" + { // read layer through OGR library - IOgrLayer ogrLayer = _datasource.GetLayer(0); + var ogrLayer = _datasource.GetLayer(0); sf = ogrLayer.GetBuffer(); - for (int idx = 0; idx < sf.NumShapes; idx++) + for (var idx = 0; idx < sf.NumShapes; idx++) { - int iField = (int)sf.CellValue[sf.FieldIndexByName["intField"], idx]; + var iField = (int)sf.CellValue[sf.FieldIndexByName["intField"], idx]; Assert.AreEqual(iField, 99, "intField value of 99 was not returned"); - DateTime dField = (DateTime)sf.CellValue[sf.FieldIndexByName["dateField"], idx]; + var dField = (DateTime)sf.CellValue[sf.FieldIndexByName["dateField"], idx]; Assert.IsTrue(DateTime.Now.DayOfYear.Equals(((DateTime)dField).DayOfYear), "dateField value of Now was not returned"); - bool bField = (bool)sf.CellValue[sf.FieldIndexByName["boolField"], idx]; + var bField = (bool)sf.CellValue[sf.FieldIndexByName["boolField"], idx]; Assert.AreEqual(bField, true, "boolField value of True was not returned"); } sf.Close(); @@ -238,26 +236,27 @@ public void ShapefileDataTest() // open and read a Shapefile created by ESRI MapObjects, including a Boolean and Date field // table has a Boolean 'Inspected' field, and a Date 'InspDate' field + const string esriShapefilePath = @"Issues\MWGIS-48-68\EsriShapefile.shp"; Assert.IsTrue(sf.Open(esriShapefilePath, this)); - for (int fld = 0; fld < sf.NumFields; fld++) + for (var fld = 0; fld < sf.NumFields; fld++) { - Console.WriteLine(string.Format("Field({0}): Name = '{1}', Fieldtype = {2}", fld, sf.Field[fld].Name, sf.Field[fld].Type)); + Console.WriteLine($"Field({fld}): Name = '{sf.Field[fld].Name}', Fieldtype = {sf.Field[fld].Type}"); } - for (int idx = 0; idx < sf.NumShapes; idx++) + + for (var idx = 0; idx < sf.NumShapes; idx++) { // read 'Inspected' value as object - object inspected = sf.CellValue[sf.FieldIndexByName["Inspected"], idx]; + var inspected = sf.CellValue[sf.FieldIndexByName["Inspected"], idx]; // verify that it's a bool Assert.IsTrue(inspected is bool); // watch for Inspected rows (there aren't many) - if ((bool)inspected == true) - { - // read 'InspDate' value as object - object dt = sf.CellValue[sf.FieldIndexByName["InspDate"], idx]; - // verify that it's a Date - Assert.IsTrue(dt is DateTime); - Console.WriteLine(string.Format("idx = {0}, Inspected = true, Inspection Date = {1}", idx, (DateTime)dt)); - } + if ((bool)inspected != true) continue; + + // read 'InspDate' value as object + var dt = sf.CellValue[sf.FieldIndexByName["InspDate"], idx]; + // verify that it's a Date + Assert.IsTrue(dt is DateTime); + Console.WriteLine($"idx = {idx}, Inspected = true, Inspection Date = {(DateTime)dt}"); } sf.Close(); } @@ -467,6 +466,27 @@ public void CreateFishnet() Helper.SaveAsShapefile(sf, Path.Combine(Path.GetTempPath(), "CreateFishnet.shp")); } + [TestMethod] + public void ImportExportWKT() + { + // Create shape from WKT: + var shp = new Shape(); + if (!shp.ImportFromWKT( + "POLYGON ((330918.422383554 5914432.9952417,330791.425601288 5914677.56286955,330799.804294765 5914682.67199867,330809.295198231 5914690.83057468,330851.753425698 5914726.8399904,330890.161005985 5914760.37492299,330891.883975456 5914761.87973075,330894.499450693 5914766.14773636,330895.001406323 5914766.9673645,330894.821345632 5914768.00066471,330895.626814712 5914772.6474656,330898.544123647 5914779.26299206,331042.140051675 5914906.23861184,331066.955908721 5914928.22692301,331071.290848669 5914932.66233604,331075.531881961 5914935.23930972,331086.549669788 5914904.76350951,331104.67722032 5914852.28308518,331120.597430814 5914804.83997655,331131.133792741 5914775.21848511,331118.884180716 5914770.93369604,331091.649916887 5914758.80097565,331072.712088731 5914748.36652613,331052.802159239 5914734.4060014,331043.093305417 5914725.75786093,331036.001117512 5914716.44788158,331028.749419581 5914706.61634015,331024.121040336 5914698.88986961,331020.742433359 5914692.31867847,331016.329278393 5914678.37004658,331011.623099594 5914661.25749118,331005.798813818 5914627.24754255,331002.264592162 5914601.86413354,330997.932682632 5914565.92459662,330994.902438802 5914545.28431325,330991.611136112 5914516.84204983,330989.261968268 5914496.56381567,330984.441627117 5914474.55626726,330974.218375295 5914440.74529109,330918.422383554 5914432.9952417))")) + Assert.Fail("Could not create shape from wkt: " + shp.ErrorMsg[shp.LastErrorCode]); + + // Convert back to WKT string + try + { + var wkt = shp.ExportToWKT(); + Console.WriteLine(wkt); + } + catch (Exception ex) + { + Assert.Fail("Exception exporting to wkt string: " + ex.Message); + } + } + [TestMethod] public void SaveAs() @@ -567,6 +587,27 @@ public void ReadRussianDataFromTable() Assert.AreEqual('д', value[3]); } + [TestMethod] + public void ReadRussianDataFromOgrDatasource() + { + // https://github.com/MapWindow/MapWinGIS/issues/121 + _axMap1 = Helper.GetAxMap(); + _axMap1.Clear(); + _axMap1.TileProvider = tkTileProvider.OpenStreetMap; + _axMap1.ZoomBehavior = tkZoomBehavior.zbDefault; + _axMap1.KnownExtents = tkKnownExtents.keWorld; + + var sfName = @"Issues\MWGIS-72\monitoring_object.shp"; + var baseName = Path.GetFileNameWithoutExtension(sfName); + var query = "Select * from monitoring_object"; + var layerHandle = _axMap1.AddLayerFromDatabase(sfName, query, true); + var shpfile = _axMap1.get_Shapefile(layerHandle); + shpfile.Labels.Generate("[Type]", tkLabelPositioning.lpCenter, false); + shpfile.Labels.FrameVisible = true; + shpfile.Labels.FrameType = tkLabelFrameType.lfRectangle; + Helper.SaveSnapshot(_axMap1, $"{baseName}.jpg", _axMap1.get_layerExtents(layerHandle)); + } + [TestMethod] public void CreateRussianCategories() { @@ -656,7 +697,8 @@ public void PointInShapefile() public void SpatialIndexMWGIS98() { const string sfName = @"Issues\MWGIS-98\3dPoint.shp"; - GetInfoShapefile(sfName); + var result = GetInfoShapefile(sfName); + Assert.IsTrue(result); TestSpatialIndex(sfName); } @@ -683,7 +725,6 @@ public void SpatialIndexAllTypes() } Assert.AreEqual(0, numErrors); - Console.ReadLine(); } private void TestSpatialIndex(string sfName) @@ -749,21 +790,36 @@ private void TestSpatialIndex(string sfName) [TestMethod] public void GetInfoAllShapefiles() { + var failed = 0; + var success = 0; // Call EnumerateFiles in a foreach-loop. - foreach (var filename in Directory.EnumerateFiles(@"D:\dev\MapwinGIS\GitHub\unittests\MapWinGISTests\Testdata\sf", "*.shp", SearchOption.AllDirectories)) + foreach (var filename in Directory.EnumerateFiles(@"sf", "*.shp", SearchOption.AllDirectories)) { try { Console.WriteLine("***************************"); - GetInfoShapefile(filename); + if (GetInfoShapefile(filename)) + { + success++; + } + else + { + failed++; + } Console.WriteLine(); + } catch (Exception e) { Console.WriteLine(e); // Don't stop + failed++; } } + + Console.WriteLine($"{success} files were read successfully, {failed} files failed."); + + Assert.IsTrue(failed == 0, "Not all files could be read."); } [TestMethod] @@ -809,141 +865,318 @@ public void UpdatePolygonZ() } - private bool GetInfoShapefile(string filename) + [TestMethod] + public void UnionShapefiles() { - if (!File.Exists(filename)) - throw new FileNotFoundException("Cannot find file", filename); + const string sfSoilLocation = @"Issues\MWGIS-125-Union\SoilTest.shp"; + const string sfUsageLocation = @"Issues\MWGIS-125-Union\UseTest.shp"; - Console.WriteLine("Working with " + filename); - var sf = new Shapefile { GlobalCallback = this }; - bool retVal; + Shapefile sfSoil = null; + Shapefile sfUsage = null; + Shapefile sfUnion = null; - try + var globalSettings = new GlobalSettings { - if (!sf.Open(filename)) - throw new Exception("Cannot open this file: " + sf.ErrorMsg[sf.LastErrorCode]); + CallbackVerbosity = tkCallbackVerbosity.cvAll, + ShapeInputValidationMode = tkShapeValidationMode.AbortOnErrors, + MinAreaToPerimeterRatio = 0.021 + }; - Console.WriteLine("ShapefileType: " + sf.ShapefileType); - Console.WriteLine("ShapefileType2D: " + sf.ShapefileType2D); - Console.WriteLine("Num fields: " + sf.NumFields); - Console.WriteLine("Num shapes: " + sf.NumShapes); - Console.WriteLine("HasSpatialIndex: " + sf.HasSpatialIndex); - Console.WriteLine("Projection: " + sf.Projection); - if (sf.NumShapes < 100) - { - Console.WriteLine("HasInvalidShapes: " + sf.HasInvalidShapes()); - } - else + try + { + sfSoil = Helper.OpenShapefile(sfSoilLocation, true, this); + var result = Helper.GetInfoShapefile(ref sfSoil); + Assert.IsTrue(result); + + sfUsage = Helper.OpenShapefile(sfUsageLocation, true, this); + result = Helper.GetInfoShapefile(ref sfUsage); + Assert.IsTrue(result); + + var stopWatch = new Stopwatch(); + stopWatch.Start(); + sfUnion = sfSoil.Union(false, sfUsage, false); + stopWatch.Stop(); + Helper.DebugMsg("Time it took to union 2 shapefile: " + stopWatch.Elapsed); + if (sfUnion == null) throw new Exception("Error in union: " + sfSoil.ErrorMsg[sfSoil.LastErrorCode]); + + // Save resulting shapefile: + var workingFolder = Helper.WorkingFolder("MWGIS-125-Union"); + var resultFilename = Path.Combine(workingFolder, "Unioned.shp"); + Helper.SaveAsShapefile(sfUnion, resultFilename); + + result = Helper.GetInfoShapefile(ref sfUnion); + Assert.IsTrue(result); + + Console.WriteLine("*************** Validate union *************"); + Console.WriteLine("globalSettings.MinPolygonArea: " + globalSettings.MinPolygonArea.ToString(CultureInfo.InvariantCulture)); + Console.WriteLine("globalSettings.MinAreaToPerimeterRatio: " + globalSettings.MinAreaToPerimeterRatio.ToString(CultureInfo.InvariantCulture)); + + // Check all fields: + var numFields = sfUnion.NumFields; + var numShapes = sfUnion.NumShapes; + for (var shapeIndex = 0; shapeIndex < numShapes; shapeIndex++) { - Console.WriteLine("Warning. Large number of shapes. Skipping HasInvalidShapes check."); + // Get shape: + var shp = sfUnion.Shape[shapeIndex]; + Console.WriteLine("Area: " + shp.Area.ToString(CultureInfo.InvariantCulture)); + Console.WriteLine("Perimeter: " + shp.Perimeter.ToString(CultureInfo.InvariantCulture)); + for (var fieldIndex = 0; fieldIndex < numFields; fieldIndex++) + { + var fieldName = sfUnion.Field[fieldIndex].Name; + var value = sfUnion.CellValue[fieldIndex, shapeIndex].ToString(); + Console.WriteLine($"Field name: {fieldName} Value: {value}"); + Assert.AreNotEqual(value, string.Empty, "Value is empty"); + } } + } + catch (Exception e) + { + Console.WriteLine(e); + Assert.Fail(e.Message); + } + finally + { + sfSoil?.Close(); + sfUsage?.Close(); + sfUnion?.Close(); + } + } + + [TestMethod] + public void ClipShapefiles() + { + const string sfSoilLocation = @"Issues\MWGIS-125-Union\SoilTest.shp"; + const string sfUsageLocation = @"Issues\MWGIS-125-Union\UseTest.shp"; + + Shapefile sfSoil = null; + Shapefile sfUsage = null; + Shapefile sfClip = null; + + var globalSettings = new GlobalSettings + { + CallbackVerbosity = tkCallbackVerbosity.cvAll, + ShapeInputValidationMode = tkShapeValidationMode.AbortOnErrors + }; - if (!sf.GeoProjection.IsEmpty) + try + { + sfSoil = Helper.OpenShapefile(sfSoilLocation, true, this); + var result = Helper.GetInfoShapefile(ref sfSoil); + Assert.IsTrue(result); + + sfUsage = Helper.OpenShapefile(sfUsageLocation, true, this); + result = Helper.GetInfoShapefile(ref sfUsage); + Assert.IsTrue(result); + + var stopWatch = new Stopwatch(); + stopWatch.Start(); + sfClip = sfSoil.Clip(false, sfUsage, false); + stopWatch.Stop(); + Helper.DebugMsg("Time it took to clip 2 shapefile: " + stopWatch.Elapsed); + if (sfClip == null) throw new Exception("Error in Clip: " + sfSoil.ErrorMsg[sfSoil.LastErrorCode]); + + result = Helper.GetInfoShapefile(ref sfClip); + Assert.IsTrue(result); + + Console.WriteLine("*************** Validate clip *************"); + Console.WriteLine("globalSettings.MinPolygonArea: " + globalSettings.MinPolygonArea.ToString(CultureInfo.InvariantCulture)); + Console.WriteLine("globalSettings.MinAreaToPerimeterRatio: " + globalSettings.MinAreaToPerimeterRatio.ToString(CultureInfo.InvariantCulture)); + + // Check all fields: + var numFields = sfClip.NumFields; + var numShapes = sfClip.NumShapes; + for (var shapeIndex = 0; shapeIndex < numShapes; shapeIndex++) { - int epsgCode; - sf.GeoProjection.TryAutoDetectEpsg(out epsgCode); - Console.WriteLine("epsgCode: " + epsgCode); + // Get shape: + var shp = sfClip.Shape[shapeIndex]; + Console.WriteLine("Area: " + shp.Area.ToString(CultureInfo.InvariantCulture)); + for (var fieldIndex = 0; fieldIndex < numFields; fieldIndex++) + { + var fieldName = sfClip.Field[fieldIndex].Name; + var value = sfClip.CellValue[fieldIndex, shapeIndex].ToString(); + Console.WriteLine($"Field name: {fieldName} Value: {value}"); + Assert.AreNotEqual(value, string.Empty, "Value is empty"); + } } - if (sf.NumShapes <= 0) - throw new Exception("Shapefile has no shapes"); + // Save resulting shapefile: + var workingFolder = Helper.WorkingFolder("MWGIS-125-Union"); + var resultFilename = Path.Combine(workingFolder, "Clipped.shp"); + Helper.SaveAsShapefile(sfClip, resultFilename); + } + catch (Exception e) + { + Console.WriteLine(e); + Assert.Fail(e.Message); + } + finally + { + sfSoil?.Close(); + sfUsage?.Close(); + sfClip?.Close(); + } + } + + [TestMethod] + public void DifferenceShapefiles() + { + const string sfSoilLocation = @"Issues\MWGIS-125-Union\SoilTest.shp"; + const string sfUsageLocation = @"Issues\MWGIS-125-Union\UseTest.shp"; - var shp = sf.Shape[0]; - if (shp == null) - throw new NullReferenceException("Cannot get shape: " + sf.ErrorMsg[sf.LastErrorCode]); + Shapefile sfSoil = null; + Shapefile sfUsage = null; + Shapefile sfDifference = null; - Console.WriteLine("numPoints: " + shp.numPoints); - Console.WriteLine("NumParts: " + shp.NumParts); - Console.WriteLine("ShapeType: " + shp.ShapeType); - Console.WriteLine("ShapeType2D: " + shp.ShapeType2D); - var isValid = shp.IsValid; - Console.WriteLine("IsValid: " + isValid); - if (!isValid) + var globalSettings = new GlobalSettings + { + CallbackVerbosity = tkCallbackVerbosity.cvAll, + ShapeInputValidationMode = tkShapeValidationMode.AbortOnErrors + }; + + try + { + sfSoil = Helper.OpenShapefile(sfSoilLocation, true, this); + var result = Helper.GetInfoShapefile(ref sfSoil); + Assert.IsTrue(result); + + sfUsage = Helper.OpenShapefile(sfUsageLocation, true, this); + result = Helper.GetInfoShapefile(ref sfUsage); + Assert.IsTrue(result); + + var stopWatch = new Stopwatch(); + stopWatch.Start(); + sfDifference = sfSoil.Difference(false, sfUsage, false); + stopWatch.Stop(); + Helper.DebugMsg("Time it took to difference 2 shapefile: " + stopWatch.Elapsed); + if (sfDifference == null) throw new Exception("Error in Clip: " + sfSoil.ErrorMsg[sfSoil.LastErrorCode]); + + result = Helper.GetInfoShapefile(ref sfDifference); + Assert.IsTrue(result); + + Console.WriteLine("*************** Validate difference *************"); + Console.WriteLine("globalSettings.MinPolygonArea: " + globalSettings.MinPolygonArea.ToString(CultureInfo.InvariantCulture)); + Console.WriteLine("globalSettings.MinAreaToPerimeterRatio: " + globalSettings.MinAreaToPerimeterRatio.ToString(CultureInfo.InvariantCulture)); + + // Check all fields: + var numFields = sfDifference.NumFields; + var numShapes = sfDifference.NumShapes; + for (var shapeIndex = 0; shapeIndex < numShapes; shapeIndex++) { - Console.WriteLine("IsValidReason: " + shp.IsValidReason); + // Get shape: + var shp = sfDifference.Shape[shapeIndex]; + Console.WriteLine("Area: " + shp.Area.ToString(CultureInfo.InvariantCulture)); + for (var fieldIndex = 0; fieldIndex < numFields; fieldIndex++) + { + var fieldName = sfDifference.Field[fieldIndex].Name; + var value = sfDifference.CellValue[fieldIndex, shapeIndex].ToString(); + Console.WriteLine($"Field name: {fieldName} Value: {value}"); + Assert.AreNotEqual(value, string.Empty, "Value is empty"); + } } + // Save resulting shapefile: + var workingFolder = Helper.WorkingFolder("MWGIS-125-Union"); + var resultFilename = Path.Combine(workingFolder, "Difference1.shp"); + Helper.SaveAsShapefile(sfDifference, resultFilename); + } + catch (Exception e) + { + Console.WriteLine(e); + Assert.Fail(e.Message); + } + finally + { + sfSoil?.Close(); + sfUsage?.Close(); + sfDifference?.Close(); + } + } + + [TestMethod] + public void DifferenceReverseShapefiles() + { + const string sfSoilLocation = @"Issues\MWGIS-125-Union\SoilTest.shp"; + const string sfUsageLocation = @"Issues\MWGIS-125-Union\UseTest.shp"; - if (sf.ShapefileType != shp.ShapeType) - Console.WriteLine("Warning shape(file) type mismatch"); + Shapefile sfSoil = null; + Shapefile sfUsage = null; + Shapefile sfDifference = null; - double x = 0d, y = 0d; - double z, m; - if (!shp.XY[0, ref x, ref y]) - throw new Exception("Cannot get XY from shape: " + shp.ErrorMsg[shp.LastErrorCode]); - Console.WriteLine("X: " + x); - Console.WriteLine("Y: " + y); + var globalSettings = new GlobalSettings + { + CallbackVerbosity = tkCallbackVerbosity.cvAll, + ShapeInputValidationMode = tkShapeValidationMode.AbortOnErrors + }; - switch (shp.ShapeType) + try + { + sfSoil = Helper.OpenShapefile(sfSoilLocation, true, this); + var result = Helper.GetInfoShapefile(ref sfSoil); + Assert.IsTrue(result); + + sfUsage = Helper.OpenShapefile(sfUsageLocation, true, this); + result = Helper.GetInfoShapefile(ref sfUsage); + Assert.IsTrue(result); + var stopWatch = new Stopwatch(); + stopWatch.Start(); + sfDifference = sfUsage.Difference(false, sfSoil, false); + stopWatch.Stop(); + Helper.DebugMsg("Time it took to difference 2 shapefile: " + stopWatch.Elapsed); + + if (sfDifference == null) throw new Exception("Error in Difference: " + sfSoil.ErrorMsg[sfSoil.LastErrorCode]); + + result = Helper.GetInfoShapefile(ref sfDifference); + Assert.IsTrue(result); + + Console.WriteLine("*************** Validate difference reverse *************"); + Console.WriteLine("globalSettings.MinPolygonArea: " + globalSettings.MinPolygonArea.ToString(CultureInfo.InvariantCulture)); + Console.WriteLine("globalSettings.MinAreaToPerimeterRatio: " + globalSettings.MinAreaToPerimeterRatio.ToString(CultureInfo.InvariantCulture)); + + // Check all fields: + var numFields = sfDifference.NumFields; + var numShapes = sfDifference.NumShapes; + for (var shapeIndex = 0; shapeIndex < numShapes; shapeIndex++) { - case ShpfileType.SHP_NULLSHAPE: - throw new Exception("Shape is a NULLSHAPE"); - case ShpfileType.SHP_POINT: - break; - case ShpfileType.SHP_POLYLINE: - Console.WriteLine("Length: " + shp.Length); - break; - case ShpfileType.SHP_POLYGON: - Console.WriteLine("Area: " + shp.Area); - Console.WriteLine("Perimeter: " + shp.Perimeter); - break; - case ShpfileType.SHP_MULTIPOINT: - break; - case ShpfileType.SHP_POINTZ: - if (!shp.get_Z(0, out z)) - throw new Exception("Cannot get Z from shape: " + shp.ErrorMsg[shp.LastErrorCode]); - Console.WriteLine("Z: " + z); - break; - case ShpfileType.SHP_POLYLINEZ: - if (!shp.get_Z(0, out z)) - throw new Exception("Cannot get Z from shape: " + shp.ErrorMsg[shp.LastErrorCode]); - Console.WriteLine("Z: " + z); - Console.WriteLine("Length: " + shp.Length); - break; - case ShpfileType.SHP_POLYGONZ: - if (!shp.get_Z(0, out z)) - throw new Exception("Cannot get Z from shape: " + shp.ErrorMsg[shp.LastErrorCode]); - Console.WriteLine("Z: " + z); - Console.WriteLine("Area: " + shp.Area); - Console.WriteLine("Perimeter: " + shp.Perimeter); - break; - case ShpfileType.SHP_MULTIPOINTZ: - break; - case ShpfileType.SHP_POINTM: - if (!shp.get_M(0, out m)) - throw new Exception("Cannot get M from shape: " + shp.ErrorMsg[shp.LastErrorCode]); - Console.WriteLine("M: " + m); - break; - case ShpfileType.SHP_POLYLINEM: - if (!shp.get_M(0, out m)) - throw new Exception("Cannot get M from shape: " + shp.ErrorMsg[shp.LastErrorCode]); - Console.WriteLine("M: " + m); - Console.WriteLine("Length: " + shp.Length); - break; - case ShpfileType.SHP_POLYGONM: - if (!shp.get_M(0, out m)) - throw new Exception("Cannot get M from shape: " + shp.ErrorMsg[shp.LastErrorCode]); - Console.WriteLine("M: " + m); - Console.WriteLine("Area: " + shp.Area); - Console.WriteLine("Perimeter: " + shp.Perimeter); - break; - case ShpfileType.SHP_MULTIPOINTM: - if (!shp.get_M(0, out m)) - throw new Exception("Cannot get M from shape: " + shp.ErrorMsg[shp.LastErrorCode]); - Console.WriteLine("M: " + m); - break; - case ShpfileType.SHP_MULTIPATCH: - break; - default: - throw new ArgumentOutOfRangeException(); + // Get shape: + var shp = sfDifference.Shape[shapeIndex]; + Console.WriteLine("Area: " + shp.Area.ToString(CultureInfo.InvariantCulture)); + for (var fieldIndex = 0; fieldIndex < numFields; fieldIndex++) + { + var fieldName = sfDifference.Field[fieldIndex].Name; + var value = sfDifference.CellValue[fieldIndex, shapeIndex].ToString(); + Console.WriteLine($"Field name: {fieldName} Value: {value}"); + Assert.AreNotEqual(value, string.Empty, "Value is empty"); + } } + var workingFolder = Helper.WorkingFolder("MWGIS-125-Union"); + var resultFilename = Path.Combine(workingFolder, "Difference2.shp"); + Helper.SaveAsShapefile(sfDifference, resultFilename); - retVal = true; + } + catch (Exception e) + { + Console.WriteLine(e); + Assert.Fail(e.Message); } finally { - sf.Close(); + sfSoil?.Close(); + sfUsage?.Close(); + sfDifference?.Close(); } + } + + private bool GetInfoShapefile(string filename) + { + if (!File.Exists(filename)) + throw new FileNotFoundException("Cannot find file", filename); + + Console.WriteLine("Working with " + filename); + var sf = new Shapefile { GlobalCallback = this }; + var result = sf.Open(filename, this); + Assert.IsTrue(result, "Could not open shapefile"); + var retVal = Helper.GetInfoShapefile(ref sf); + sf.Close(); return retVal; } diff --git a/unittests/MapWinGISTests/Testdata/Issues/MWGIS-125-Union/SoilTest.dbf b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-125-Union/SoilTest.dbf new file mode 100644 index 00000000..5083a7fe Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-125-Union/SoilTest.dbf differ diff --git a/unittests/MapWinGISTests/Testdata/Issues/MWGIS-125-Union/SoilTest.prj b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-125-Union/SoilTest.prj new file mode 100644 index 00000000..5c6f76df --- /dev/null +++ b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-125-Union/SoilTest.prj @@ -0,0 +1 @@ +PROJCS["WGS_1984_Web_Mercator_Auxiliary_Sphere",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Mercator_Auxiliary_Sphere"],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",0.0],PARAMETER["Standard_Parallel_1",0.0],PARAMETER["Auxiliary_Sphere_Type",0.0],UNIT["Meter",1.0]] \ No newline at end of file diff --git a/unittests/MapWinGISTests/Testdata/Issues/MWGIS-125-Union/SoilTest.shp b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-125-Union/SoilTest.shp new file mode 100644 index 00000000..06209438 Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-125-Union/SoilTest.shp differ diff --git a/unittests/MapWinGISTests/Testdata/Issues/MWGIS-125-Union/SoilTest.shx b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-125-Union/SoilTest.shx new file mode 100644 index 00000000..05b1f937 Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-125-Union/SoilTest.shx differ diff --git a/unittests/MapWinGISTests/Testdata/Issues/MWGIS-125-Union/UseTest.dbf b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-125-Union/UseTest.dbf new file mode 100644 index 00000000..03a74d7d Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-125-Union/UseTest.dbf differ diff --git a/unittests/MapWinGISTests/Testdata/Issues/MWGIS-125-Union/UseTest.prj b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-125-Union/UseTest.prj new file mode 100644 index 00000000..5c6f76df --- /dev/null +++ b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-125-Union/UseTest.prj @@ -0,0 +1 @@ +PROJCS["WGS_1984_Web_Mercator_Auxiliary_Sphere",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Mercator_Auxiliary_Sphere"],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",0.0],PARAMETER["Standard_Parallel_1",0.0],PARAMETER["Auxiliary_Sphere_Type",0.0],UNIT["Meter",1.0]] \ No newline at end of file diff --git a/unittests/MapWinGISTests/Testdata/Issues/MWGIS-125-Union/UseTest.shp b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-125-Union/UseTest.shp new file mode 100644 index 00000000..03e63eef Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-125-Union/UseTest.shp differ diff --git a/unittests/MapWinGISTests/Testdata/Issues/MWGIS-125-Union/UseTest.shx b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-125-Union/UseTest.shx new file mode 100644 index 00000000..baf2d49f Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-125-Union/UseTest.shx differ diff --git a/unittests/MapWinGISTests/Testdata/Issues/MWGIS-72/monitoring_object.cpg b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-72/monitoring_object.cpg new file mode 100644 index 00000000..3ad133c0 --- /dev/null +++ b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-72/monitoring_object.cpg @@ -0,0 +1 @@ +UTF-8 \ No newline at end of file diff --git a/unittests/MapWinGISTests/Testdata/Issues/MWGIS-72/monitoring_object.dbf b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-72/monitoring_object.dbf new file mode 100644 index 00000000..20799680 Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-72/monitoring_object.dbf differ diff --git a/unittests/MapWinGISTests/Testdata/Issues/MWGIS-72/monitoring_object.lbl b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-72/monitoring_object.lbl new file mode 100644 index 00000000..03de7bcd --- /dev/null +++ b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-72/monitoring_object.lbl @@ -0,0 +1,113 @@ + + + + diff --git a/unittests/MapWinGISTests/Testdata/Issues/MWGIS-72/monitoring_object.prj b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-72/monitoring_object.prj new file mode 100644 index 00000000..f45cbadf --- /dev/null +++ b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-72/monitoring_object.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]] \ No newline at end of file diff --git a/unittests/MapWinGISTests/Testdata/Issues/MWGIS-72/monitoring_object.sbn b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-72/monitoring_object.sbn new file mode 100644 index 00000000..267a610e Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-72/monitoring_object.sbn differ diff --git a/unittests/MapWinGISTests/Testdata/Issues/MWGIS-72/monitoring_object.sbx b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-72/monitoring_object.sbx new file mode 100644 index 00000000..ff4d949d Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-72/monitoring_object.sbx differ diff --git a/unittests/MapWinGISTests/Testdata/Issues/MWGIS-72/monitoring_object.shp b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-72/monitoring_object.shp new file mode 100644 index 00000000..a41aee79 Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-72/monitoring_object.shp differ diff --git a/unittests/MapWinGISTests/Testdata/Issues/MWGIS-72/monitoring_object.shx b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-72/monitoring_object.shx new file mode 100644 index 00000000..8fc0c174 Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/Issues/MWGIS-72/monitoring_object.shx differ diff --git a/support/lib/v120/x64/spatialindex-mw.lib b/unittests/MapWinGISTests/Testdata/sf/LotsOfPolygons.dbf similarity index 65% rename from support/lib/v120/x64/spatialindex-mw.lib rename to unittests/MapWinGISTests/Testdata/sf/LotsOfPolygons.dbf index 4561c216..7befd708 100644 Binary files a/support/lib/v120/x64/spatialindex-mw.lib and b/unittests/MapWinGISTests/Testdata/sf/LotsOfPolygons.dbf differ diff --git a/unittests/MapWinGISTests/Testdata/sf/LotsOfPolygons.prj b/unittests/MapWinGISTests/Testdata/sf/LotsOfPolygons.prj new file mode 100644 index 00000000..049d2d23 --- /dev/null +++ b/unittests/MapWinGISTests/Testdata/sf/LotsOfPolygons.prj @@ -0,0 +1 @@ +PROJCS["WGS_1984_UTM_Zone_32N",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["Meter",1]] \ No newline at end of file diff --git a/unittests/MapWinGISTests/Testdata/sf/LotsOfPolygons.shp b/unittests/MapWinGISTests/Testdata/sf/LotsOfPolygons.shp new file mode 100644 index 00000000..2fd43c4e Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/sf/LotsOfPolygons.shp differ diff --git a/unittests/MapWinGISTests/Testdata/sf/LotsOfPolygons.shx b/unittests/MapWinGISTests/Testdata/sf/LotsOfPolygons.shx new file mode 100644 index 00000000..27bc2452 Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/sf/LotsOfPolygons.shx differ diff --git a/unittests/MapWinGISTests/Testdata/sf/MultiPolygon.dbf b/unittests/MapWinGISTests/Testdata/sf/MultiPolygon.dbf new file mode 100644 index 00000000..652cea7e Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/sf/MultiPolygon.dbf differ diff --git a/unittests/MapWinGISTests/Testdata/sf/MultiPolygon.prj b/unittests/MapWinGISTests/Testdata/sf/MultiPolygon.prj new file mode 100644 index 00000000..4a3b7cad --- /dev/null +++ b/unittests/MapWinGISTests/Testdata/sf/MultiPolygon.prj @@ -0,0 +1 @@ +PROJCS["Amersfoort_RD_New",GEOGCS["GCS_Amersfoort",DATUM["D_Amersfoort",SPHEROID["Bessel_1841",6377397.155,299.1528128]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Double_Stereographic"],PARAMETER["latitude_of_origin",52.15616055555555],PARAMETER["central_meridian",5.38763888888889],PARAMETER["scale_factor",0.9999079],PARAMETER["false_easting",155000],PARAMETER["false_northing",463000],UNIT["Meter",1]] \ No newline at end of file diff --git a/unittests/MapWinGISTests/Testdata/sf/MultiPolygon.shp b/unittests/MapWinGISTests/Testdata/sf/MultiPolygon.shp new file mode 100644 index 00000000..5d908468 Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/sf/MultiPolygon.shp differ diff --git a/unittests/MapWinGISTests/Testdata/sf/MultiPolygon.shx b/unittests/MapWinGISTests/Testdata/sf/MultiPolygon.shx new file mode 100644 index 00000000..2d123af7 Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/sf/MultiPolygon.shx differ diff --git a/unittests/MapWinGISTests/Testdata/sf/Reach2_simplify100_smooth.dbf b/unittests/MapWinGISTests/Testdata/sf/Reach2_simplify100_smooth.dbf new file mode 100644 index 00000000..0ffe697f Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/sf/Reach2_simplify100_smooth.dbf differ diff --git a/unittests/MapWinGISTests/Testdata/sf/Reach2_simplify100_smooth.prj b/unittests/MapWinGISTests/Testdata/sf/Reach2_simplify100_smooth.prj new file mode 100644 index 00000000..4a3b7cad --- /dev/null +++ b/unittests/MapWinGISTests/Testdata/sf/Reach2_simplify100_smooth.prj @@ -0,0 +1 @@ +PROJCS["Amersfoort_RD_New",GEOGCS["GCS_Amersfoort",DATUM["D_Amersfoort",SPHEROID["Bessel_1841",6377397.155,299.1528128]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Double_Stereographic"],PARAMETER["latitude_of_origin",52.15616055555555],PARAMETER["central_meridian",5.38763888888889],PARAMETER["scale_factor",0.9999079],PARAMETER["false_easting",155000],PARAMETER["false_northing",463000],UNIT["Meter",1]] \ No newline at end of file diff --git a/unittests/MapWinGISTests/Testdata/sf/Reach2_simplify100_smooth.shp b/unittests/MapWinGISTests/Testdata/sf/Reach2_simplify100_smooth.shp new file mode 100644 index 00000000..d1f313be Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/sf/Reach2_simplify100_smooth.shp differ diff --git a/unittests/MapWinGISTests/Testdata/sf/Reach2_simplify100_smooth.shx b/unittests/MapWinGISTests/Testdata/sf/Reach2_simplify100_smooth.shx new file mode 100644 index 00000000..187aab00 Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/sf/Reach2_simplify100_smooth.shx differ diff --git a/unittests/MapWinGISTests/Testdata/sf/Tracks.dbf b/unittests/MapWinGISTests/Testdata/sf/Tracks.dbf new file mode 100644 index 00000000..272c4726 Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/sf/Tracks.dbf differ diff --git a/unittests/MapWinGISTests/Testdata/sf/Tracks.prj b/unittests/MapWinGISTests/Testdata/sf/Tracks.prj new file mode 100644 index 00000000..4a3b7cad --- /dev/null +++ b/unittests/MapWinGISTests/Testdata/sf/Tracks.prj @@ -0,0 +1 @@ +PROJCS["Amersfoort_RD_New",GEOGCS["GCS_Amersfoort",DATUM["D_Amersfoort",SPHEROID["Bessel_1841",6377397.155,299.1528128]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Double_Stereographic"],PARAMETER["latitude_of_origin",52.15616055555555],PARAMETER["central_meridian",5.38763888888889],PARAMETER["scale_factor",0.9999079],PARAMETER["false_easting",155000],PARAMETER["false_northing",463000],UNIT["Meter",1]] \ No newline at end of file diff --git a/unittests/MapWinGISTests/Testdata/sf/Tracks.shp b/unittests/MapWinGISTests/Testdata/sf/Tracks.shp new file mode 100644 index 00000000..b7c52e59 Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/sf/Tracks.shp differ diff --git a/unittests/MapWinGISTests/Testdata/sf/Tracks.shx b/unittests/MapWinGISTests/Testdata/sf/Tracks.shx new file mode 100644 index 00000000..5f472653 Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/sf/Tracks.shx differ diff --git a/unittests/MapWinGISTests/Testdata/sf/embankments_buffered_split.dbf b/unittests/MapWinGISTests/Testdata/sf/embankments_buffered_split.dbf new file mode 100644 index 00000000..7f2eb85a Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/sf/embankments_buffered_split.dbf differ diff --git a/unittests/MapWinGISTests/Testdata/sf/embankments_buffered_split.prj b/unittests/MapWinGISTests/Testdata/sf/embankments_buffered_split.prj new file mode 100644 index 00000000..4a3b7cad --- /dev/null +++ b/unittests/MapWinGISTests/Testdata/sf/embankments_buffered_split.prj @@ -0,0 +1 @@ +PROJCS["Amersfoort_RD_New",GEOGCS["GCS_Amersfoort",DATUM["D_Amersfoort",SPHEROID["Bessel_1841",6377397.155,299.1528128]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Double_Stereographic"],PARAMETER["latitude_of_origin",52.15616055555555],PARAMETER["central_meridian",5.38763888888889],PARAMETER["scale_factor",0.9999079],PARAMETER["false_easting",155000],PARAMETER["false_northing",463000],UNIT["Meter",1]] \ No newline at end of file diff --git a/unittests/MapWinGISTests/Testdata/sf/embankments_buffered_split.shp b/unittests/MapWinGISTests/Testdata/sf/embankments_buffered_split.shp new file mode 100644 index 00000000..1d3ca67e Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/sf/embankments_buffered_split.shp differ diff --git a/unittests/MapWinGISTests/Testdata/sf/embankments_buffered_split.shx b/unittests/MapWinGISTests/Testdata/sf/embankments_buffered_split.shx new file mode 100644 index 00000000..cb9d317d Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/sf/embankments_buffered_split.shx differ diff --git a/unittests/MapWinGISTests/Testdata/sf/polygonWithHole.dbf b/unittests/MapWinGISTests/Testdata/sf/polygonWithHole.dbf new file mode 100644 index 00000000..9fb548fb Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/sf/polygonWithHole.dbf differ diff --git a/unittests/MapWinGISTests/Testdata/sf/polygonWithHole.prj b/unittests/MapWinGISTests/Testdata/sf/polygonWithHole.prj new file mode 100644 index 00000000..4a3b7cad --- /dev/null +++ b/unittests/MapWinGISTests/Testdata/sf/polygonWithHole.prj @@ -0,0 +1 @@ +PROJCS["Amersfoort_RD_New",GEOGCS["GCS_Amersfoort",DATUM["D_Amersfoort",SPHEROID["Bessel_1841",6377397.155,299.1528128]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Double_Stereographic"],PARAMETER["latitude_of_origin",52.15616055555555],PARAMETER["central_meridian",5.38763888888889],PARAMETER["scale_factor",0.9999079],PARAMETER["false_easting",155000],PARAMETER["false_northing",463000],UNIT["Meter",1]] \ No newline at end of file diff --git a/unittests/MapWinGISTests/Testdata/sf/polygonWithHole.shp b/unittests/MapWinGISTests/Testdata/sf/polygonWithHole.shp new file mode 100644 index 00000000..81ec7fb4 Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/sf/polygonWithHole.shp differ diff --git a/unittests/MapWinGISTests/Testdata/sf/polygonWithHole.shx b/unittests/MapWinGISTests/Testdata/sf/polygonWithHole.shx new file mode 100644 index 00000000..92b0dcec Binary files /dev/null and b/unittests/MapWinGISTests/Testdata/sf/polygonWithHole.shx differ diff --git a/unittests/MapWinGISTests/TilesTests.cs b/unittests/MapWinGISTests/TilesTests.cs index 663fc902..6b606802 100644 --- a/unittests/MapWinGISTests/TilesTests.cs +++ b/unittests/MapWinGISTests/TilesTests.cs @@ -1,5 +1,7 @@ using System; +using System.Diagnostics; using System.IO; +using System.Threading; using AxMapWinGIS; using MapWinGIS; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -25,7 +27,6 @@ public TilesTests() // Tiles settings: _axMap1.Tiles.GlobalCallback = this; - _settings.StartLogTileRequests(@"D:\tmp\axmap.tiles\TileRequests.log"); } ~TilesTests() @@ -43,28 +44,86 @@ public void ShowProviderDetails() } [TestMethod] - public void PrefetchToFolder() + public void PrefetchToFolderDutchOSM() { - Console.WriteLine("Tiles projection status: " + _axMap1.Tiles.ProjectionStatus); - Console.WriteLine("_axMap1.Extents: " + _axMap1.Extents.ToDebugString()); + var latLongExtents = new Extents(); + latLongExtents.SetBounds(3.3700, 50.7500, 0, 7.2100, 53.4700, 0); // The Netherlands in WGS84: http://spatialreference.org/ref/epsg/28992/ + + Helper.DebugMsg("PrefetchToFolderDutchOSM Maxzoom: 11"); + PrefetchToFolder(tkTileProvider.OpenStreetMap, 11, latLongExtents); + } + + [TestMethod] + public void PrefetchToFolderDutchGoogleHybrid() + { + var latLongExtents = new Extents(); + latLongExtents.SetBounds(3.3700, 50.7500, 0, 7.2100, 53.4700, 0); // The Netherlands in WGS84: http://spatialreference.org/ref/epsg/28992/ + + Helper.DebugMsg("PrefetchToFolderDutchGoogleHybrid Maxzoom: 11"); + PrefetchToFolder(tkTileProvider.GoogleHybrid, 11, latLongExtents); + } + + [TestMethod] + public void PrefetchToFolderDutchBingHybrid() + { + var latLongExtents = new Extents(); + latLongExtents.SetBounds(3.3700, 50.7500, 0, 7.2100, 53.4700, 0); // The Netherlands in WGS84: http://spatialreference.org/ref/epsg/28992/ + + Helper.DebugMsg("PrefetchToFolderDutchBingHybrid Maxzoom: 11"); + PrefetchToFolder(tkTileProvider.BingHybrid, 11, latLongExtents); + } + + private void PrefetchToFolder(tkTileProvider tileProvider, int maxZoom, Extents latLongExtents) + { + // Set tiles provider: + _axMap1.Tiles.Provider = tileProvider; + _axMap1.Tiles.GlobalCallback = this; + + Helper.DebugMsg("Tiles projection status: " + _axMap1.Tiles.ProjectionStatus); + Helper.DebugMsg("_axMap1.Tiles.ProjectionIsSphericalMercator: " + _axMap1.Tiles.ProjectionIsSphericalMercator); var outputFolder = $@"D:\tmp\axmap.tiles\{_axMap1.Tiles.Provider.ToString()}"; if (!Directory.Exists(outputFolder)) Directory.CreateDirectory(outputFolder); + _settings.StartLogTileRequests($@"D:\tmp\axmap.tiles\TileRequests-{_axMap1.Tiles.Provider.ToString()}.log"); - var numTilesToCache = _axMap1.Tiles.PrefetchToFolder(_axMap1.Extents, 5, - Convert.ToInt32(tkTileProvider.OpenStreetMap), outputFolder, ".png", this); - Console.WriteLine("numTilesToCache: " + numTilesToCache); - } + var providerId = Convert.ToInt32(tileProvider); + var numRounds = 0; // To prevent endless loops + var maximizedZoom = Math.Min(maxZoom, _axMap1.Tiles.MaxZoom); // Prevent asking higher zoom than allowed + + var stopWatch = new Stopwatch(); + stopWatch.Start(); + while (true) + { + var numTilesToCache = 0; + for (var i = _axMap1.Tiles.MinZoom; i < maximizedZoom; i++) + { + numTilesToCache += + _axMap1.Tiles.PrefetchToFolder(latLongExtents, i, providerId, outputFolder, ".png", this); + Helper.DebugMsg($"numTilesToCache: {numTilesToCache} for zoom {i}"); + // Wait a moment: + Thread.Sleep(2000); + } + + numRounds++; + Helper.DebugMsg($"Finished round {numRounds}"); + // Wait before doing another round: + if (numTilesToCache > 0) Thread.Sleep(2000); + if (numTilesToCache == 0 || numRounds > 9) break; + } + + stopWatch.Stop(); + Helper.DebugMsg("Time it took: " + stopWatch.Elapsed); + } public void Progress(string KeyOfSender, int Percent, string Message) { - Console.WriteLine($"{Percent} {Message}"); + Console.WriteLine($"Callback {Percent} {Message}"); } public void Error(string KeyOfSender, string ErrorMsg) { - Console.WriteLine("Error: " + ErrorMsg); + Console.WriteLine("Callback Error: " + ErrorMsg); } public bool StopFunction() diff --git a/unittests/TestExecutor/Program.cs b/unittests/TestExecutor/Program.cs index 3f517d50..109ee3e9 100644 --- a/unittests/TestExecutor/Program.cs +++ b/unittests/TestExecutor/Program.cs @@ -44,7 +44,19 @@ public static void Main(string[] args) //t.ReadAttributesFromPostGISLayer(); //var t = new ShapefileTests(); + //t.ImportExportWKT(); //t.ShapefileDataTest(); + // t.UnionShapefiles(); + //var t = new PointInPolygonTests(); + //t.TestPointInSimplePolygon(); + //t.TestPointInPolygonM(); + //t.TestPointInPolygonZ(); + //t.TestPointInPolygonWithHole(); + //t.TestPointInMultiPolygon(); + //t.TestPointInLotsOfPolygons(); + + var t = new NewSfMethodsTests(); + t.DifferenceTest2(); //t.Reproject2280Test(); //t.ReadRussionDataFromTable(); @@ -70,8 +82,8 @@ public static void Main(string[] args) // t.DissolveSmallfileClipper(); //t.DissolveLargefileClipper(); - var t = new UtilTests(); - t.ProjectionStrings(); + //var t = new UtilTests(); + //t.ProjectionStrings(); //t.ZonalStatistics(); //t.ReclassifyRaster(); //t.ClipPolygon(); diff --git a/unittests/interopCreator/Form1.cs b/unittests/interopCreator/Form1.cs index bf730796..ec9245b2 100644 --- a/unittests/interopCreator/Form1.cs +++ b/unittests/interopCreator/Form1.cs @@ -472,10 +472,12 @@ private void button11_Click(object sender, EventArgs e) axMap1.KnownExtents = tkKnownExtents.keNetherlands; } + axMap1.Redraw(); axMap1.ZoomBehavior = tkZoomBehavior.zbUseTileLevels; var outputFolder = $@"D:\tmp\axmap.tiles\{axMap1.Tiles.Provider.ToString()}"; if (!Directory.Exists(outputFolder)) Directory.CreateDirectory(outputFolder); + _settings.StartLogTileRequests(@"D:\tmp\axmap.tiles\TileRequests.log"); var numTilesToCache = axMap1.Tiles.PrefetchToFolder(axMap1.Extents, axMap1.Tiles.CurrentZoom, Convert.ToInt32(axMap1.Tiles.Provider), outputFolder, ".png", null); txtResults.Text += $@"{Environment.NewLine}numTilesToCache: " + numTilesToCache; diff --git a/unittests/interopCreator/interopCreator.csproj b/unittests/interopCreator/interopCreator.csproj index 987bc95b..d9c248c4 100644 --- a/unittests/interopCreator/interopCreator.csproj +++ b/unittests/interopCreator/interopCreator.csproj @@ -61,6 +61,8 @@ x86 prompt false + + bin\x86\Release\