前回は、「Dotspatial」のラベル表示で「Roslyn for Scripting」を利用し、属性を表示するところまで確認しましたが、今回は、QGISなどでは標準で実装されている、図形データの表示も対応ができないか試してみることにしました。
1.ラベル設定画面の変更
前回までのラベル設定では、属性値のみだったので、グリッドを使用していましたが、今回は図形データも扱うことになったのでQGISと同じように、ツリー表示してみました。また、「Roslyn for Scripting」では、コンパイルで失敗した際に、エラーメッセージが出力されるので、失敗時これを画面に表示させます。
以下、改定後ラベル設定クラスです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Text; using System.Linq; using System.Windows.Forms; using DevExpress.XtraEditors; using DotSpatial.Data; using DotSpatial.Symbology; using DotSpatial.Symbology.Forms; using DevExpress.XtraEditors.DXErrorProvider; using DevExpress.XtraTreeList; using DevExpress.XtraTreeList.Nodes; namespace DotSpatial.Plugins.CustomLegend { public partial class CustomExpressionControl : DevExpress.XtraEditors.XtraUserControl { private IAttributeSource attributeSource; private Expression exp; private DXErrorProvider errorProvider; private IFeatureLayer featureLayer; public CustomExpressionControl() { this.InitializeComponent(); this.exp = new Expression(); this.treeList.OptionsFilter.ExpandNodesOnFiltering = true; this.treeList.OptionsFind.AlwaysVisible = false; this.treeList.OptionsFind.FindMode = DevExpress.XtraTreeList.FindMode.FindClick; this.treeList.OptionsView.ShowColumns = false; this.treeList.OptionsView.ShowHorzLines = false; this.treeList.OptionsView.ShowIndicator = false; this.treeList.OptionsView.ShowTreeLines = DevExpress.Utils.DefaultBoolean.False; this.treeList.OptionsView.ShowVertLines = false; this.treeList.OptionsView.ShowTreeLines = DevExpress.Utils.DefaultBoolean.False; this.treeList.ShowButtonMode = DevExpress.XtraTreeList.ShowButtonModeEnum.Default; } public bool AllowEmptyExpression { get; set; } public IFeatureLayer FeatureLayer { get { return this.featureLayer; } set { if (value == null) { return; } this.featureLayer = value; this.UpdateFields(); } } public string ExpressionText { get { return this.memoExpression.Text.Trim(); } set { this.memoExpression.Text = value; } } public DXErrorProvider ErrorProvider { set { this.errorProvider = value; this.errorProvider.SetIconAlignment(this.memoExpression, ErrorIconAlignment.TopLeft); this.errorProvider.ClearErrors(); } } private void treeList1_DoubleClick(object sender, EventArgs e) { var tree = sender as TreeList; var hi = tree.CalcHitInfo(tree.PointToClient(Control.MousePosition)); if (hi.Node != null) { var expData = ((ExpressionDataColumn)hi.Node.Tag); if (expData.NodeType != "Category") { this.memoExpression.SelectedText += expData.LabelText; } } } private void treeList_Click(object sender, EventArgs e) { var tree = sender as TreeList; var hi = tree.CalcHitInfo(tree.PointToClient(Control.MousePosition)); if (hi.Node != null) { var expData = ((ExpressionDataColumn)hi.Node.Tag); var font = tree.Appearance.FocusedCell.Font; var style1 = $"font-family:'{font.Name}';font-size:{font.SizeInPoints}pt;font-weight:bold;color:#1f497d;"; var style2 = $"font-family:'{font.Name}';font-size:{font.SizeInPoints}pt;"; var head = $"<div style=\"{style1}\">{{0}}</div>"; var data = $"<div style=\"{style2}\"> {{0}}</div>"; if (expData.NodeType == "DataRow") { var html = new StringBuilder(); html.AppendFormat($"<html>"); html.AppendFormat(head,"フィールド名"); html.AppendFormat(data, expData.ColumnName); html.AppendFormat(head, "データタイプ"); html.AppendFormat(data, expData.DataType); html.AppendFormat(head, "ラベル表現"); html.AppendFormat(data, expData.LabelText); html.AppendLine($"<html>"); this.richEditControl.HtmlText = html.ToString(); } else if (expData.NodeType == "Geometry") { var html = new StringBuilder(); html.AppendLine("<html>"); html.AppendFormat(head, "フィールド名"); html.AppendFormat(data, expData.ColumnName); html.AppendFormat(head, "ラベル表現"); html.AppendFormat(data, expData.LabelText); html.AppendFormat(head, "説明"); html.AppendFormat(data, expData.Comment); html.AppendLine("<html>"); this.richEditControl.HtmlText = html.ToString(); } } } public bool ValidateExpression() { this.memoResult.Text = string.Empty; var ret = true; if (string.IsNullOrWhiteSpace(this.memoExpression.Text)) { return ret; } this.errorProvider.ClearErrors(); ret = this.exp.ParseExpression(this.memoExpression.Text); if (ret == false) { this.errorProvider.SetError(this.memoExpression, exp.ErrorMessage); return ret; } var feature = this.featureLayer.DataSet.GetFeature(0); if (feature != null) { var retVal = string.Empty; ret = this.exp.IsValidOperation(ref retVal, feature); if (ret == false) { this.errorProvider.SetError(this.memoExpression, exp.ErrorMessage); return ret; } this.memoResult.Text = retVal; } return ret; } private void UpdateFields() { bool hasFID = false; var elist = new List<ExpressionDataColumn>(); if (this.featureLayer.DataSet.DataTable != null) { foreach (DataColumn dc in this.featureLayer.DataSet.DataTable.Columns) { elist.Add(new ExpressionDataColumn() { ColumnName = dc.ColumnName, DataType = dc.DataType.ToString().Replace("System.", "") }); if (dc.ColumnName.ToLower() == "_fid_") { hasFID = true; } } } if (!hasFID) { elist.Add(new ExpressionDataColumn() { ColumnName = "_fid_", DataType = typeof(long).ToString().Replace("System.", "") }); } this.treeList.Nodes.Clear(); var dataRowsNode = this.AppendTreeNode( new ExpressionDataColumn() { ColumnName = "属性値", NodeType = "Category", }, null ); foreach(var exp in elist) { exp.LabelText = $"DataRow[\"{exp.ColumnName}\"]"; this.AppendTreeNode(exp, dataRowsNode); } var geomsNode = this.AppendTreeNode( new ExpressionDataColumn() { ColumnName = "図形", NodeType = "Category", }, null ); this.AppendTreeNode( new ExpressionDataColumn() { ColumnName = "面積", NodeType = "Geometry", Comment = "図形の面積を返します", LabelText = "$\"{Geometry.Area:n2}㎡\"", }, geomsNode ); this.AppendTreeNode( new ExpressionDataColumn() { ColumnName = "長さ", NodeType = "Geometry", Comment = "図形の長さを返します", LabelText = "$\"{Geometry.Length:n2}m\"" }, geomsNode ); this.AppendTreeNode( new ExpressionDataColumn() { ColumnName = "中心位置", NodeType = "Geometry", Comment = "図形の中心位置(X座標、Y座標)を返します", LabelText = "$\"X={Geometry.Centroid.X:n2},Y={Geometry.Centroid.Y:n2}\"" }, geomsNode ); } private TreeListNode AppendTreeNode(ExpressionDataColumn expData, TreeListNode parentNode) { var nodeTypes = new List<string> { "Category", "DataRow", "Geometry" }; var nodeData = new object[] { expData.ColumnName, expData.DataType, expData.LabelText, }; var node = this.treeList.AppendNode(nodeData, parentNode); node.ImageIndex = nodeTypes.IndexOf(expData.NodeType); node.SelectImageIndex = node.ImageIndex; node.StateImageIndex = node.ImageIndex; node.Tag = expData; return node; } } } |
2.Expressionクラスの変更
今回は、図形データの表示を行うため、スクリプトを渡す引数クラスにGeometoryデータを渡します。
この対応により、ラベル描画を行う処理の引数に、Featureデータを渡します。
以下、改定後のソースです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
using System; using System.Collections.Generic; using System.Data; using System.Text.RegularExpressions; using DotSpatial.Data; using Microsoft.CodeAnalysis.CSharp.Scripting; using Microsoft.CodeAnalysis.Scripting; using GeoAPI.Geometries; namespace DotSpatial.Symbology { public class Expression { private string _expressionString; // Expression string that is used to calculate the expression for the DataRows private Script<object> _script; public class GlobalClass { public DataRow DataRow; public IGeometry Geometry; public GlobalClass() { } } public string ErrorMessage { get; private set; } public string CalculateRowValue(IFeature feature) { var expval = string.Empty; this.ErrorMessage = string.Empty; if (!string.IsNullOrEmpty(_expressionString)) { try { var globals = new GlobalClass(); globals.DataRow = feature.DataRow; globals.Geometry = feature.Geometry; var result = this._script.RunAsync(globals: globals).Result; expval = result.ReturnValue.ToString(); } catch (CompilationErrorException ex) { this.ErrorMessage = ex.Message; } catch (Exception ex) { this.ErrorMessage = ex.Message; } } return expval; } public bool IsValidOperation(ref string retVal, IFeature f = null) { var ret = true; this.ErrorMessage = string.Empty; if (string.IsNullOrEmpty(_expressionString)) { ret = true; } else { retVal = this.CalculateRowValue(f); if (!string.IsNullOrEmpty(this.ErrorMessage)) { ret = false; } } return ret; } public bool ParseExpression(string s) { var ret = true; this.ErrorMessage = string.Empty; if (string.IsNullOrEmpty(s)) { ret = true; } else if (_expressionString != s) { try { var options = ScriptOptions.Default .WithImports( "System", "System.Diagnostics") .WithReferences( typeof(object).Assembly, typeof(Microsoft.CSharp.RuntimeBinder.Binder).Assembly); this._script = CSharpScript.Create(s, options, typeof(GlobalClass)); this._expressionString = s; } catch (CompilationErrorException ex) { this.ErrorMessage = ex.Message; ret = false; } } return ret; } public bool UpdateFields(DataColumnCollection columns) { return true; } #endregion } } |
以下は設定画面です、図形データが選択できるようになっています。
ラベルの表示が失敗した場合も、エラーメッセージが表示されるようになっています。
成功した場合は、表示例に表示されます。
設定後のラベル表示イメージです、図形の長さが表示されています。
長さを㎞表示にする場合は、以下のようにします。
㎞表示されています。