「Dotspatial」のラベル表示は、独自クラス(DotSpatial.Symbology.Expression)で独自の構文解析を行っていますが、処理複雑で、カスタマイズし難いクラスでした。
また、改行が設定できなかったり、QGISなどでは標準で実装されている、図形データの表示もできませんでした。
そこで、「Roslyn for Scripting」を利用して、これらの対応ができないか試してみることにしました。
「Roslyn for Scripting」は、C#言語でスクリプト言語が実行できる機能です。これを導入することにより、GISアプリ側でC#のプログラムをスクリプトとして動的に実行することができ、色んなことができるようになると思います。
ちなみに、ArcGIS Proでもスクリプトを採用していますが、こちらはPython言語です。
1.「Microsoft.CodeAnalysis.CSharp.Scripting」の参照の追加
まず最初に、「Microsoft.CodeAnalysis.CSharp.Scripting」の参照を追加します。
NuGetパッケージマネージャーなんかで追加します。NuGetが嫌いな方は以下を参考にして参照を追加してください。
1 2 3 4 5 6 7 8 9 10 |
・Microsoft.CodeAnalysis.Common.3.8.0/lib/netstandard2.0/Microsoft.CodeAnalysis.dll ・Microsoft.CodeAnalysis.CSharp.Scripting.3.8.0/lib/netstandard2.0/Microsoft.CodeAnalysis.CSharp.Scripting.dll ・Microsoft.CodeAnalysis.Scripting.Common.3.8.0/lib/netstandard2.0/Microsoft.CodeAnalysis.Scripting.dll ・System.Buffers.4.5.1/lib/net461/System.Buffers.dll ・System.Collections.Immutable.5.0.0/lib/net461/System.Collections.Immutable.dll ・System.Memory.4.5.4/lib/net461/System.Memory.dll ・System.Numerics.Vectors.4.5.0/lib/net46/System.Numerics.Vectors.dll ・System.Reflection.Metadata.5.0.0/lib/net461/System.Reflection.Metadata.dll ・System.Runtime.CompilerServices.Unsafe.4.7.1/lib/net461/System.Runtime.CompilerServices.Unsafe.dll ・System.Text.Encoding.CodePages.4.5.1/lib/net461/System.Text.Encoding.CodePages.dll |
2.Expressionクラスの変更
Expressionクラスに、「Microsoft.CodeAnalysis.CSharp.Scripting」をしてラベル表示できるようにします。
「Roslyn for Scripting」では、スクリプトといっても結局はコンパイルは必要になります。このコンパイルの処理が非常に遅いので、ラベル設定画面でコンパイルを済ませておき、画面表示時に、コンパイル済みのスクリプトを実行させます。
改変前のプログラムでは、独自の構文解析のため、非常に難解なプログラムが記述されていましたが、「Roslyn for Scripting」導入により、非常にシンプルなプログラムになりました。
また、今回は実装していませんが、スクリプトに変数を渡せるので、図形データの情報を引数に持たせれば図形の面積や、長さといった情報もラベル表示できると思います。
以下、改定後のExpressionクラスです。
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 |
using System; using System.Collections.Generic; using System.Data; using System.Text.RegularExpressions; using Microsoft.CodeAnalysis.CSharp.Scripting; using Microsoft.CodeAnalysis.Scripting; namespace DotSpatial.Symbology { public class Expression { private string _expressionString; private Script<object> _script; public class GlobalClass { public DataRow Row; } public string ErrorMessage { get; private set; } public string CalculateRowValue(DataRow row, int fid) { var expval = string.Empty; this.ErrorMessage = string.Empty; if (!string.IsNullOrEmpty(_expressionString)) { try { var globals = new GlobalClass(); globals.Row = row; 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, DataRow row = null) { var ret = true; this.ErrorMessage = string.Empty; if (string.IsNullOrEmpty(_expressionString)) { ret = true; } else { retVal = this.CalculateRowValue(row, 0); 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 { _expressionString = s; var text = "\"\" + " + this._expressionString.Replace("[", "Row[\"").Replace("]", "\"]"); var options = ScriptOptions.Default .WithImports( "System", "System.Diagnostics") .WithReferences( typeof(object).Assembly, typeof(Microsoft.CSharp.RuntimeBinder.Binder).Assembly); this._script = CSharpScript.Create(text, options, typeof(GlobalClass)); } catch (CompilationErrorException ex) { this.ErrorMessage = ex.Message; ret = false; } } return ret; } public bool UpdateFields(DataColumnCollection columns) { return true; } } } |
以下は設定画面です、改定前では表示できなかった、改行を設定しています。
設定後のラベル表示です、バス系統と路線名の間が改行表示されています。