From 0416f7e6ea3b7a03b66fb25ca5a6b0d03d4ac963 Mon Sep 17 00:00:00 2001 From: Mike van Riel Date: Thu, 10 Oct 2024 08:33:41 +0200 Subject: [PATCH 01/11] Add base classes and initial expression for new styling system --- Assets/Scripts/Layers/LayerTypes/LayerData.cs | 2 ++ Assets/_BuildingBlocks/LayerStyles.meta | 3 +++ .../_BuildingBlocks/LayerStyles/Scripts.meta | 3 +++ .../LayerStyles/Scripts/BoolExpression.cs | 23 +++++++++++++++++++ .../Scripts/BoolExpression.cs.meta | 3 +++ .../LayerStyles/Scripts/Expression.cs | 13 +++++++++++ .../LayerStyles/Scripts/Expression.cs.meta | 3 +++ .../LayerStyles/Scripts/ExpressionContext.cs | 8 +++++++ .../Scripts/ExpressionContext.cs.meta | 3 +++ .../LayerStyles/Scripts/LayerStyle.cs | 13 +++++++++++ .../LayerStyles/Scripts/LayerStyle.cs.meta | 3 +++ .../LayerStyles/Scripts/LiteralExpression.cs | 6 +++++ .../Scripts/LiteralExpression.cs.meta | 3 +++ .../LayerStyles/Scripts/Metadata.cs | 10 ++++++++ .../LayerStyles/Scripts/Metadata.cs.meta | 3 +++ .../LayerStyles/Scripts/StylingRule.cs | 11 +++++++++ .../LayerStyles/Scripts/StylingRule.cs.meta | 3 +++ .../LayerStyles/Scripts/Symbolizer.cs | 21 +++++++++++++++++ .../LayerStyles/Scripts/Symbolizer.cs.meta | 3 +++ .../Scripts/VectorSymbologyExtension.cs | 23 +++++++++++++++++++ .../Scripts/VectorSymbologyExtension.cs.meta | 3 +++ 21 files changed, 163 insertions(+) create mode 100644 Assets/_BuildingBlocks/LayerStyles.meta create mode 100644 Assets/_BuildingBlocks/LayerStyles/Scripts.meta create mode 100644 Assets/_BuildingBlocks/LayerStyles/Scripts/BoolExpression.cs create mode 100644 Assets/_BuildingBlocks/LayerStyles/Scripts/BoolExpression.cs.meta create mode 100644 Assets/_BuildingBlocks/LayerStyles/Scripts/Expression.cs create mode 100644 Assets/_BuildingBlocks/LayerStyles/Scripts/Expression.cs.meta create mode 100644 Assets/_BuildingBlocks/LayerStyles/Scripts/ExpressionContext.cs create mode 100644 Assets/_BuildingBlocks/LayerStyles/Scripts/ExpressionContext.cs.meta create mode 100644 Assets/_BuildingBlocks/LayerStyles/Scripts/LayerStyle.cs create mode 100644 Assets/_BuildingBlocks/LayerStyles/Scripts/LayerStyle.cs.meta create mode 100644 Assets/_BuildingBlocks/LayerStyles/Scripts/LiteralExpression.cs create mode 100644 Assets/_BuildingBlocks/LayerStyles/Scripts/LiteralExpression.cs.meta create mode 100644 Assets/_BuildingBlocks/LayerStyles/Scripts/Metadata.cs create mode 100644 Assets/_BuildingBlocks/LayerStyles/Scripts/Metadata.cs.meta create mode 100644 Assets/_BuildingBlocks/LayerStyles/Scripts/StylingRule.cs create mode 100644 Assets/_BuildingBlocks/LayerStyles/Scripts/StylingRule.cs.meta create mode 100644 Assets/_BuildingBlocks/LayerStyles/Scripts/Symbolizer.cs create mode 100644 Assets/_BuildingBlocks/LayerStyles/Scripts/Symbolizer.cs.meta create mode 100644 Assets/_BuildingBlocks/LayerStyles/Scripts/VectorSymbologyExtension.cs create mode 100644 Assets/_BuildingBlocks/LayerStyles/Scripts/VectorSymbologyExtension.cs.meta diff --git a/Assets/Scripts/Layers/LayerTypes/LayerData.cs b/Assets/Scripts/Layers/LayerTypes/LayerData.cs index 07c03a43..7ea0ad9b 100644 --- a/Assets/Scripts/Layers/LayerTypes/LayerData.cs +++ b/Assets/Scripts/Layers/LayerTypes/LayerData.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; +using Netherlands3D.LayerStyles; using Netherlands3D.Twin.Layers.Properties; using Netherlands3D.Twin.Projects; using Newtonsoft.Json; @@ -20,6 +21,7 @@ public class LayerData [SerializeField, DataMember] protected List children = new(); [JsonIgnore] protected LayerData parent; //not serialized to avoid a circular reference [SerializeField, DataMember] protected List layerProperties = new(); + [SerializeField, DataMember] protected List styles = new(); [JsonIgnore] public RootLayer Root => ProjectData.Current.RootLayer; [JsonIgnore] public LayerData ParentLayer => parent; diff --git a/Assets/_BuildingBlocks/LayerStyles.meta b/Assets/_BuildingBlocks/LayerStyles.meta new file mode 100644 index 00000000..48cb279a --- /dev/null +++ b/Assets/_BuildingBlocks/LayerStyles.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2c212b533aa242f78fa3f02650bcdebf +timeCreated: 1728486454 \ No newline at end of file diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts.meta b/Assets/_BuildingBlocks/LayerStyles/Scripts.meta new file mode 100644 index 00000000..149488ac --- /dev/null +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d587f7ab430a4b3791433f920f69fcae +timeCreated: 1728486468 \ No newline at end of file diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/BoolExpression.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/BoolExpression.cs new file mode 100644 index 00000000..f9b011bf --- /dev/null +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/BoolExpression.cs @@ -0,0 +1,23 @@ +using System.Runtime.Serialization; + +namespace Netherlands3D.LayerStyles +{ + [DataContract(Namespace = "https://netherlands3d.eu/schemas/projects/layers/styling/expressions", Name = "Bool")] + public class BoolExpression : LiteralExpression + { + private readonly bool value; + + public BoolExpression(bool value) + { + this.value = value; + } + + public static BoolExpression True() => new(true); + public static BoolExpression False() => new(false); + + public override object Resolve(ExpressionContext context) + { + return value; + } + } +} \ No newline at end of file diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/BoolExpression.cs.meta b/Assets/_BuildingBlocks/LayerStyles/Scripts/BoolExpression.cs.meta new file mode 100644 index 00000000..5f7f1923 --- /dev/null +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/BoolExpression.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 23a4b505a3e346b3a10cdd91c2f02611 +timeCreated: 1728488102 \ No newline at end of file diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/Expression.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/Expression.cs new file mode 100644 index 00000000..ce114bb6 --- /dev/null +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/Expression.cs @@ -0,0 +1,13 @@ +namespace Netherlands3D.LayerStyles +{ + public abstract class Expression + { + /// + /// Resolve the expression by performing any action necessary, optionally including information from + /// context that can be passed to more complex expressions. + /// + /// An object containing information on the current visualisation, dataLayer and/or feature + /// + public abstract object Resolve(ExpressionContext context); + } +} \ No newline at end of file diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/Expression.cs.meta b/Assets/_BuildingBlocks/LayerStyles/Scripts/Expression.cs.meta new file mode 100644 index 00000000..5e651a7f --- /dev/null +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/Expression.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: faadcca18c9c493d864be8ddd8ac9662 +timeCreated: 1728488063 \ No newline at end of file diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/ExpressionContext.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/ExpressionContext.cs new file mode 100644 index 00000000..b0db14e6 --- /dev/null +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/ExpressionContext.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace Netherlands3D.LayerStyles +{ + public class ExpressionContext : Dictionary + { + } +} \ No newline at end of file diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/ExpressionContext.cs.meta b/Assets/_BuildingBlocks/LayerStyles/Scripts/ExpressionContext.cs.meta new file mode 100644 index 00000000..acabd5d1 --- /dev/null +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/ExpressionContext.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e6687e7b37e742229116515fac87e61d +timeCreated: 1728505904 \ No newline at end of file diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/LayerStyle.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/LayerStyle.cs new file mode 100644 index 00000000..ffe0325d --- /dev/null +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/LayerStyle.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace Netherlands3D.LayerStyles +{ + [DataContract(Namespace = "https://netherlands3d.eu/schemas/projects/layers/styling", Name = "LayerStyle")] + public class LayerStyle + { + public Metadata Metadata { get; } + + public List StylingRules { get; } = new(); + } +} \ No newline at end of file diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/LayerStyle.cs.meta b/Assets/_BuildingBlocks/LayerStyles/Scripts/LayerStyle.cs.meta new file mode 100644 index 00000000..88441f20 --- /dev/null +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/LayerStyle.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 937836a1262c4bc89ecc4d6f2663da77 +timeCreated: 1728486482 \ No newline at end of file diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/LiteralExpression.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/LiteralExpression.cs new file mode 100644 index 00000000..462cced6 --- /dev/null +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/LiteralExpression.cs @@ -0,0 +1,6 @@ +namespace Netherlands3D.LayerStyles +{ + public abstract class LiteralExpression : Expression + { + } +} \ No newline at end of file diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/LiteralExpression.cs.meta b/Assets/_BuildingBlocks/LayerStyles/Scripts/LiteralExpression.cs.meta new file mode 100644 index 00000000..a0d16999 --- /dev/null +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/LiteralExpression.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4bddd39105674ddd87adce0a17bdc4fe +timeCreated: 1728488072 \ No newline at end of file diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/Metadata.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/Metadata.cs new file mode 100644 index 00000000..3edd5d28 --- /dev/null +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/Metadata.cs @@ -0,0 +1,10 @@ +using System.Runtime.Serialization; + +namespace Netherlands3D.LayerStyles +{ + [DataContract(Namespace = "https://netherlands3d.eu/schemas/projects/layers/styling", Name = "Metadata")] + public struct Metadata + { + public string Name { get; } + } +} \ No newline at end of file diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/Metadata.cs.meta b/Assets/_BuildingBlocks/LayerStyles/Scripts/Metadata.cs.meta new file mode 100644 index 00000000..6a4061ea --- /dev/null +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/Metadata.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f6dc15055cb04ff3a8fd62bc98983197 +timeCreated: 1728488018 \ No newline at end of file diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/StylingRule.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/StylingRule.cs new file mode 100644 index 00000000..713d69c3 --- /dev/null +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/StylingRule.cs @@ -0,0 +1,11 @@ +namespace Netherlands3D.LayerStyles +{ + public class StylingRule + { + public string Name { get; } + public Symbolizer Symbolizer { get; } = new(); + + // By default, everything matches. And that means a "True" literal boolean. + public Expression Selector { get; } = BoolExpression.True(); + } +} \ No newline at end of file diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/StylingRule.cs.meta b/Assets/_BuildingBlocks/LayerStyles/Scripts/StylingRule.cs.meta new file mode 100644 index 00000000..cce581cb --- /dev/null +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/StylingRule.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0f22af5176e548fcbd1f058d45b30c05 +timeCreated: 1728488031 \ No newline at end of file diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/Symbolizer.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/Symbolizer.cs new file mode 100644 index 00000000..9fdde2b3 --- /dev/null +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/Symbolizer.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace Netherlands3D.LayerStyles +{ + [DataContract(Namespace = "https://netherlands3d.eu/schemas/projects/layers/styling", Name = "Symbolizer")] + public sealed class Symbolizer + { + private readonly Dictionary properties = new(); + + internal object GetProperty(string key) + { + return properties.ContainsKey(key) ? properties[key] : null; + } + + internal void SetProperty(string key, object value) + { + properties[key] = value; + } + } +} \ No newline at end of file diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/Symbolizer.cs.meta b/Assets/_BuildingBlocks/LayerStyles/Scripts/Symbolizer.cs.meta new file mode 100644 index 00000000..b2184327 --- /dev/null +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/Symbolizer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ea14efebde144df0b10553c2198cdc17 +timeCreated: 1728488041 \ No newline at end of file diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/VectorSymbologyExtension.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/VectorSymbologyExtension.cs new file mode 100644 index 00000000..1c14e5ab --- /dev/null +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/VectorSymbologyExtension.cs @@ -0,0 +1,23 @@ +using UnityEngine; + +namespace Netherlands3D.LayerStyles +{ + public static class VectorSymbologyExtension + { + public static void SetFillColor(this Symbolizer symbology, Color color) + { + symbology.SetProperty("fillColor", $"#{ColorUtility.ToHtmlStringRGB(color)}"); + } + + public static Color GetFillColor(this Symbolizer symbology) + { + var colorString = symbology.GetProperty("fill") as string ?? ""; + if (string.IsNullOrEmpty(colorString)) return default; + + var success = ColorUtility.TryParseHtmlString(colorString, out var color); + if (!success) return default; + + return color; + } + } +} \ No newline at end of file diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/VectorSymbologyExtension.cs.meta b/Assets/_BuildingBlocks/LayerStyles/Scripts/VectorSymbologyExtension.cs.meta new file mode 100644 index 00000000..667cfbdc --- /dev/null +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/VectorSymbologyExtension.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5e196c977c2d4c8f95c2704ef1c4bce1 +timeCreated: 1728488056 \ No newline at end of file From 34ccda846ae6b13ed98fa69d8fb622cfabb0de37 Mon Sep 17 00:00:00 2001 From: Mike van Riel Date: Thu, 10 Oct 2024 16:37:00 +0200 Subject: [PATCH 02/11] Clean up and directly serialize a color --- .../LayerStyles/Scripts/Symbolizer.cs | 1 + .../LayerStyles/Scripts/VectorSymbologyExtension.cs | 12 +++--------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/Symbolizer.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/Symbolizer.cs index 9fdde2b3..92695ff7 100644 --- a/Assets/_BuildingBlocks/LayerStyles/Scripts/Symbolizer.cs +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/Symbolizer.cs @@ -10,6 +10,7 @@ public sealed class Symbolizer internal object GetProperty(string key) { + // explicitly return null when value is not present, so that caller knows it should ignore using this field return properties.ContainsKey(key) ? properties[key] : null; } diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/VectorSymbologyExtension.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/VectorSymbologyExtension.cs index 1c14e5ab..429fea9d 100644 --- a/Assets/_BuildingBlocks/LayerStyles/Scripts/VectorSymbologyExtension.cs +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/VectorSymbologyExtension.cs @@ -6,18 +6,12 @@ public static class VectorSymbologyExtension { public static void SetFillColor(this Symbolizer symbology, Color color) { - symbology.SetProperty("fillColor", $"#{ColorUtility.ToHtmlStringRGB(color)}"); + symbology.SetProperty("fill", color); } - public static Color GetFillColor(this Symbolizer symbology) + public static Color? GetFillColor(this Symbolizer symbology) { - var colorString = symbology.GetProperty("fill") as string ?? ""; - if (string.IsNullOrEmpty(colorString)) return default; - - var success = ColorUtility.TryParseHtmlString(colorString, out var color); - if (!success) return default; - - return color; + return symbology.GetProperty("fill") as Color?; } } } \ No newline at end of file From 5530565903d6a95b6ec8b442bedafd3e63b48117 Mon Sep 17 00:00:00 2001 From: Mike van Riel Date: Thu, 10 Oct 2024 16:37:38 +0200 Subject: [PATCH 03/11] Color should be set in de ImportAdapters once, and read subsequently. After this commit we can rework the coloring to use the fill color --- .../DataTypeAdapters/GeoJSONImportAdapter.cs | 5 ++++- .../DataTypeAdapters/WFSGeoJSONImportAdapter.cs | 5 +++++ .../Layers/LayerTypes/GeoJsonLayerGameObject.cs | 15 --------------- .../LayerTypes/WFSGeoJSONLayerGameObject.cs | 4 +--- 4 files changed, 10 insertions(+), 19 deletions(-) diff --git a/Assets/Scripts/Layers/Adapters/DataTypeAdapters/GeoJSONImportAdapter.cs b/Assets/Scripts/Layers/Adapters/DataTypeAdapters/GeoJSONImportAdapter.cs index 5bf5d6a5..11270c19 100644 --- a/Assets/Scripts/Layers/Adapters/DataTypeAdapters/GeoJSONImportAdapter.cs +++ b/Assets/Scripts/Layers/Adapters/DataTypeAdapters/GeoJSONImportAdapter.cs @@ -59,13 +59,16 @@ private void CreateGeoJSONLayer(LocalFile localFile, UnityEvent onErrorC if(localFile.SourceUrl.Length > 0) geoJsonLayerName = localFile.SourceUrl; - //Create a new geojson layer with random color (untill UI provides ways to choose colors) GeoJsonLayerGameObject newLayer = Instantiate(layerPrefab); newLayer.Name = geoJsonLayerName; newLayer.gameObject.name = geoJsonLayerName; if (onErrorCallback != null) newLayer.OnParseError.AddListener(onErrorCallback.Invoke); + //GeoJSON layer+visual colors are set to random colors until user can pick colors in UI + var randomLayerColor = Color.HSVToRGB(UnityEngine.Random.value, UnityEngine.Random.Range(0.5f, 1f), 1); + randomLayerColor.a = 0.5f; + newLayer.LayerData.Color = randomLayerColor; newLayer.SetURL(localFilePath, localFile.SourceUrl); } diff --git a/Assets/Scripts/Layers/Adapters/DataTypeAdapters/WFSGeoJSONImportAdapter.cs b/Assets/Scripts/Layers/Adapters/DataTypeAdapters/WFSGeoJSONImportAdapter.cs index 301496d1..24086ce3 100644 --- a/Assets/Scripts/Layers/Adapters/DataTypeAdapters/WFSGeoJSONImportAdapter.cs +++ b/Assets/Scripts/Layers/Adapters/DataTypeAdapters/WFSGeoJSONImportAdapter.cs @@ -115,6 +115,11 @@ private void AddWFSLayer(string featureType, string sourceUrl, FolderLayer folde newLayer.LayerData.SetParent(folderLayer); newLayer.Name = title; newLayer.SetURL(getFeatureUrl); + + //GeoJSON layer+visual colors are set to random colors until user can pick colors in UI + var randomLayerColor = Color.HSVToRGB(UnityEngine.Random.value, UnityEngine.Random.Range(0.5f, 1f), 1); + randomLayerColor.a = 0.5f; + newLayer.LayerData.Color = randomLayerColor; } private UriBuilder CreateLayerUri(string featureType, string sourceUrl) diff --git a/Assets/Scripts/Layers/LayerTypes/GeoJsonLayerGameObject.cs b/Assets/Scripts/Layers/LayerTypes/GeoJsonLayerGameObject.cs index 3ff16182..38e98052 100644 --- a/Assets/Scripts/Layers/LayerTypes/GeoJsonLayerGameObject.cs +++ b/Assets/Scripts/Layers/LayerTypes/GeoJsonLayerGameObject.cs @@ -35,8 +35,6 @@ public class GeoJsonLayerGameObject : LayerGameObject, ILayerWithPropertyData [SerializeField] private GeoJSONLineLayer lineLayerPrefab; [SerializeField] private GeoJSONPointLayer pointLayerPrefab; - [SerializeField] private bool randomizeColorPerFeature = false; - public bool RandomizeColorPerFeature { get => randomizeColorPerFeature; set => randomizeColorPerFeature = value; } public int MaxFeatureVisualsPerFrame { get => maxFeatureVisualsPerFrame; set => maxFeatureVisualsPerFrame = value; } [Space] @@ -45,19 +43,6 @@ public class GeoJsonLayerGameObject : LayerGameObject, ILayerWithPropertyData protected LayerURLPropertyData urlPropertyData = new(); LayerPropertyData ILayerWithPropertyData.PropertyData => urlPropertyData; - protected virtual void Awake() - { - LoadDefaultValues(); - } - - protected virtual void LoadDefaultValues() - { - //GeoJSON layer+visual colors are set to random colors until user can pick colors in UI - var randomLayerColor = Color.HSVToRGB(UnityEngine.Random.value, UnityEngine.Random.Range(0.5f, 1f), 1); - randomLayerColor.a = 0.5f; - LayerData.Color = randomLayerColor; - } - /// /// Load properties is only used when restoring a layer from a project file. /// After getting the property containing the url, the GeoJSON file is downloaded and parsed. diff --git a/Assets/Scripts/Layers/LayerTypes/WFSGeoJSONLayerGameObject.cs b/Assets/Scripts/Layers/LayerTypes/WFSGeoJSONLayerGameObject.cs index e53fd1e3..9d0e57f2 100644 --- a/Assets/Scripts/Layers/LayerTypes/WFSGeoJSONLayerGameObject.cs +++ b/Assets/Scripts/Layers/LayerTypes/WFSGeoJSONLayerGameObject.cs @@ -14,10 +14,8 @@ public class WFSGeoJsonLayerGameObject : GeoJsonLayerGameObject [SerializeField] private WFSGeoJSONTileDataLayer cartesianTileWFSLayer; public WFSGeoJSONTileDataLayer CartesianTileWFSLayer { get => cartesianTileWFSLayer; } - protected override void Awake() + protected void Awake() { - base.Awake(); - CartesianTileWFSLayer.WFSGeoJSONLayer = this; } From d4aa7f8d237138953458a50373fa0aa903247159 Mon Sep 17 00:00:00 2001 From: Mike van Riel Date: Mon, 14 Oct 2024 09:48:50 +0200 Subject: [PATCH 04/11] Add an explanation how Expressions match with the Interpreter design pattern for ease of understanding --- Assets/_BuildingBlocks/LayerStyles/Scripts/Expression.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/Expression.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/Expression.cs index ce114bb6..5c3c03c0 100644 --- a/Assets/_BuildingBlocks/LayerStyles/Scripts/Expression.cs +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/Expression.cs @@ -5,6 +5,10 @@ public abstract class Expression /// /// Resolve the expression by performing any action necessary, optionally including information from /// context that can be passed to more complex expressions. + /// + /// This is an implementation of the Interpreter design pattern (https://sourcemaking.com/design_patterns/interpreter) + /// where the method that 'does the work' is called "Resolve" because in common (developer) language you say + /// that you "resolve" an expression. /// /// An object containing information on the current visualisation, dataLayer and/or feature /// From e6284f8fed5cd49c51293456e08e531ca2a9b8bd Mon Sep 17 00:00:00 2001 From: Mike van Riel Date: Mon, 14 Oct 2024 13:14:04 +0200 Subject: [PATCH 05/11] Flesh out styling classes and introduce default styling into LayerData, including the first bits regarding colors --- Assets/Scripts/Layers/LayerTypes/LayerData.cs | 54 +++++++++++++++++-- .../LayerStyles/Scripts/LayerStyle.cs | 28 ++++++++-- .../LayerStyles/Scripts/Metadata.cs | 4 +- .../LayerStyles/Scripts/StylingRule.cs | 22 ++++++-- 4 files changed, 96 insertions(+), 12 deletions(-) diff --git a/Assets/Scripts/Layers/LayerTypes/LayerData.cs b/Assets/Scripts/Layers/LayerTypes/LayerData.cs index 7ea0ad9b..a4747dfc 100644 --- a/Assets/Scripts/Layers/LayerTypes/LayerData.cs +++ b/Assets/Scripts/Layers/LayerTypes/LayerData.cs @@ -12,16 +12,38 @@ namespace Netherlands3D.Twin.Layers { [Serializable] - public class LayerData + public abstract class LayerData { [SerializeField, DataMember] protected Guid UUID = Guid.NewGuid(); [SerializeField, DataMember] protected string name; [SerializeField, DataMember] protected bool activeSelf = true; - [SerializeField, DataMember] protected Color color = new Color(86f / 256f, 160f / 256f, 227f / 255f); + + /// + /// The default color of a layer. + /// + /// This will influence how it is displayed in the layers side-panel, it does not automatically imply any + /// coloring in the styling of the layer but can be used to tell layers apart from one another in the layer + /// panel. + /// + /// Each type of layer could decide to use this value to influence the default styling by listening to the + /// ColorChanged event and applying the color to the relevant color field in the default Style, such as fill + /// for polygon vector layers, or stroke color for line polygon layers. + /// + [SerializeField, DataMember] protected Color color = new(86f / 256f, 160f / 256f, 227f / 255f); + [SerializeField, DataMember] protected List children = new(); [JsonIgnore] protected LayerData parent; //not serialized to avoid a circular reference [SerializeField, DataMember] protected List layerProperties = new(); - [SerializeField, DataMember] protected List styles = new(); + + /// + /// A list of styles with their names (which are meant as machine-readable names and not human-readable names, + /// for the latter the 'title' field exists), including a default style that always applies. + /// + [DataMember] private Dictionary styles = new() + { + {"default", LayerStyle.CreateDefaultStyle()} + }; + [JsonIgnore] public RootLayer Root => ProjectData.Current.RootLayer; [JsonIgnore] public LayerData ParentLayer => parent; @@ -96,6 +118,14 @@ [JsonIgnore] public List LayerProperties [JsonIgnore] public bool HasProperties => layerProperties.Count > 0; + [JsonIgnore] private Dictionary Styles => styles; + + /// + /// Every layer has a default style, this is a style that applies to all objects and features in this + /// layer without any conditions. + /// + [JsonIgnore] public LayerStyle GetDefaultStyle => Styles["default"]; + [JsonIgnore] public readonly UnityEvent NameChanged = new(); [JsonIgnore] public readonly UnityEvent LayerActiveInHierarchyChanged = new(); [JsonIgnore] public readonly UnityEvent ColorChanged = new(); @@ -109,6 +139,8 @@ [JsonIgnore] public List LayerProperties [JsonIgnore] public readonly UnityEvent ParentOrSiblingIndexChanged = new(); [JsonIgnore] public readonly UnityEvent PropertyAdded = new(); [JsonIgnore] public readonly UnityEvent PropertyRemoved = new(); + [JsonIgnore] public readonly UnityEvent StyleAdded = new(); + [JsonIgnore] public readonly UnityEvent StyleRemoved = new(); public void InitializeParent(LayerData initialParent = null) { @@ -223,6 +255,22 @@ public void RemoveProperty(LayerPropertyData propertyData) PropertyRemoved.Invoke(propertyData); } + public void AddStyle(LayerStyle style) + { + if (Styles.TryAdd(style.Metadata.Name, style)) + { + StyleAdded.Invoke(style); + } + } + + public void RemoveStyle(LayerStyle style) + { + if (Styles.Remove(style.Metadata.Name)) + { + StyleRemoved.Invoke(style); + } + } + /// /// Recursively collect all assets from each of the property data elements for loading and saving /// purposes. diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/LayerStyle.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/LayerStyle.cs index ffe0325d..d5606ecc 100644 --- a/Assets/_BuildingBlocks/LayerStyles/Scripts/LayerStyle.cs +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/LayerStyle.cs @@ -4,10 +4,32 @@ namespace Netherlands3D.LayerStyles { [DataContract(Namespace = "https://netherlands3d.eu/schemas/projects/layers/styling", Name = "LayerStyle")] - public class LayerStyle + public sealed class LayerStyle { - public Metadata Metadata { get; } + public Metadata Metadata { get; } = new(); - public List StylingRules { get; } = new(); + public List StylingRules { get; } = new() + { + new StylingRule("default") + }; + + /// + /// Static factory method to construct a default style with. + /// + /// This static factory method will ensure that the LayerStyle class is in control what makes up + /// for a default layer style, and if there are properties that need to come with that. This way, any + /// other part of the code that wants a fresh 'default' style can use this method and does not need to know + /// what that actually means. + /// + /// + public static LayerStyle CreateDefaultStyle() + { + return new LayerStyle("default"); + } + + public LayerStyle(string name) + { + Metadata.Name = name; + } } } \ No newline at end of file diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/Metadata.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/Metadata.cs index 3edd5d28..68074724 100644 --- a/Assets/_BuildingBlocks/LayerStyles/Scripts/Metadata.cs +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/Metadata.cs @@ -3,8 +3,8 @@ namespace Netherlands3D.LayerStyles { [DataContract(Namespace = "https://netherlands3d.eu/schemas/projects/layers/styling", Name = "Metadata")] - public struct Metadata + public class Metadata { - public string Name { get; } + public string Name { get; set; } } } \ No newline at end of file diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/StylingRule.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/StylingRule.cs index 713d69c3..e796be6d 100644 --- a/Assets/_BuildingBlocks/LayerStyles/Scripts/StylingRule.cs +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/StylingRule.cs @@ -1,11 +1,25 @@ +using System.Runtime.Serialization; + namespace Netherlands3D.LayerStyles { - public class StylingRule + [DataContract(Namespace = "https://netherlands3d.eu/schemas/projects/layers/styling", Name = "StylingRule")] + public sealed class StylingRule { - public string Name { get; } + public string Name { get; private set; } public Symbolizer Symbolizer { get; } = new(); - // By default, everything matches. And that means a "True" literal boolean. - public Expression Selector { get; } = BoolExpression.True(); + public Expression Selector { get; private set; } + + public StylingRule(string name) + { + Name = name; + Selector = BoolExpression.True(); + } + + public StylingRule(string name, Expression selector) + { + Name = name; + Selector = selector; + } } } \ No newline at end of file From cd88ba6e55dfb1ef7f909da7cb09710553fa6a18 Mon Sep 17 00:00:00 2001 From: Mike van Riel Date: Thu, 17 Oct 2024 07:48:49 +0200 Subject: [PATCH 06/11] Refactored "Utility" into a Factory class (which it is) and cleaned up the API in preparation for introducing a service that can apply styling --- .../Layers/LayerTypes/GeoJSONLineLayer.cs | 5 +- .../Layers/LayerTypes/GeoJSONPointLayer.cs | 5 +- .../Layers/LayerTypes/GeoJSONPolygonLayer.cs | 14 ++- ...Visualisations.meta => GeoJsonLayers.meta} | 0 .../FeatureLineVisualisations.cs | 0 .../FeatureLineVisualisations.cs.meta | 0 .../FeaturePointVisualisations.cs | 0 .../FeaturePointVisualisations.cs.meta | 0 .../FeaturePolygonVisualisations.cs | 0 .../FeaturePolygonVisualisations.cs.meta | 0 .../GeometryVisualizationFactory.cs} | 108 +++++++++++------- .../GeometryVisualizationFactory.cs.meta | 3 + .../IFeatureVisualisation.cs | 0 .../IFeatureVisualisation.cs.meta | 0 .../GeoJSONGeometryVisualizerUtility.cs.meta | 11 -- 15 files changed, 88 insertions(+), 58 deletions(-) rename Assets/Scripts/Layers/LayerTypes/{GeoJSONLayerFeatureVisualisations.meta => GeoJsonLayers.meta} (100%) rename Assets/Scripts/Layers/LayerTypes/{GeoJSONLayerFeatureVisualisations => GeoJsonLayers}/FeatureLineVisualisations.cs (100%) rename Assets/Scripts/Layers/LayerTypes/{GeoJSONLayerFeatureVisualisations => GeoJsonLayers}/FeatureLineVisualisations.cs.meta (100%) rename Assets/Scripts/Layers/LayerTypes/{GeoJSONLayerFeatureVisualisations => GeoJsonLayers}/FeaturePointVisualisations.cs (100%) rename Assets/Scripts/Layers/LayerTypes/{GeoJSONLayerFeatureVisualisations => GeoJsonLayers}/FeaturePointVisualisations.cs.meta (100%) rename Assets/Scripts/Layers/LayerTypes/{GeoJSONLayerFeatureVisualisations => GeoJsonLayers}/FeaturePolygonVisualisations.cs (100%) rename Assets/Scripts/Layers/LayerTypes/{GeoJSONLayerFeatureVisualisations => GeoJsonLayers}/FeaturePolygonVisualisations.cs.meta (100%) rename Assets/Scripts/{Utility/GeoJSONGeometryVisualizerUtility.cs => Layers/LayerTypes/GeoJsonLayers/GeometryVisualizationFactory.cs} (52%) create mode 100644 Assets/Scripts/Layers/LayerTypes/GeoJsonLayers/GeometryVisualizationFactory.cs.meta rename Assets/Scripts/Layers/LayerTypes/{GeoJSONLayerFeatureVisualisations => GeoJsonLayers}/IFeatureVisualisation.cs (100%) rename Assets/Scripts/Layers/LayerTypes/{GeoJSONLayerFeatureVisualisations => GeoJsonLayers}/IFeatureVisualisation.cs.meta (100%) delete mode 100644 Assets/Scripts/Utility/GeoJSONGeometryVisualizerUtility.cs.meta diff --git a/Assets/Scripts/Layers/LayerTypes/GeoJSONLineLayer.cs b/Assets/Scripts/Layers/LayerTypes/GeoJSONLineLayer.cs index e2e06360..b475a1c3 100644 --- a/Assets/Scripts/Layers/LayerTypes/GeoJSONLineLayer.cs +++ b/Assets/Scripts/Layers/LayerTypes/GeoJSONLineLayer.cs @@ -6,6 +6,7 @@ using GeoJSON.Net.Feature; using GeoJSON.Net.Geometry; using Netherlands3D.Coordinates; +using Netherlands3D.Twin.Layers.LayerTypes.GeoJsonLayers; using Netherlands3D.Twin.Projects; using Netherlands3D.Twin.UI.LayerInspector; using Netherlands3D.Visualisers; @@ -54,12 +55,12 @@ public void AddAndVisualizeFeature(Feature feature, CoordinateSystem original if (feature.Geometry is MultiLineString multiLineString) { - var newLines = GeoJSONGeometryVisualizerUtility.VisualizeMultiLineString(multiLineString, originalCoordinateSystem, lineRenderer3D); + var newLines = GeometryVisualizationFactory.CreateLineVisualisation(multiLineString, originalCoordinateSystem, lineRenderer3D); newFeatureVisualisation.lines.AddRange(newLines); } else if(feature.Geometry is LineString lineString) { - var newLine = GeoJSONGeometryVisualizerUtility.VisualizeLineString(lineString, originalCoordinateSystem, lineRenderer3D); + var newLine = GeometryVisualizationFactory.CreateLineVisualization(lineString, originalCoordinateSystem, lineRenderer3D); newFeatureVisualisation.lines.Add(newLine); } diff --git a/Assets/Scripts/Layers/LayerTypes/GeoJSONPointLayer.cs b/Assets/Scripts/Layers/LayerTypes/GeoJSONPointLayer.cs index c2f78cac..40a24ae6 100644 --- a/Assets/Scripts/Layers/LayerTypes/GeoJSONPointLayer.cs +++ b/Assets/Scripts/Layers/LayerTypes/GeoJSONPointLayer.cs @@ -5,6 +5,7 @@ using GeoJSON.Net.Feature; using GeoJSON.Net.Geometry; using Netherlands3D.Coordinates; +using Netherlands3D.Twin.Layers.LayerTypes.GeoJsonLayers; using Netherlands3D.Twin.Projects; using UnityEngine; @@ -51,12 +52,12 @@ public void AddAndVisualizeFeature(Feature feature, CoordinateSystem original if (feature.Geometry is MultiPoint multiPoint) { - var newPointCollection = GeoJSONGeometryVisualizerUtility.VisualizeMultiPoint(multiPoint, originalCoordinateSystem, PointRenderer3D); + var newPointCollection = GeometryVisualizationFactory.CreatePointVisualisation(multiPoint, originalCoordinateSystem, PointRenderer3D); newFeatureVisualisation.pointCollection.Add(newPointCollection); } else if(feature.Geometry is Point point) { - var newPointCollection = GeoJSONGeometryVisualizerUtility.VisualizePoint(point, originalCoordinateSystem, PointRenderer3D); + var newPointCollection = GeometryVisualizationFactory.CreatePointVisualization(point, originalCoordinateSystem, PointRenderer3D); newFeatureVisualisation.pointCollection.Add(newPointCollection); } diff --git a/Assets/Scripts/Layers/LayerTypes/GeoJSONPolygonLayer.cs b/Assets/Scripts/Layers/LayerTypes/GeoJSONPolygonLayer.cs index c74bb199..7ead8edb 100644 --- a/Assets/Scripts/Layers/LayerTypes/GeoJSONPolygonLayer.cs +++ b/Assets/Scripts/Layers/LayerTypes/GeoJSONPolygonLayer.cs @@ -7,6 +7,7 @@ using GeoJSON.Net.Geometry; using Netherlands3D.Coordinates; using Netherlands3D.SelectionTools; +using Netherlands3D.Twin.Layers.LayerTypes.GeoJsonLayers; using Netherlands3D.Twin.Projects; using Netherlands3D.Twin.UI.LayerInspector; using UnityEngine; @@ -22,7 +23,7 @@ public partial class GeoJSONPolygonLayer : LayerGameObject public bool RandomizeColorPerFeature { get => randomizeColorPerFeature; set => randomizeColorPerFeature = value; } [SerializeField] private Material polygonVisualizationMaterial; - private Material polygonVisualizationMaterialInstance; + internal Material polygonVisualizationMaterialInstance; public Material PolygonVisualizationMaterial { @@ -59,12 +60,12 @@ public void AddAndVisualizeFeature(Feature feature, CoordinateSystem original }; if (feature.Geometry is MultiPolygon multiPolygon) { - var polygonVisualisations = GeoJSONGeometryVisualizerUtility.VisualizeMultiPolygon(multiPolygon, originalCoordinateSystem, featureRenderMaterial); + var polygonVisualisations = GeometryVisualizationFactory.CreatePolygonVisualization(multiPolygon, originalCoordinateSystem, featureRenderMaterial); newFeatureVisualisation.AppendVisualisations(polygonVisualisations); } else if (feature.Geometry is Polygon polygon) { - var singlePolygonVisualisation = GeoJSONGeometryVisualizerUtility.VisualizePolygon(polygon, originalCoordinateSystem, featureRenderMaterial); + var singlePolygonVisualisation = GeometryVisualizationFactory.CreatePolygonVisualisation(polygon, originalCoordinateSystem, featureRenderMaterial); newFeatureVisualisation.AppendVisualisations(singlePolygonVisualisation); } @@ -85,7 +86,12 @@ private Material GetMaterialInstance() // Default to material with layer color if (polygonVisualizationMaterialInstance == null) - polygonVisualizationMaterialInstance = new Material(PolygonVisualizationMaterial) { color = LayerData.Color }; + { + polygonVisualizationMaterialInstance = new Material(PolygonVisualizationMaterial) + { + color = LayerData.Color + }; + } return polygonVisualizationMaterialInstance; } diff --git a/Assets/Scripts/Layers/LayerTypes/GeoJSONLayerFeatureVisualisations.meta b/Assets/Scripts/Layers/LayerTypes/GeoJsonLayers.meta similarity index 100% rename from Assets/Scripts/Layers/LayerTypes/GeoJSONLayerFeatureVisualisations.meta rename to Assets/Scripts/Layers/LayerTypes/GeoJsonLayers.meta diff --git a/Assets/Scripts/Layers/LayerTypes/GeoJSONLayerFeatureVisualisations/FeatureLineVisualisations.cs b/Assets/Scripts/Layers/LayerTypes/GeoJsonLayers/FeatureLineVisualisations.cs similarity index 100% rename from Assets/Scripts/Layers/LayerTypes/GeoJSONLayerFeatureVisualisations/FeatureLineVisualisations.cs rename to Assets/Scripts/Layers/LayerTypes/GeoJsonLayers/FeatureLineVisualisations.cs diff --git a/Assets/Scripts/Layers/LayerTypes/GeoJSONLayerFeatureVisualisations/FeatureLineVisualisations.cs.meta b/Assets/Scripts/Layers/LayerTypes/GeoJsonLayers/FeatureLineVisualisations.cs.meta similarity index 100% rename from Assets/Scripts/Layers/LayerTypes/GeoJSONLayerFeatureVisualisations/FeatureLineVisualisations.cs.meta rename to Assets/Scripts/Layers/LayerTypes/GeoJsonLayers/FeatureLineVisualisations.cs.meta diff --git a/Assets/Scripts/Layers/LayerTypes/GeoJSONLayerFeatureVisualisations/FeaturePointVisualisations.cs b/Assets/Scripts/Layers/LayerTypes/GeoJsonLayers/FeaturePointVisualisations.cs similarity index 100% rename from Assets/Scripts/Layers/LayerTypes/GeoJSONLayerFeatureVisualisations/FeaturePointVisualisations.cs rename to Assets/Scripts/Layers/LayerTypes/GeoJsonLayers/FeaturePointVisualisations.cs diff --git a/Assets/Scripts/Layers/LayerTypes/GeoJSONLayerFeatureVisualisations/FeaturePointVisualisations.cs.meta b/Assets/Scripts/Layers/LayerTypes/GeoJsonLayers/FeaturePointVisualisations.cs.meta similarity index 100% rename from Assets/Scripts/Layers/LayerTypes/GeoJSONLayerFeatureVisualisations/FeaturePointVisualisations.cs.meta rename to Assets/Scripts/Layers/LayerTypes/GeoJsonLayers/FeaturePointVisualisations.cs.meta diff --git a/Assets/Scripts/Layers/LayerTypes/GeoJSONLayerFeatureVisualisations/FeaturePolygonVisualisations.cs b/Assets/Scripts/Layers/LayerTypes/GeoJsonLayers/FeaturePolygonVisualisations.cs similarity index 100% rename from Assets/Scripts/Layers/LayerTypes/GeoJSONLayerFeatureVisualisations/FeaturePolygonVisualisations.cs rename to Assets/Scripts/Layers/LayerTypes/GeoJsonLayers/FeaturePolygonVisualisations.cs diff --git a/Assets/Scripts/Layers/LayerTypes/GeoJSONLayerFeatureVisualisations/FeaturePolygonVisualisations.cs.meta b/Assets/Scripts/Layers/LayerTypes/GeoJsonLayers/FeaturePolygonVisualisations.cs.meta similarity index 100% rename from Assets/Scripts/Layers/LayerTypes/GeoJSONLayerFeatureVisualisations/FeaturePolygonVisualisations.cs.meta rename to Assets/Scripts/Layers/LayerTypes/GeoJsonLayers/FeaturePolygonVisualisations.cs.meta diff --git a/Assets/Scripts/Utility/GeoJSONGeometryVisualizerUtility.cs b/Assets/Scripts/Layers/LayerTypes/GeoJsonLayers/GeometryVisualizationFactory.cs similarity index 52% rename from Assets/Scripts/Utility/GeoJSONGeometryVisualizerUtility.cs rename to Assets/Scripts/Layers/LayerTypes/GeoJsonLayers/GeometryVisualizationFactory.cs index 8e0188fe..9041667f 100644 --- a/Assets/Scripts/Utility/GeoJSONGeometryVisualizerUtility.cs +++ b/Assets/Scripts/Layers/LayerTypes/GeoJsonLayers/GeometryVisualizationFactory.cs @@ -5,31 +5,37 @@ using Netherlands3D.Twin.FloatingOrigin; using UnityEngine; -namespace Netherlands3D.Twin +namespace Netherlands3D.Twin.Layers.LayerTypes.GeoJsonLayers { - public static class GeoJSONGeometryVisualizerUtility + public static class GeometryVisualizationFactory { - public static List VisualizeMultiPolygon(MultiPolygon multiPolygon, CoordinateSystem originalCoordinateSystem, Material visualizationMaterial) - { - var visualizations = new List(multiPolygon.Coordinates.Count); - foreach (var polygon in multiPolygon.Coordinates) + public static List CreatePolygonVisualization( + MultiPolygon geometry, + CoordinateSystem coordinateSystem, + Material material + ) { + var visualizations = new List(geometry.Coordinates.Count); + foreach (var polygon in geometry.Coordinates) { - var visualization = VisualizePolygon(polygon, originalCoordinateSystem, visualizationMaterial); + var visualization = CreatePolygonVisualisation(polygon, coordinateSystem, material); visualizations.Add(visualization); } return visualizations; } - public static PolygonVisualisation VisualizePolygon(Polygon polygon, CoordinateSystem originalCoordinateSystem, Material visualizationMaterial) - { - var ringList = new List>(polygon.Coordinates.Count); + public static PolygonVisualisation CreatePolygonVisualisation( + Polygon geometry, + CoordinateSystem coordinateSystem, + Material material + ) { + var ringList = new List>(geometry.Coordinates.Count); - foreach (var lineString in polygon.Coordinates) + foreach (var lineString in geometry.Coordinates) { - var ring = ConvertToUnityCoordinates(lineString, originalCoordinateSystem); - var unityRing = new List(ring.Count); - foreach (var coord in ring) + var loop = ConvertToUnityCoordinates(lineString, coordinateSystem); + var unityRing = new List(loop.Count); + foreach (var coord in loop) { unityRing.Add(coord.ToUnity()); } @@ -37,15 +43,18 @@ public static PolygonVisualisation VisualizePolygon(Polygon polygon, CoordinateS ringList.Add(unityRing); } - return CreatePolygonMesh(ringList, 10f, visualizationMaterial); + return CreatePolygonMesh(ringList, 10f, material); } - public static List> VisualizeMultiLineString(MultiLineString multiLineString, CoordinateSystem originalCoordinateSystem, LineRenderer3D renderer) - { - var lines = new List>(multiLineString.Coordinates.Count); - foreach (var lineString in multiLineString.Coordinates) + public static List> CreateLineVisualisation( + MultiLineString geometry, + CoordinateSystem coordinateSystem, + LineRenderer3D renderer + ) { + var lines = new List>(geometry.Coordinates.Count); + foreach (var lineString in geometry.Coordinates) { - var convertedLineString = ConvertToUnityCoordinates(lineString, originalCoordinateSystem); + var convertedLineString = ConvertToUnityCoordinates(lineString, coordinateSystem); lines.Add(convertedLineString); } renderer.AppendLines(lines); @@ -53,33 +62,44 @@ public static List> VisualizeMultiLineString(MultiLineString mu return lines; } - public static List VisualizeLineString(LineString lineString, CoordinateSystem originalCoordinateSystem, LineRenderer3D renderer) - { - var line = ConvertToUnityCoordinates(lineString, originalCoordinateSystem); - renderer.AppendLine(line); + public static List CreateLineVisualization( + LineString geometry, + CoordinateSystem coordinateSystem, + LineRenderer3D renderer + ) { + renderer.AppendLine(ConvertToUnityCoordinates(geometry, coordinateSystem)); - return line; + return ConvertToUnityCoordinates(geometry, coordinateSystem); } - public static List VisualizeMultiPoint(MultiPoint multipoint, CoordinateSystem coordinateSystem, BatchedMeshInstanceRenderer renderer) - { - var convertedPoints = ConvertToUnityCoordinates(multipoint, coordinateSystem); + public static List CreatePointVisualisation( + MultiPoint geometry, + CoordinateSystem coordinateSystem, + BatchedMeshInstanceRenderer renderer + ) { + var convertedPoints = ConvertToUnityCoordinates(geometry, coordinateSystem); renderer.AppendCollection(convertedPoints); return convertedPoints; } - public static List VisualizePoint(Point point, CoordinateSystem coordinateSystem, BatchedMeshInstanceRenderer renderer) - { - var convertedPoint = ConvertToCoordinate(coordinateSystem, point.Coordinates); + public static List CreatePointVisualization( + Point geometry, + CoordinateSystem coordinateSystem, + BatchedMeshInstanceRenderer renderer + ) { + var convertedPoint = ConvertToCoordinate(coordinateSystem, geometry.Coordinates); var singlePointList = new List() { convertedPoint }; renderer.AppendCollection(singlePointList); return singlePointList; } - public static PolygonVisualisation CreatePolygonMesh(List> contours, float polygonExtrusionHeight, Material polygonMeshMaterial) - { + private static PolygonVisualisation CreatePolygonMesh( + List> contours, + float polygonExtrusionHeight, + Material polygonMeshMaterial + ) { var polygonVisualisation = PolygonVisualisationUtility.CreateAndReturnPolygonObject(contours, polygonExtrusionHeight, false, false, false, polygonMeshMaterial); //Add the polygon shifter to the polygon visualisation, so it can move with our origin shifts @@ -90,8 +110,11 @@ public static PolygonVisualisation CreatePolygonMesh(List> contour return polygonVisualisation; } - private static List ConvertToUnityCoordinates(LineString lineString, CoordinateSystem originalCoordinateSystem, float defaultNAPHeight = 0) - { + private static List ConvertToUnityCoordinates( + LineString lineString, + CoordinateSystem originalCoordinateSystem, + float defaultNAPHeight = 0 + ) { var convertedCoordinates = new List(lineString.Coordinates.Count); for (var i = 0; i < lineString.Coordinates.Count; i++) @@ -104,8 +127,11 @@ private static List ConvertToUnityCoordinates(LineString lineString, return convertedCoordinates; } - private static List ConvertToUnityCoordinates(MultiPoint multiPoint, CoordinateSystem originalCoordinateSystem, float defaultNAPHeight = 0) - { + private static List ConvertToUnityCoordinates( + MultiPoint multiPoint, + CoordinateSystem originalCoordinateSystem, + float defaultNAPHeight = 0 + ) { var convertedCoordinates = new List(multiPoint.Coordinates.Count); for (var i = 0; i < multiPoint.Coordinates.Count; i++) @@ -114,11 +140,15 @@ private static List ConvertToUnityCoordinates(MultiPoint multiPoint, var coordinate = ConvertToCoordinate(originalCoordinateSystem, point.Coordinates, defaultNAPHeight); convertedCoordinates.Add(coordinate); } + return convertedCoordinates; } - private static Coordinate ConvertToCoordinate(CoordinateSystem originalCoordinateSystem, IPosition point, float defaultNAPHeight = 0) - { + private static Coordinate ConvertToCoordinate( + CoordinateSystem originalCoordinateSystem, + IPosition point, + float defaultNAPHeight = 0 + ) { var lat = point.Latitude; var lon = point.Longitude; var alt = point.Altitude; diff --git a/Assets/Scripts/Layers/LayerTypes/GeoJsonLayers/GeometryVisualizationFactory.cs.meta b/Assets/Scripts/Layers/LayerTypes/GeoJsonLayers/GeometryVisualizationFactory.cs.meta new file mode 100644 index 00000000..c4e85199 --- /dev/null +++ b/Assets/Scripts/Layers/LayerTypes/GeoJsonLayers/GeometryVisualizationFactory.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2ec7c66706214de192b1edf177828bb2 +timeCreated: 1729143253 \ No newline at end of file diff --git a/Assets/Scripts/Layers/LayerTypes/GeoJSONLayerFeatureVisualisations/IFeatureVisualisation.cs b/Assets/Scripts/Layers/LayerTypes/GeoJsonLayers/IFeatureVisualisation.cs similarity index 100% rename from Assets/Scripts/Layers/LayerTypes/GeoJSONLayerFeatureVisualisations/IFeatureVisualisation.cs rename to Assets/Scripts/Layers/LayerTypes/GeoJsonLayers/IFeatureVisualisation.cs diff --git a/Assets/Scripts/Layers/LayerTypes/GeoJSONLayerFeatureVisualisations/IFeatureVisualisation.cs.meta b/Assets/Scripts/Layers/LayerTypes/GeoJsonLayers/IFeatureVisualisation.cs.meta similarity index 100% rename from Assets/Scripts/Layers/LayerTypes/GeoJSONLayerFeatureVisualisations/IFeatureVisualisation.cs.meta rename to Assets/Scripts/Layers/LayerTypes/GeoJsonLayers/IFeatureVisualisation.cs.meta diff --git a/Assets/Scripts/Utility/GeoJSONGeometryVisualizerUtility.cs.meta b/Assets/Scripts/Utility/GeoJSONGeometryVisualizerUtility.cs.meta deleted file mode 100644 index 6af584b2..00000000 --- a/Assets/Scripts/Utility/GeoJSONGeometryVisualizerUtility.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: eb273087fe3a948338e09f3695262507 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: From f568f2072496430bc002b9f3d9f00359617f19f6 Mon Sep 17 00:00:00 2001 From: Mike van Riel Date: Mon, 28 Oct 2024 11:11:19 +0100 Subject: [PATCH 07/11] Add missing serialization attributes --- .../LayerStyles/Scripts/BoolExpression.cs | 2 +- .../LayerStyles/Scripts/LayerStyle.cs | 6 +++--- .../LayerStyles/Scripts/Metadata.cs | 2 +- .../LayerStyles/Scripts/StylingRule.cs | 8 ++++---- .../LayerStyles/Scripts/Symbolizer.cs | 4 ++-- .../Scripts/VectorSymbologyExtension.cs | 19 +++++++++++++++++-- 6 files changed, 28 insertions(+), 13 deletions(-) diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/BoolExpression.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/BoolExpression.cs index f9b011bf..1afd33f7 100644 --- a/Assets/_BuildingBlocks/LayerStyles/Scripts/BoolExpression.cs +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/BoolExpression.cs @@ -5,7 +5,7 @@ namespace Netherlands3D.LayerStyles [DataContract(Namespace = "https://netherlands3d.eu/schemas/projects/layers/styling/expressions", Name = "Bool")] public class BoolExpression : LiteralExpression { - private readonly bool value; + [DataMember] private bool value; public BoolExpression(bool value) { diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/LayerStyle.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/LayerStyle.cs index d5606ecc..c9a71afa 100644 --- a/Assets/_BuildingBlocks/LayerStyles/Scripts/LayerStyle.cs +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/LayerStyle.cs @@ -4,11 +4,11 @@ namespace Netherlands3D.LayerStyles { [DataContract(Namespace = "https://netherlands3d.eu/schemas/projects/layers/styling", Name = "LayerStyle")] - public sealed class LayerStyle + public class LayerStyle { - public Metadata Metadata { get; } = new(); + [DataMember] public Metadata Metadata { get; } = new(); - public List StylingRules { get; } = new() + [DataMember] public List StylingRules { get; } = new() { new StylingRule("default") }; diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/Metadata.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/Metadata.cs index 68074724..ff0aad33 100644 --- a/Assets/_BuildingBlocks/LayerStyles/Scripts/Metadata.cs +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/Metadata.cs @@ -5,6 +5,6 @@ namespace Netherlands3D.LayerStyles [DataContract(Namespace = "https://netherlands3d.eu/schemas/projects/layers/styling", Name = "Metadata")] public class Metadata { - public string Name { get; set; } + [DataMember] public string Name { get; set; } } } \ No newline at end of file diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/StylingRule.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/StylingRule.cs index e796be6d..17134265 100644 --- a/Assets/_BuildingBlocks/LayerStyles/Scripts/StylingRule.cs +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/StylingRule.cs @@ -3,12 +3,12 @@ namespace Netherlands3D.LayerStyles { [DataContract(Namespace = "https://netherlands3d.eu/schemas/projects/layers/styling", Name = "StylingRule")] - public sealed class StylingRule + public class StylingRule { - public string Name { get; private set; } - public Symbolizer Symbolizer { get; } = new(); + [DataMember] public string Name { get; private set; } + [DataMember] public Symbolizer Symbolizer { get; } = new(); - public Expression Selector { get; private set; } + [DataMember] public Expression Selector { get; private set; } public StylingRule(string name) { diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/Symbolizer.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/Symbolizer.cs index 92695ff7..45affda3 100644 --- a/Assets/_BuildingBlocks/LayerStyles/Scripts/Symbolizer.cs +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/Symbolizer.cs @@ -4,9 +4,9 @@ namespace Netherlands3D.LayerStyles { [DataContract(Namespace = "https://netherlands3d.eu/schemas/projects/layers/styling", Name = "Symbolizer")] - public sealed class Symbolizer + public class Symbolizer { - private readonly Dictionary properties = new(); + [DataMember] private Dictionary properties = new(); internal object GetProperty(string key) { diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/VectorSymbologyExtension.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/VectorSymbologyExtension.cs index 429fea9d..20138b0d 100644 --- a/Assets/_BuildingBlocks/LayerStyles/Scripts/VectorSymbologyExtension.cs +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/VectorSymbologyExtension.cs @@ -2,16 +2,31 @@ namespace Netherlands3D.LayerStyles { + /// public static class VectorSymbologyExtension { + /// public static void SetFillColor(this Symbolizer symbology, Color color) { - symbology.SetProperty("fill", color); + symbology.SetProperty("fill-color", color); } + /// public static Color? GetFillColor(this Symbolizer symbology) { - return symbology.GetProperty("fill") as Color?; + return symbology.GetProperty("fill-color") as Color?; + } + + /// + public static void SetStrokeColor(this Symbolizer symbology, Color color) + { + symbology.SetProperty("stroke-color", color); + } + + /// + public static Color? GetStrokeColor(this Symbolizer symbology) + { + return symbology.GetProperty("stroke-color") as Color?; } } } \ No newline at end of file From 5174708d8ede6dac336c88164a42af89aab609ee Mon Sep 17 00:00:00 2001 From: Mike van Riel Date: Mon, 28 Oct 2024 11:12:56 +0100 Subject: [PATCH 08/11] Set a random fill and stroke color during import, and ensure that is used --- .../DataTypeAdapters/GeoJSONImportAdapter.cs | 15 +++-- .../WFSGeoJSONImportAdapter.cs | 5 ++ .../Layers/LayerTypes/GeoJSONLineLayer.cs | 20 ++----- .../Layers/LayerTypes/GeoJSONPointLayer.cs | 21 ++----- .../Layers/LayerTypes/GeoJSONPolygonLayer.cs | 56 +++++++++---------- Assets/Scripts/Layers/LayerTypes/LayerData.cs | 10 +++- 6 files changed, 57 insertions(+), 70 deletions(-) diff --git a/Assets/Scripts/Layers/Adapters/DataTypeAdapters/GeoJSONImportAdapter.cs b/Assets/Scripts/Layers/Adapters/DataTypeAdapters/GeoJSONImportAdapter.cs index 1927baf7..d0f2faa2 100644 --- a/Assets/Scripts/Layers/Adapters/DataTypeAdapters/GeoJSONImportAdapter.cs +++ b/Assets/Scripts/Layers/Adapters/DataTypeAdapters/GeoJSONImportAdapter.cs @@ -6,6 +6,8 @@ using Netherlands3D.Twin.Layers.Properties; using Netherlands3D.Twin.Projects; using System; +using System.Linq; +using Netherlands3D.LayerStyles; using Netherlands3D.Twin.Projects.ExtensionMethods; namespace Netherlands3D.Twin @@ -74,14 +76,15 @@ private void CreateGeoJSONLayer(LocalFile localFile, UnityEvent onErrorC randomLayerColor.a = 0.5f; newLayer.LayerData.Color = randomLayerColor; + var symbolizer = newLayer.LayerData.DefaultSymbolizer; + symbolizer?.SetFillColor(randomLayerColor); + symbolizer?.SetStrokeColor(randomLayerColor); + var localPath = localFile.LocalFilePath; - var fileName = Path.GetFileName(localPath); var propertyData = newLayer.PropertyData as LayerURLPropertyData; - - if (localFile.SourceUrl.StartsWith("http")) - propertyData.Data = AssetUriFactory.CreateRemoteAssetUri(localFile.SourceUrl); - else - propertyData.Data = AssetUriFactory.CreateProjectAssetUri(localPath); + propertyData.Data = localFile.SourceUrl.StartsWith("http") + ? AssetUriFactory.CreateRemoteAssetUri(localFile.SourceUrl) + : AssetUriFactory.CreateProjectAssetUri(localPath); } } } \ No newline at end of file diff --git a/Assets/Scripts/Layers/Adapters/DataTypeAdapters/WFSGeoJSONImportAdapter.cs b/Assets/Scripts/Layers/Adapters/DataTypeAdapters/WFSGeoJSONImportAdapter.cs index 1d794077..836c3579 100644 --- a/Assets/Scripts/Layers/Adapters/DataTypeAdapters/WFSGeoJSONImportAdapter.cs +++ b/Assets/Scripts/Layers/Adapters/DataTypeAdapters/WFSGeoJSONImportAdapter.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.Xml.Serialization; +using Netherlands3D.LayerStyles; using Netherlands3D.Twin.Layers; using UnityEngine.Networking; using Netherlands3D.Twin.Layers.Properties; @@ -128,6 +129,10 @@ private void AddWFSLayer(string featureType, string sourceUrl, FolderLayer folde var randomLayerColor = Color.HSVToRGB(UnityEngine.Random.value, UnityEngine.Random.Range(0.5f, 1f), 1); randomLayerColor.a = 0.5f; newLayer.LayerData.Color = randomLayerColor; + + var symbolizer = newLayer.LayerData.DefaultSymbolizer; + symbolizer?.SetFillColor(randomLayerColor); + symbolizer?.SetStrokeColor(randomLayerColor); } private UriBuilder CreateLayerUri(string featureType, string sourceUrl) diff --git a/Assets/Scripts/Layers/LayerTypes/GeoJSONLineLayer.cs b/Assets/Scripts/Layers/LayerTypes/GeoJSONLineLayer.cs index b475a1c3..6f5d4a88 100644 --- a/Assets/Scripts/Layers/LayerTypes/GeoJSONLineLayer.cs +++ b/Assets/Scripts/Layers/LayerTypes/GeoJSONLineLayer.cs @@ -6,6 +6,7 @@ using GeoJSON.Net.Feature; using GeoJSON.Net.Geometry; using Netherlands3D.Coordinates; +using Netherlands3D.LayerStyles; using Netherlands3D.Twin.Layers.LayerTypes.GeoJsonLayers; using Netherlands3D.Twin.Projects; using Netherlands3D.Twin.UI.LayerInspector; @@ -19,9 +20,6 @@ public partial class GeoJSONLineLayer : LayerGameObject { public List SpawnedVisualisations = new(); - private bool randomizeColorPerFeature = false; - public bool RandomizeColorPerFeature { get => randomizeColorPerFeature; set => randomizeColorPerFeature = value; } - [SerializeField] private LineRenderer3D lineRenderer3D; public LineRenderer3D LineRenderer3D @@ -69,20 +67,10 @@ public void AddAndVisualizeFeature(Feature feature, CoordinateSystem original private Material GetMaterialInstance() { - Material featureMaterialInstance; - // Create material with random color if randomize per feature is enabled - if (RandomizeColorPerFeature) + return new Material(lineRenderer3D.LineMaterial) { - var randomColor = UnityEngine.Random.ColorHSV(); - randomColor.a = LayerData.Color.a; - - featureMaterialInstance = new Material(lineRenderer3D.LineMaterial) { color = randomColor }; - return featureMaterialInstance; - } - - // Default to material with layer color - featureMaterialInstance = new Material(lineRenderer3D.LineMaterial) { color = LayerData.Color }; - return featureMaterialInstance; + color = LayerData.DefaultSymbolizer?.GetStrokeColor() ?? Color.white + }; } /// diff --git a/Assets/Scripts/Layers/LayerTypes/GeoJSONPointLayer.cs b/Assets/Scripts/Layers/LayerTypes/GeoJSONPointLayer.cs index 40a24ae6..aa074e11 100644 --- a/Assets/Scripts/Layers/LayerTypes/GeoJSONPointLayer.cs +++ b/Assets/Scripts/Layers/LayerTypes/GeoJSONPointLayer.cs @@ -5,6 +5,7 @@ using GeoJSON.Net.Feature; using GeoJSON.Net.Geometry; using Netherlands3D.Coordinates; +using Netherlands3D.LayerStyles; using Netherlands3D.Twin.Layers.LayerTypes.GeoJsonLayers; using Netherlands3D.Twin.Projects; using UnityEngine; @@ -16,9 +17,6 @@ public partial class GeoJSONPointLayer : LayerGameObject { public List SpawnedVisualisations = new(); - private bool randomizeColorPerFeature = false; - public bool RandomizeColorPerFeature { get => randomizeColorPerFeature; set => randomizeColorPerFeature = value; } - [SerializeField] private BatchedMeshInstanceRenderer pointRenderer3D; public BatchedMeshInstanceRenderer PointRenderer3D @@ -47,7 +45,6 @@ public void AddAndVisualizeFeature(Feature feature, CoordinateSystem original var newFeatureVisualisation = new FeaturePointVisualisations() { feature = feature }; - // Create visual with random color if enabled pointRenderer3D.Material = GetMaterialInstance(); if (feature.Geometry is MultiPoint multiPoint) @@ -66,20 +63,10 @@ public void AddAndVisualizeFeature(Feature feature, CoordinateSystem original private Material GetMaterialInstance() { - Material featureMaterialInstance; - // Create material with random color if randomize per feature is enabled - if (RandomizeColorPerFeature) + return new Material(pointRenderer3D.Material) { - var randomColor = UnityEngine.Random.ColorHSV(); - randomColor.a = LayerData.Color.a; - - featureMaterialInstance = new Material(pointRenderer3D.Material) { color = randomColor }; - return featureMaterialInstance; - } - - // Default to material with layer color - featureMaterialInstance = new Material(pointRenderer3D.Material) { color = LayerData.Color }; - return featureMaterialInstance; + color = LayerData.DefaultSymbolizer?.GetFillColor() ?? Color.white + }; } /// diff --git a/Assets/Scripts/Layers/LayerTypes/GeoJSONPolygonLayer.cs b/Assets/Scripts/Layers/LayerTypes/GeoJSONPolygonLayer.cs index 7ead8edb..69faa296 100644 --- a/Assets/Scripts/Layers/LayerTypes/GeoJSONPolygonLayer.cs +++ b/Assets/Scripts/Layers/LayerTypes/GeoJSONPolygonLayer.cs @@ -6,6 +6,7 @@ using GeoJSON.Net.Feature; using GeoJSON.Net.Geometry; using Netherlands3D.Coordinates; +using Netherlands3D.LayerStyles; using Netherlands3D.SelectionTools; using Netherlands3D.Twin.Layers.LayerTypes.GeoJsonLayers; using Netherlands3D.Twin.Projects; @@ -19,28 +20,29 @@ public partial class GeoJSONPolygonLayer : LayerGameObject { public List SpawnedVisualisations = new(); - private bool randomizeColorPerFeature = false; - public bool RandomizeColorPerFeature { get => randomizeColorPerFeature; set => randomizeColorPerFeature = value; } - [SerializeField] private Material polygonVisualizationMaterial; internal Material polygonVisualizationMaterialInstance; public Material PolygonVisualizationMaterial { - get { return polygonVisualizationMaterial; } + get => polygonVisualizationMaterial; set { polygonVisualizationMaterial = value; foreach (var featureVisualisation in SpawnedVisualisations) + { featureVisualisation.SetMaterial(value); + } } } public override void OnLayerActiveInHierarchyChanged(bool activeInHierarchy) { foreach (var visualization in SpawnedVisualisations) + { visualization.ShowVisualisations(activeInHierarchy); + } } public void AddAndVisualizeFeature(Feature feature, CoordinateSystem originalCoordinateSystem) @@ -50,23 +52,30 @@ public void AddAndVisualizeFeature(Feature feature, CoordinateSystem original if (SpawnedVisualisations.Any(f => f.feature.GetHashCode() == feature.GetHashCode())) return; - // Create visual with random color if enabled - Material featureRenderMaterial = GetMaterialInstance(); - - // Add visualisation to the layer, and store it in the SpawnedVisualisations list where we tie our Feature to the visualisations var newFeatureVisualisation = new FeaturePolygonVisualisations { feature = feature, geoJsonPolygonLayer = this }; - if (feature.Geometry is MultiPolygon multiPolygon) - { - var polygonVisualisations = GeometryVisualizationFactory.CreatePolygonVisualization(multiPolygon, originalCoordinateSystem, featureRenderMaterial); - newFeatureVisualisation.AppendVisualisations(polygonVisualisations); - } - else if (feature.Geometry is Polygon polygon) + Material featureRenderMaterial = GetMaterialInstance(); + + // Add visualisation to the layer, and store it in the SpawnedVisualisations list where we tie our Feature + // to the visualisations + switch (feature.Geometry) { - var singlePolygonVisualisation = GeometryVisualizationFactory.CreatePolygonVisualisation(polygon, originalCoordinateSystem, featureRenderMaterial); - newFeatureVisualisation.AppendVisualisations(singlePolygonVisualisation); + case MultiPolygon multiPolygon: + newFeatureVisualisation.AppendVisualisations(GeometryVisualizationFactory.CreatePolygonVisualization( + multiPolygon, + originalCoordinateSystem, + featureRenderMaterial + )); + break; + case Polygon polygon: + newFeatureVisualisation.AppendVisualisations(GeometryVisualizationFactory.CreatePolygonVisualisation( + polygon, + originalCoordinateSystem, + featureRenderMaterial + )); + break; } SpawnedVisualisations.Add(newFeatureVisualisation); @@ -74,22 +83,11 @@ public void AddAndVisualizeFeature(Feature feature, CoordinateSystem original private Material GetMaterialInstance() { - // Create material with random color if randomize per feature is enabled - if (RandomizeColorPerFeature) - { - var randomColor = UnityEngine.Random.ColorHSV(); - randomColor.a = LayerData.Color.a; - - var featureMaterialInstance = new Material(PolygonVisualizationMaterial) { color = randomColor }; - return featureMaterialInstance; - } - - // Default to material with layer color - if (polygonVisualizationMaterialInstance == null) + if (!polygonVisualizationMaterialInstance) { polygonVisualizationMaterialInstance = new Material(PolygonVisualizationMaterial) { - color = LayerData.Color + color = LayerData.DefaultSymbolizer?.GetFillColor() ?? Color.white }; } diff --git a/Assets/Scripts/Layers/LayerTypes/LayerData.cs b/Assets/Scripts/Layers/LayerTypes/LayerData.cs index 2303176e..7d1cd97f 100644 --- a/Assets/Scripts/Layers/LayerTypes/LayerData.cs +++ b/Assets/Scripts/Layers/LayerTypes/LayerData.cs @@ -40,7 +40,7 @@ public abstract class LayerData /// A list of styles with their names (which are meant as machine-readable names and not human-readable names, /// for the latter the 'title' field exists), including a default style that always applies. /// - [DataMember] private Dictionary styles = new() + [SerializeField, DataMember] protected Dictionary styles = new() { {"default", LayerStyle.CreateDefaultStyle()} }; @@ -152,7 +152,13 @@ public bool HasValidCredentials /// Every layer has a default style, this is a style that applies to all objects and features in this /// layer without any conditions. /// - [JsonIgnore] public LayerStyle GetDefaultStyle => Styles["default"]; + [JsonIgnore] public LayerStyle DefaultStyle => Styles["default"]; + + /// + /// Every layer has a default symbolizer, drawn from the default style, that can be queried for the appropriate + /// properties. + /// + [JsonIgnore] public Symbolizer DefaultSymbolizer => DefaultStyle.StylingRules.FirstOrDefault()?.Symbolizer; [JsonIgnore] public readonly UnityEvent NameChanged = new(); [JsonIgnore] public readonly UnityEvent LayerActiveInHierarchyChanged = new(); From 0e62a91f8715e738ca72867ec83610f79f58e2ec Mon Sep 17 00:00:00 2001 From: Mike van Riel Date: Mon, 28 Oct 2024 11:45:06 +0100 Subject: [PATCH 09/11] Ensure styles are persisted correctly when saving a project, and that the project can be reloaded. For some reason there is still an issue reloading the project as all strokes and fills are now the default white. --- .../LayerTypes/GeoJsonLayerGameObject.cs | 60 +++++++++++++------ .../LayerStyles/Scripts/StylingRule.cs | 7 +++ 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/Assets/Scripts/Layers/LayerTypes/GeoJsonLayerGameObject.cs b/Assets/Scripts/Layers/LayerTypes/GeoJsonLayerGameObject.cs index 21dd850c..cc3e47aa 100644 --- a/Assets/Scripts/Layers/LayerTypes/GeoJsonLayerGameObject.cs +++ b/Assets/Scripts/Layers/LayerTypes/GeoJsonLayerGameObject.cs @@ -13,6 +13,7 @@ using UnityEngine.Events; using Netherlands3D.Twin.Layers.Properties; using System.Linq; +using Netherlands3D.LayerStyles; using Netherlands3D.Twin.Projects.ExtensionMethods; using UnityEngine.Networking; @@ -234,16 +235,25 @@ private GeoJSONPolygonLayer CreateOrGetPolygonLayer() var childrenInLayerData = LayerData.ChildrenLayers; foreach (var child in childrenInLayerData) { - if(child is ReferencedLayerData referencedLayerData) - { - if(referencedLayerData.Reference is GeoJSONPolygonLayer polygonLayer) - return polygonLayer; - } + if (child is not ReferencedLayerData referencedLayerData) continue; + if (referencedLayerData.Reference is not GeoJSONPolygonLayer polygonLayer) continue; + + return polygonLayer; } GeoJSONPolygonLayer newPolygonLayerGameObject = Instantiate(polygonLayerPrefab); newPolygonLayerGameObject.LayerData.Color = LayerData.Color; + + // TODO: This should respond to changes in the style, and not be set directly. But that is beyond scope + // of my current activity + var lineMaterial = new Material(newPolygonLayerGameObject.PolygonVisualizationMaterial) + { + color = newPolygonLayerGameObject.LayerData.DefaultSymbolizer.GetFillColor() ?? Color.white + }; + newPolygonLayerGameObject.polygonVisualizationMaterialInstance = lineMaterial; + newPolygonLayerGameObject.LayerData.SetParent(LayerData); + return newPolygonLayerGameObject; } @@ -252,17 +262,25 @@ private GeoJSONLineLayer CreateOrGetLineLayer() var childrenInLayerData = LayerData.ChildrenLayers; foreach (var child in childrenInLayerData) { - if(child is ReferencedLayerData referencedLayerData) - { - if(referencedLayerData.Reference is GeoJSONLineLayer lineLayer) - return lineLayer; - } + if (child is not ReferencedLayerData referencedLayerData) continue; + if (referencedLayerData.Reference is not GeoJSONLineLayer lineLayer) continue; + + return lineLayer; } GeoJSONLineLayer newLineLayerGameObject = Instantiate(lineLayerPrefab); newLineLayerGameObject.LayerData.Color = LayerData.Color; - var lineMaterial = new Material(newLineLayerGameObject.LineRenderer3D.LineMaterial) { color = LayerData.Color }; + // Replace default style with the parent's default style + newLineLayerGameObject.LayerData.RemoveStyle(newLineLayerGameObject.LayerData.DefaultStyle); + newLineLayerGameObject.LayerData.AddStyle(LayerData.DefaultStyle); + + // TODO: This should respond to changes in the style, and not be set directly. But that is beyond scope + // of my current activity + var lineMaterial = new Material(newLineLayerGameObject.LineRenderer3D.LineMaterial) + { + color = newLineLayerGameObject.LayerData.DefaultSymbolizer.GetStrokeColor() ?? Color.white + }; newLineLayerGameObject.LineRenderer3D.LineMaterial = lineMaterial; newLineLayerGameObject.LayerData.SetParent(LayerData); @@ -274,17 +292,25 @@ private GeoJSONPointLayer CreateOrGetPointLayer() var childrenInLayerData = LayerData.ChildrenLayers; foreach (var child in childrenInLayerData) { - if(child is ReferencedLayerData referencedLayerData) - { - if(referencedLayerData.Reference is GeoJSONPointLayer pointLayer) - return pointLayer; - } + if (child is not ReferencedLayerData referencedLayerData) continue; + if (referencedLayerData.Reference is not GeoJSONPointLayer pointLayer) continue; + + return pointLayer; } GeoJSONPointLayer newPointLayerGameObject = Instantiate(pointLayerPrefab); newPointLayerGameObject.LayerData.Color = LayerData.Color; - var pointMaterial = new Material(newPointLayerGameObject.PointRenderer3D.Material) { color = LayerData.Color }; + // Replace default style with the parent's default style + newPointLayerGameObject.LayerData.RemoveStyle(newPointLayerGameObject.LayerData.DefaultStyle); + newPointLayerGameObject.LayerData.AddStyle(LayerData.DefaultStyle); + + // TODO: This should respond to changes in the style, and not be set directly. But that is beyond scope + // of my current activity + var pointMaterial = new Material(newPointLayerGameObject.PointRenderer3D.Material) + { + color = newPointLayerGameObject.LayerData.DefaultSymbolizer.GetFillColor() ?? Color.white + }; newPointLayerGameObject.PointRenderer3D.Material = pointMaterial; newPointLayerGameObject.LayerData.SetParent(LayerData); diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/StylingRule.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/StylingRule.cs index 17134265..57b3afea 100644 --- a/Assets/_BuildingBlocks/LayerStyles/Scripts/StylingRule.cs +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/StylingRule.cs @@ -1,4 +1,5 @@ using System.Runtime.Serialization; +using Newtonsoft.Json; namespace Netherlands3D.LayerStyles { @@ -10,6 +11,12 @@ public class StylingRule [DataMember] public Expression Selector { get; private set; } + [JsonConstructor] + private StylingRule() + { + Name = ""; + } + public StylingRule(string name) { Name = name; From 6e0db0c1825c444c665fbc2e6dbeeb3fdb4bdba6 Mon Sep 17 00:00:00 2001 From: Mike van Riel Date: Mon, 28 Oct 2024 15:28:53 +0100 Subject: [PATCH 10/11] Add EditorWindows to debug LayerData These editor windows will help determine whether projects correctly load the styles from disk --- Assets/Scripts/Editor/Layers.meta | 3 + .../Layers/GeoJsonLayerGameObjectEditor.cs | 23 +++ .../GeoJsonLayerGameObjectEditor.cs.meta | 3 + .../GeoJsonLineLayerGameObjectEditor.cs | 23 +++ .../GeoJsonLineLayerGameObjectEditor.cs.meta | 3 + .../GeoJsonPointLayerGameObjectEditor.cs | 23 +++ .../GeoJsonPointLayerGameObjectEditor.cs.meta | 3 + .../GeoJsonPolygonLayerGameObjectEditor.cs | 23 +++ ...eoJsonPolygonLayerGameObjectEditor.cs.meta | 3 + ...HierarchicalObjectLayerGameObjectEditor.cs | 23 +++ ...rchicalObjectLayerGameObjectEditor.cs.meta | 3 + .../Editor/Layers/LayerDataVisualElements.cs | 150 ++++++++++++++++++ .../Layers/LayerDataVisualElements.cs.meta | 3 + .../Layers/WfsGeoJsonLayerGameObjectEditor.cs | 23 +++ .../WfsGeoJsonLayerGameObjectEditor.cs.meta | 3 + .../Layers/LayerTypes/GeoJSONLineLayer.cs | 3 +- Assets/Scripts/Layers/LayerTypes/LayerData.cs | 2 +- .../LayerStyles/Scripts/BoolExpression.cs | 5 + .../LayerStyles/Scripts/Symbolizer.cs | 15 +- 19 files changed, 334 insertions(+), 3 deletions(-) create mode 100644 Assets/Scripts/Editor/Layers.meta create mode 100644 Assets/Scripts/Editor/Layers/GeoJsonLayerGameObjectEditor.cs create mode 100644 Assets/Scripts/Editor/Layers/GeoJsonLayerGameObjectEditor.cs.meta create mode 100644 Assets/Scripts/Editor/Layers/GeoJsonLineLayerGameObjectEditor.cs create mode 100644 Assets/Scripts/Editor/Layers/GeoJsonLineLayerGameObjectEditor.cs.meta create mode 100644 Assets/Scripts/Editor/Layers/GeoJsonPointLayerGameObjectEditor.cs create mode 100644 Assets/Scripts/Editor/Layers/GeoJsonPointLayerGameObjectEditor.cs.meta create mode 100644 Assets/Scripts/Editor/Layers/GeoJsonPolygonLayerGameObjectEditor.cs create mode 100644 Assets/Scripts/Editor/Layers/GeoJsonPolygonLayerGameObjectEditor.cs.meta create mode 100644 Assets/Scripts/Editor/Layers/HierarchicalObjectLayerGameObjectEditor.cs create mode 100644 Assets/Scripts/Editor/Layers/HierarchicalObjectLayerGameObjectEditor.cs.meta create mode 100644 Assets/Scripts/Editor/Layers/LayerDataVisualElements.cs create mode 100644 Assets/Scripts/Editor/Layers/LayerDataVisualElements.cs.meta create mode 100644 Assets/Scripts/Editor/Layers/WfsGeoJsonLayerGameObjectEditor.cs create mode 100644 Assets/Scripts/Editor/Layers/WfsGeoJsonLayerGameObjectEditor.cs.meta diff --git a/Assets/Scripts/Editor/Layers.meta b/Assets/Scripts/Editor/Layers.meta new file mode 100644 index 00000000..f441582e --- /dev/null +++ b/Assets/Scripts/Editor/Layers.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c22ab3caa6ca421281eef5bc2c12bb41 +timeCreated: 1730115130 \ No newline at end of file diff --git a/Assets/Scripts/Editor/Layers/GeoJsonLayerGameObjectEditor.cs b/Assets/Scripts/Editor/Layers/GeoJsonLayerGameObjectEditor.cs new file mode 100644 index 00000000..5574597f --- /dev/null +++ b/Assets/Scripts/Editor/Layers/GeoJsonLayerGameObjectEditor.cs @@ -0,0 +1,23 @@ +using Netherlands3D.Twin.Layers; +using UnityEditor; +using UnityEditor.UIElements; +using UnityEngine.UIElements; + +namespace Netherlands3D.Twin.Editor.Layers +{ + [CustomEditor(typeof(GeoJsonLayerGameObject))] + public class GeoJsonLayerGameObjectEditor : UnityEditor.Editor + { + public override VisualElement CreateInspectorGUI() + { + var root = new VisualElement(); + + InspectorElement.FillDefaultInspector(root, serializedObject, this); + + var layerGameObject = (GeoJsonLayerGameObject)target; + LayerDataVisualElements.LayerData(layerGameObject.LayerData, root); + + return root; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Editor/Layers/GeoJsonLayerGameObjectEditor.cs.meta b/Assets/Scripts/Editor/Layers/GeoJsonLayerGameObjectEditor.cs.meta new file mode 100644 index 00000000..67eb89c2 --- /dev/null +++ b/Assets/Scripts/Editor/Layers/GeoJsonLayerGameObjectEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8ee8ef76e7ab426881fa1335ec6aac13 +timeCreated: 1730124498 \ No newline at end of file diff --git a/Assets/Scripts/Editor/Layers/GeoJsonLineLayerGameObjectEditor.cs b/Assets/Scripts/Editor/Layers/GeoJsonLineLayerGameObjectEditor.cs new file mode 100644 index 00000000..bb44d08e --- /dev/null +++ b/Assets/Scripts/Editor/Layers/GeoJsonLineLayerGameObjectEditor.cs @@ -0,0 +1,23 @@ +using Netherlands3D.Twin.Layers; +using UnityEditor; +using UnityEditor.UIElements; +using UnityEngine.UIElements; + +namespace Netherlands3D.Twin.Editor.Layers +{ + [CustomEditor(typeof(GeoJSONLineLayer))] + public class GeoJsonLineLayerGameObjectEditor : UnityEditor.Editor + { + public override VisualElement CreateInspectorGUI() + { + var root = new VisualElement(); + + InspectorElement.FillDefaultInspector(root, serializedObject, this); + + var layerGameObject = (GeoJSONLineLayer)target; + LayerDataVisualElements.LayerData(layerGameObject.LayerData, root); + + return root; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Editor/Layers/GeoJsonLineLayerGameObjectEditor.cs.meta b/Assets/Scripts/Editor/Layers/GeoJsonLineLayerGameObjectEditor.cs.meta new file mode 100644 index 00000000..08c25933 --- /dev/null +++ b/Assets/Scripts/Editor/Layers/GeoJsonLineLayerGameObjectEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d35b3a45d80447b6a8f9da3a06f73027 +timeCreated: 1730124551 \ No newline at end of file diff --git a/Assets/Scripts/Editor/Layers/GeoJsonPointLayerGameObjectEditor.cs b/Assets/Scripts/Editor/Layers/GeoJsonPointLayerGameObjectEditor.cs new file mode 100644 index 00000000..130313b8 --- /dev/null +++ b/Assets/Scripts/Editor/Layers/GeoJsonPointLayerGameObjectEditor.cs @@ -0,0 +1,23 @@ +using Netherlands3D.Twin.Layers; +using UnityEditor; +using UnityEditor.UIElements; +using UnityEngine.UIElements; + +namespace Netherlands3D.Twin.Editor.Layers +{ + [CustomEditor(typeof(GeoJSONPointLayer))] + public class GeoJsonPointLayerGameObjectEditor : UnityEditor.Editor + { + public override VisualElement CreateInspectorGUI() + { + var root = new VisualElement(); + + InspectorElement.FillDefaultInspector(root, serializedObject, this); + + var layerGameObject = (GeoJSONPointLayer)target; + LayerDataVisualElements.LayerData(layerGameObject.LayerData, root); + + return root; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Editor/Layers/GeoJsonPointLayerGameObjectEditor.cs.meta b/Assets/Scripts/Editor/Layers/GeoJsonPointLayerGameObjectEditor.cs.meta new file mode 100644 index 00000000..9f694e2d --- /dev/null +++ b/Assets/Scripts/Editor/Layers/GeoJsonPointLayerGameObjectEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7b82c6f17c654ebcad504b1aa8a541b9 +timeCreated: 1730124574 \ No newline at end of file diff --git a/Assets/Scripts/Editor/Layers/GeoJsonPolygonLayerGameObjectEditor.cs b/Assets/Scripts/Editor/Layers/GeoJsonPolygonLayerGameObjectEditor.cs new file mode 100644 index 00000000..4456e461 --- /dev/null +++ b/Assets/Scripts/Editor/Layers/GeoJsonPolygonLayerGameObjectEditor.cs @@ -0,0 +1,23 @@ +using Netherlands3D.Twin.Layers; +using UnityEditor; +using UnityEditor.UIElements; +using UnityEngine.UIElements; + +namespace Netherlands3D.Twin.Editor.Layers +{ + [CustomEditor(typeof(GeoJSONPolygonLayer))] + public class GeoJsonPolygonLayerGameObjectEditor : UnityEditor.Editor + { + public override VisualElement CreateInspectorGUI() + { + var root = new VisualElement(); + + InspectorElement.FillDefaultInspector(root, serializedObject, this); + + var layerGameObject = (GeoJSONPolygonLayer)target; + LayerDataVisualElements.LayerData(layerGameObject.LayerData, root); + + return root; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Editor/Layers/GeoJsonPolygonLayerGameObjectEditor.cs.meta b/Assets/Scripts/Editor/Layers/GeoJsonPolygonLayerGameObjectEditor.cs.meta new file mode 100644 index 00000000..23a70995 --- /dev/null +++ b/Assets/Scripts/Editor/Layers/GeoJsonPolygonLayerGameObjectEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 325b86e1fb8644f18fc5fe222a5dab65 +timeCreated: 1730124602 \ No newline at end of file diff --git a/Assets/Scripts/Editor/Layers/HierarchicalObjectLayerGameObjectEditor.cs b/Assets/Scripts/Editor/Layers/HierarchicalObjectLayerGameObjectEditor.cs new file mode 100644 index 00000000..13b39ea2 --- /dev/null +++ b/Assets/Scripts/Editor/Layers/HierarchicalObjectLayerGameObjectEditor.cs @@ -0,0 +1,23 @@ +using Netherlands3D.Twin.Layers; +using UnityEditor; +using UnityEditor.UIElements; +using UnityEngine.UIElements; + +namespace Netherlands3D.Twin.Editor.Layers +{ + [CustomEditor(typeof(HierarchicalObjectLayerGameObject))] + public class HierarchicalObjectLayerGameObjectEditor : UnityEditor.Editor + { + public override VisualElement CreateInspectorGUI() + { + VisualElement root = new VisualElement(); + + InspectorElement.FillDefaultInspector(root, serializedObject, this); + + var layerGameObject = (HierarchicalObjectLayerGameObject)target; + LayerDataVisualElements.LayerData(layerGameObject.LayerData, root); + + return root; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Editor/Layers/HierarchicalObjectLayerGameObjectEditor.cs.meta b/Assets/Scripts/Editor/Layers/HierarchicalObjectLayerGameObjectEditor.cs.meta new file mode 100644 index 00000000..f5ce76a9 --- /dev/null +++ b/Assets/Scripts/Editor/Layers/HierarchicalObjectLayerGameObjectEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1b92c365faf2493ca0395d36e8f125b5 +timeCreated: 1730114947 \ No newline at end of file diff --git a/Assets/Scripts/Editor/Layers/LayerDataVisualElements.cs b/Assets/Scripts/Editor/Layers/LayerDataVisualElements.cs new file mode 100644 index 00000000..e1f26599 --- /dev/null +++ b/Assets/Scripts/Editor/Layers/LayerDataVisualElements.cs @@ -0,0 +1,150 @@ +using Netherlands3D.LayerStyles; +using Netherlands3D.Twin.Layers; +using UnityEngine; +using UnityEngine.UIElements; + +namespace Netherlands3D.Twin.Editor.Layers +{ + public static class LayerDataVisualElements + { + public static void LayerData(LayerData layerData, VisualElement root) + { + // Add a divider first + root.Add(Divider(2, 8)); + root.Add(Heading("Layer Data")); + root.Add(Divider()); + + if (layerData == null) + { + root.Add(FieldContent("This layer doesn't have any data associated with it (yet).")); + return; + } + + root.Add(FieldWithCaption("Name", layerData.Name)); + root.Add(FieldWithCaption("Color", ColorUtility.ToHtmlStringRGBA(layerData.Color))); + + root.Add(FieldCaption("Styles")); + if (layerData.Styles.Count == 0) + { + root.Add(FieldContent("This layer doesn't have any styles associated with it (yet).")); + } + + bool first = true; + foreach (var (_, style) in layerData.Styles) + { + var foldout = StyleFoldout(style); + // first should be folded out, rest collapsed by default + foldout.value = first; + if (first) first = false; + + root.Add(foldout); + } + } + + private static Foldout StyleFoldout(LayerStyle layerStyle) + { + var foldout = new Foldout { text = layerStyle.Metadata.Name }; + var group = GroupWithBorder(); + group.Add(FieldWithCaption("Name", layerStyle.Metadata.Name)); + group.Add(FieldCaption("Rules")); + if (layerStyle.StylingRules.Count == 0) + { + group.Add(FieldContent("This style doesn't have any styling rules associated with it (yet).")); + } + foreach (var stylingRule in layerStyle.StylingRules) + { + group.Add(StyleRuleFoldout(stylingRule)); + } + foldout.Add(group); + + return foldout; + } + + private static Foldout StyleRuleFoldout(StylingRule stylingRule) + { + var ruleFoldout = new Foldout { text = stylingRule.Name }; + ruleFoldout.Add(FieldWithCaption("Name", stylingRule.Name)); + ruleFoldout.Add(FieldWithCaption("Selector", "If " + stylingRule.Selector)); + var styles = stylingRule.Symbolizer.ToString(); + ruleFoldout.Add(FieldWithCaption("Styles", string.IsNullOrEmpty(styles) ? "[None]" : styles )); + + return ruleFoldout; + } + + private static VisualElement FieldWithCaption(string caption, string content) + { + var group = new VisualElement() + { + style = + { + marginBottom = 4 + } + }; + group.Add(FieldCaption(caption)); + group.Add(FieldContent(content)); + + return group; + } + + private static Label FieldCaption(string caption) + { + return new Label(caption) { style = { unityFontStyleAndWeight = FontStyle.Bold}}; + } + + private static Label FieldContent(string content) + { + return new Label(content); + } + + private static VisualElement GroupWithBorder() + { + return new VisualElement + { + style = + { + borderTopWidth = 1, + borderRightWidth = 1, + borderBottomWidth = 1, + borderLeftWidth = 1, + borderBottomColor = new Color(0.7f, 0.7f, 0.7f), // Light gray color + borderTopColor = new Color(0.7f, 0.7f, 0.7f), // Light gray color + borderLeftColor = new Color(0.7f, 0.7f, 0.7f), // Light gray color + borderRightColor = new Color(0.7f, 0.7f, 0.7f), // Light gray color + paddingLeft = 4, + paddingRight = 8, + paddingTop = 4, + paddingBottom = 4, + marginTop = 4, + marginBottom = 4 + } + }; + } + + private static VisualElement Divider(int thickness = 1, int marginTop = 4, int marginBottom = 4) + { + return new VisualElement + { + style = + { + height = thickness, + marginTop = marginTop, + marginBottom = marginBottom, + backgroundColor = new Color(0.7f, 0.7f, 0.7f) + } + }; + } + + private static VisualElement Heading(string caption) + { + return new Label(caption) + { + style = + { + unityFontStyleAndWeight = FontStyle.Bold, + fontSize = 14, + marginBottom = 4 + } + }; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Editor/Layers/LayerDataVisualElements.cs.meta b/Assets/Scripts/Editor/Layers/LayerDataVisualElements.cs.meta new file mode 100644 index 00000000..f645cc8f --- /dev/null +++ b/Assets/Scripts/Editor/Layers/LayerDataVisualElements.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 59933b650957492a90f7d0f3c27b6460 +timeCreated: 1730122229 \ No newline at end of file diff --git a/Assets/Scripts/Editor/Layers/WfsGeoJsonLayerGameObjectEditor.cs b/Assets/Scripts/Editor/Layers/WfsGeoJsonLayerGameObjectEditor.cs new file mode 100644 index 00000000..cd4ae262 --- /dev/null +++ b/Assets/Scripts/Editor/Layers/WfsGeoJsonLayerGameObjectEditor.cs @@ -0,0 +1,23 @@ +using Netherlands3D.Twin.Layers; +using UnityEditor; +using UnityEditor.UIElements; +using UnityEngine.UIElements; + +namespace Netherlands3D.Twin.Editor.Layers +{ + [CustomEditor(typeof(WFSGeoJsonLayerGameObject))] + public class WfsGeoJsonLayerGameObjectEditor : UnityEditor.Editor + { + public override VisualElement CreateInspectorGUI() + { + var root = new VisualElement(); + + InspectorElement.FillDefaultInspector(root, serializedObject, this); + + var layerGameObject = (WFSGeoJsonLayerGameObject)target; + LayerDataVisualElements.LayerData(layerGameObject.LayerData, root); + + return root; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Editor/Layers/WfsGeoJsonLayerGameObjectEditor.cs.meta b/Assets/Scripts/Editor/Layers/WfsGeoJsonLayerGameObjectEditor.cs.meta new file mode 100644 index 00000000..410c94e0 --- /dev/null +++ b/Assets/Scripts/Editor/Layers/WfsGeoJsonLayerGameObjectEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 26502f77f25b45eb98deded15d7292c8 +timeCreated: 1730125127 \ No newline at end of file diff --git a/Assets/Scripts/Layers/LayerTypes/GeoJSONLineLayer.cs b/Assets/Scripts/Layers/LayerTypes/GeoJSONLineLayer.cs index 6f5d4a88..426a1c7e 100644 --- a/Assets/Scripts/Layers/LayerTypes/GeoJSONLineLayer.cs +++ b/Assets/Scripts/Layers/LayerTypes/GeoJSONLineLayer.cs @@ -67,9 +67,10 @@ public void AddAndVisualizeFeature(Feature feature, CoordinateSystem original private Material GetMaterialInstance() { + var strokeColor = LayerData.DefaultSymbolizer?.GetStrokeColor() ?? Color.white; return new Material(lineRenderer3D.LineMaterial) { - color = LayerData.DefaultSymbolizer?.GetStrokeColor() ?? Color.white + color = strokeColor }; } diff --git a/Assets/Scripts/Layers/LayerTypes/LayerData.cs b/Assets/Scripts/Layers/LayerTypes/LayerData.cs index 7d1cd97f..21b93ec4 100644 --- a/Assets/Scripts/Layers/LayerTypes/LayerData.cs +++ b/Assets/Scripts/Layers/LayerTypes/LayerData.cs @@ -146,7 +146,7 @@ public bool HasValidCredentials [JsonIgnore] public bool HasProperties => layerProperties.Count > 0; - [JsonIgnore] private Dictionary Styles => styles; + [JsonIgnore] public Dictionary Styles => styles; /// /// Every layer has a default style, this is a style that applies to all objects and features in this diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/BoolExpression.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/BoolExpression.cs index 1afd33f7..2a8e5223 100644 --- a/Assets/_BuildingBlocks/LayerStyles/Scripts/BoolExpression.cs +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/BoolExpression.cs @@ -19,5 +19,10 @@ public override object Resolve(ExpressionContext context) { return value; } + + public override string ToString() + { + return this.value ? "true" : "false"; + } } } \ No newline at end of file diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/Symbolizer.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/Symbolizer.cs index 45affda3..8b9fe9e2 100644 --- a/Assets/_BuildingBlocks/LayerStyles/Scripts/Symbolizer.cs +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/Symbolizer.cs @@ -1,12 +1,14 @@ using System.Collections.Generic; using System.Runtime.Serialization; +using UnityEngine; namespace Netherlands3D.LayerStyles { [DataContract(Namespace = "https://netherlands3d.eu/schemas/projects/layers/styling", Name = "Symbolizer")] public class Symbolizer { - [DataMember] private Dictionary properties = new(); + [DataMember(Name = "properties")] + private Dictionary properties = new(); internal object GetProperty(string key) { @@ -18,5 +20,16 @@ internal void SetProperty(string key, object value) { properties[key] = value; } + + public override string ToString() + { + var result = ""; + foreach (var (name, value) in properties) + { + result += $"{name}: {value}\n"; + } + + return result; + } } } \ No newline at end of file From 2d7cb526fbc125afbbe0506137577f7bb6855be1 Mon Sep 17 00:00:00 2001 From: Mike van Riel Date: Tue, 29 Oct 2024 12:08:08 +0100 Subject: [PATCH 11/11] Tighten names and properties, and check why colors won't be applied yet --- .../Editor/Layers/LayerDataVisualElements.cs | 8 +++- .../Layers/LayerTypes/GeoJSONLineLayer.cs | 31 ++++++++------- .../Layers/LayerTypes/GeoJSONPointLayer.cs | 9 ++++- .../Layers/LayerTypes/GeoJSONPolygonLayer.cs | 7 +++- .../LayerTypes/GeoJsonLayerGameObject.cs | 38 +++++++------------ Assets/Scripts/Layers/LayerTypes/LayerData.cs | 2 +- .../LayerStyles/Scripts/BoolExpression.cs | 2 +- .../LayerStyles/Scripts/LayerStyle.cs | 17 +++++++-- .../LayerStyles/Scripts/Metadata.cs | 2 +- .../LayerStyles/Scripts/StylingRule.cs | 7 ++-- 10 files changed, 68 insertions(+), 55 deletions(-) diff --git a/Assets/Scripts/Editor/Layers/LayerDataVisualElements.cs b/Assets/Scripts/Editor/Layers/LayerDataVisualElements.cs index e1f26599..7e27d1b0 100644 --- a/Assets/Scripts/Editor/Layers/LayerDataVisualElements.cs +++ b/Assets/Scripts/Editor/Layers/LayerDataVisualElements.cs @@ -51,7 +51,7 @@ private static Foldout StyleFoldout(LayerStyle layerStyle) { group.Add(FieldContent("This style doesn't have any styling rules associated with it (yet).")); } - foreach (var stylingRule in layerStyle.StylingRules) + foreach (var (_, stylingRule) in layerStyle.StylingRules) { group.Add(StyleRuleFoldout(stylingRule)); } @@ -93,6 +93,10 @@ private static Label FieldCaption(string caption) private static Label FieldContent(string content) { + if (string.IsNullOrEmpty(content)) + { + content = "[None]"; + } return new Label(content); } @@ -110,7 +114,7 @@ private static VisualElement GroupWithBorder() borderTopColor = new Color(0.7f, 0.7f, 0.7f), // Light gray color borderLeftColor = new Color(0.7f, 0.7f, 0.7f), // Light gray color borderRightColor = new Color(0.7f, 0.7f, 0.7f), // Light gray color - paddingLeft = 4, + paddingLeft = 8, paddingRight = 8, paddingTop = 4, paddingBottom = 4, diff --git a/Assets/Scripts/Layers/LayerTypes/GeoJSONLineLayer.cs b/Assets/Scripts/Layers/LayerTypes/GeoJSONLineLayer.cs index 426a1c7e..a42e27a8 100644 --- a/Assets/Scripts/Layers/LayerTypes/GeoJSONLineLayer.cs +++ b/Assets/Scripts/Layers/LayerTypes/GeoJSONLineLayer.cs @@ -8,9 +8,6 @@ using Netherlands3D.Coordinates; using Netherlands3D.LayerStyles; using Netherlands3D.Twin.Layers.LayerTypes.GeoJsonLayers; -using Netherlands3D.Twin.Projects; -using Netherlands3D.Twin.UI.LayerInspector; -using Netherlands3D.Visualisers; using UnityEngine; namespace Netherlands3D.Twin.Layers @@ -24,15 +21,9 @@ public partial class GeoJSONLineLayer : LayerGameObject public LineRenderer3D LineRenderer3D { - get { return lineRenderer3D; } - set - { - //todo: move old lines to new renderer, remove old lines from old renderer without clearing entire list? - // value.SetLines(lineRenderer3D.Lines); - // Destroy(lineRenderer3D.gameObject); - lineRenderer3D = value; - } - } + get => lineRenderer3D; + set => lineRenderer3D = value; + } public override void OnLayerActiveInHierarchyChanged(bool activeInHierarchy) { @@ -46,10 +37,11 @@ public void AddAndVisualizeFeature(Feature feature, CoordinateSystem original if (SpawnedVisualisations.Any(f => f.feature.GetHashCode() == feature.GetHashCode())) return; - var newFeatureVisualisation = new FeatureLineVisualisations() { feature = feature }; + var newFeatureVisualisation = new FeatureLineVisualisations { feature = feature }; - // Create visual with random color if enabled - lineRenderer3D.LineMaterial = GetMaterialInstance(); + Debug.Log("AddAndVisualizeFeature"); + + ApplyStyling(); if (feature.Geometry is MultiLineString multiLineString) { @@ -65,9 +57,16 @@ public void AddAndVisualizeFeature(Feature feature, CoordinateSystem original SpawnedVisualisations.Add(newFeatureVisualisation); } + public void ApplyStyling() + { + lineRenderer3D.LineMaterial = GetMaterialInstance(); + } + private Material GetMaterialInstance() { - var strokeColor = LayerData.DefaultSymbolizer?.GetStrokeColor() ?? Color.white; + Debug.Log("Get Material Instance"); + Debug.Log(LayerData.Styles["default"].StylingRules["default"].Symbolizer.GetStrokeColor()); + var strokeColor = LayerData.DefaultSymbolizer.GetStrokeColor() ?? Color.white; return new Material(lineRenderer3D.LineMaterial) { color = strokeColor diff --git a/Assets/Scripts/Layers/LayerTypes/GeoJSONPointLayer.cs b/Assets/Scripts/Layers/LayerTypes/GeoJSONPointLayer.cs index aa074e11..9cca7210 100644 --- a/Assets/Scripts/Layers/LayerTypes/GeoJSONPointLayer.cs +++ b/Assets/Scripts/Layers/LayerTypes/GeoJSONPointLayer.cs @@ -43,9 +43,9 @@ public void AddAndVisualizeFeature(Feature feature, CoordinateSystem original if (SpawnedVisualisations.Any(f => f.feature.GetHashCode() == feature.GetHashCode())) return; - var newFeatureVisualisation = new FeaturePointVisualisations() { feature = feature }; + var newFeatureVisualisation = new FeaturePointVisualisations { feature = feature }; - pointRenderer3D.Material = GetMaterialInstance(); + ApplyStyling(); if (feature.Geometry is MultiPoint multiPoint) { @@ -61,6 +61,11 @@ public void AddAndVisualizeFeature(Feature feature, CoordinateSystem original SpawnedVisualisations.Add(newFeatureVisualisation); } + public void ApplyStyling() + { + pointRenderer3D.Material = GetMaterialInstance(); + } + private Material GetMaterialInstance() { return new Material(pointRenderer3D.Material) diff --git a/Assets/Scripts/Layers/LayerTypes/GeoJSONPolygonLayer.cs b/Assets/Scripts/Layers/LayerTypes/GeoJSONPolygonLayer.cs index 69faa296..47b005c0 100644 --- a/Assets/Scripts/Layers/LayerTypes/GeoJSONPolygonLayer.cs +++ b/Assets/Scripts/Layers/LayerTypes/GeoJSONPolygonLayer.cs @@ -81,13 +81,18 @@ public void AddAndVisualizeFeature(Feature feature, CoordinateSystem original SpawnedVisualisations.Add(newFeatureVisualisation); } + public void ApplyStyling() + { + SpawnedVisualisations.ForEach(visualisations => visualisations.SetMaterial(GetMaterialInstance())); + } + private Material GetMaterialInstance() { if (!polygonVisualizationMaterialInstance) { polygonVisualizationMaterialInstance = new Material(PolygonVisualizationMaterial) { - color = LayerData.DefaultSymbolizer?.GetFillColor() ?? Color.white + color = LayerData.DefaultSymbolizer.GetFillColor() ?? Color.white }; } diff --git a/Assets/Scripts/Layers/LayerTypes/GeoJsonLayerGameObject.cs b/Assets/Scripts/Layers/LayerTypes/GeoJsonLayerGameObject.cs index cc3e47aa..43532869 100644 --- a/Assets/Scripts/Layers/LayerTypes/GeoJsonLayerGameObject.cs +++ b/Assets/Scripts/Layers/LayerTypes/GeoJsonLayerGameObject.cs @@ -243,14 +243,11 @@ private GeoJSONPolygonLayer CreateOrGetPolygonLayer() GeoJSONPolygonLayer newPolygonLayerGameObject = Instantiate(polygonLayerPrefab); newPolygonLayerGameObject.LayerData.Color = LayerData.Color; - - // TODO: This should respond to changes in the style, and not be set directly. But that is beyond scope - // of my current activity - var lineMaterial = new Material(newPolygonLayerGameObject.PolygonVisualizationMaterial) - { - color = newPolygonLayerGameObject.LayerData.DefaultSymbolizer.GetFillColor() ?? Color.white - }; - newPolygonLayerGameObject.polygonVisualizationMaterialInstance = lineMaterial; + + // Replace default style with the parent's default style + newPolygonLayerGameObject.LayerData.RemoveStyle(newPolygonLayerGameObject.LayerData.DefaultStyle); + newPolygonLayerGameObject.LayerData.AddStyle(LayerData.DefaultStyle); + newPolygonLayerGameObject.ApplyStyling(); newPolygonLayerGameObject.LayerData.SetParent(LayerData); @@ -265,23 +262,23 @@ private GeoJSONLineLayer CreateOrGetLineLayer() if (child is not ReferencedLayerData referencedLayerData) continue; if (referencedLayerData.Reference is not GeoJSONLineLayer lineLayer) continue; + Debug.Log("Return found line layer"); + Debug.Log(lineLayer.LayerData.DefaultSymbolizer); + var stroke2Color = lineLayer.LayerData.DefaultSymbolizer.GetStrokeColor(); + Debug.Log(stroke2Color); + return lineLayer; } GeoJSONLineLayer newLineLayerGameObject = Instantiate(lineLayerPrefab); newLineLayerGameObject.LayerData.Color = LayerData.Color; + Debug.Log("Copy default style from parent onto child"); + // Replace default style with the parent's default style newLineLayerGameObject.LayerData.RemoveStyle(newLineLayerGameObject.LayerData.DefaultStyle); newLineLayerGameObject.LayerData.AddStyle(LayerData.DefaultStyle); - - // TODO: This should respond to changes in the style, and not be set directly. But that is beyond scope - // of my current activity - var lineMaterial = new Material(newLineLayerGameObject.LineRenderer3D.LineMaterial) - { - color = newLineLayerGameObject.LayerData.DefaultSymbolizer.GetStrokeColor() ?? Color.white - }; - newLineLayerGameObject.LineRenderer3D.LineMaterial = lineMaterial; + newLineLayerGameObject.ApplyStyling(); newLineLayerGameObject.LayerData.SetParent(LayerData); return newLineLayerGameObject; @@ -304,14 +301,7 @@ private GeoJSONPointLayer CreateOrGetPointLayer() // Replace default style with the parent's default style newPointLayerGameObject.LayerData.RemoveStyle(newPointLayerGameObject.LayerData.DefaultStyle); newPointLayerGameObject.LayerData.AddStyle(LayerData.DefaultStyle); - - // TODO: This should respond to changes in the style, and not be set directly. But that is beyond scope - // of my current activity - var pointMaterial = new Material(newPointLayerGameObject.PointRenderer3D.Material) - { - color = newPointLayerGameObject.LayerData.DefaultSymbolizer.GetFillColor() ?? Color.white - }; - newPointLayerGameObject.PointRenderer3D.Material = pointMaterial; + newPointLayerGameObject.ApplyStyling(); newPointLayerGameObject.LayerData.SetParent(LayerData); diff --git a/Assets/Scripts/Layers/LayerTypes/LayerData.cs b/Assets/Scripts/Layers/LayerTypes/LayerData.cs index 21b93ec4..0b3e38b8 100644 --- a/Assets/Scripts/Layers/LayerTypes/LayerData.cs +++ b/Assets/Scripts/Layers/LayerTypes/LayerData.cs @@ -158,7 +158,7 @@ public bool HasValidCredentials /// Every layer has a default symbolizer, drawn from the default style, that can be queried for the appropriate /// properties. /// - [JsonIgnore] public Symbolizer DefaultSymbolizer => DefaultStyle.StylingRules.FirstOrDefault()?.Symbolizer; + [JsonIgnore] public Symbolizer DefaultSymbolizer => DefaultStyle.StylingRules["default"].Symbolizer; [JsonIgnore] public readonly UnityEvent NameChanged = new(); [JsonIgnore] public readonly UnityEvent LayerActiveInHierarchyChanged = new(); diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/BoolExpression.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/BoolExpression.cs index 2a8e5223..82b34418 100644 --- a/Assets/_BuildingBlocks/LayerStyles/Scripts/BoolExpression.cs +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/BoolExpression.cs @@ -5,7 +5,7 @@ namespace Netherlands3D.LayerStyles [DataContract(Namespace = "https://netherlands3d.eu/schemas/projects/layers/styling/expressions", Name = "Bool")] public class BoolExpression : LiteralExpression { - [DataMember] private bool value; + [DataMember(Name = "value")] private bool value; public BoolExpression(bool value) { diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/LayerStyle.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/LayerStyle.cs index c9a71afa..2c6671b2 100644 --- a/Assets/_BuildingBlocks/LayerStyles/Scripts/LayerStyle.cs +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/LayerStyle.cs @@ -1,16 +1,17 @@ using System.Collections.Generic; using System.Runtime.Serialization; +using Newtonsoft.Json; namespace Netherlands3D.LayerStyles { [DataContract(Namespace = "https://netherlands3d.eu/schemas/projects/layers/styling", Name = "LayerStyle")] public class LayerStyle { - [DataMember] public Metadata Metadata { get; } = new(); + [DataMember(Name = "metadata")] public Metadata Metadata { get; } = new(); - [DataMember] public List StylingRules { get; } = new() + [DataMember(Name = "stylingRules")] public Dictionary StylingRules { get; } = new() { - new StylingRule("default") + { "default", new StylingRule("default") } }; /// @@ -27,6 +28,16 @@ public static LayerStyle CreateDefaultStyle() return new LayerStyle("default"); } + /// + /// Empty constructor needed to prevent name from clearing, when you create a layer style from code + /// we want to require you to immediately add the name because this is needed in the dictionary lookups; but the + /// JSON Constructor should _not_ have it because the name is not an immediate member + /// + [JsonConstructor] + private LayerStyle() + { + } + public LayerStyle(string name) { Metadata.Name = name; diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/Metadata.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/Metadata.cs index ff0aad33..7671994c 100644 --- a/Assets/_BuildingBlocks/LayerStyles/Scripts/Metadata.cs +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/Metadata.cs @@ -5,6 +5,6 @@ namespace Netherlands3D.LayerStyles [DataContract(Namespace = "https://netherlands3d.eu/schemas/projects/layers/styling", Name = "Metadata")] public class Metadata { - [DataMember] public string Name { get; set; } + [DataMember(Name = "name")] public string Name { get; set; } } } \ No newline at end of file diff --git a/Assets/_BuildingBlocks/LayerStyles/Scripts/StylingRule.cs b/Assets/_BuildingBlocks/LayerStyles/Scripts/StylingRule.cs index 57b3afea..4a6a9ad6 100644 --- a/Assets/_BuildingBlocks/LayerStyles/Scripts/StylingRule.cs +++ b/Assets/_BuildingBlocks/LayerStyles/Scripts/StylingRule.cs @@ -6,15 +6,14 @@ namespace Netherlands3D.LayerStyles [DataContract(Namespace = "https://netherlands3d.eu/schemas/projects/layers/styling", Name = "StylingRule")] public class StylingRule { - [DataMember] public string Name { get; private set; } - [DataMember] public Symbolizer Symbolizer { get; } = new(); + [DataMember(Name = "name")] public string Name { get; private set; } + [DataMember(Name = "symbolizer")] public Symbolizer Symbolizer { get; } = new(); - [DataMember] public Expression Selector { get; private set; } + [DataMember(Name = "selector")] public Expression Selector { get; private set; } [JsonConstructor] private StylingRule() { - Name = ""; } public StylingRule(string name)