From e5908916800bb72d55fe92fc6e297006fa8bba1d Mon Sep 17 00:00:00 2001 From: Wagram Airiian Date: Thu, 5 Dec 2024 18:11:18 +0100 Subject: [PATCH 01/11] * Add an initial transition attribute style * Add attribute-mask style property to filter attributes by fields * Improve Tile Source Data selection dialog to pre-load the previous selection --- README.md | 1 + erdblick_app/app/editor.component.ts | 1 + .../sourcedataselection.dialog.component.ts | 36 +++++++++++++-- libs/core/include/erdblick/rule.h | 2 + libs/core/src/rule.cpp | 9 ++++ libs/core/src/visualization.cpp | 46 +++++++++++++++++++ 6 files changed, 90 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0878ec6f..ecfefb55 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,7 @@ Each rule within the YAML `rules` array can have the following fields: | `relation-recursive` | Specifies whether relations should be resolved recursively. Only done if `mode=="Highlight"`, and only works for relations within the same layer. | Boolean | `true`, `false` | | `relation-merge-twoway` | Specifies whether bidirectional relations should be followed and merged. | Boolean | `true`, `false` | | `attribute-type` | A regular expression to match against an attribute type. | String | `SPEED_LIMIT_.*` | +| `attribute-mask` | A regular expression to match against a value of the attribute's field. | String | `attributeValue.speedLimitKmh > 100` | | `attribute-layer-type` | A regular expression to match against the attribute layer type name. | String | `Road.*Layer` | | `attribute-validity-geom` | Set to `required`, `none` or `any` to control whether matching attributes must have a validity geometry. | String | `Road.*Layer` | | `label-color` | Text color of the label. | String | `#00ccdd` | diff --git a/erdblick_app/app/editor.component.ts b/erdblick_app/app/editor.component.ts index f4b40d06..45768e5f 100644 --- a/erdblick_app/app/editor.component.ts +++ b/erdblick_app/app/editor.component.ts @@ -60,6 +60,7 @@ const completionsList = [ {label: "offset-scale-by-distance", type: "property"}, {label: "first-of", type: "property"}, {label: "attribute-type", type: "property"}, + {label: "attribute-mask", type: "property"}, {label: "attribute-layer-type", type: "property"}, {label: "point-merge-grid-cell", type: "property"}, {label: "FILL", type: "keyword"}, diff --git a/erdblick_app/app/sourcedataselection.dialog.component.ts b/erdblick_app/app/sourcedataselection.dialog.component.ts index 9368e6cb..b2dfbe3f 100644 --- a/erdblick_app/app/sourcedataselection.dialog.component.ts +++ b/erdblick_app/app/sourcedataselection.dialog.component.ts @@ -124,8 +124,19 @@ export class SourceDataLayerSelectionDialogComponent { this.mapIdsPerTileId.set(id, maps); } + if (this.menuService.lastInspectedTileSourceDataOption.getValue()) { + const savedTileId = this.menuService.lastInspectedTileSourceDataOption.getValue()?.tileId; + let tileIdSelection = this.tileIds.find(element => + Number(element.id) == savedTileId && !element.disabled && [...this.mapService.tileLayersForTileId(element.id as bigint)].length + ); + if (tileIdSelection) { + this.setCurrentTileId(tileIdSelection); + } + return; + } + // Pre-select the tile ID. - let tileIdSelection = this.tileIds.find(element => + const tileIdSelection = this.tileIds.find(element => !element.disabled && [...this.mapService.tileLayersForTileId(element.id as bigint)].length ); if (tileIdSelection) { @@ -165,13 +176,18 @@ export class SourceDataLayerSelectionDialogComponent { this.onTileIdChange(tileId); if (this.customMapId) { - let mapSelection = this.mapIds.find(entry => entry.id == this.customMapId); + const mapSelection = this.mapIds.find(entry => entry.id == this.customMapId); if (mapSelection) { this.selectedMapId = mapSelection; - } - else { + } else { this.mapIds.unshift({ id: this.customMapId, name: this.customMapId }); } + } else if (this.menuService.lastInspectedTileSourceDataOption.getValue()) { + const savedMapId = this.menuService.lastInspectedTileSourceDataOption.getValue()?.mapId; + const mapSelection = this.mapIds.find(entry => entry.id == savedMapId); + if (mapSelection) { + this.selectedMapId = mapSelection; + } } if (this.mapIds.length) { @@ -180,7 +196,17 @@ export class SourceDataLayerSelectionDialogComponent { } this.onMapIdChange(this.selectedMapId); if (this.sourceDataLayers.length) { - this.selectedSourceDataLayer = this.sourceDataLayers[0]; + if (this.menuService.lastInspectedTileSourceDataOption.getValue()) { + const savedLayerId = this.menuService.lastInspectedTileSourceDataOption.getValue()?.layerId; + const layerSelection = this.sourceDataLayers.find(entry => entry.id == savedLayerId); + if (layerSelection) { + this.selectedSourceDataLayer = layerSelection; + } else { + this.selectedSourceDataLayer = this.sourceDataLayers[0]; + } + } else { + this.selectedSourceDataLayer = this.sourceDataLayers[0]; + } this.onLayerIdChange(this.selectedSourceDataLayer); } } diff --git a/libs/core/include/erdblick/rule.h b/libs/core/include/erdblick/rule.h index 5b924c92..187e33f6 100644 --- a/libs/core/include/erdblick/rule.h +++ b/libs/core/include/erdblick/rule.h @@ -78,6 +78,7 @@ class FeatureStyleRule [[nodiscard]] bool relationMergeTwoWay() const; [[nodiscard]] std::optional const& attributeType() const; + [[nodiscard]] std::optional const& attributeMask() const; [[nodiscard]] std::optional const& attributeLayerType() const; [[nodiscard]] std::optional const& attributeValidityGeometry() const; @@ -166,6 +167,7 @@ class FeatureStyleRule bool relationMergeTwoWay_ = false; std::optional attributeType_; + std::optional attributeMask_; std::optional attributeLayerType_; std::optional attributeValidityGeometry_; diff --git a/libs/core/src/rule.cpp b/libs/core/src/rule.cpp index e64c3b73..c2279270 100644 --- a/libs/core/src/rule.cpp +++ b/libs/core/src/rule.cpp @@ -259,6 +259,10 @@ void FeatureStyleRule::parse(const YAML::Node& yaml) // Parse an attribute type regular expression, e.g. `SPEED_LIMIT_.*` attributeType_ = yaml["attribute-type"].as(); } + if (yaml["attribute-mask"].IsDefined()) { + // Parse an attribute based on it's field value, e.g. `speedLimitKmh > 100` + attributeMask_ = yaml["attribute-mask"].as(); + } if (yaml["attribute-layer-type"].IsDefined()) { // Parse an attribute type regular expression, e.g. `Road.*Layer` attributeLayerType_ = yaml["attribute-layer-type"].as(); @@ -691,6 +695,11 @@ std::optional const& FeatureStyleRule::attributeType() const return attributeType_; } +std::optional const& FeatureStyleRule::attributeMask() const +{ + return attributeMask_; +} + std::optional const& FeatureStyleRule::attributeLayerType() const { return attributeLayerType_; diff --git a/libs/core/src/visualization.cpp b/libs/core/src/visualization.cpp index cf1eb26f..dc93305a 100644 --- a/libs/core/src/visualization.cpp +++ b/libs/core/src/visualization.cpp @@ -281,6 +281,52 @@ void FeatureLayerVisualization::addFeature( } // Iterate over all the layer's attributes. layer->forEachAttribute([&, this](auto&& attr){ + // Check if the attribute's values match the attribute mask for the rule. + if (auto const& attrMask = rule.attributeMask()) { + if (!attrMask->empty()) { + auto const& constAttr = static_cast(*attr); + simfil::OverlayNode attrEvaluationContext(simfil::Value::field(constAttr)); + + try { + constAttr.forEachField([&](auto const& fieldName, auto const& fieldValue) { + if (fieldValue) { + attrEvaluationContext.set( + internalStringPoolCopy_->emplace(fieldName), + simfil::Value::field(fieldValue)); + } + return true; + }); + + addOptionsToSimfilContext(attrEvaluationContext); + + auto boundEvalFun = BoundEvalFun{ + attrEvaluationContext, + [this, &attrEvaluationContext](auto&& str) { + try { + return evaluateExpression(str, attrEvaluationContext); + } catch (const std::exception& e) { + std::cerr << "Error evaluating attribute expression: " << e.what() << std::endl; + return simfil::Value::null(); + } + }}; + + try { + auto result = boundEvalFun.eval_(*attrMask); + if ((result.isa(simfil::ValueType::Bool) && !result.as()) || + result.isa(simfil::ValueType::Undef) || result.isa(simfil::ValueType::Null)) { + return true; + } + } catch (std::exception const& e) { + std::cerr << "Error evaluating attribute mask: " << e.what() << std::endl; + return true; + } + } catch (const std::exception& e) { + std::cerr << "Error setting up attribute evaluation context: " << e.what() << std::endl; + return true; + } + } + } + addAttribute( feature, layerName, From a628e17ac2ef3b8625297d56e20c07925adcf278 Mon Sep 17 00:00:00 2001 From: Wagram Airiian Date: Thu, 5 Dec 2024 18:41:10 +0100 Subject: [PATCH 02/11] Fix "logical not is only applied to the left hand side of comparison" error --- libs/core/src/visualization.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/core/src/visualization.cpp b/libs/core/src/visualization.cpp index dc93305a..e6d40ede 100644 --- a/libs/core/src/visualization.cpp +++ b/libs/core/src/visualization.cpp @@ -312,7 +312,7 @@ void FeatureLayerVisualization::addFeature( try { auto result = boundEvalFun.eval_(*attrMask); - if ((result.isa(simfil::ValueType::Bool) && !result.as()) || + if ((result.isa(simfil::ValueType::Bool) && !(result.as())) || result.isa(simfil::ValueType::Undef) || result.isa(simfil::ValueType::Null)) { return true; } From d809261fa922360813afba7d017c2e611bbae39e Mon Sep 17 00:00:00 2001 From: Wagram Airiian Date: Mon, 9 Dec 2024 20:49:19 +0100 Subject: [PATCH 03/11] PR fixes: attribute-mask -> attribute-filter --- README.md | 110 +++++++++++++++--------------- libs/core/include/erdblick/rule.h | 4 +- libs/core/src/rule.cpp | 8 +-- libs/core/src/visualization.cpp | 57 +++------------- 4 files changed, 72 insertions(+), 107 deletions(-) diff --git a/README.md b/README.md index ecfefb55..2440efcd 100644 --- a/README.md +++ b/README.md @@ -65,61 +65,61 @@ The style editor automatically verifies YAML for syntax parsing errors and provi Each rule within the YAML `rules` array can have the following fields: -| Field | Description | Type | Example Value | -|-------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------|----------------------------------------------------| -| `geometry` | List of geometry type(s) or single type the rule applies to. | At least one of `"point"`,`"mesh"`, `"line"`, `"polygon"`. | `["point", "mesh"]`, `line` | -| `aspect` | Specifies the aspect to which the rule applies: `"feature"`, `"relation"`, or `"attribute"`. | String | `"feature"`, `"relation"` | -| `mode` | Specifies the highlight mode: `"none"` or `"hover"` or `"selection"`. | String | `"none"`, `"hover"` | -| `type` | A regular expression to match against a feature type. | String | `"Lane\|Boundary"` | -| `filter` | A [simfil](https://github.com/klebert-engineering/simfil) filter expression over the feature's JSON representation. | String | `*roadClass == 4` | -| `selectable` | Indicates if the feature is selectable. | Boolean | `true`, `false` | -| `color` | A hexadecimal color code or [CSS color name](https://www.w3.org/wiki/CSS/Properties/color/keywords). | String | `"#FF5733"`, `red` | -| `color-expression` | A [simfil](https://github.com/klebert-engineering/simfil) expression which may return an RGBA integer or color string (see above). The expression is evaluated over the current feature/relation/attribute. | String | `isBridge and "#FF5733" or "black"` | -| `opacity` | A float value between 0 and 1 indicating the opacity. | Float | `0.8` | -| `width` | Specifies the line width or point diameter (default in pixels). | Float | `4.5` | -| `flat` | Clamps the feature to the ground (Does not work for meshes). | Boolean | `true`, `false` | -| `outline-color` | Point outline color. | String | `green`, `#fff` | -| `outline-width` | Point outline width in px. | Float | `3.6` | -| `point-merge-grid-cell` | WGS84/altutide meter tolerance for merging point visualizations. | Array of three Floats. | `[0.000000084, 0.000000084, 0.01]` | -| `near-far-scale` | For points, indicate (`near-alt-meters`, `near-scale`, `far-alt-meters`, `far-scale`). | Array of four Floats. | `[1.5e2,10,8.0e6,0]` | -| `icon-url-expression` | A [simfil](https://github.com/klebert-engineering/simfil) expression to evaluate on the point feature the icon belongs to. | String | `category == 5 and "/icons/ev-charging.png" or ""` | -| `icon-url` | A placeholder in case the simfil expression either isn't necessary or won't produce a result. | String | `/icons/unknown.png` | -| `offset` | Apply a fixed offset to each shape-point in meters. Can be used for z-ordering. | Array of three Floats. | `[0, 0, 5]` | -| `arrow` | For arrow-heads: One of `none`, `forward`, `backward`, `double`. Not compatible with `dashed`. | String | `single` | -| `arrow-expression` | A [simfil](https://github.com/klebert-engineering/simfil) expression which may return `none`, `forward`, `backward`, or `double`. | String | `select(arr("single", "double"), 1)` | -| `dashed` | Indicate that a line has dashes. | Boolean. | `true` | -| `gap-color` | If a gap between dashes has a color. | String | `blue`, `#aaa` | -| `dash-length` | Size of a dash in pixels. | Integer. | `16` | -| `dash-pattern` | A 16-bit pattern for the dash. | Integer. | `255` | -| `relation-type` | A regular expression to match against a relation type, e.g., `"connectedFrom"`. | String | `"connectedFrom\|connectedTo"` | -| `relation-line-height-offset` | Vertical offset for relation line in meters. | Float | `0.5` | -| `relation-line-end-markers` | Style for the relation line end-markers. | Sub-rule object | See example below. | -| `relation-source-style` | Style for the relation source geometry. | Sub-rule object | See example below. | -| `relation-target-style` | Style for the relation target geometry. | Sub-rule object | See example below. | -| `relation-recursive` | Specifies whether relations should be resolved recursively. Only done if `mode=="Highlight"`, and only works for relations within the same layer. | Boolean | `true`, `false` | -| `relation-merge-twoway` | Specifies whether bidirectional relations should be followed and merged. | Boolean | `true`, `false` | -| `attribute-type` | A regular expression to match against an attribute type. | String | `SPEED_LIMIT_.*` | -| `attribute-mask` | A regular expression to match against a value of the attribute's field. | String | `attributeValue.speedLimitKmh > 100` | -| `attribute-layer-type` | A regular expression to match against the attribute layer type name. | String | `Road.*Layer` | -| `attribute-validity-geom` | Set to `required`, `none` or `any` to control whether matching attributes must have a validity geometry. | String | `Road.*Layer` | -| `label-color` | Text color of the label. | String | `#00ccdd` | -| `label-outline-color` | Text outline color of the label. | String | `#111111` | -| `label-outline-width` | Text outline width of the label. | Float | `1.0` | -| `label-font` | The font used to draw the label (using the same syntax as the CSS 'font' property). | String | `24px Helvetica` | -| `label-background-color` | Background color of the label. | String | `#000000` | -| `label-background-padding` | Background padding in pixels. | Pair of Integers. | `[7, 5]` | -| `label-horizontal-origin` | Determines if the label is drawn to `LEFT`, `CENTER`, or `RIGHT` of its anchor position. | String | `LEFT` | -| `label-vertical-origin` | Determines if the label is to `ABOVE`, `BELOW`, at `CENTER` or at `BASELINE` of its anchor position. | String | `BASELINE` | -| `label-text-expression` | A [simfil](https://github.com/klebert-engineering/simfil) expression to evaluate on the feature/relation the label belongs to. | String | `**.speedLimitKmh` | -| `label-text` | A placeholder in case the simfil expression either isn't necessary or won't produce a result. | String | `No speed limit` | -| `label-style` | Describes how to draw a label using `FILL`, `OUTLINE` or `FILL_AND_OUTLINE`. | String | `FILL` | -| `label-scale` | The uniform scale that is multiplied with the label's size in pixels. | Float | `1.0` | -| `label-pixel-offset` | The offset in screen space from the origin of this label (the screen space origin is the top, left corner of the canvas). | Pair of Floats. | `[5.0, 30.0]` | -| `label-eye-offset` | Gets and sets the 3D Cartesian offset applied to this label in eye coordinates. | Tuple of three Floats. | `[5.0, 10.0, 15.0]` | -| `translucency-by-distance` | Near and far translucency properties of a Label based on the Label's distance from the camera. | Array of four Floats. | `[1.5e2, 3, 8.0e6, 0.0]` | -| `scale-by-distance` | Near and far scaling properties of a Label based on the label's distance from the camera. | Array of four Floats. | `[1.5e2, 3, 8.0e6, 0.0]` | -| `offset-scale-by-distance` | Near and far pixel offset scaling properties of a Label based on the Label's distance from the camera. | Array of four Floats. | `[1.5e2, 3, 8.0e6, 0.0]` | -| `first-of` | Mark a rule as a parent of a fallback rule list. See description below. | Array of Rule objects. | See example below. | +| Field | Description | Type | Example Value | +|-------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------|----------------------------------------------------| +| `geometry` | List of geometry type(s) or single type the rule applies to. | At least one of `"point"`,`"mesh"`, `"line"`, `"polygon"`. | `["point", "mesh"]`, `line` | +| `aspect` | Specifies the aspect to which the rule applies: `"feature"`, `"relation"`, or `"attribute"`. | String | `"feature"`, `"relation"` | +| `mode` | Specifies the highlight mode: `"none"` or `"hover"` or `"selection"`. | String | `"none"`, `"hover"` | +| `type` | A regular expression to match against a feature type. | String | `"Lane\|Boundary"` | +| `filter` | A [simfil](https://github.com/klebert-engineering/simfil) filter expression over the feature's JSON representation. | String | `*roadClass == 4` | +| `selectable` | Indicates if the feature is selectable. | Boolean | `true`, `false` | +| `color` | A hexadecimal color code or [CSS color name](https://www.w3.org/wiki/CSS/Properties/color/keywords). | String | `"#FF5733"`, `red` | +| `color-expression` | A [simfil](https://github.com/klebert-engineering/simfil) expression which may return an RGBA integer or color string (see above). The expression is evaluated over the current feature/relation/attribute. | String | `isBridge and "#FF5733" or "black"` | +| `opacity` | A float value between 0 and 1 indicating the opacity. | Float | `0.8` | +| `width` | Specifies the line width or point diameter (default in pixels). | Float | `4.5` | +| `flat` | Clamps the feature to the ground (Does not work for meshes). | Boolean | `true`, `false` | +| `outline-color` | Point outline color. | String | `green`, `#fff` | +| `outline-width` | Point outline width in px. | Float | `3.6` | +| `point-merge-grid-cell` | WGS84/altutide meter tolerance for merging point visualizations. | Array of three Floats. | `[0.000000084, 0.000000084, 0.01]` | +| `near-far-scale` | For points, indicate (`near-alt-meters`, `near-scale`, `far-alt-meters`, `far-scale`). | Array of four Floats. | `[1.5e2,10,8.0e6,0]` | +| `icon-url-expression` | A [simfil](https://github.com/klebert-engineering/simfil) expression to evaluate on the point feature the icon belongs to. | String | `category == 5 and "/icons/ev-charging.png" or ""` | +| `icon-url` | A placeholder in case the simfil expression either isn't necessary or won't produce a result. | String | `/icons/unknown.png` | +| `offset` | Apply a fixed offset to each shape-point in meters. Can be used for z-ordering. | Array of three Floats. | `[0, 0, 5]` | +| `arrow` | For arrow-heads: One of `none`, `forward`, `backward`, `double`. Not compatible with `dashed`. | String | `single` | +| `arrow-expression` | A [simfil](https://github.com/klebert-engineering/simfil) expression which may return `none`, `forward`, `backward`, or `double`. | String | `select(arr("single", "double"), 1)` | +| `dashed` | Indicate that a line has dashes. | Boolean. | `true` | +| `gap-color` | If a gap between dashes has a color. | String | `blue`, `#aaa` | +| `dash-length` | Size of a dash in pixels. | Integer. | `16` | +| `dash-pattern` | A 16-bit pattern for the dash. | Integer. | `255` | +| `relation-type` | A regular expression to match against a relation type, e.g., `"connectedFrom"`. | String | `"connectedFrom\|connectedTo"` | +| `relation-line-height-offset` | Vertical offset for relation line in meters. | Float | `0.5` | +| `relation-line-end-markers` | Style for the relation line end-markers. | Sub-rule object | See example below. | +| `relation-source-style` | Style for the relation source geometry. | Sub-rule object | See example below. | +| `relation-target-style` | Style for the relation target geometry. | Sub-rule object | See example below. | +| `relation-recursive` | Specifies whether relations should be resolved recursively. Only done if `mode=="Highlight"`, and only works for relations within the same layer. | Boolean | `true`, `false` | +| `relation-merge-twoway` | Specifies whether bidirectional relations should be followed and merged. | Boolean | `true`, `false` | +| `attribute-type` | A regular expression to match against an attribute type. | String | `SPEED_LIMIT_.*` | +| `attribute-filter` | A [simfil](https://github.com/klebert-engineering/simfil) expression to evaluate against the attribute's field. | String | `attributeValue.speedLimitKmh > 100` | +| `attribute-layer-type` | A regular expression to match against the attribute layer type name. | String | `Road.*Layer` | +| `attribute-validity-geom` | Set to `required`, `none` or `any` to control whether matching attributes must have a validity geometry. | String | `Road.*Layer` | +| `label-color` | Text color of the label. | String | `#00ccdd` | +| `label-outline-color` | Text outline color of the label. | String | `#111111` | +| `label-outline-width` | Text outline width of the label. | Float | `1.0` | +| `label-font` | The font used to draw the label (using the same syntax as the CSS 'font' property). | String | `24px Helvetica` | +| `label-background-color` | Background color of the label. | String | `#000000` | +| `label-background-padding` | Background padding in pixels. | Pair of Integers. | `[7, 5]` | +| `label-horizontal-origin` | Determines if the label is drawn to `LEFT`, `CENTER`, or `RIGHT` of its anchor position. | String | `LEFT` | +| `label-vertical-origin` | Determines if the label is to `ABOVE`, `BELOW`, at `CENTER` or at `BASELINE` of its anchor position. | String | `BASELINE` | +| `label-text-expression` | A [simfil](https://github.com/klebert-engineering/simfil) expression to evaluate on the feature/relation the label belongs to. | String | `**.speedLimitKmh` | +| `label-text` | A placeholder in case the simfil expression either isn't necessary or won't produce a result. | String | `No speed limit` | +| `label-style` | Describes how to draw a label using `FILL`, `OUTLINE` or `FILL_AND_OUTLINE`. | String | `FILL` | +| `label-scale` | The uniform scale that is multiplied with the label's size in pixels. | Float | `1.0` | +| `label-pixel-offset` | The offset in screen space from the origin of this label (the screen space origin is the top, left corner of the canvas). | Pair of Floats. | `[5.0, 30.0]` | +| `label-eye-offset` | Gets and sets the 3D Cartesian offset applied to this label in eye coordinates. | Tuple of three Floats. | `[5.0, 10.0, 15.0]` | +| `translucency-by-distance` | Near and far translucency properties of a Label based on the Label's distance from the camera. | Array of four Floats. | `[1.5e2, 3, 8.0e6, 0.0]` | +| `scale-by-distance` | Near and far scaling properties of a Label based on the label's distance from the camera. | Array of four Floats. | `[1.5e2, 3, 8.0e6, 0.0]` | +| `offset-scale-by-distance` | Near and far pixel offset scaling properties of a Label based on the Label's distance from the camera. | Array of four Floats. | `[1.5e2, 3, 8.0e6, 0.0]` | +| `first-of` | Mark a rule as a parent of a fallback rule list. See description below. | Array of Rule objects. | See example below. | ### Labels in Erdblick diff --git a/libs/core/include/erdblick/rule.h b/libs/core/include/erdblick/rule.h index 187e33f6..ec409202 100644 --- a/libs/core/include/erdblick/rule.h +++ b/libs/core/include/erdblick/rule.h @@ -78,7 +78,7 @@ class FeatureStyleRule [[nodiscard]] bool relationMergeTwoWay() const; [[nodiscard]] std::optional const& attributeType() const; - [[nodiscard]] std::optional const& attributeMask() const; + [[nodiscard]] std::optional const& attributeFilter() const; [[nodiscard]] std::optional const& attributeLayerType() const; [[nodiscard]] std::optional const& attributeValidityGeometry() const; @@ -167,7 +167,7 @@ class FeatureStyleRule bool relationMergeTwoWay_ = false; std::optional attributeType_; - std::optional attributeMask_; + std::optional attributeFilter_; std::optional attributeLayerType_; std::optional attributeValidityGeometry_; diff --git a/libs/core/src/rule.cpp b/libs/core/src/rule.cpp index c2279270..ac2b1bd8 100644 --- a/libs/core/src/rule.cpp +++ b/libs/core/src/rule.cpp @@ -259,9 +259,9 @@ void FeatureStyleRule::parse(const YAML::Node& yaml) // Parse an attribute type regular expression, e.g. `SPEED_LIMIT_.*` attributeType_ = yaml["attribute-type"].as(); } - if (yaml["attribute-mask"].IsDefined()) { + if (yaml["attribute-filter"].IsDefined()) { // Parse an attribute based on it's field value, e.g. `speedLimitKmh > 100` - attributeMask_ = yaml["attribute-mask"].as(); + attributeFilter_ = yaml["attribute-filter"].as(); } if (yaml["attribute-layer-type"].IsDefined()) { // Parse an attribute type regular expression, e.g. `Road.*Layer` @@ -695,9 +695,9 @@ std::optional const& FeatureStyleRule::attributeType() const return attributeType_; } -std::optional const& FeatureStyleRule::attributeMask() const +std::optional const& FeatureStyleRule::attributeFilter() const { - return attributeMask_; + return attributeFilter_; } std::optional const& FeatureStyleRule::attributeLayerType() const diff --git a/libs/core/src/visualization.cpp b/libs/core/src/visualization.cpp index e6d40ede..5768610d 100644 --- a/libs/core/src/visualization.cpp +++ b/libs/core/src/visualization.cpp @@ -281,52 +281,6 @@ void FeatureLayerVisualization::addFeature( } // Iterate over all the layer's attributes. layer->forEachAttribute([&, this](auto&& attr){ - // Check if the attribute's values match the attribute mask for the rule. - if (auto const& attrMask = rule.attributeMask()) { - if (!attrMask->empty()) { - auto const& constAttr = static_cast(*attr); - simfil::OverlayNode attrEvaluationContext(simfil::Value::field(constAttr)); - - try { - constAttr.forEachField([&](auto const& fieldName, auto const& fieldValue) { - if (fieldValue) { - attrEvaluationContext.set( - internalStringPoolCopy_->emplace(fieldName), - simfil::Value::field(fieldValue)); - } - return true; - }); - - addOptionsToSimfilContext(attrEvaluationContext); - - auto boundEvalFun = BoundEvalFun{ - attrEvaluationContext, - [this, &attrEvaluationContext](auto&& str) { - try { - return evaluateExpression(str, attrEvaluationContext); - } catch (const std::exception& e) { - std::cerr << "Error evaluating attribute expression: " << e.what() << std::endl; - return simfil::Value::null(); - } - }}; - - try { - auto result = boundEvalFun.eval_(*attrMask); - if ((result.isa(simfil::ValueType::Bool) && !(result.as())) || - result.isa(simfil::ValueType::Undef) || result.isa(simfil::ValueType::Null)) { - return true; - } - } catch (std::exception const& e) { - std::cerr << "Error evaluating attribute mask: " << e.what() << std::endl; - return true; - } - } catch (const std::exception& e) { - std::cerr << "Error setting up attribute evaluation context: " << e.what() << std::endl; - return true; - } - } - } - addAttribute( feature, layerName, @@ -774,6 +728,17 @@ void FeatureLayerVisualization::addAttribute( return evaluateExpression(str, attrEvaluationContext); }}; + // Check if the attribute's values match the attribute filter for the rule. + if (auto const& attrFilter = rule.attributeFilter()) { + if (!attrFilter->empty()) { + auto result = boundEvalFun.eval_(*attrFilter); + if ((result.isa(simfil::ValueType::Bool) && !result.template as()) || + result.isa(simfil::ValueType::Undef) || result.isa(simfil::ValueType::Null)) { + return; + } + } + } + // Bump visual offset factor for next visualized attribute. ++offsetFactor; From fcd49d8bc6465ed5efddcab7788fc2bfcc852117 Mon Sep 17 00:00:00 2001 From: Wagram Airiian Date: Mon, 9 Dec 2024 23:24:37 +0100 Subject: [PATCH 04/11] Fix keyword --- erdblick_app/app/editor.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erdblick_app/app/editor.component.ts b/erdblick_app/app/editor.component.ts index 45768e5f..fd6e3357 100644 --- a/erdblick_app/app/editor.component.ts +++ b/erdblick_app/app/editor.component.ts @@ -60,7 +60,7 @@ const completionsList = [ {label: "offset-scale-by-distance", type: "property"}, {label: "first-of", type: "property"}, {label: "attribute-type", type: "property"}, - {label: "attribute-mask", type: "property"}, + {label: "attribute-filter", type: "property"}, {label: "attribute-layer-type", type: "property"}, {label: "point-merge-grid-cell", type: "property"}, {label: "FILL", type: "keyword"}, From 8a20e504418511bd2783b9089cd177d727e96b99 Mon Sep 17 00:00:00 2001 From: Joseph Birkner Date: Tue, 10 Dec 2024 13:07:58 +0100 Subject: [PATCH 05/11] Ensure that the rule supports the geometry type for attribute validities as well. --- libs/core/src/visualization.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libs/core/src/visualization.cpp b/libs/core/src/visualization.cpp index 5768610d..21814885 100644 --- a/libs/core/src/visualization.cpp +++ b/libs/core/src/visualization.cpp @@ -321,6 +321,9 @@ void FeatureLayerVisualization::addGeometry( BoundEvalFun& evalFun, glm::dvec3 const& offset) { + if (!rule.supports(geom.geomType_)) + return; + // Combine the ID with the mapTileKey to create an // easy link from the geometry back to the feature. auto tileFeatureId = JsValue::Undefined(); From f0de1ad7ea4efca3dfc94fa23ceba5319ccb2bcf Mon Sep 17 00:00:00 2001 From: Joseph Birkner Date: Tue, 10 Dec 2024 13:55:40 +0100 Subject: [PATCH 06/11] Ensure that the validity collection is non-empty if a validity is required by the rule. --- README.md | 110 ++++++++++++++++---------------- libs/core/src/visualization.cpp | 2 +- 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 2440efcd..608b258d 100644 --- a/README.md +++ b/README.md @@ -65,61 +65,61 @@ The style editor automatically verifies YAML for syntax parsing errors and provi Each rule within the YAML `rules` array can have the following fields: -| Field | Description | Type | Example Value | -|-------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------|----------------------------------------------------| -| `geometry` | List of geometry type(s) or single type the rule applies to. | At least one of `"point"`,`"mesh"`, `"line"`, `"polygon"`. | `["point", "mesh"]`, `line` | -| `aspect` | Specifies the aspect to which the rule applies: `"feature"`, `"relation"`, or `"attribute"`. | String | `"feature"`, `"relation"` | -| `mode` | Specifies the highlight mode: `"none"` or `"hover"` or `"selection"`. | String | `"none"`, `"hover"` | -| `type` | A regular expression to match against a feature type. | String | `"Lane\|Boundary"` | -| `filter` | A [simfil](https://github.com/klebert-engineering/simfil) filter expression over the feature's JSON representation. | String | `*roadClass == 4` | -| `selectable` | Indicates if the feature is selectable. | Boolean | `true`, `false` | -| `color` | A hexadecimal color code or [CSS color name](https://www.w3.org/wiki/CSS/Properties/color/keywords). | String | `"#FF5733"`, `red` | -| `color-expression` | A [simfil](https://github.com/klebert-engineering/simfil) expression which may return an RGBA integer or color string (see above). The expression is evaluated over the current feature/relation/attribute. | String | `isBridge and "#FF5733" or "black"` | -| `opacity` | A float value between 0 and 1 indicating the opacity. | Float | `0.8` | -| `width` | Specifies the line width or point diameter (default in pixels). | Float | `4.5` | -| `flat` | Clamps the feature to the ground (Does not work for meshes). | Boolean | `true`, `false` | -| `outline-color` | Point outline color. | String | `green`, `#fff` | -| `outline-width` | Point outline width in px. | Float | `3.6` | -| `point-merge-grid-cell` | WGS84/altutide meter tolerance for merging point visualizations. | Array of three Floats. | `[0.000000084, 0.000000084, 0.01]` | -| `near-far-scale` | For points, indicate (`near-alt-meters`, `near-scale`, `far-alt-meters`, `far-scale`). | Array of four Floats. | `[1.5e2,10,8.0e6,0]` | -| `icon-url-expression` | A [simfil](https://github.com/klebert-engineering/simfil) expression to evaluate on the point feature the icon belongs to. | String | `category == 5 and "/icons/ev-charging.png" or ""` | -| `icon-url` | A placeholder in case the simfil expression either isn't necessary or won't produce a result. | String | `/icons/unknown.png` | -| `offset` | Apply a fixed offset to each shape-point in meters. Can be used for z-ordering. | Array of three Floats. | `[0, 0, 5]` | -| `arrow` | For arrow-heads: One of `none`, `forward`, `backward`, `double`. Not compatible with `dashed`. | String | `single` | -| `arrow-expression` | A [simfil](https://github.com/klebert-engineering/simfil) expression which may return `none`, `forward`, `backward`, or `double`. | String | `select(arr("single", "double"), 1)` | -| `dashed` | Indicate that a line has dashes. | Boolean. | `true` | -| `gap-color` | If a gap between dashes has a color. | String | `blue`, `#aaa` | -| `dash-length` | Size of a dash in pixels. | Integer. | `16` | -| `dash-pattern` | A 16-bit pattern for the dash. | Integer. | `255` | -| `relation-type` | A regular expression to match against a relation type, e.g., `"connectedFrom"`. | String | `"connectedFrom\|connectedTo"` | -| `relation-line-height-offset` | Vertical offset for relation line in meters. | Float | `0.5` | -| `relation-line-end-markers` | Style for the relation line end-markers. | Sub-rule object | See example below. | -| `relation-source-style` | Style for the relation source geometry. | Sub-rule object | See example below. | -| `relation-target-style` | Style for the relation target geometry. | Sub-rule object | See example below. | -| `relation-recursive` | Specifies whether relations should be resolved recursively. Only done if `mode=="Highlight"`, and only works for relations within the same layer. | Boolean | `true`, `false` | -| `relation-merge-twoway` | Specifies whether bidirectional relations should be followed and merged. | Boolean | `true`, `false` | -| `attribute-type` | A regular expression to match against an attribute type. | String | `SPEED_LIMIT_.*` | -| `attribute-filter` | A [simfil](https://github.com/klebert-engineering/simfil) expression to evaluate against the attribute's field. | String | `attributeValue.speedLimitKmh > 100` | -| `attribute-layer-type` | A regular expression to match against the attribute layer type name. | String | `Road.*Layer` | -| `attribute-validity-geom` | Set to `required`, `none` or `any` to control whether matching attributes must have a validity geometry. | String | `Road.*Layer` | -| `label-color` | Text color of the label. | String | `#00ccdd` | -| `label-outline-color` | Text outline color of the label. | String | `#111111` | -| `label-outline-width` | Text outline width of the label. | Float | `1.0` | -| `label-font` | The font used to draw the label (using the same syntax as the CSS 'font' property). | String | `24px Helvetica` | -| `label-background-color` | Background color of the label. | String | `#000000` | -| `label-background-padding` | Background padding in pixels. | Pair of Integers. | `[7, 5]` | -| `label-horizontal-origin` | Determines if the label is drawn to `LEFT`, `CENTER`, or `RIGHT` of its anchor position. | String | `LEFT` | -| `label-vertical-origin` | Determines if the label is to `ABOVE`, `BELOW`, at `CENTER` or at `BASELINE` of its anchor position. | String | `BASELINE` | -| `label-text-expression` | A [simfil](https://github.com/klebert-engineering/simfil) expression to evaluate on the feature/relation the label belongs to. | String | `**.speedLimitKmh` | -| `label-text` | A placeholder in case the simfil expression either isn't necessary or won't produce a result. | String | `No speed limit` | -| `label-style` | Describes how to draw a label using `FILL`, `OUTLINE` or `FILL_AND_OUTLINE`. | String | `FILL` | -| `label-scale` | The uniform scale that is multiplied with the label's size in pixels. | Float | `1.0` | -| `label-pixel-offset` | The offset in screen space from the origin of this label (the screen space origin is the top, left corner of the canvas). | Pair of Floats. | `[5.0, 30.0]` | -| `label-eye-offset` | Gets and sets the 3D Cartesian offset applied to this label in eye coordinates. | Tuple of three Floats. | `[5.0, 10.0, 15.0]` | -| `translucency-by-distance` | Near and far translucency properties of a Label based on the Label's distance from the camera. | Array of four Floats. | `[1.5e2, 3, 8.0e6, 0.0]` | -| `scale-by-distance` | Near and far scaling properties of a Label based on the label's distance from the camera. | Array of four Floats. | `[1.5e2, 3, 8.0e6, 0.0]` | -| `offset-scale-by-distance` | Near and far pixel offset scaling properties of a Label based on the Label's distance from the camera. | Array of four Floats. | `[1.5e2, 3, 8.0e6, 0.0]` | -| `first-of` | Mark a rule as a parent of a fallback rule list. See description below. | Array of Rule objects. | See example below. | +| Field | Description | Type | Example Value | +|-------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------|----------------------------------------------------| +| `geometry` | List of geometry type(s) or single type the rule applies to. | At least one of `"point"`,`"mesh"`, `"line"`, `"polygon"`. | `["point", "mesh"]`, `line` | +| `aspect` | Specifies the aspect to which the rule applies: `"feature"`, `"relation"`, or `"attribute"`. | String | `"feature"`, `"relation"` | +| `mode` | Specifies the highlight mode: `"none"` or `"hover"` or `"selection"`. | String | `"none"`, `"hover"` | +| `type` | A regular expression to match against a feature type. | String | `"Lane\|Boundary"` | +| `filter` | A [simfil](https://github.com/klebert-engineering/simfil) filter expression over the feature's JSON representation. | String | `*roadClass == 4` | +| `selectable` | Indicates if the feature is selectable. | Boolean | `true`, `false` | +| `color` | A hexadecimal color code or [CSS color name](https://www.w3.org/wiki/CSS/Properties/color/keywords). | String | `"#FF5733"`, `red` | +| `color-expression` | A [simfil](https://github.com/klebert-engineering/simfil) expression which may return an RGBA integer or color string (see above). The expression is evaluated over the current feature/relation/attribute. | String | `isBridge and "#FF5733" or "black"` | +| `opacity` | A float value between 0 and 1 indicating the opacity. | Float | `0.8` | +| `width` | Specifies the line width or point diameter (default in pixels). | Float | `4.5` | +| `flat` | Clamps the feature to the ground (Does not work for meshes). | Boolean | `true`, `false` | +| `outline-color` | Point outline color. | String | `green`, `#fff` | +| `outline-width` | Point outline width in px. | Float | `3.6` | +| `point-merge-grid-cell` | WGS84/altutide meter tolerance for merging point visualizations. | Array of three Floats. | `[0.000000084, 0.000000084, 0.01]` | +| `near-far-scale` | For points, indicate (`near-alt-meters`, `near-scale`, `far-alt-meters`, `far-scale`). | Array of four Floats. | `[1.5e2,10,8.0e6,0]` | +| `icon-url-expression` | A [simfil](https://github.com/klebert-engineering/simfil) expression to evaluate on the point feature the icon belongs to. | String | `category == 5 and "/icons/ev-charging.png" or ""` | +| `icon-url` | A placeholder in case the simfil expression either isn't necessary or won't produce a result. | String | `/icons/unknown.png` | +| `offset` | Apply a fixed offset to each shape-point in meters. Can be used for z-ordering. | Array of three Floats. | `[0, 0, 5]` | +| `arrow` | For arrow-heads: One of `none`, `forward`, `backward`, `double`. Not compatible with `dashed`. | String | `single` | +| `arrow-expression` | A [simfil](https://github.com/klebert-engineering/simfil) expression which may return `none`, `forward`, `backward`, or `double`. | String | `select(arr("single", "double"), 1)` | +| `dashed` | Indicate that a line has dashes. | Boolean. | `true` | +| `gap-color` | If a gap between dashes has a color. | String | `blue`, `#aaa` | +| `dash-length` | Size of a dash in pixels. | Integer. | `16` | +| `dash-pattern` | A 16-bit pattern for the dash. | Integer. | `255` | +| `relation-type` | A regular expression to match against a relation type, e.g., `"connectedFrom"`. | String | `"connectedFrom\|connectedTo"` | +| `relation-line-height-offset` | Vertical offset for relation line in meters. | Float | `0.5` | +| `relation-line-end-markers` | Style for the relation line end-markers. | Sub-rule object | See example below. | +| `relation-source-style` | Style for the relation source geometry. | Sub-rule object | See example below. | +| `relation-target-style` | Style for the relation target geometry. | Sub-rule object | See example below. | +| `relation-recursive` | Specifies whether relations should be resolved recursively. Only done if `mode=="Highlight"`, and only works for relations within the same layer. | Boolean | `true`, `false` | +| `relation-merge-twoway` | Specifies whether bidirectional relations should be followed and merged. | Boolean | `true`, `false` | +| `attribute-type` | A regular expression to match against an attribute type. | String | `SPEED_LIMIT_.*` | +| `attribute-filter` | A [simfil](https://github.com/klebert-engineering/simfil) expression to evaluate against the attribute's fields. | String | `attributeValue.speedLimitKmh > 100` | +| `attribute-layer-type` | A regular expression to match against the attribute layer type name. | String | `Road.*Layer` | +| `attribute-validity-geom` | Set to `required`, `none` or `any` to control whether matching attributes must have a validity geometry. | String | `Road.*Layer` | +| `label-color` | Text color of the label. | String | `#00ccdd` | +| `label-outline-color` | Text outline color of the label. | String | `#111111` | +| `label-outline-width` | Text outline width of the label. | Float | `1.0` | +| `label-font` | The font used to draw the label (using the same syntax as the CSS 'font' property). | String | `24px Helvetica` | +| `label-background-color` | Background color of the label. | String | `#000000` | +| `label-background-padding` | Background padding in pixels. | Pair of Integers. | `[7, 5]` | +| `label-horizontal-origin` | Determines if the label is drawn to `LEFT`, `CENTER`, or `RIGHT` of its anchor position. | String | `LEFT` | +| `label-vertical-origin` | Determines if the label is to `ABOVE`, `BELOW`, at `CENTER` or at `BASELINE` of its anchor position. | String | `BASELINE` | +| `label-text-expression` | A [simfil](https://github.com/klebert-engineering/simfil) expression to evaluate on the feature/relation the label belongs to. | String | `**.speedLimitKmh` | +| `label-text` | A placeholder in case the simfil expression either isn't necessary or won't produce a result. | String | `No speed limit` | +| `label-style` | Describes how to draw a label using `FILL`, `OUTLINE` or `FILL_AND_OUTLINE`. | String | `FILL` | +| `label-scale` | The uniform scale that is multiplied with the label's size in pixels. | Float | `1.0` | +| `label-pixel-offset` | The offset in screen space from the origin of this label (the screen space origin is the top, left corner of the canvas). | Pair of Floats. | `[5.0, 30.0]` | +| `label-eye-offset` | Gets and sets the 3D Cartesian offset applied to this label in eye coordinates. | Tuple of three Floats. | `[5.0, 10.0, 15.0]` | +| `translucency-by-distance` | Near and far translucency properties of a Label based on the Label's distance from the camera. | Array of four Floats. | `[1.5e2, 3, 8.0e6, 0.0]` | +| `scale-by-distance` | Near and far scaling properties of a Label based on the label's distance from the camera. | Array of four Floats. | `[1.5e2, 3, 8.0e6, 0.0]` | +| `offset-scale-by-distance` | Near and far pixel offset scaling properties of a Label based on the Label's distance from the camera. | Array of four Floats. | `[1.5e2, 3, 8.0e6, 0.0]` | +| `first-of` | Mark a rule as a parent of a fallback rule list. See description below. | Array of Rule objects. | See example below. | ### Labels in Erdblick diff --git a/libs/core/src/visualization.cpp b/libs/core/src/visualization.cpp index 21814885..b9edf398 100644 --- a/libs/core/src/visualization.cpp +++ b/libs/core/src/visualization.cpp @@ -697,7 +697,7 @@ void FeatureLayerVisualization::addAttribute( // Check if the attribute validity is accepted for the rule. if (auto const& validityGeomRequired = rule.attributeValidityGeometry()) { - if (*validityGeomRequired != attr->validityOrNull()) { + if (*validityGeomRequired != (attr->validityOrNull() && attr->validityOrNull()->size())) { return; } } From ea273f8f69063f5513bc2e974f68ecf9961f5a27 Mon Sep 17 00:00:00 2001 From: Joseph Birkner Date: Tue, 10 Dec 2024 14:06:58 +0100 Subject: [PATCH 07/11] Fix some small Clang tidy hints. --- libs/core/src/visualization.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/core/src/visualization.cpp b/libs/core/src/visualization.cpp index b9edf398..ffe416cc 100644 --- a/libs/core/src/visualization.cpp +++ b/libs/core/src/visualization.cpp @@ -342,6 +342,7 @@ void FeatureLayerVisualization::addGeometry( } std::vector vertsCartesian; + vertsCartesian.reserve(geom.points_.size()); for (auto const& vertCarto : geom.points_) { vertsCartesian.emplace_back(wgsToCartesian(vertCarto, offset)); } @@ -421,7 +422,7 @@ void FeatureLayerVisualization::addGeometry( evalFun, [&](auto& augmentedEvalFun) { - return labelCollection_.labelParams( + return CesiumLabelCollection::labelParams( xyzPos, text, rule, From 968c2ade9435810f8d6fce118ecdf113e02bbc40 Mon Sep 17 00:00:00 2001 From: Wagram Airiian Date: Fri, 13 Dec 2024 10:18:08 +0100 Subject: [PATCH 08/11] * Add geometry name in inspection * Add featureId on validities when present * Add access to TileFeatureLayer in inspection * Add debug symbols for WASM code when built as debug preset * Add feature index for inspection nodes with hoverId for mapTileKey referencing * Add onKeyHover and onKeyHoverExit methods to the inspection panel --- CMakeLists.txt | 9 ++-- CMakePresets.json | 6 ++- erdblick_app/app/feature.panel.component.ts | 27 +++++++++- erdblick_app/app/inspection.service.ts | 11 ++-- libs/core/CMakeLists.txt | 6 +++ libs/core/include/erdblick/inspection.h | 11 ++-- libs/core/src/inspection.cpp | 58 +++++++++++++-------- libs/core/src/visualization.cpp | 30 +++++++++-- 8 files changed, 118 insertions(+), 40 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ee23ee6..73120bfc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ include(FetchContent) set(CMAKE_CXX_STANDARD 20) # Treat warnings as errors, with some exceptions for Cesium. -set (ERDBLICK_CXX_FLAGS +set(ERDBLICK_CXX_FLAGS "-Wall -Wno-error=sign-conversion -Wno-sign-compare -Wno-sign-conversion -Wno-unused-local-typedefs -Wno-comment -Wno-effc++") if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ERDBLICK_CXX_FLAGS} -Wno-bool-compare") @@ -14,8 +14,12 @@ elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ERDBLICK_CXX_FLAGS} -Wno-error=shorten-64-to-32") endif() -# External dependencies. +# Adjust CXX_FLAGS for Debug builds +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") +endif() +# External dependencies message("Building for ${CMAKE_SYSTEM_NAME}.") FetchContent_Declare(mapget @@ -44,4 +48,3 @@ else() WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" DEPENDS erdblick-core) endif() - diff --git a/CMakePresets.json b/CMakePresets.json index 0a6c4045..3b97b56a 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -16,7 +16,11 @@ "inherits": "common", "displayName": "Debug", "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug" + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_CXX_FLAGS_DEBUG": "-g" + }, + "environment": { + "EMCC_DEBUG": "1" } }, { diff --git a/erdblick_app/app/feature.panel.component.ts b/erdblick_app/app/feature.panel.component.ts index f983b1a3..3f1f47d6 100644 --- a/erdblick_app/app/feature.panel.component.ts +++ b/erdblick_app/app/feature.panel.component.ts @@ -79,7 +79,10 @@ interface Column { {{ rowData['key'] }} + (mouseover)="onKeyHover($event, rowData)" + (mouseout)="onKeyHoverExit($event, rowData)" + style="cursor: pointer"> + {{ rowData['key'] }} @@ -402,6 +405,11 @@ export class FeaturePanelComponent implements OnInit, AfterViewInit, OnDestroy rowData["mapId"], rowData["value"], coreLib.HighlightMode.HOVER_HIGHLIGHT).then(); + } else if (rowData["hoverId"]) { + // this.mapService.highlightFeatures([{ + // mapTileKey: this.inspectionService.selectedFeatures[rowData["featureIndex"]].featureTile.mapTileKey, + // featureId: rowData["hoverId"] + // }], false, coreLib.HighlightMode.HOVER_HIGHLIGHT).then(); } } @@ -412,6 +420,23 @@ export class FeaturePanelComponent implements OnInit, AfterViewInit, OnDestroy } } + onKeyHover(event: any, rowData: any) { + event.stopPropagation(); + if (rowData["hoverId"]) { + // this.mapService.highlightFeatures([{ + // mapTileKey: this.inspectionService.selectedFeatures[rowData["featureIndex"]].featureTile.mapTileKey, + // featureId: rowData["hoverId"] + // }], false, coreLib.HighlightMode.HOVER_HIGHLIGHT).then(); + } + } + + onKeyHoverExit(event: any, rowData: any) { + event.stopPropagation(); + // if (rowData["type"] == this.InspectionValueType.FEATUREID.value) { + // this.mapService.highlightFeatures([], false, coreLib.HighlightMode.HOVER_HIGHLIGHT).then(); + // } + } + getStyleClassByType(valueType: number): string { switch (valueType) { case this.InspectionValueType.SECTION.value: diff --git a/erdblick_app/app/inspection.service.ts b/erdblick_app/app/inspection.service.ts index c70b6264..40776151 100644 --- a/erdblick_app/app/inspection.service.ts +++ b/erdblick_app/app/inspection.service.ts @@ -131,7 +131,7 @@ export class InspectionService { } getFeatureTreeDataFromModel() { - let convertToTreeTableNodes = (dataNodes: Array): TreeTableNode[] => { + let convertToTreeTableNodes = (dataNodes: Array, featureIndex: number): TreeTableNode[] => { let treeNodes: Array = []; for (const data of dataNodes) { const node: TreeTableNode = {}; @@ -164,6 +164,8 @@ export class InspectionService { } if (data.hasOwnProperty("hoverId")) { node.data["hoverId"] = data.hoverId; + // Necessary to query one of the selectedFeatures for its mapTileKey + node.data["featureIndex"] = featureIndex; } if (data.hasOwnProperty("mapId")) { node.data["mapId"] = data.mapId; @@ -174,7 +176,7 @@ export class InspectionService { if (data.hasOwnProperty("sourceDataReferences")) { node.data["sourceDataReferences"] = data.sourceDataReferences; } - node.children = data.hasOwnProperty("children") ? convertToTreeTableNodes(data.children) : []; + node.children = data.hasOwnProperty("children") ? convertToTreeTableNodes(data.children, featureIndex) : []; treeNodes.push(node); } return treeNodes; @@ -182,7 +184,8 @@ export class InspectionService { let treeNodes: Array = []; if (this.selectedFeatureInspectionModel) { - for (const section of this.selectedFeatureInspectionModel) { + for (let i = 0; i < this.selectedFeatureInspectionModel.length; i++) { + const section = this.selectedFeatureInspectionModel[i]; const node: TreeTableNode = {}; node.data = {key: section.key, value: section.value, type: section.type}; if (section.hasOwnProperty("info")) { @@ -191,7 +194,7 @@ export class InspectionService { if (section.hasOwnProperty("sourceDataReferences")) { node.data["sourceDataReferences"] = section.sourceDataReferences; } - node.children = convertToTreeTableNodes(section.children); + node.children = convertToTreeTableNodes(section.children, i); treeNodes.push(node); } } diff --git a/libs/core/CMakeLists.txt b/libs/core/CMakeLists.txt index fd822fe2..5d5b451d 100644 --- a/libs/core/CMakeLists.txt +++ b/libs/core/CMakeLists.txt @@ -44,6 +44,12 @@ set(ERDBLICK_SOURCE_FILES ) if(${CMAKE_SYSTEM_NAME} STREQUAL "Emscripten") + # Defaulting to debug symbols if the build type is Debug + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g") + endif() + list(APPEND ERDBLICK_SOURCE_FILES src/bindings.cpp) add_executable(erdblick-core ${ERDBLICK_SOURCE_FILES}) target_compile_definitions(erdblick-core PUBLIC EMSCRIPTEN) diff --git a/libs/core/include/erdblick/inspection.h b/libs/core/include/erdblick/inspection.h index b224d7ba..2df708c7 100644 --- a/libs/core/include/erdblick/inspection.h +++ b/libs/core/include/erdblick/inspection.h @@ -1,13 +1,13 @@ #pragma once +#include +#include +#include #include "cesium-interface/object.h" #include "mapget/model/feature.h" -#include "mapget/model/sourceinfo.h" -#include "simfil/model/string-pool.h" #include "sfl/small_vector.hpp" -#include -#include -#include +#include "simfil/model/string-pool.h" +#include "mapget/model/featurelayer.h" namespace erdblick { @@ -94,6 +94,7 @@ class InspectionConverter std::shared_ptr stringPool_; std::unordered_map translatedFieldNames_; std::unordered_map relationsByType_; + mapget::TileFeatureLayer* tile_ = nullptr; }; } // namespace erdblick diff --git a/libs/core/src/inspection.cpp b/libs/core/src/inspection.cpp index 879f558f..a7383d51 100644 --- a/libs/core/src/inspection.cpp +++ b/libs/core/src/inspection.cpp @@ -45,6 +45,7 @@ JsValue InspectionConverter::convert(model_ptr const& featurePtr) { stringPool_ = featurePtr->model().strings(); featureId_ = featurePtr->id()->toString(); + tile_ = &featurePtr->model(); // Top-Level Feature Item auto featureScope = push("Feature", "", ValueType::Section); @@ -206,6 +207,7 @@ void InspectionConverter::convertAttributeLayer( convertValidity(convertStringView("validity"), validity); } + attrScope->mapId_ = convertStringView(tile_->mapId()); attrScope->hoverId_ = featureId_+":attribute#"+std::to_string(nextAttributeIndex_); ++nextAttributeIndex_; @@ -237,19 +239,23 @@ void InspectionConverter::convertRelation(const model_ptr& r) void InspectionConverter::convertGeometry(JsValue const& key, const model_ptr& g) { - // TODO: Show geometry name auto geomScope = push( key, key.type() == JsValue::Type::Number ? FieldOrIndex(key.as()) : FieldOrIndex(key.as()), ValueType::String); + std::string typeString; switch (g->geomType()) { - case GeomType::Points: geomScope->value_ = convertStringView("Points"); break; - case GeomType::Line: geomScope->value_ = convertStringView("Polyline"); break; - case GeomType::Polygon: geomScope->value_ = convertStringView("Polygon"); break; - case GeomType::Mesh: geomScope->value_ = convertStringView("Mesh"); break; + case GeomType::Points: typeString = "Points"; break; + case GeomType::Line: typeString = "Polyline"; break; + case GeomType::Polygon: typeString = "Polygon"; break; + case GeomType::Mesh: typeString = "Mesh"; break; } + if (g->name()) { + typeString += fmt::format(" ({})", *g->name()); + } + geomScope->value_ = convertStringView(typeString); convertSourceDataReferences(g->sourceDataReferences(), *geomScope); @@ -296,6 +302,10 @@ void InspectionConverter::convertValidity( } } + if (auto featureId = v.featureId()) { + push("featureId", "featureId", ValueType::FeatureId)->value_ = convertStringView(featureId_); + } + if (auto geom = v.simpleGeometry()) { convertGeometry(JsValue("simpleGeometry"), geom); return true; @@ -364,23 +374,29 @@ InspectionConverter::convertField(const JsValue& fieldName, const simfil::ModelN bool isArray = false; OptionalValueAndType singleValue; - switch (value->type()) { - case simfil::ValueType::Undef: return {}; - case simfil::ValueType::TransientObject: break; - case simfil::ValueType::Null: singleValue = {JsValue(), ValueType::Null}; break; - case simfil::ValueType::Bool: singleValue = {JsValue(std::get(value->value())), ValueType::Boolean}; break; - case simfil::ValueType::Int: singleValue = {JsValue(std::get(value->value())), ValueType::Number}; break; - case simfil::ValueType::Float: singleValue = {JsValue(std::get(value->value())), ValueType::Number}; break; - case simfil::ValueType::String: { - auto vv = value->value(); - if (std::holds_alternative(vv)) - singleValue = {convertStringView(std::get(vv)), ValueType::String}; - else - singleValue = {JsValue(std::get(vv)), ValueType::String}; - break; + if (value->addr().column() == TileFeatureLayer::ColumnId::FeatureIds) { + singleValue = {convertStringView(tile_->resolveFeatureId(*value)->toString()), ValueType::FeatureId}; + fieldScope->mapId_ = convertStringView(tile_->mapId()); } - case simfil::ValueType::Object: break; - case simfil::ValueType::Array: isArray = true; break; + else { + switch (value->type()) { + case simfil::ValueType::Undef: return {}; + case simfil::ValueType::TransientObject: break; + case simfil::ValueType::Null: singleValue = {JsValue(), ValueType::Null}; break; + case simfil::ValueType::Bool: singleValue = {JsValue(std::get(value->value())), ValueType::Boolean}; break; + case simfil::ValueType::Int: singleValue = {JsValue(std::get(value->value())), ValueType::Number}; break; + case simfil::ValueType::Float: singleValue = {JsValue(std::get(value->value())), ValueType::Number}; break; + case simfil::ValueType::String: { + auto vv = value->value(); + if (std::holds_alternative(vv)) + singleValue = {convertStringView(std::get(vv)), ValueType::String}; + else + singleValue = {JsValue(std::get(vv)), ValueType::String}; + break; + } + case simfil::ValueType::Object: break; + case simfil::ValueType::Array: isArray = true; break; + } } if (singleValue) { diff --git a/libs/core/src/visualization.cpp b/libs/core/src/visualization.cpp index ffe416cc..17f8fcfc 100644 --- a/libs/core/src/visualization.cpp +++ b/libs/core/src/visualization.cpp @@ -243,7 +243,16 @@ void FeatureLayerVisualization::addFeature( { auto featureId = feature->id()->toString(); if (!featureIdSubset_.empty()) { - if (featureIdSubset_.find(featureId) == featureIdSubset_.end()) { + bool isAllowed = false; + for (auto const& allowedFeatureId : featureIdSubset_) { + // The featureId may also refer to an attribute, + // in this case :attribute# is appended to the string. + if (allowedFeatureId == featureId || allowedFeatureId.starts_with(featureId + ':')) { + isAllowed = true; + break; + } + } + if (!isAllowed) { return; } } @@ -273,14 +282,24 @@ void FeatureLayerVisualization::addFeature( break; uint32_t offsetFactor = 0; + uint32_t attrIndex = 0; attrLayers->forEachLayer([&, this](auto&& layerName, auto&& layer){ // Check if the attribute layer name is accepted for the rule. if (auto const& attrLayerTypeRegex = rule.attributeLayerType()) { - if (!std::regex_match(layerName.begin(), layerName.end(), *attrLayerTypeRegex)) + if (!std::regex_match(layerName.begin(), layerName.end(), *attrLayerTypeRegex)) { + attrIndex += layer->size(); return true; + } } // Iterate over all the layer's attributes. layer->forEachAttribute([&, this](auto&& attr){ + // if (!featureIdSubset_.empty()) { + // if (!featureIdSubset_.contains(fmt::format("{}:attribute#{}", featureId, attrIndex))) { + // attrIndex++; + // return true; + // } + // } + attrIndex++; addAttribute( feature, layerName, @@ -724,6 +743,7 @@ void FeatureLayerVisualization::addAttribute( internalStringPoolCopy_->emplace("$layer"), simfil::Value(layer)); + // Function which can evaluate a simfil expression in the attribute context. auto boundEvalFun = BoundEvalFun{ attrEvaluationContext, @@ -732,6 +752,9 @@ void FeatureLayerVisualization::addAttribute( return evaluateExpression(str, attrEvaluationContext); }}; + // Bump visual offset factor for next visualized attribute. + ++offsetFactor; + // Check if the attribute's values match the attribute filter for the rule. if (auto const& attrFilter = rule.attributeFilter()) { if (!attrFilter->empty()) { @@ -743,9 +766,6 @@ void FeatureLayerVisualization::addAttribute( } } - // Bump visual offset factor for next visualized attribute. - ++offsetFactor; - // Draw validity geometry. if (auto multiValidity = attr->validityOrNull()) { multiValidity->forEach([&, this](auto&& validity) From 9e4a79ccf119feb24d638f633f2adcb139e03ab8 Mon Sep 17 00:00:00 2001 From: Joseph Birkner Date: Mon, 16 Dec 2024 15:37:25 +0100 Subject: [PATCH 09/11] Fix some ASAN findings in inspection.cpp --- erdblick_app/app/debugapi.component.ts | 11 ++++ libs/core/include/erdblick/inspection.h | 6 +- libs/core/src/bindings.cpp | 4 ++ libs/core/src/inspection.cpp | 76 ++++++++++++++----------- 4 files changed, 62 insertions(+), 35 deletions(-) diff --git a/erdblick_app/app/debugapi.component.ts b/erdblick_app/app/debugapi.component.ts index b9c2c7dc..d42c724f 100644 --- a/erdblick_app/app/debugapi.component.ts +++ b/erdblick_app/app/debugapi.component.ts @@ -89,4 +89,15 @@ export class ErdblickDebugApi { coreLib(): ErdblickCore_ { return coreLib; } + + /** Run some simfil query to reproduce problems with search. */ + runSimfilQuery(query: string = "**.transition") { + for (const [_, tile] of this.mapService.loadedTileLayers) { + tile.peek(parsedTile => { + let search = new coreLib.FeatureLayerSearch(parsedTile); + const matchingFeatures = search.filter(query); + search.delete(); + }) + } + } } diff --git a/libs/core/include/erdblick/inspection.h b/libs/core/include/erdblick/inspection.h index 2df708c7..23ad2478 100644 --- a/libs/core/include/erdblick/inspection.h +++ b/libs/core/include/erdblick/inspection.h @@ -82,8 +82,10 @@ class InspectionConverter OptionalValueAndType convertField(std::string_view const& fieldName, simfil::ModelNode::Ptr const& value); OptionalValueAndType convertField(JsValue const& fieldName, simfil::ModelNode::Ptr const& value); - JsValue convertStringView(const simfil::StringId& f); - JsValue convertStringView(const std::string_view& f); + JsValue convertString(const simfil::StringId& f); + JsValue convertString(const std::string_view& f); + JsValue convertString(const std::string& f); + JsValue convertString(const char* s); std::string featureId_; uint32_t nextRelationIndex_ = 0; diff --git a/libs/core/src/bindings.cpp b/libs/core/src/bindings.cpp index a6c59e36..51efc67b 100644 --- a/libs/core/src/bindings.cpp +++ b/libs/core/src/bindings.cpp @@ -8,12 +8,16 @@ #include #include +#include #if defined(__has_feature) #if __has_feature(address_sanitizer) const char *__lsan_default_options() { return "verbosity=1:malloc_context_size=64"; } +const char *__asan_default_options() { + return "verbosity=1:malloc_context_size=64:detect_container_overflow=0"; +} #endif #endif diff --git a/libs/core/src/inspection.cpp b/libs/core/src/inspection.cpp index a7383d51..a115a1dc 100644 --- a/libs/core/src/inspection.cpp +++ b/libs/core/src/inspection.cpp @@ -54,12 +54,12 @@ JsValue InspectionConverter::convert(model_ptr const& featurePtr) // Identifiers section. { - auto scope = push(convertStringView("Identifiers"), "", ValueType::Section); - push("type", "typeId", ValueType::String)->value_ = convertStringView(featurePtr->typeId()); + auto scope = push(convertString("Identifiers"), "", ValueType::Section); + push("type", "typeId", ValueType::String)->value_ = convertString(featurePtr->typeId()); // Add map and layer names to the Identifiers section. - push("mapId", "mapId", ValueType::String)->value_ = convertStringView(featurePtr->model().mapId()); - push("layerId", "layerId", ValueType::String)->value_ = convertStringView(featurePtr->model().layerInfo()->layerId_); + push("mapId", "mapId", ValueType::String)->value_ = convertString(featurePtr->model().mapId()); + push("layerId", "layerId", ValueType::String)->value_ = convertString(featurePtr->model().layerInfo()->layerId_); // TODO: Investigate and fix the issue for "index out of bounds" error. // Affects boundaries and lane connectors @@ -74,17 +74,17 @@ JsValue InspectionConverter::convert(model_ptr const& featurePtr) for (auto const& [key, value]: featurePtr->id()->keyValuePairs()) { auto &field = current_->children_.emplace_back(); - field.key_ = convertStringView(key); + field.key_ = convertString(key); field.value_ = JsValue::fromVariant(value); field.type_ = ValueType::String; - field.geoJsonPath_ = convertStringView(key).toString(); + field.geoJsonPath_ = convertString(key).toString(); } } // Basic attributes section. if (auto attrs = featurePtr->attributesOrNull()) { - auto scope = push(convertStringView("Basic Attributes"), "properties", ValueType::Section); + auto scope = push(convertString("Basic Attributes"), "properties", ValueType::Section); for (auto const& [k, v] : attrs->fields()) { convertField(k, v); } @@ -93,7 +93,7 @@ JsValue InspectionConverter::convert(model_ptr const& featurePtr) // Flexible attributes section. if (auto layers = featurePtr->attributeLayersOrNull()) { - auto scope = push(convertStringView("Attribute Layers"), "properties.layer", ValueType::Section); + auto scope = push(convertString("Attribute Layers"), "properties.layer", ValueType::Section); layers->forEachLayer([this](auto&& layerName, auto&& layer) -> bool { convertAttributeLayer(layerName, layer); return true; @@ -104,7 +104,7 @@ JsValue InspectionConverter::convert(model_ptr const& featurePtr) using namespace mapget; if (featurePtr->numRelations()) { - auto scope = push(convertStringView("Relations"), "relations", ValueType::Section); + auto scope = push(convertString("Relations"), "relations", ValueType::Section); std::unordered_map>> relsByName; featurePtr->forEachRelation([this](model_ptr const& relation) -> bool { convertRelation(relation); @@ -115,7 +115,7 @@ JsValue InspectionConverter::convert(model_ptr const& featurePtr) // Geometry section. if (auto geomCollection = featurePtr->geomOrNull()) { - auto scope = push(convertStringView("Geometry"), "geometry", ValueType::Section); + auto scope = push(convertString("Geometry"), "geometry", ValueType::Section); uint32_t geomIndex = 0; geomCollection->forEachGeometry([this, &geomIndex](model_ptr const& geom) -> bool { convertGeometry(JsValue(geomIndex++), geom); @@ -153,7 +153,7 @@ InspectionConverter::InspectionNodeScope InspectionConverter::push( FieldOrIndex const& path, InspectionConverter::ValueType type) { - return push(convertStringView(key), path, type); + return push(convertString(key), path, type); } InspectionConverter::InspectionNodeScope @@ -178,10 +178,10 @@ void InspectionConverter::convertAttributeLayer( const std::string_view& name, const model_ptr& l) { - auto layerScope = push(convertStringView(name), name); + auto layerScope = push(convertString(name), name); l->forEachAttribute([this](model_ptr const& attr) { - auto attrScope = push(convertStringView(attr->name()), attr->name(), ValueType::Null); + auto attrScope = push(convertString(attr->name()), attr->name(), ValueType::Null); convertSourceDataReferences(attr->sourceDataReferences(), *attrScope); auto numValues = 0; @@ -204,10 +204,10 @@ void InspectionConverter::convertAttributeLayer( } if (auto validity = attr->validityOrNull()) { - convertValidity(convertStringView("validity"), validity); + convertValidity(convertString("validity"), validity); } - attrScope->mapId_ = convertStringView(tile_->mapId()); + attrScope->mapId_ = JsValue(tile_->mapId()); attrScope->hoverId_ = featureId_+":attribute#"+std::to_string(nextAttributeIndex_); ++nextAttributeIndex_; @@ -229,10 +229,10 @@ void InspectionConverter::convertRelation(const model_ptr& r) relScope->hoverId_ = featureId_+":relation#"+std::to_string(nextRelationIndex_); convertSourceDataReferences(r->sourceDataReferences(), *relScope); if (auto const sourceValidity = r->sourceValidityOrNull()) { - convertValidity(convertStringView("sourceValidity"), sourceValidity); + convertValidity(convertString("sourceValidity"), sourceValidity); } if (auto const targetValidity = r->targetValidityOrNull()) { - convertValidity(convertStringView("targetValidity"), targetValidity); + convertValidity(convertString("targetValidity"), targetValidity); } ++nextRelationIndex_; } @@ -255,7 +255,7 @@ void InspectionConverter::convertGeometry(JsValue const& key, const model_ptrname()) { typeString += fmt::format(" ({})", *g->name()); } - geomScope->value_ = convertStringView(typeString); + geomScope->value_ = convertString(typeString); convertSourceDataReferences(g->sourceDataReferences(), *geomScope); @@ -287,23 +287,23 @@ void InspectionConverter::convertValidity( auto dirScope = push("direction", "direction", ValueType::String); switch (direction) { case Validity::Positive: - dirScope->value_ = convertStringView("POSITIVE"); + dirScope->value_ = convertString("POSITIVE"); break; case Validity::Negative: - dirScope->value_ = convertStringView("NEGATIVE"); + dirScope->value_ = convertString("NEGATIVE"); break; case Validity::Both: - dirScope->value_ = convertStringView("BOTH"); + dirScope->value_ = convertString("BOTH"); break; case Validity::None: - dirScope->value_ = convertStringView("NONE"); + dirScope->value_ = convertString("NONE"); break; default: break; } } if (auto featureId = v.featureId()) { - push("featureId", "featureId", ValueType::FeatureId)->value_ = convertStringView(featureId_); + push("featureId", "featureId", ValueType::FeatureId)->value_ = convertString(featureId_); } if (auto geom = v.simpleGeometry()) { @@ -312,7 +312,7 @@ void InspectionConverter::convertValidity( } if (auto geomName = v.geometryName()) { - push("geometryName", "geometryName", ValueType::String)->value_ = convertStringView(*geomName); + push("geometryName", "geometryName", ValueType::String)->value_ = convertString(*geomName); } auto renderOffset = [this, &v](Point const& data, std::string_view const& name) @@ -357,14 +357,14 @@ InspectionConverter::OptionalValueAndType InspectionConverter::convertField( const simfil::StringId& fieldId, const simfil::ModelNode::Ptr& value) { - return convertField(convertStringView(fieldId), value); + return convertField(convertString(fieldId), value); } InspectionConverter::OptionalValueAndType InspectionConverter::convertField( const std::string_view& fieldName, const simfil::ModelNode::Ptr& value) { - return convertField(convertStringView(fieldName), value); + return convertField(convertString(fieldName), value); } InspectionConverter::OptionalValueAndType @@ -375,8 +375,8 @@ InspectionConverter::convertField(const JsValue& fieldName, const simfil::ModelN OptionalValueAndType singleValue; if (value->addr().column() == TileFeatureLayer::ColumnId::FeatureIds) { - singleValue = {convertStringView(tile_->resolveFeatureId(*value)->toString()), ValueType::FeatureId}; - fieldScope->mapId_ = convertStringView(tile_->mapId()); + singleValue = {convertString(tile_->resolveFeatureId(*value)->toString()), ValueType::FeatureId}; + fieldScope->mapId_ = JsValue(tile_->mapId()); } else { switch (value->type()) { @@ -389,7 +389,7 @@ InspectionConverter::convertField(const JsValue& fieldName, const simfil::ModelN case simfil::ValueType::String: { auto vv = value->value(); if (std::holds_alternative(vv)) - singleValue = {convertStringView(std::get(vv)), ValueType::String}; + singleValue = {convertString(std::get(vv)), ValueType::String}; else singleValue = {JsValue(std::get(vv)), ValueType::String}; break; @@ -411,7 +411,7 @@ InspectionConverter::convertField(const JsValue& fieldName, const simfil::ModelN if (isArray) kk = JsValue(index); else - kk = convertStringView(k); + kk = convertString(k); auto singleValueForField = convertField(kk, v); if (singleValueForField) { ++numValues; @@ -427,15 +427,15 @@ InspectionConverter::convertField(const JsValue& fieldName, const simfil::ModelN return {}; } -JsValue InspectionConverter::convertStringView(const simfil::StringId& f) +JsValue InspectionConverter::convertString(const simfil::StringId& f) { if (auto fieldStr = stringPool_->resolve(f)) { - return convertStringView(*fieldStr); + return convertString(*fieldStr); } return {}; } -JsValue InspectionConverter::convertStringView(const std::string_view& f) +JsValue InspectionConverter::convertString(const std::string_view& f) { auto translation = translatedFieldNames_.find(f); if (translation != translatedFieldNames_.end()) @@ -444,6 +444,16 @@ JsValue InspectionConverter::convertStringView(const std::string_view& f) return newTrans->second; } +JsValue InspectionConverter::convertString(const std::string& s) +{ + return convertString(std::string_view(s)); +} + +JsValue InspectionConverter::convertString(const char* s) +{ + return convertString(std::string_view(s)); +} + JsValue InspectionConverter::InspectionNode::toJsValue() const { auto newDict = JsValue::Dict({ From 1183aa66c7695163fbf1fbbce3664a5859458fa1 Mon Sep 17 00:00:00 2001 From: Joseph Birkner Date: Mon, 16 Dec 2024 17:50:48 +0100 Subject: [PATCH 10/11] Bundle unminified Cesium sources in debug build. --- angular.json | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/angular.json b/angular.json index 6d27c1bf..5ed801fa 100644 --- a/angular.json +++ b/angular.json @@ -27,7 +27,7 @@ "assets": [ { "glob": "**/*", - "input": "node_modules/cesium/Build/Cesium", + "input": "node_modules/cesium/Build/CesiumUnminified", "output": "/bundle/cesium" }, { @@ -58,9 +58,7 @@ "node_modules/material-icons/iconfont/material-icons.css", "erdblick_app/styles.scss" ], - "scripts": [ - - ], + "scripts": [], "customWebpackConfig": { "path": "./webpack.config.js" }, @@ -80,7 +78,34 @@ "maximumError": "4kb" } ], - "outputHashing": "all" + "outputHashing": "all", + "assets": [ + { + "glob": "**/*", + "input": "node_modules/cesium/Build/Cesium", + "output": "/bundle/cesium" + }, + { + "glob": "**/*", + "input": "config/styles", + "output": "/bundle/styles" + }, + { + "glob": "**/*", + "input": "images", + "output": "/bundle/images" + }, + { + "glob": "**/*.json", + "input": "config", + "output": "/" + }, + { + "glob": "VERSION", + "input": ".", + "output": "/bundle/" + } + ] }, "development": { "buildOptimizer": false, @@ -123,8 +148,7 @@ "zone.js/testing" ], "tsConfig": "tsconfig.spec.json", - "assets": [ - ], + "assets": [], "styles": [ "erdblick_app/styles.scss" ], @@ -136,6 +160,6 @@ } }, "cli": { - "analytics": false + "analytics": false } } From fe1376e27620b37c9bfc1f4edf7dd564457bf9cf Mon Sep 17 00:00:00 2001 From: Serein Pfeiffer Date: Mon, 16 Dec 2024 18:48:18 +0100 Subject: [PATCH 11/11] deps: Use cesium 1.124.0 which fixes rendering exception when using distance based opacity/scaling. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ec521841..a5363d02 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "@ngx-formly/core": "^6.3.9", "assert": "^2.1.0", "browserify-zlib": "^0.2.0", - "cesium": "1.120.0", + "cesium": "1.124.0", "codemirror": "^6.0.1", "https-browserify": "^1.0.0", "js-yaml": "^4.1.0",