You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2023/07/01 15:23:07 UTC
[sis] branch master updated (d59f279d23 -> 2f048bc7a0)
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/sis.git
from d59f279d23 Merge branch 'geoapi-3.1'.
add 9992bd5235 Style : add internal implementation of GeoAPI Style
add 4ee789634f Review of the new style package added in previous commit. Summary of changes:
add b35d75f713 Rename `org.apache.sis.internal.style` package as `org.apache.sis.style.se1`. By moving the package outside `internal` namespace, we are making it public. However we still target SIS 1.5, not SIS 1.4, for release.
add d38b17c046 Add a placeholder for a JAXB adapter for expressions, then verifies that current JAXB annotations are valid.
add 1dacb571d6 Add `CoverageStyle` and reintroduce `StyleFactory` classes (in different form). `CoverageStyle` is defined by OGC 05-077r4 in complement to `FeatureTypeStyle`. Those two classes are identical except for the kind of data on which they work: `FeatureTypeStyle` styles `Feature` while `CoverageStyle` styles `BandedCoverage`. For making that difference possible, it was necessary to add parameterized type <R> on all classes in replacement for the previously hard-coded <Featu [...]
add 5ee494c8e8 Remove last links to GeoAPI-pending `org.opengis.style` package. Add a `Style` interface as a placeholder for future style API (to be determined according the result of OGC/ISO works).
add 27cab0a6de Merge the style classes from branch 'geoapi-4.0' into geoapi-3.1.
new 2f048bc7a0 Merge the style classes from branch 'geoapi-3.1'.
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../org/apache/sis/feature/FeatureOperations.java | 2 +-
.../main/java/org/apache/sis/xml/Namespaces.java | 18 +-
core/sis-portrayal/pom.xml | 5 +
.../src/main/java/org/apache/sis/style/Style.java | 29 ++
.../java/org/apache/sis/style/package-info.java | 41 ++
.../org/apache/sis/style/se1/AbstractStyle.java | 348 ++++++++++++++
.../java/org/apache/sis/style/se1/AnchorPoint.java | 160 ++++++
.../org/apache/sis/style/se1/ChannelSelection.java | 193 ++++++++
.../java/org/apache/sis/style/se1/ColorMap.java | 94 ++++
.../org/apache/sis/style/se1/ColorReplacement.java | 87 ++++
.../apache/sis/style/se1/ContrastEnhancement.java | 168 +++++++
.../org/apache/sis/style/se1/ContrastMethod.java | 54 +++
.../org/apache/sis/style/se1/CoverageStyle.java | 68 +++
.../java/org/apache/sis/style/se1/Description.java | 160 ++++++
.../org/apache/sis/style/se1/Displacement.java | 163 +++++++
.../java/org/apache/sis/style/se1/ElseFilter.java | 51 ++
.../apache/sis/style/se1/ExpressionAdapter.java | 52 ++
.../org/apache/sis/style/se1/ExternalGraphic.java | 139 ++++++
.../org/apache/sis/style/se1/FeatureTypeStyle.java | 81 ++++
.../main/java/org/apache/sis/style/se1/Fill.java | 248 ++++++++++
.../main/java/org/apache/sis/style/se1/Font.java | 224 +++++++++
.../java/org/apache/sis/style/se1/Graphic.java | 351 ++++++++++++++
.../java/org/apache/sis/style/se1/GraphicFill.java | 138 ++++++
.../org/apache/sis/style/se1/GraphicStroke.java | 203 ++++++++
.../org/apache/sis/style/se1/GraphicalElement.java | 52 ++
.../org/apache/sis/style/se1/GraphicalSymbol.java | 181 +++++++
.../main/java/org/apache/sis/style/se1/Halo.java | 170 +++++++
.../org/apache/sis/style/se1/LabelPlacement.java | 80 +++
.../org/apache/sis/style/se1/LegendGraphic.java | 130 +++++
.../org/apache/sis/style/se1/LinePlacement.java | 283 +++++++++++
.../org/apache/sis/style/se1/LineSymbolizer.java | 173 +++++++
.../main/java/org/apache/sis/style/se1/Mark.java | 293 +++++++++++
.../org/apache/sis/style/se1/OverlapBehavior.java | 52 ++
.../org/apache/sis/style/se1/PointPlacement.java | 209 ++++++++
.../org/apache/sis/style/se1/PointSymbolizer.java | 172 +++++++
.../apache/sis/style/se1/PolygonSymbolizer.java | 324 +++++++++++++
.../org/apache/sis/style/se1/RasterSymbolizer.java | 358 ++++++++++++++
.../main/java/org/apache/sis/style/se1/Rule.java | 513 ++++++++++++++++++++
.../org/apache/sis/style/se1/SelectedChannel.java | 173 +++++++
.../org/apache/sis/style/se1/SemanticType.java | 67 +++
.../org/apache/sis/style/se1/ShadedRelief.java | 155 ++++++
.../main/java/org/apache/sis/style/se1/Stroke.java | 492 +++++++++++++++++++
.../org/apache/sis/style/se1/StyleElement.java | 219 +++++++++
.../org/apache/sis/style/se1/StyleFactory.java | 534 +++++++++++++++++++++
.../java/org/apache/sis/style/se1/Symbolizer.java | 314 ++++++++++++
.../java/org/apache/sis/style/se1/Symbology.java | 255 ++++++++++
.../org/apache/sis/style/se1/TextSymbolizer.java | 288 +++++++++++
.../java/org/apache/sis/style/se1/Translucent.java | 49 ++
.../org/apache/sis/style/se1/package-info.java | 66 +++
.../org/apache/sis/style/se1/AnchorPointTest.java | 53 ++
.../apache/sis/style/se1/ChannelSelectionTest.java | 71 +++
.../sis/style/se1/ContrastEnhancementTest.java | 67 +++
.../org/apache/sis/style/se1/DescriptionTest.java | 60 +++
.../org/apache/sis/style/se1/DisplacementTest.java | 53 ++
.../apache/sis/style/se1/ExternalGraphicTest.java | 102 ++++
.../apache/sis/style/se1/FeatureTypeStyleTest.java | 134 ++++++
.../java/org/apache/sis/style/se1/FillTest.java | 93 ++++
.../java/org/apache/sis/style/se1/FontTest.java | 100 ++++
.../apache/sis/style/se1/GraphicStrokeTest.java | 65 +++
.../java/org/apache/sis/style/se1/GraphicTest.java | 128 +++++
.../java/org/apache/sis/style/se1/HaloTest.java | 73 +++
.../apache/sis/style/se1/LinePlacementTest.java | 125 +++++
.../apache/sis/style/se1/LineSymbolizerTest.java | 72 +++
.../java/org/apache/sis/style/se1/MarkTest.java | 86 ++++
.../apache/sis/style/se1/PointPlacementTest.java | 90 ++++
.../apache/sis/style/se1/PointSymbolizerTest.java | 57 +++
.../sis/style/se1/PolygonSymbolizerTest.java | 109 +++++
.../apache/sis/style/se1/RasterSymbolizerTest.java | 146 ++++++
.../java/org/apache/sis/style/se1/RuleTest.java | 184 +++++++
.../apache/sis/style/se1/SelectedChannelTest.java | 68 +++
.../org/apache/sis/style/se1/ShadedReliefTest.java | 66 +++
.../java/org/apache/sis/style/se1/StrokeTest.java | 187 ++++++++
.../org/apache/sis/style/se1/StyleTestCase.java | 120 +++++
.../org/apache/sis/style/se1/SymbolizerTest.java | 103 ++++
.../org/apache/sis/style/se1/SymbologyTest.java | 114 +++++
.../apache/sis/style/se1/TextSymbolizerTest.java | 122 +++++
.../java/org/apache/sis/style/se1/XmlTest.java | 50 ++
77 files changed, 11375 insertions(+), 2 deletions(-)
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/Style.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/package-info.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/AbstractStyle.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/AnchorPoint.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/ChannelSelection.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/ColorMap.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/ColorReplacement.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/ContrastEnhancement.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/ContrastMethod.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/CoverageStyle.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Description.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Displacement.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/ElseFilter.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/ExpressionAdapter.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/ExternalGraphic.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/FeatureTypeStyle.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Fill.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Font.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Graphic.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/GraphicFill.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/GraphicStroke.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/GraphicalElement.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/GraphicalSymbol.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Halo.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/LabelPlacement.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/LegendGraphic.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/LinePlacement.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/LineSymbolizer.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Mark.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/OverlapBehavior.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/PointPlacement.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/PointSymbolizer.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/PolygonSymbolizer.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/RasterSymbolizer.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Rule.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/SelectedChannel.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/SemanticType.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/ShadedRelief.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Stroke.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/StyleElement.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/StyleFactory.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Symbolizer.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Symbology.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/TextSymbolizer.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Translucent.java
create mode 100644 core/sis-portrayal/src/main/java/org/apache/sis/style/se1/package-info.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/AnchorPointTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/ChannelSelectionTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/ContrastEnhancementTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/DescriptionTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/DisplacementTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/ExternalGraphicTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/FeatureTypeStyleTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/FillTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/FontTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/GraphicStrokeTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/GraphicTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/HaloTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/LinePlacementTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/LineSymbolizerTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/MarkTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/PointPlacementTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/PointSymbolizerTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/PolygonSymbolizerTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/RasterSymbolizerTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/RuleTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/SelectedChannelTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/ShadedReliefTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/StrokeTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/StyleTestCase.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/SymbolizerTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/SymbologyTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/TextSymbolizerTest.java
create mode 100644 core/sis-portrayal/src/test/java/org/apache/sis/style/se1/XmlTest.java
[sis] 01/01: Merge the style classes from branch 'geoapi-3.1'.
Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sis.git
commit 2f048bc7a0f49e618fafcf9487446d4a28e7298c
Merge: d59f279d23 27cab0a6de
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Sat Jul 1 15:14:40 2023 +0200
Merge the style classes from branch 'geoapi-3.1'.
https://issues.apache.org/jira/browse/SIS-583
.../org/apache/sis/feature/FeatureOperations.java | 2 +-
.../main/java/org/apache/sis/xml/Namespaces.java | 18 +-
core/sis-portrayal/pom.xml | 5 +
.../src/main/java/org/apache/sis/style/Style.java | 29 ++
.../java/org/apache/sis/style/package-info.java | 41 ++
.../org/apache/sis/style/se1/AbstractStyle.java | 348 ++++++++++++++
.../java/org/apache/sis/style/se1/AnchorPoint.java | 160 ++++++
.../org/apache/sis/style/se1/ChannelSelection.java | 193 ++++++++
.../java/org/apache/sis/style/se1/ColorMap.java | 94 ++++
.../org/apache/sis/style/se1/ColorReplacement.java | 87 ++++
.../apache/sis/style/se1/ContrastEnhancement.java | 168 +++++++
.../org/apache/sis/style/se1/ContrastMethod.java | 54 +++
.../org/apache/sis/style/se1/CoverageStyle.java | 68 +++
.../java/org/apache/sis/style/se1/Description.java | 160 ++++++
.../org/apache/sis/style/se1/Displacement.java | 163 +++++++
.../java/org/apache/sis/style/se1/ElseFilter.java | 51 ++
.../apache/sis/style/se1/ExpressionAdapter.java | 52 ++
.../org/apache/sis/style/se1/ExternalGraphic.java | 139 ++++++
.../org/apache/sis/style/se1/FeatureTypeStyle.java | 81 ++++
.../main/java/org/apache/sis/style/se1/Fill.java | 248 ++++++++++
.../main/java/org/apache/sis/style/se1/Font.java | 224 +++++++++
.../java/org/apache/sis/style/se1/Graphic.java | 351 ++++++++++++++
.../java/org/apache/sis/style/se1/GraphicFill.java | 138 ++++++
.../org/apache/sis/style/se1/GraphicStroke.java | 203 ++++++++
.../org/apache/sis/style/se1/GraphicalElement.java | 52 ++
.../org/apache/sis/style/se1/GraphicalSymbol.java | 181 +++++++
.../main/java/org/apache/sis/style/se1/Halo.java | 170 +++++++
.../org/apache/sis/style/se1/LabelPlacement.java | 80 +++
.../org/apache/sis/style/se1/LegendGraphic.java | 130 +++++
.../org/apache/sis/style/se1/LinePlacement.java | 283 +++++++++++
.../org/apache/sis/style/se1/LineSymbolizer.java | 173 +++++++
.../main/java/org/apache/sis/style/se1/Mark.java | 293 +++++++++++
.../org/apache/sis/style/se1/OverlapBehavior.java | 52 ++
.../org/apache/sis/style/se1/PointPlacement.java | 209 ++++++++
.../org/apache/sis/style/se1/PointSymbolizer.java | 172 +++++++
.../apache/sis/style/se1/PolygonSymbolizer.java | 324 +++++++++++++
.../org/apache/sis/style/se1/RasterSymbolizer.java | 358 ++++++++++++++
.../main/java/org/apache/sis/style/se1/Rule.java | 513 ++++++++++++++++++++
.../org/apache/sis/style/se1/SelectedChannel.java | 173 +++++++
.../org/apache/sis/style/se1/SemanticType.java | 67 +++
.../org/apache/sis/style/se1/ShadedRelief.java | 155 ++++++
.../main/java/org/apache/sis/style/se1/Stroke.java | 492 +++++++++++++++++++
.../org/apache/sis/style/se1/StyleElement.java | 219 +++++++++
.../org/apache/sis/style/se1/StyleFactory.java | 534 +++++++++++++++++++++
.../java/org/apache/sis/style/se1/Symbolizer.java | 314 ++++++++++++
.../java/org/apache/sis/style/se1/Symbology.java | 255 ++++++++++
.../org/apache/sis/style/se1/TextSymbolizer.java | 288 +++++++++++
.../java/org/apache/sis/style/se1/Translucent.java | 49 ++
.../org/apache/sis/style/se1/package-info.java | 66 +++
.../org/apache/sis/style/se1/AnchorPointTest.java | 53 ++
.../apache/sis/style/se1/ChannelSelectionTest.java | 71 +++
.../sis/style/se1/ContrastEnhancementTest.java | 67 +++
.../org/apache/sis/style/se1/DescriptionTest.java | 60 +++
.../org/apache/sis/style/se1/DisplacementTest.java | 53 ++
.../apache/sis/style/se1/ExternalGraphicTest.java | 102 ++++
.../apache/sis/style/se1/FeatureTypeStyleTest.java | 134 ++++++
.../java/org/apache/sis/style/se1/FillTest.java | 93 ++++
.../java/org/apache/sis/style/se1/FontTest.java | 100 ++++
.../apache/sis/style/se1/GraphicStrokeTest.java | 65 +++
.../java/org/apache/sis/style/se1/GraphicTest.java | 128 +++++
.../java/org/apache/sis/style/se1/HaloTest.java | 73 +++
.../apache/sis/style/se1/LinePlacementTest.java | 125 +++++
.../apache/sis/style/se1/LineSymbolizerTest.java | 72 +++
.../java/org/apache/sis/style/se1/MarkTest.java | 86 ++++
.../apache/sis/style/se1/PointPlacementTest.java | 90 ++++
.../apache/sis/style/se1/PointSymbolizerTest.java | 57 +++
.../sis/style/se1/PolygonSymbolizerTest.java | 109 +++++
.../apache/sis/style/se1/RasterSymbolizerTest.java | 146 ++++++
.../java/org/apache/sis/style/se1/RuleTest.java | 184 +++++++
.../apache/sis/style/se1/SelectedChannelTest.java | 68 +++
.../org/apache/sis/style/se1/ShadedReliefTest.java | 66 +++
.../java/org/apache/sis/style/se1/StrokeTest.java | 187 ++++++++
.../org/apache/sis/style/se1/StyleTestCase.java | 120 +++++
.../org/apache/sis/style/se1/SymbolizerTest.java | 103 ++++
.../org/apache/sis/style/se1/SymbologyTest.java | 114 +++++
.../apache/sis/style/se1/TextSymbolizerTest.java | 122 +++++
.../java/org/apache/sis/style/se1/XmlTest.java | 50 ++
77 files changed, 11375 insertions(+), 2 deletions(-)
diff --cc core/sis-feature/src/main/java/org/apache/sis/feature/FeatureOperations.java
index 3e79790812,7986a36733..4324ac0310
--- a/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureOperations.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureOperations.java
@@@ -314,7 -307,7 +314,7 @@@ public final class FeatureOperations ex
/**
* Creates an operation which delegates the computation to a given expression.
* The {@code expression} argument should generally be an instance of
-- * {@link org.opengis.filter.Expression},
++ * {@link org.apache.sis.filter.Expression},
* but more generic functions are accepted as well.
*
* <h4>Read/write behavior</h4>
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/AbstractStyle.java
index 0000000000,0bcc54eada..8b0dc11e3b
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/AbstractStyle.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/AbstractStyle.java
@@@ -1,0 -1,347 +1,348 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import java.util.Set;
+ import java.util.List;
+ import java.util.ArrayList;
+ import java.util.EnumSet;
+ import java.util.Optional;
+ import jakarta.xml.bind.Unmarshaller;
+ import jakarta.xml.bind.annotation.XmlType;
+ import jakarta.xml.bind.annotation.XmlElement;
+ import jakarta.xml.bind.annotation.XmlAttribute;
+ import org.opengis.util.GenericName;
+
+ // Branch-dependent imports
-import org.opengis.filter.ResourceId;
++import org.apache.sis.filter.Filter;
+
+
+ /**
+ * Defines the styling that is to be applied on data of some arbitrary type.
+ * The type of data is specified by the {@code <R>} parameterized type and depends on the concrete subclass.
- * The two main types are {@link org.opengis.feature.Feature} and {@link org.apache.sis.coverage.BandedCoverage}.
++ * The two main types are {@link org.apache.sis.feature.AbstractFeature}
++ * and {@link org.apache.sis.coverage.BandedCoverage}.
+ *
+ * <!-- Following list of authors contains credits to OGC GeoAPI 2 contributors. -->
+ * @author Johann Sorel (Geomatys)
+ * @author Chris Dillard (SYS Technologies)
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
+ *
+ * @since 1.5
+ */
+ @XmlType(propOrder = {
+ "name",
+ "description",
+ "featureTypeName",
+ // "semanticTypeIdentifiers",
+ "rules"
+ })
+ public abstract class AbstractStyle<R> extends StyleElement<R> {
+ /**
+ * Version number of the Symbology Encoding Implementation Specification standard currently implemented.
+ * This version number may change in future Apache SIS releases if new standards are published.
+ * The current value is {@value}.
+ */
+ public static final String VERSION = "1.1.0";
+
+ /**
+ * Version number of the Symbology Encoding standard used.
+ * This value should not be changed, unless this style has been unmarshalled
+ * from a XML document that specifies a different version number.
+ *
+ * @see #getVersion()
+ */
+ @XmlAttribute
+ protected String version;
+
+ /**
+ * Name for this style, or {@code null} if none.
+ *
+ * @see #getName()
+ * @see #setName(String)
+ */
+ @XmlElement(name = "Name")
+ protected String name;
+
+ /**
+ * Information for user interfaces, or {@code null} if none.
+ *
+ * @see #getDescription()
+ * @see #setDescription(Description)
+ */
+ @XmlElement(name = "Description")
+ protected Description<R> description;
+
+ /**
+ * Identification of feature instances on which to apply the style, or {@code null} if none.
+ *
+ * @see #getFeatureInstanceIDs()
+ * @see #setFeatureInstanceIDs(ResourceId)
+ */
- protected ResourceId<? super R> featureInstanceIDs;
++ protected Filter<? super R> featureInstanceIDs;
+
+ /**
+ * Name of the feature type that this style is meant to act upon, or {@code null} if none.
+ *
+ * @see #getFeatureTypeName()
+ * @see #setFeatureTypeName(GenericName)
+ */
+ @XmlElement(name = "FeatureTypeName")
+ protected GenericName featureTypeName;
+
+ /**
+ * Types of geometry that this style is meant to act upon, as a live collection.
+ * May be empty but never null.
+ *
+ * @see #semanticTypeIdentifiers()
+ */
+ // @XmlElement(name = "SemanticTypeIdentifier")
+ private EnumSet<SemanticType> semanticTypeIdentifiers;
+
+ /**
+ * List of rules.
+ *
+ * <h4>Online rules</h4>
+ * A XML document may contain links to rules instead of a full definitions.
+ * Such {@code se:OnlineResource} elements are handled at marshalling time.
+ * When reading a XML document, the rule links are resolved automatically.
+ * When writing a XML document, some rules may be replaced by online resources
+ * if {@link Rule#getOnlineSource()} is provided.
+ *
+ * @todo JAXB adapter for handling the online case is not yet written.
+ *
+ * @see #rules()
+ */
+ @XmlElement(name = "Rule")
+ private List<Rule<R>> rules;
+
+ /**
+ * Invoked by JAXB before unmarshalling this mark.
+ * Avoid giving the false impression that the XML document contained a version string.
+ */
+ private void beforeUnmarshal(Unmarshaller caller, Object parent) {
+ version = "unspecified";
+ }
+
+ /**
+ * Creates an initially empty feature type style.
+ * Callers should set the following properties after construction:
+ *
+ * <ul>
+ * <li>Either the {@linkplain #setFeatureTypeName feature type name}
+ * or {@linkplain #semanticTypeIdentifiers() semantic type identifiers}, or both.</li>
+ * <li>At least one {@linkplain #rules() rule} should be added.</li>
+ * </ul>
+ *
+ * @param factory the factory to use for creating expressions and child elements.
+ */
+ public AbstractStyle(final StyleFactory<R> factory) {
+ super(factory);
+ version = VERSION;
+ semanticTypeIdentifiers = EnumSet.noneOf(SemanticType.class);
+ rules = new ArrayList<>();
+ }
+
+ /**
+ * Creates a shallow copy of the given object.
+ * For a deep copy, see {@link #clone()} instead.
+ *
+ * @param source the object to copy.
+ */
+ public AbstractStyle(final AbstractStyle<R> source) {
+ super(source);
+ version = source.version;
+ name = source.name;
+ description = source.description;
+ featureInstanceIDs = source.featureInstanceIDs;
+ featureTypeName = source.featureTypeName;
+ semanticTypeIdentifiers = source.semanticTypeIdentifiers.clone();
+ rules = new ArrayList<>(source.rules);
+ }
+
+ /**
+ * Returns the version number of the Symbology Encoding standard used.
+ * This is fixed to {@value #VERSION} in current Apache SIS release.
+ * This value cannot be changed, unless this style has been unmarshalled
+ * from a XML document that specifies a different version number.
+ *
+ * @return version number of the Symbology Encoding standard used.
+ */
+ public String getVersion() {
+ return version;
+ }
+
+ /**
+ * Returns the name for this style.
+ * This can be any string that uniquely identifies this style within a given canvas.
+ * It is not meant to be human-friendly. For a human-friendly label,
+ * see the {@linkplain Description#getTitle() title} instead.
+ *
+ * @return a name for this style.
+ */
+ public Optional<String> getName() {
+ return Optional.ofNullable(name);
+ }
+
+ /**
+ * Sets a name for this style.
+ * If this method is never invoked, then the default value is absence.
+ *
+ * @param value new name for this style.
+ */
+ public void setName(final String value) {
+ name = value;
+ }
+
+ /**
+ * Returns the description of this style.
+ * The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this style, and conversely.
+ *
+ * @return information for user interfaces.
+ */
+ public Optional<Description<R>> getDescription() {
+ return Optional.ofNullable(description);
+ }
+
+ /**
+ * Sets a description of this style.
+ * The given instance is stored by reference, it is not cloned.
+ * If this method is never invoked, then the default value is absence.
+ *
+ * @param value new information for user interfaces, or {@code null} if none.
+ */
+ public void setDescription(final Description<R> value) {
+ description = value;
+ }
+
+ /**
+ * Returns an identification of feature instances on which to apply the style.
+ * This method enable the possibility to use a feature type style on a given list
+ * of features only, instead of all instances of the feature type.
+ *
+ * @return identification of the feature instances.
+ */
- public Optional<ResourceId<? super R>> getFeatureInstanceIDs() {
++ public Optional<Filter<? super R>> getFeatureInstanceIDs() {
+ return Optional.ofNullable(featureInstanceIDs);
+ }
+
+ /**
+ * Sets an identification of feature instances on which to apply the style.
+ * If this method is never invoked, then the default value is absence.
+ *
+ * @param value new identification of feature instances, or {@code null} if none.
+ */
- public void setFeatureInstanceIDs(final ResourceId<? super R> value) {
++ public void setFeatureInstanceIDs(final Filter<? super R> value) {
+ featureInstanceIDs = value;
+ }
+
+ /**
+ * Returns the name of the feature type that this style is meant to act upon.
+ * It is allowed to be null but only if the feature type can be inferred by other means,
+ * for example from context or using {@link SemanticType} identifiers.
+ *
+ * @return name of the feature type that this style is meant to act upon.
+ */
+ public Optional<GenericName> getFeatureTypeName() {
+ return Optional.ofNullable(featureTypeName);
+ }
+
+ /**
+ * Sets the name of the feature type that this style is meant to act upon.
+ * If this method is never invoked, then the default value is absence.
+ *
+ * @param value new name of the feature type, or {@code null} if none.
+ */
+ public void setFeatureTypeName(final GenericName value) {
+ featureTypeName = value;
+ }
+
+ /**
+ * Returns the most general types of geometry that this style is meant to act upon.
+ * The syntax is currently undefined, but the following values are reserved to indicate
+ * that the style applies to feature with default geometry of specific type:
+ *
+ * <ul>
+ * <li>{@code generic:point}</li>
+ * <li>{@code generic:line}</li>
+ * <li>{@code generic:polygon}</li>
+ * <li>{@code generic:text}</li>
+ * <li>{@code generic:raster}</li>
+ * <li>{@code generic:any}</li>
+ * </ul>
+ *
+ * <p>The returned collection is <em>live</em>:
+ * changes in that collection are reflected into this object, and conversely.</p>
+ *
+ * @return types of geometry that this style is meant to act upon, as a live collection.
+ */
+ @SuppressWarnings("ReturnOfCollectionOrArrayField")
+ public Set<SemanticType> semanticTypeIdentifiers() {
+ return semanticTypeIdentifiers;
+ }
+
+ /**
+ * Returns the list of rules contained by this style.
+ * Order matter: the first item in a list will be the
+ * first item plotted and hence appears on the bottom.
+ *
+ * <p>The returned collection is <em>live</em>:
+ * changes in that collection are reflected into this object, and conversely.</p>
+ *
+ * @return ordered list of rules, as a live collection.
+ */
+ @SuppressWarnings("ReturnOfCollectionOrArrayField")
+ public List<Rule<R>> rules() {
+ return rules;
+ }
+
+ /**
+ * Returns all properties contained in this class.
+ * This is used for {@link #equals(Object)} and {@link #hashCode()} implementations.
+ */
+ @Override
+ final Object[] properties() {
+ return new Object[] {name, description, featureInstanceIDs, featureTypeName, semanticTypeIdentifiers, rules};
+ }
+
+ /**
+ * Returns a deep clone of this object. All style elements are cloned,
+ * but expressions are not on the assumption that they are immutable.
+ *
+ * @return deep clone of all style elements.
+ */
+ @Override
+ public AbstractStyle<R> clone() {
+ final var clone = (AbstractStyle<R>) super.clone();
+ clone.selfClone();
+ return clone;
+ }
+
+ /**
+ * Clones the mutable style fields of this element.
+ */
+ private void selfClone() {
+ if (description != null) description = description.clone();
+ semanticTypeIdentifiers = semanticTypeIdentifiers.clone();
+ rules = new ArrayList<>(rules);
+ rules.replaceAll(Rule::clone);
+ }
+ }
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/AnchorPoint.java
index 0000000000,bd2c32907f..dc9cdf95e6
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/AnchorPoint.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/AnchorPoint.java
@@@ -1,0 -1,160 +1,160 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import jakarta.xml.bind.annotation.XmlType;
+ import jakarta.xml.bind.annotation.XmlElement;
+ import jakarta.xml.bind.annotation.XmlRootElement;
+
+ // Branch-dependent imports
-import org.opengis.filter.Expression;
++import org.apache.sis.filter.Expression;
+
+
+ /**
+ * The location inside a graphic or label to use as an "anchor" for positioning it relative to a point.
+ * The coordinates are given as (<var>x</var>,<var>y</var>) floating-point numbers between 0 and 1 inclusive.
+ * The bounding box of the graphic/label to be rendered is considered to be in a coordinate space from 0
+ * (lower-left corner) to 1 (upper-right corner), and the anchor position is specified as a point in this space.
+ * The default anchor point is <var>x</var> = 0.5, <var>y</var> = 0.5,
+ * which is at the middle height and middle length of the graphic/label.
+ *
+ * <!-- Following list of authors contains credits to OGC GeoAPI 2 contributors. -->
+ * @author Johann Sorel (Geomatys)
+ * @author Ian Turton (CCG)
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
+ *
+ * @since 1.5
+ */
+ @XmlType(name = "AnchorPointType", propOrder = {
+ "anchorPointX",
+ "anchorPointY"
+ })
+ @XmlRootElement(name = "AnchorPoint")
+ public class AnchorPoint<R> extends StyleElement<R> {
+ /**
+ * The <var>x</var> coordinate of the anchor point.
+ * This property is mandatory.
+ *
+ * @see #getAnchorPointX()
+ * @see #setAnchorPointX(Expression)
+ */
+ @XmlElement(name = "AnchorPointX", required = true)
+ protected Expression<R, ? extends Number> anchorPointX;
+
+ /**
+ * The <var>y</var> coordinate of the anchor point.
+ * This property is mandatory.
+ *
+ * @see #getAnchorPointY()
+ * @see #setAnchorPointY(Expression)
+ */
+ @XmlElement(name = "AnchorPointY", required = true)
+ protected Expression<R, ? extends Number> anchorPointY;
+
+ /**
+ * For JAXB unmarshalling only.
+ */
+ private AnchorPoint() {
+ // Thread-local factory will be used.
+ }
+
+ /**
+ * Creates an anchor point initialized to <var>x</var> = 0.5 and <var>y</var> = 0.5.
+ * This initial position is the center of the graphic/label.
+ *
+ * @param factory the factory to use for creating expressions and child elements.
+ */
+ public AnchorPoint(final StyleFactory<R> factory) {
+ super(factory);
+ anchorPointX = anchorPointY = factory.half;
+ }
+
+ /**
+ * Creates a shallow copy of the given object.
+ * For a deep copy, see {@link #clone()} instead.
+ *
+ * @param source the object to copy.
+ */
+ public AnchorPoint(final AnchorPoint<R> source) {
+ super(source);
+ anchorPointX = source.anchorPointX;
+ anchorPointY = source.anchorPointY;
+ }
+
+ /**
+ * Returns the <var>x</var> coordinate of the anchor point.
+ * It should be a floating point number between 0 and 1 inclusive.
+ *
+ * @return the expression fetching the <var>x</var> coordinate.
+ */
+ public Expression<R, ? extends Number> getAnchorPointX() {
+ return anchorPointX;
+ }
+
+ /**
+ * Sets the <var>x</var> coordinate of the anchor point.
+ * If this method is never invoked, then the default value is literal 0.5.
+ *
+ * @param value new <var>x</var> coordinate, or {@code null} for resetting the default value.
+ */
+ public void setAnchorPointX(final Expression<R, ? extends Number> value) {
+ anchorPointX = defaultToHalf(value);
+ }
+
+ /**
+ * Returns the <var>y</var> coordinate of the anchor point.
+ * It should be a floating point number between 0 and 1 inclusive.
+ *
+ * @return the expression fetching the <var>y</var> coordinate.
+ */
+ public Expression<R, ? extends Number> getAnchorPointY() {
+ return anchorPointY;
+ }
+
+ /**
+ * Sets the <var>y</var> coordinate of the anchor point.
+ * If this method is never invoked, then the default value is literal 0.5.
+ *
+ * @param value new <var>y</var> coordinate, or {@code null} for resetting the default value.
+ */
+ public void setAnchorPointY(final Expression<R, ? extends Number> value) {
+ anchorPointY = defaultToHalf(value);
+ }
+
+ /**
+ * Returns all properties contained in this class.
+ * This is used for {@link #equals(Object)} and {@link #hashCode()} implementations.
+ */
+ @Override
+ final Object[] properties() {
+ return new Object[] {anchorPointX, anchorPointY};
+ }
+
+ /**
+ * Returns a deep clone of this object. All style elements are cloned,
+ * but expressions are not on the assumption that they are immutable.
+ *
+ * @return deep clone of all style elements.
+ */
+ @Override
+ public AnchorPoint<R> clone() {
+ return (AnchorPoint<R>) super.clone();
+ }
+ }
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/ContrastEnhancement.java
index 0000000000,2069d30b05..3540992206
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/ContrastEnhancement.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/ContrastEnhancement.java
@@@ -1,0 -1,168 +1,168 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import jakarta.xml.bind.annotation.XmlType;
+ import jakarta.xml.bind.annotation.XmlElement;
+ import jakarta.xml.bind.annotation.XmlRootElement;
+
+ // Branch-dependent imports
-import org.opengis.filter.Expression;
++import org.apache.sis.filter.Expression;
+
+
+ /**
+ * Contrast enhancement for an image or an individual image channel.
+ * In the case of a color image, the relative grayscale brightness of a pixel color is used.
+ *
+ * <!-- Following list of authors contains credits to OGC GeoAPI 2 contributors. -->
+ * @author Johann Sorel (Geomatys)
+ * @author Ian Turton (CCG)
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
+ *
+ * @since 1.5
+ */
+ @XmlType(name = "ContrastEnhancementType", propOrder = {
+ // "normalize",
+ // "histogram",
+ "gammaValue"
+ })
+ @XmlRootElement(name = "ContrastEnhancement")
+ public class ContrastEnhancement<R> extends StyleElement<R> {
+ /**
+ * Method to use for applying contrast enhancement, or {@code null} for the default value.
+ * The default value depends on whether or not a {@linkplain #gammaValue gamma value} is defined.
+ *
+ * @see #getMethod()
+ * @see #setMethod(ContrastMethod)
+ *
+ * @todo Marshall as empty "Normalize" or "Histogram" XML element.
+ */
+ protected ContrastMethod method;
+
+ /**
+ * How much to brighten or dim an image, or {@code null} for the default value.
+ *
+ * @see #getGammaValue()
+ * @see #setGammaValue(Expression)
+ *
+ * @todo Add a JAXB adapter for marshalling as a plain number.
+ */
+ @XmlElement(name = "GammaValue")
+ protected Expression<R, ? extends Number> gammaValue;
+
+ /**
+ * For JAXB unmarshalling only.
+ */
+ private ContrastEnhancement() {
+ // Thread-local factory will be used.
+ }
+
+ /**
+ * Creates a contrast enhancement initialized to no operation.
+ *
+ * @param factory the factory to use for creating expressions and child elements.
+ */
+ public ContrastEnhancement(final StyleFactory<R> factory) {
+ super(factory);
+ }
+
+ /**
+ * Creates a shallow copy of the given object.
+ * For a deep copy, see {@link #clone()} instead.
+ *
+ * @param source the object to copy.
+ */
+ public ContrastEnhancement(final ContrastEnhancement<R> source) {
+ super(source);
+ method = source.method;
+ gammaValue = source.gammaValue;
+ }
+
+ /**
+ * Returns the method to use for applying contrast enhancement.
+ *
+ * @return method to use for applying contrast enhancement.
+ */
+ public ContrastMethod getMethod() {
+ final var value = method;
+ if (value != null) {
+ return value;
+ }
+ return (gammaValue != null) ? ContrastMethod.GAMMA : ContrastMethod.NONE;
+ }
+
+ /**
+ * Sets the method to use for applying contrast enhancement.
+ * Setting this method to anything else than {@link ContrastMethod#GAMMA}
+ * clears the {@linkplain #getGammaValue() gamma value}.
+ *
+ * @param value new method to use, or {@code null} for none.
+ */
+ public void setMethod(final ContrastMethod value) {
+ method = value;
+ if (value != ContrastMethod.GAMMA) {
+ gammaValue = null;
+ }
+ }
+
+ /**
+ * Tells how much to brighten (values greater than 1) or dim (values less than 1) an image.
+ * A value of 1 means no change.
+ *
+ * @return expression to control gamma adjustment.
+ */
+ public Expression<R, ? extends Number> getGammaValue() {
+ return defaultToOne(gammaValue);
+ }
+
+ /**
+ * Sets how much to brighten (values greater than 1) or dim (values less than 1) an image.
+ * Setting a non-null value sets the method to {@link ContrastMethod#GAMMA}.
+ * If this method is never invoked, then the default value is literal 1.
+ *
+ * @param value new expression to control gamma adjustment, or {@code null} for the default.
+ */
+ public void setGammaValue(final Expression<R, ? extends Number> value) {
+ gammaValue = value;
+ if (value != null) {
+ method = null;
+ }
+ }
+
+ /**
+ * Returns all properties contained in this class.
+ * This is used for {@link #equals(Object)} and {@link #hashCode()} implementations.
+ */
+ @Override
+ final Object[] properties() {
+ return new Object[] {method, gammaValue};
+ }
+
+ /**
+ * Returns a deep clone of this object. All style elements are cloned,
+ * but expressions are not on the assumption that they are immutable.
+ *
+ * @return deep clone of all style elements.
+ */
+ @Override
+ public ContrastEnhancement<R> clone() {
+ return (ContrastEnhancement<R>) super.clone();
+ }
+ }
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Displacement.java
index 0000000000,84702112c9..b9d8fc311e
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Displacement.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Displacement.java
@@@ -1,0 -1,163 +1,163 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import jakarta.xml.bind.annotation.XmlType;
+ import jakarta.xml.bind.annotation.XmlElement;
+ import jakarta.xml.bind.annotation.XmlRootElement;
+
+ // Branch-dependent imports
-import org.opengis.filter.Expression;
++import org.apache.sis.filter.Expression;
+
+
+ /**
+ * A two-dimensional offset from the original geometry.
+ * Displacements may be used to avoid over-plotting, or for supplying shadows.
+ * The displacements units depend on the context:
+ * in {@linkplain Symbolizer#getUnitOfMeasure() symbolizer unit of measurements}
+ * when the displacement is applied by a {@link PolygonSymbolizer},
+ * but in pixels when the displacement is applied by a {@link TextSymbolizer}.
+ * The displacements are to the right of the point.
+ * The default displacement is <var>x</var> = 0, <var>y</var> = 0,
+ *
+ * <!-- Following list of authors contains credits to OGC GeoAPI 2 contributors. -->
+ * @author Johann Sorel (Geomatys)
+ * @author Ian Turton (CCG)
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
+ *
+ * @since 1.5
+ *
+ * @see PointPlacement#getDisplacement()
+ * @see Graphic#getDisplacement()
+ * @see PolygonSymbolizer#getDisplacement()
+ */
+ @XmlType(name = "DisplacementType", propOrder = {
+ "displacementX",
+ "displacementY"
+ })
+ @XmlRootElement(name = "Displacement")
+ public class Displacement<R> extends StyleElement<R> {
+ /**
+ * The <var>x</var> offset from the geometry point.
+ * This property is mandatory.
+ *
+ * @see #getDisplacementX()
+ * @see #setDisplacementX(Expression)
+ */
+ @XmlElement(name = "DisplacementX", required = true)
+ protected Expression<R, ? extends Number> displacementX;
+
+ /**
+ * The <var>y</var> offset from the geometry point.
+ * This property is mandatory.
+ *
+ * @see #getDisplacementY()
+ * @see #setDisplacementY(Expression)
+ */
+ @XmlElement(name = "DisplacementY", required = true)
+ protected Expression<R, ? extends Number> displacementY;
+
+ /**
+ * For JAXB unmarshalling only.
+ */
+ private Displacement() {
+ // Thread-local factory will be used.
+ }
+
+ /**
+ * Creates a displacement initialized to zero offsets.
+ *
+ * @param factory the factory to use for creating expressions and child elements.
+ */
+ public Displacement(final StyleFactory<R> factory) {
+ super(factory);
+ displacementX = displacementY = factory.zero;
+ }
+
+ /**
+ * Creates a shallow copy of the given object.
+ * For a deep copy, see {@link #clone()} instead.
+ *
+ * @param source the object to copy.
+ */
+ public Displacement(final Displacement<R> source) {
+ super(source);
+ displacementX = source.displacementX;
+ displacementY = source.displacementY;
+ }
+
+ /**
+ * Returns an expression fetching an <var>x</var> offset from the geometry point.
+ *
+ * @return <var>x</var> offset from the geometry point.
+ */
+ public Expression<R, ? extends Number> getDisplacementX() {
+ return displacementX;
+ }
+
+ /**
+ * Sets the expression fetching an <var>x</var> offset.
+ * If this method is never invoked, then the default value is literal 0.
+ *
+ * @param value new <var>x</var> offset, or {@code null} for resetting the default value.
+ */
+ public void setDisplacementX(final Expression<R, ? extends Number> value) {
+ displacementX = defaultToZero(value);
+ }
+
+ /**
+ * Returns an expression fetching an <var>y</var> offset from the geometry point.
+ *
+ * @return <var>y</var> offset from the geometry point.
+ */
+ public Expression<R, ? extends Number> getDisplacementY() {
+ return displacementY;
+ }
+
+ /**
+ * Sets the expression fetching an <var>y</var> offset.
+ * If this method is never invoked, then the default value is literal 0.
+ *
+ * @param value new <var>y</var> offset, or {@code null} for resetting the default value.
+ */
+ public void setDisplacementY(final Expression<R, ? extends Number> value) {
+ displacementY = defaultToZero(value);
+ }
+
+ /**
+ * Returns all properties contained in this class.
+ * This is used for {@link #equals(Object)} and {@link #hashCode()} implementations.
+ */
+ @Override
+ final Object[] properties() {
+ return new Object[] {displacementX, displacementY};
+ }
+
+ /**
+ * Returns a deep clone of this object. All style elements are cloned,
+ * but expressions are not on the assumption that they are immutable.
+ *
+ * @return deep clone of all style elements.
+ */
+ @Override
+ public Displacement<R> clone() {
+ return (Displacement<R>) super.clone();
+ }
+ }
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/ExpressionAdapter.java
index 0000000000,3ea09310de..a5adcb5d2a
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/ExpressionAdapter.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/ExpressionAdapter.java
@@@ -1,0 -1,52 +1,52 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import jakarta.xml.bind.annotation.adapters.XmlAdapter;
+
+ // Branch-dependent imports
-import org.opengis.filter.Expression;
++import org.apache.sis.filter.Expression;
+
+
+ /**
+ * Adapter for expression in style.
+ * This is a place-holder for future work.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
+ *
+ * @since 1.5
+ */
+ final class ExpressionAdapter<R> extends XmlAdapter<String, Expression<R,?>> {
+ /**
+ * Creates an adapter.
+ */
+ public ExpressionAdapter() {
+ }
+
+ @Override
+ public String marshal(Expression<R,?> value) {
+ return null;
+ }
+
+ @Override
+ public Expression<R,?> unmarshal(String value) {
+ return null;
+ }
+ }
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/FeatureTypeStyle.java
index 0000000000,6aa50648ee..73b192bac6
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/FeatureTypeStyle.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/FeatureTypeStyle.java
@@@ -1,0 -1,81 +1,81 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import jakarta.xml.bind.annotation.XmlType;
+ import jakarta.xml.bind.annotation.XmlRootElement;
+ import org.apache.sis.filter.DefaultFilterFactory;
+
+ // Branch-dependent imports
-import org.opengis.feature.Feature;
++import org.apache.sis.feature.AbstractFeature;
+
+
+ /**
+ * Defines the styling that is to be applied to a single feature type.
+ *
+ * <!-- Following list of authors contains credits to OGC GeoAPI 2 contributors. -->
+ * @author Johann Sorel (Geomatys)
+ * @author Chris Dillard (SYS Technologies)
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ * @since 1.5
+ */
+ @XmlType(name = "FeatureTypeStyleType")
+ @XmlRootElement(name = "FeatureTypeStyle")
-public class FeatureTypeStyle extends AbstractStyle<Feature> {
++public class FeatureTypeStyle extends AbstractStyle<AbstractFeature> {
+ /**
+ * The default style factory for features.
+ */
- public static final StyleFactory<Feature> FACTORY =
++ public static final StyleFactory<AbstractFeature> FACTORY =
+ new StyleFactory<>(DefaultFilterFactory.forFeatures());
+
+ /**
+ * Creates an initially empty feature type style.
+ * Callers should set the following properties after construction:
+ *
+ * <ul>
+ * <li>Either the {@linkplain #setFeatureTypeName feature type name}
+ * or {@linkplain #semanticTypeIdentifiers() semantic type identifiers}, or both.</li>
+ * <li>At least one {@linkplain #rules() rule} should be added.</li>
+ * </ul>
+ */
+ public FeatureTypeStyle() {
+ super(FACTORY);
+ }
+
+ /**
+ * Creates a shallow copy of the given object.
+ * For a deep copy, see {@link #clone()} instead.
+ *
+ * @param source the object to copy.
+ */
+ public FeatureTypeStyle(final FeatureTypeStyle source) {
+ super(source);
+ }
+
+ /**
+ * Returns a deep clone of this object. All style elements are cloned,
+ * but expressions are not on the assumption that they are immutable.
+ *
+ * @return deep clone of all style elements.
+ */
+ @Override
+ public FeatureTypeStyle clone() {
+ final var clone = (FeatureTypeStyle) super.clone();
+ return clone;
+ }
+ }
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Fill.java
index 0000000000,0f8717a441..dc1d63f54c
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Fill.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Fill.java
@@@ -1,0 -1,248 +1,248 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import java.awt.Color;
+ import java.util.Optional;
+ import jakarta.xml.bind.annotation.XmlType;
+ import jakarta.xml.bind.annotation.XmlElement;
+ import jakarta.xml.bind.annotation.XmlRootElement;
+
+ // Branch-dependent imports
-import org.opengis.filter.Expression;
++import org.apache.sis.filter.Expression;
+
+
+ /**
+ * Instructions about how to fill the interior of polygons.
+ * A fill can be a solid color or a repeated {@link GraphicFill}.
+ *
+ * <!-- Following list of authors contains credits to OGC GeoAPI 2 contributors. -->
+ * @author Johann Sorel (Geomatys)
+ * @author Chris Dillard (SYS Technologies)
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
+ *
+ * @since 1.5
+ */
+ @XmlType(name = "FillType", propOrder = {
+ "graphicFill",
+ // "svgParameter"
+ })
+ @XmlRootElement(name = "Fill")
+ public class Fill<R> extends StyleElement<R> implements Translucent<R> {
+ /**
+ * The image to use for filling the area, or {@code null} if a solid color should be used.
+ *
+ * @see #getGraphicFill()
+ * @see #setGraphicFill(GraphicFill)
+ */
+ @XmlElement(name = "GraphicFill")
+ protected GraphicFill<R> graphicFill;
+
+ /**
+ * Color of the interior if it is to be solid-color filled, or {@code null} for the default value.
+ * The default value specified by OGC 05-077r4 standard is gray.
+ *
+ * <p>This property is used when {@link #graphicFill} is null.
+ * In XML documents, this is encoded inside a {@code <SvgParameter name="fill">} element.</p>
+ *
+ * @see #getColor()
+ * @see #setColor(Expression)
+ */
+ protected Expression<R,Color> color;
+
+ /**
+ * Level of translucency as a floating point number between 0 and 1 (inclusive), or {@code null} the default value.
+ * The default value specified by OGC 05-077r4 standard is 1.
+ *
+ * <p>In XML documents, this is encoded inside a {@code <SvgParameter name="fill-opacity">} element.</p>
+ *
+ * @see #getOpacity()
+ * @see #setOpacity(Expression)
+ */
+ protected Expression<R, ? extends Number> opacity;
+
+ /**
+ * For JAXB unmarshalling only.
+ */
+ private Fill() {
+ // Thread-local factory will be used.
+ }
+
+ /**
+ * Creates an opaque fill initialized to the gray color.
+ *
+ * @param factory the factory to use for creating expressions and child elements.
+ */
+ public Fill(final StyleFactory<R> factory) {
+ super(factory);
+ }
+
+ /**
+ * Creates a shallow copy of the given object.
+ * For a deep copy, see {@link #clone()} instead.
+ *
+ * @param source the object to copy.
+ */
+ public Fill(final Fill<R> source) {
+ super(source);
+ graphicFill = source.graphicFill;
+ color = source.color;
+ opacity = source.opacity;
+ }
+
+ /**
+ * If filling with tiled copies of an image, returns the image that should be drawn.
+ * Otherwise returns an empty value, which means that a solid color should be used.
+ *
+ * <p>The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this fill, and conversely.</p>
+ *
+ * @return image to repeat for filling the area.
+ *
+ * @see Stroke#getGraphicFill()
+ */
+ public Optional<GraphicFill<R>> getGraphicFill() {
+ return Optional.ofNullable(graphicFill);
+ }
+
+ /**
+ * Specifies that area should be filled with the given graphic.
+ * The given instance is stored by reference, it is not cloned.
+ * If this method is never invoked, then the default value is absence.
+ *
+ * @param value new image to repeat for filling the area, or {@code null} if none.
+ *
+ * @see Stroke#setGraphicFill(GraphicFill)
+ */
+ public void setGraphicFill(final GraphicFill<R> value) {
+ graphicFill = value;
+ }
+
+ /**
+ * Indicates the color of the area if it is to be solid-color filled.
+ * This is used when {@linkplain #getGraphicFill() graphic fill} is null.
+ *
+ * @return color of the area if it is to be solid-color filled.
+ *
+ * @see Stroke#getColor()
+ */
+ public Expression<R,Color> getColor() {
+ final var value = color;
+ return (value != null) ? value : factory.gray;
+ }
+
+ /**
+ * Sets the color of the area if it is to be solid-color filled.
+ * If this method is never invoked, then the default value is {@link Fill#GRAY}.
+ * That default value is standardized by OGC 05-077r4.
+ *
+ * <p>Setting a non-null value clears the {@linkplain #getGraphicFill() graphic fill}
+ * because those two properties are mutually exclusive.</p>
+ *
+ * @param value color of the area if solid-color filled, or {@code null} for resetting the default value.
+ *
+ * @see Stroke#setColor(Expression)
+ */
+ public void setColor(final Expression<R,Color> value) {
+ color = value;
+ if (value != null) {
+ graphicFill = null;
+ }
+ }
+
+ /**
+ * Sets the color and opacity together.
+ * The opacity is derived from the alpha value of the given color.
+ *
+ * @param value new color and opacity, or {@code null} for resetting the defaults.
+ */
+ public void setColorAndOpacity(Color value) {
+ if (value == null) {
+ color = null;
+ opacity = null;
+ } else {
+ if ((opacity = opacity(value)) != null) {
+ value = new Color(value.getRGB() | 0xFF000000);
+ }
+ color = literal(value);
+ }
+ }
+
+ /**
+ * Indicates the level of translucency as a floating point number between 0 and 1 (inclusive).
+ * A value of zero means completely transparent. A value of 1.0 means completely opaque.
+ *
+ * @return the level of translucency as a floating point number between 0 and 1 (inclusive).
+ *
+ * @see Stroke#getOpacity()
+ * @see Graphic#getOpacity()
+ * @see RasterSymbolizer#getOpacity()
+ */
+ @Override
+ public Expression<R, ? extends Number> getOpacity() {
+ return defaultToOne(opacity);
+ }
+
+ /**
+ * Sets the level of translucency as a floating point number between 0 and 1 (inclusive).
+ * If this method is never invoked, then the default value is literal 1 (totally opaque).
+ * That default value is standardized by OGC 05-077r4.
+ *
+ * @param value new level of translucency, or {@code null} for resetting the default value.
+ */
+ @Override
+ public void setOpacity(final Expression<R, ? extends Number> value) {
+ opacity = value;
+ }
+
+ /*
+ * TODO: we need a private method like below for formatting above SVG parameters:
+ * See Stroke for more detais.
+ */
+
+ /**
+ * Returns all properties contained in this class.
+ * This is used for {@link #equals(Object)} and {@link #hashCode()} implementations.
+ */
+ @Override
+ final Object[] properties() {
+ return new Object[] {graphicFill, color, opacity};
+ }
+
+ /**
+ * Returns a deep clone of this object. All style elements are cloned,
+ * but expressions are not on the assumption that they are immutable.
+ *
+ * @return deep clone of all style elements.
+ */
+ @Override
+ public Fill<R> clone() {
+ final var clone = (Fill<R>) super.clone();
+ clone.selfClone();
+ return clone;
+ }
+
+ /**
+ * Clones the mutable style fields of this element.
+ */
+ private void selfClone() {
+ if (graphicFill != null) graphicFill = graphicFill.clone();
+ }
+ }
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Font.java
index 0000000000,ea91e8d00c..a32b7822cb
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Font.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Font.java
@@@ -1,0 -1,224 +1,224 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import java.util.List;
+ import java.util.ArrayList;
+ import jakarta.xml.bind.annotation.XmlType;
+ import jakarta.xml.bind.annotation.XmlRootElement;
+
+ // Branch-dependent imports
-import org.opengis.filter.Expression;
++import org.apache.sis.filter.Expression;
+
+
+ /**
+ * Identification of a font of a certain family, style, and size.
+ *
+ * <!-- Following list of authors contains credits to OGC GeoAPI 2 contributors. -->
+ * @author Johann Sorel (Geomatys)
+ * @author Chris Dillard (SYS Technologies)
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
+ *
+ * @since 1.5
+ */
+ @XmlType(name = "FontType")
+ @XmlRootElement(name = "Font")
+ public class Font<R> extends StyleElement<R> {
+ /**
+ * Family names of the font to use, in preference order.
+ *
+ * <p>In XML documents, this is encoded inside a {@code <SvgParameter name="font-family">} element.</p>
+ *
+ * @see #family()
+ */
+ private List<Expression<R,String>> family;
+
+ /**
+ * Style (normal or italic) to use for a font.
+ *
+ * <p>In XML documents, this is encoded inside a {@code <SvgParameter name="font-style">} element.</p>
+ *
+ * @see #getStyle()
+ * @see #setStyle(Expression)
+ */
+ protected Expression<R,String> style;
+
+ /**
+ * Amount of weight or boldness to use for a font.
+ *
+ * <p>In XML documents, this is encoded inside a {@code <SvgParameter name="font-weight">} element.</p>
+ *
+ * @see #getWeight()
+ * @see #setWeight(Expression)
+ */
+ protected Expression<R,String> weight;
+
+ /**
+ * Size (in pixels) to use for the font.
+ *
+ * <p>In XML documents, this is encoded inside a {@code <SvgParameter name="font-size">} element.</p>
+ *
+ * @see #getSize()
+ * @see #setSize(Expression)
+ */
+ protected Expression<R, ? extends Number> size;
+
+ /**
+ * For JAXB unmarshalling only.
+ */
+ private Font() {
+ // Thread-local factory will be used.
+ }
+
+ /**
+ * Creates a font initialized to normal style, normal weight and a size of 10 pixels.
+ *
+ * @param factory the factory to use for creating expressions and child elements.
+ */
+ public Font(final StyleFactory<R> factory) {
+ super(factory);
+ family = new ArrayList<>();
+ }
+
+ /**
+ * Creates a shallow copy of the given object.
+ * For a deep copy, see {@link #clone()} instead.
+ *
+ * @param source the object to copy.
+ */
+ public Font(final Font<R> source) {
+ super(source);
+ family = new ArrayList<>(source.family);
+ style = source.style;
+ weight = source.weight;
+ size = source.size;
+ }
+
+ /**
+ * Returns the family names of the font to use, in preference order.
+ * Allowed values are system-dependent.
+ *
+ * <p>The returned collection is <em>live</em>:
+ * changes in that collection are reflected into this object, and conversely.</p>
+ *
+ * @return the family names in preference order, as a live collection.
+ */
+ @SuppressWarnings("ReturnOfCollectionOrArrayField")
+ public List<Expression<R,String>> family() {
+ return family;
+ }
+
+ /**
+ * Returns the style (normal or italic) to use for a font.
+ * The allowed values are "normal", "italic", and "oblique".
+ *
+ * @return style to use for a font.
+ */
+ public Expression<R,String> getStyle() {
+ final var value = style;
+ return (value != null) ? value : factory.normal;
+ }
+
+ /**
+ * Sets the style (normal or italic) to use for a font.
+ * If this method is never invoked, then the default value is the "normal" literal.
+ *
+ * @param value new style to use for a font, or {@code null} for resetting the default value.
+ */
+ public void setStyle(final Expression<R,String> value) {
+ style = value;
+ }
+
+ /**
+ * Returns the amount of weight or boldness to use for a font.
+ * The allowed values are "normal" and "bold".
+ *
+ * @return amount of weight or boldness to use for a font.
+ */
+ public Expression<R,String> getWeight() {
+ final var value = weight;
+ return (value != null) ? value : factory.normal;
+ }
+
+ /**
+ * Sets the amount of weight or boldness to use for a font.
+ * If this method is never invoked, then the default value is the "normal" literal.
+ *
+ * @param value new amount of weight to use for a font, or {@code null} for resetting the default value.
+ */
+ public void setWeight(final Expression<R,String> value) {
+ weight = value;
+ }
+
+ /**
+ * Returns the size (in pixels) to use for the font.
+ *
+ * @return size (in pixels) to use for the font.
+ */
+ public Expression<R, ? extends Number> getSize() {
+ final var value = size;
+ return (value != null) ? value : factory.ten;
+ }
+
+ /**
+ * Sets the size (in pixels) to use for the font.
+ * If this method is never invoked, then the default value is 10 pixels.
+ * That default value is standardized by OGC 05-077r4.
+ *
+ * @param value new size to use for the font, or {@code null} for resetting the default value.
+ */
+ public void setSize(final Expression<R, ? extends Number> value) {
+ size = value;
+ }
+
+ /*
+ * TODO: we need a private method like below for formatting above SVG parameters:
+ * See Stroke for more detais.
+ */
+
+ /**
+ * Returns all properties contained in this class.
+ * This is used for {@link #equals(Object)} and {@link #hashCode()} implementations.
+ */
+ @Override
+ final Object[] properties() {
+ return new Object[] {family, style, weight, size};
+ }
+
+ /**
+ * Returns a deep clone of this object. All style elements are cloned,
+ * but expressions are not on the assumption that they are immutable.
+ *
+ * @return deep clone of all style elements.
+ */
+ @Override
+ public Font<R> clone() {
+ final var clone = (Font<R>) super.clone();
+ clone.selfClone();
+ return clone;
+ }
+
+ /**
+ * Clones the mutable style fields of this element.
+ */
+ private void selfClone() {
+ family = new ArrayList<>(family);
+ }
+ }
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Graphic.java
index 0000000000,4864a81be2..e2fd39bd6b
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Graphic.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Graphic.java
@@@ -1,0 -1,351 +1,351 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import java.util.List;
+ import java.util.ArrayList;
+ import jakarta.xml.bind.annotation.XmlType;
+ import jakarta.xml.bind.annotation.XmlElement;
+ import jakarta.xml.bind.annotation.XmlElements;
+ import jakarta.xml.bind.annotation.XmlRootElement;
+
+ // Branch-dependent imports
-import org.opengis.filter.Expression;
++import org.apache.sis.filter.Expression;
+
+
+ /**
+ * A "graphic symbol" with an inherent shape, color(s), and possibly size.
+ * A "graphic" can be very informally defined as "a little picture"
+ * and can be of either a raster or vector-graphic source type.
+ *
+ * <!-- Following list of authors contains credits to OGC GeoAPI 2 contributors. -->
+ * @author Johann Sorel (Geomatys)
+ * @author Chris Dillard (SYS Technologies)
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
+ *
+ * @since 1.5
+ */
+ @XmlType(name = "GraphicType", propOrder = {
+ "graphicalSymbols",
+ "opacity",
+ "size",
+ "rotation",
+ "anchorPoint",
+ "displacement"
+ })
+ @XmlRootElement(name = "Graphic")
+ public class Graphic<R> extends StyleElement<R> implements Translucent<R> {
+ /**
+ * List of external image files or marks that comprise this graphic.
+ * All elements of the list must be instances of either {@link Mark} or {@link ExternalGraphic}.
+ * If empty it is to be treated a single default mark.
+ *
+ * @see #graphicalSymbols()
+ */
+ @XmlElements({
+ @XmlElement(name = "Mark", type = Mark.class),
+ @XmlElement(name = "ExternalGraphic", type = ExternalGraphic.class)
+ })
+ private List<GraphicalSymbol<R>> graphicalSymbols;
+
+ /**
+ * Level of translucency as a floating point number, or {@code null} for the default value.
+ *
+ * @see #getOpacity()
+ * @see #setOpacity(Expression)
+ *
+ * @todo Needs a JAXB adapter to {@code ParameterValueType}.
+ */
+ @XmlElement(name = "Opacity")
+ protected Expression<R, ? extends Number> opacity;
+
+ /**
+ * Absolute size of the graphic as a floating point number, or {@code null} for the default value.
+ *
+ * @see #getSize()
+ * @see #setSize(Expression)
+ *
+ * @todo Needs a JAXB adapter to {@code ParameterValueType}.
+ */
+ @XmlElement(name = "Size")
+ protected Expression<R, ? extends Number> size;
+
+ /**
+ * Rotation angle of the graphic when it is drawn, or {@code null} for the default value.
+ *
+ * @see #getRotation()
+ * @see #setRotation(Expression)
+ *
+ * @todo Needs a JAXB adapter to {@code ParameterValueType}.
+ */
+ @XmlElement(name = "Rotation")
+ protected Expression<R, ? extends Number> rotation;
+
+ /**
+ * Location to use for anchoring the graphic to the geometry, or {@code null} for lazily constructed default.
+ *
+ * @see #getAnchorPoint()
+ * @see #setAnchorPoint(AnchorPoint)
+ */
+ @XmlElement(name = "AnchorPoint")
+ protected AnchorPoint<R> anchorPoint;
+
+ /**
+ * Displacement from the "hot-spot" point, or {@code null} for lazily constructed default.
+ *
+ * @see #getDisplacement()
+ * @see #setDisplacement(Displacement)
+ */
+ @XmlElement(name = "Displacement")
+ protected Displacement<R> displacement;
+
+ /**
+ * For JAXB unmarshalling only.
+ */
+ private Graphic() {
+ // Thread-local factory will be used.
+ }
+
+ /**
+ * Creates a graphic initialized to opaque default mark, default size and no rotation.
+ *
+ * @param factory the factory to use for creating expressions and child elements.
+ */
+ public Graphic(final StyleFactory<R> factory) {
+ super(factory);
+ graphicalSymbols = new ArrayList<>();
+ }
+
+ /**
+ * Creates a shallow copy of the given object.
+ * For a deep copy, see {@link #clone()} instead.
+ *
+ * @param source the object to copy.
+ */
+ public Graphic(final Graphic<R> source) {
+ super(source);
+ graphicalSymbols = new ArrayList<>(source.graphicalSymbols);
+ opacity = source.opacity;
+ size = source.size;
+ rotation = source.rotation;
+ anchorPoint = source.anchorPoint;
+ displacement = source.displacement;
+ }
+
+ /**
+ * Returns the list of external image files or marks that comprise this graphic.
+ * All elements of the list shall be instances of either {@link Mark} or {@link ExternalGraphic}.
+ * The list may contain multiple external URLs and marks with the semantic that they all provide
+ * the equivalent graphic in different {@linkplain GraphicalSymbol#getFormat() formats}.
+ *
+ * <p>If the list is empty, it is to be treated as a single default mark.
+ * That default is a square with with a 50%-gray fill and a black outline,
+ * with a size of 6 pixels, unless an explicit {@linkplain #getSize() size} is specified.</p>
+ *
+ * <p>The returned collection is <em>live</em>:
+ * changes in that collection are reflected into this object, and conversely.</p>
+ *
+ * @return list of marks or external graphics, as a live collection.
+ */
+ @SuppressWarnings("ReturnOfCollectionOrArrayField")
+ public List<GraphicalSymbol<R>> graphicalSymbols() {
+ return graphicalSymbols;
+ }
+
+ /**
+ * Indicates the level of translucency as a floating point number between 0 and 1 (inclusive).
+ * A value of zero means completely transparent. A value of 1.0 means completely opaque.
+ *
+ * @return the level of translucency as a floating point number between 0 and 1 (inclusive).
+ *
+ * @see Fill#getOpacity()
+ * @see Stroke#getOpacity()
+ * @see RasterSymbolizer#getOpacity()
+ */
+ @Override
+ public Expression<R, ? extends Number> getOpacity() {
+ return defaultToOne(opacity);
+ }
+
+ /**
+ * Sets the level of translucency as a floating point number between 0 and 1 (inclusive).
+ * If this method is never invoked, then the default value is literal 1 (totally opaque).
+ * That default value is standardized by OGC 05-077r4.
+ *
+ * @param value new level of translucency, or {@code null} for resetting the default value.
+ */
+ @Override
+ public void setOpacity(final Expression<R, ? extends Number> value) {
+ opacity = value;
+ }
+
+ /**
+ * Returns the absolute size of the graphic as a floating point number.
+ * If a size is specified, the height of the graphic will be scaled to
+ * that size and the aspect ratio will be used for computing the width.
+ * The unit of measurement is given by {@link Symbolizer#getUnitOfMeasure()}.
+ * Value shall be positive.
+ *
+ * <h4>Default value</h4>
+ * The default size of an image format is the inherent size of the image.
+ * The default size of an image format without an inherent size is defined
+ * to be 16 pixels in height and the corresponding aspect in width.
+ * If no image or mark is specified, then the size of the default mark is 6 pixels.
+ * That default value is standardized by OGC 05-077r4.
+ *
+ * @return absolute size of the graphic as a floating point number, or {@code null} for the default value.
+ */
+ public Expression<R, ? extends Number> getSize() {
+ final var value = size;
+ if (value == null && graphicalSymbols.isEmpty()) {
+ return factory.six;
+ }
+ return value;
+ }
+
+ /**
+ * Sets the absolute size of the graphic as a floating point number.
+ * If this method is never invoked, then the default value is {@code null}.
+ *
+ * @param value new absolute size of the graphic, or {@code null} for the default value.
+ */
+ public void setSize(final Expression<R, ? extends Number> value) {
+ size = value;
+ }
+
+ /**
+ * Returns the rotation angle of the graphic when it is drawn.
+ * The rotation is in the clockwise direction around the graphic center point in decimal degrees.
+ * Negative values mean counter-clockwise rotation. The default value is 0.0 (no rotation).
+ *
+ * <p>The point within the graphic about which it is rotated is format dependent.
+ * If a format does not include an inherent rotation point,
+ * then the point of rotation should be the centroid.</p>
+ *
+ * @return the rotation angle of the graphic when it is drawn.
+ */
+ public Expression<R, ? extends Number> getRotation() {
+ return defaultToZero(rotation);
+ }
+
+ /**
+ * Sets the rotation angle of the graphic when it is drawn.
+ * If this method is never invoked, then the default value is literal 0.
+ *
+ * @param value new rotation angle of the graphic, or {@code null} for resetting the default value.
+ */
+ public void setRotation(final Expression<R, ? extends Number> value) {
+ rotation = value;
+ }
+
+ /**
+ * Returns the location inside of a graphic to use for anchoring the graphic to the main-geometry point.
+ * The coordinates are given as (<var>x</var>,<var>y</var>) floating-point numbers
+ * relative the the graphic bounding box, where (0,0) is the lower-left corner and
+ * (1,1) is the upper-right corner.
+ *
+ * <p>The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this graphic, and conversely.</p>
+ *
+ * @return the anchor point.
+ */
+ public AnchorPoint<R> getAnchorPoint() {
+ if (anchorPoint == null) {
+ anchorPoint = factory.createAnchorPoint();
+ }
+ return anchorPoint;
+ }
+
+ /**
+ * Sets the location inside of a graphic to use for anchoring the graphic to the main-geometry point.
+ * The given instance is stored by reference, it is not cloned. If this method is never invoked,
+ * then the default value is a {@linkplain AnchorPoint#AnchorPoint() default anchor point}.
+ *
+ * @param value new anchor point, or {@code null} for resetting the default value.
+ */
+ public void setAnchorPoint(final AnchorPoint<R> value) {
+ anchorPoint = value;
+ }
+
+ /**
+ * Returns the two-dimensional displacement from the "hot-spot" point.
+ * This element may be used to avoid over-plotting of multiple graphic symbols for the same point.
+ * The unit of measurement is given by {@link Symbolizer#getUnitOfMeasure()}.
+ * Positive values are to the right of the point.
+ * The default displacement is <var>x</var> = 0, <var>y</var> = 0,
+ *
+ * <p>If displacement is used in conjunction with size and/or rotation
+ * then the graphic symbol shall be scaled and/or rotated before it is displaced.</p>
+ *
+ * @return displacement from the "hot-spot" point.
+ *
+ * @see PolygonSymbolizer#getDisplacement()
+ * @see PointPlacement#getDisplacement()
+ */
+ public Displacement<R> getDisplacement() {
+ if (displacement == null) {
+ displacement = factory.createDisplacement();
+ }
+ return displacement;
+ }
+
+ /**
+ * Sets the two-dimensional displacement from the "hot-spot" point.
+ * The given instance is stored by reference, it is not cloned. If this method is never invoked,
+ * then the default value is a {@linkplain Displacement#Displacement() default displacement}.
+ *
+ * @param value new displacement from the "hot-spot" point, or {@code null} for resetting the default value.
+ */
+ public void setDisplacement(final Displacement<R> value) {
+ displacement = value;
+ }
+
+ /**
+ * Returns all properties contained in this class.
+ * This is used for {@link #equals(Object)} and {@link #hashCode()} implementations.
+ */
+ @Override
+ final Object[] properties() {
+ return new Object[] {graphicalSymbols, opacity, size, rotation, anchorPoint, displacement};
+ }
+
+ /**
+ * Returns a deep clone of this object. All style elements are cloned,
+ * but expressions are not on the assumption that they are immutable.
+ *
+ * @return deep clone of all style elements.
+ */
+ @Override
+ public Graphic<R> clone() {
+ final var clone = (Graphic<R>) super.clone();
+ clone.selfClone();
+ return clone;
+ }
+
+ /**
+ * Clones the mutable style fields of this element.
+ */
+ private void selfClone() {
+ graphicalSymbols = new ArrayList<>(graphicalSymbols);
+ graphicalSymbols.replaceAll(GraphicalSymbol::clone);
+ if (anchorPoint != null) anchorPoint = anchorPoint .clone();
+ if (displacement != null) displacement = displacement.clone();
+ }
+ }
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/GraphicStroke.java
index 0000000000,7d528f9a28..c1503dadcd
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/GraphicStroke.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/GraphicStroke.java
@@@ -1,0 -1,203 +1,203 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import jakarta.xml.bind.annotation.XmlType;
+ import jakarta.xml.bind.annotation.XmlElement;
+ import jakarta.xml.bind.annotation.XmlRootElement;
+
+ // Branch-dependent imports
-import org.opengis.filter.Expression;
++import org.apache.sis.filter.Expression;
+
+
+ /**
+ * Repeated-linear-graphic stroke.
+ * A graphic stroke can only be used by a {@link Stroke}.
+ * This class contains a {@link Graphic} property,
+ * but its encapsulation in {@code GraphicStroke} means that
+ * the graphic should be bent around the curves of the line string.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
+ *
+ * @since 1.5
+ */
+ @XmlType(name = "GraphicStrokeType", propOrder = {
+ "graphic",
+ "initialGap",
+ "gap"
+ })
+ @XmlRootElement(name = "GraphicStroke")
+ public class GraphicStroke<R> extends StyleElement<R> implements GraphicalElement<R> {
+ /**
+ * The graphic to be repeated, or {@code null} for lazily constructed default.
+ * This property is mandatory: a null value <em>shall</em> be replaced by a default value when first requested.
+ *
+ * @see #getGraphic()
+ * @see #setGraphic(Graphic)
+ */
+ protected Graphic<R> graphic;
+
+ /**
+ * How far away the first graphic will be drawn, or {@code null} for the default value.
+ *
+ * @see #getInitialGap()
+ * @see #setInitialGap(Expression)
+ */
+ @XmlElement(name = "InitialGap")
+ protected Expression<R, ? extends Number> initialGap;
+
+ /**
+ * Distance between two graphics, or {@code null} for the default value.
+ *
+ * @see #getGap()
+ * @see #setGap(Expression)
+ */
+ @XmlElement(name = "Gap")
+ protected Expression<R, ? extends Number> gap;
+
+ /**
+ * For JAXB unmarshalling only.
+ */
+ private GraphicStroke() {
+ // Thread-local factory will be used.
+ }
+
+ /**
+ * Creates a graphic stroke initialized to a default graphic and no gap.
+ *
+ * @param factory the factory to use for creating expressions and child elements.
+ */
+ public GraphicStroke(final StyleFactory<R> factory) {
+ super(factory);
+ }
+
+ /**
+ * Creates a shallow copy of the given object.
+ * For a deep copy, see {@link #clone()} instead.
+ *
+ * @param source the object to copy.
+ */
+ public GraphicStroke(final GraphicStroke<R> source) {
+ super(source);
+ graphic = source.graphic;
+ initialGap = source.initialGap;
+ gap = source.gap;
+ }
+
+ /**
+ * Returns the graphic to be repeated.
+ * The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this stroke, and conversely.
+ *
+ * @return the graphic to be repeated.
+ *
+ * @see GraphicFill#getGraphic()
+ */
+ @Override
+ @XmlElement(name = "Graphic", required = true)
+ public final Graphic<R> getGraphic() {
+ if (graphic == null) {
+ graphic = factory.createGraphic();
+ }
+ return graphic;
+ }
+
+ /**
+ * Sets the graphic to be repeated.
+ * The given instance is stored by reference, it is not cloned.
+ * If this method is never invoked, then the default value is a {@linkplain Graphic#Graphic() default instance}.
+ *
+ * @param value new graphic, or {@code null} for resetting the default value.
+ *
+ * @see GraphicFill#setGraphic(Graphic)
+ */
+ @Override
+ public final void setGraphic(final Graphic<R> value) {
+ graphic = value;
+ }
+
+ /**
+ * Returns how far away the first graphic will be drawn relative to the start of the rendering line.
+ *
+ * @return distance of first graphic relative to the rendering start.
+ */
+ public Expression<R, ? extends Number> getInitialGap() {
+ return defaultToZero(initialGap);
+ }
+
+ /**
+ * Sets how far away the first graphic will be drawn relative to the start of the rendering line.
+ * If this method is never invoked, then the default value is literal 0.
+ *
+ * @param value new distance relative to rendering start, or {@code null} for resetting the default value.
+ */
+ public void setInitialGap(final Expression<R, ? extends Number> value) {
+ initialGap = value;
+ }
+
+ /**
+ * Returns the distance between two graphics.
+ *
+ * @return distance between two graphics.
+ */
+ public Expression<R, ? extends Number> getGap() {
+ return defaultToZero(gap);
+ }
+
+ /**
+ * Sets the distance between two graphics.
+ * If this method is never invoked, then the default value is literal 0.
+ *
+ * @param value new distance between two graphics, or {@code null} for resetting the default value.
+ */
+ public void setGap(final Expression<R, ? extends Number> value) {
+ gap = value;
+ }
+
+ /**
+ * Returns all properties contained in this class.
+ * This is used for {@link #equals(Object)} and {@link #hashCode()} implementations.
+ */
+ @Override
+ final Object[] properties() {
+ return new Object[] {graphic, initialGap, gap};
+ }
+
+ /**
+ * Returns a deep clone of this object. All style elements are cloned,
+ * but expressions are not on the assumption that they are immutable.
+ *
+ * @return deep clone of all style elements.
+ */
+ @Override
+ public GraphicStroke<R> clone() {
+ final var clone = (GraphicStroke<R>) super.clone();
+ clone.selfClone();
+ return clone;
+ }
+
+ /**
+ * Clones the mutable style fields of this element.
+ */
+ private void selfClone() {
+ if (graphic != null) graphic = graphic.clone();
+ }
+ }
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Halo.java
index 0000000000,1639204ca7..6a456f75e8
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Halo.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Halo.java
@@@ -1,0 -1,170 +1,170 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import jakarta.xml.bind.annotation.XmlType;
+ import jakarta.xml.bind.annotation.XmlElement;
+ import jakarta.xml.bind.annotation.XmlRootElement;
+
+ // Branch-dependent imports
-import org.opengis.filter.Expression;
++import org.apache.sis.filter.Expression;
+
+
+ /**
+ * Fill that is applied to the backgrounds of font glyphs.
+ * The use of halos improves the readability of text labels.
+ *
+ * <!-- Following list of authors contains credits to OGC GeoAPI 2 contributors. -->
+ * @author Johann Sorel (Geomatys)
+ * @author Chris Dillard (SYS Technologies)
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
+ *
+ * @since 1.5
+ */
+ @XmlType(name = "HaloType", propOrder = {
+ "radius",
+ "fill"
+ })
+ @XmlRootElement(name = "Halo")
+ public class Halo<R> extends StyleElement<R> {
+ /**
+ * Radius (in pixels) of the the halo around the text, or {@code null} for the default value.
+ *
+ * @see #getRadius()
+ * @see #setRadius(Expression)
+ */
+ @XmlElement(name = "Radius")
+ protected Expression<R, ? extends Number> radius;
+
+ /**
+ * How the halo area around the text should be filled, or {@code null} for the default value.
+ *
+ * @see #getFill()
+ * @see #setFill(Fill)
+ */
+ @XmlElement(name = "Fill")
+ protected Fill<R> fill;
+
+ /**
+ * For JAXB unmarshalling only.
+ */
+ private Halo() {
+ // Thread-local factory will be used.
+ }
+
+ /**
+ * Creates an halo initialized to a white color and a radius of 1 pixel.
+ *
+ * @param factory the factory to use for creating expressions and child elements.
+ */
+ public Halo(final StyleFactory<R> factory) {
+ super(factory);
+ }
+
+ /**
+ * Creates a shallow copy of the given object.
+ * For a deep copy, see {@link #clone()} instead.
+ *
+ * @param source the object to copy.
+ */
+ public Halo(final Halo<R> source) {
+ super(source);
+ fill = source.fill;
+ radius = source.radius;
+ }
+
+ /**
+ * Returns the expression fetching the pixel radius of the halo around the text.
+ * This is the absolute size of a halo radius in pixels.
+ * It extends the area to the outside edge of glyphs and the inside edge of "holes" in the glyphs.
+ * Negative values are not allowed.
+ *
+ * @return radius (in pixels) of the the halo around the text.
+ */
+ public Expression<R, ? extends Number> getRadius() {
+ return defaultToOne(radius);
+ }
+
+ /**
+ * Sets the expression fetching the pixel radius of the halo around the text.
+ * If this method is never invoked, then the default value is 1 pixel.
+ *
+ * @param value new radius (in pixels), or {@code null} for resetting the default value.
+ */
+ public void setRadius(Expression<R, ? extends Number> value) {
+ radius = value;
+ }
+
+ /**
+ * Returns the fill for the halo area around the text.
+ * The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this halo, and conversely.
+ *
+ * @return graphic, color and opacity of the text to draw.
+ */
+ public Fill<R> getFill() {
+ if (fill == null) {
+ fill = factory.createFill();
+ fill.setColor(factory.white);
+ }
+ return fill;
+ }
+
+ /**
+ * Sets the fill for the halo area around the text.
+ * The given instance is stored by reference, it is not cloned.
+ * If this method is never invoked, then the default value is a solid white color.
+ * That default value is standardized by OGC 05-077r4.
+ *
+ * @param value new fill of the text to draw, or {@code null} for resetting the default value.
+ */
+ public void setFill(final Fill<R> value) {
+ fill = value;
+ }
+
+ /**
+ * Returns all properties contained in this class.
+ * This is used for {@link #equals(Object)} and {@link #hashCode()} implementations.
+ */
+ @Override
+ final Object[] properties() {
+ return new Object[] {radius, fill};
+ }
+
+ /**
+ * Returns a deep clone of this object. All style elements are cloned,
+ * but expressions are not on the assumption that they are immutable.
+ *
+ * @return deep clone of all style elements.
+ */
+ @Override
+ public Halo<R> clone() {
+ final var clone = (Halo<R>) super.clone();
+ clone.selfClone();
+ return clone;
+ }
+
+ /**
+ * Clones the mutable style fields of this element.
+ */
+ private void selfClone() {
+ if (fill != null) fill = fill.clone();
+ }
+ }
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/LinePlacement.java
index 0000000000,2e6d001ceb..3da4712138
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/LinePlacement.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/LinePlacement.java
@@@ -1,0 -1,283 +1,283 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import jakarta.xml.bind.annotation.XmlType;
+ import jakarta.xml.bind.annotation.XmlElement;
+ import jakarta.xml.bind.annotation.XmlRootElement;
+
+ // Branch-dependent imports
-import org.opengis.filter.Expression;
++import org.apache.sis.filter.Expression;
+
+
+ /**
+ * Instructions about where and how a text label should be rendered relative to a line.
+ *
+ * <!-- Following list of authors contains credits to OGC GeoAPI 2 contributors. -->
+ * @author Johann Sorel (Geomatys)
+ * @author Ian Turton (CCG)
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
+ *
+ * @since 1.5
+ */
+ @XmlType(name = "LinePlacementType", propOrder = {
+ "perpendicularOffset",
+ "isRepeated",
+ "initialGap",
+ "gap",
+ "isAligned",
+ "generalizeLine"
+ })
+ @XmlRootElement(name = "LinePlacement")
+ public class LinePlacement<R> extends LabelPlacement<R> {
+ /**
+ * Perpendicular distance away from a line where to draw a label, or {@code null} for the default value.
+ *
+ * @see #getPerpendicularOffset()
+ * @see #setPerpendicularOffset(Expression)
+ */
+ @XmlElement(name = "PerpendicularOffset")
+ protected Expression<R, ? extends Number> perpendicularOffset;
+
+ /**
+ * Whether the label will be repeatedly drawn along the line, or {@code null} for the default value.
+ *
+ * @see #isRepeated()
+ * @see #setRepeated(boolean)
+ *
+ * @todo Needs an adapter from expression to plain boolean.
+ */
+ @XmlElement(name = "IsRepeated")
+ protected Expression<R,Boolean> isRepeated;
+
+ /**
+ * How far away the first label will be drawn, or {@code null} for the default value.
+ *
+ * @see #getInitialGap()
+ * @see #setInitialGap(Expression)
+ */
+ @XmlElement(name = "InitialGap")
+ protected Expression<R, ? extends Number> initialGap;
+
+ /**
+ * Distance between two labels, or {@code null} for the default value.
+ *
+ * @see #getGap()
+ * @see #setGap(Expression)
+ */
+ @XmlElement(name = "Gap")
+ protected Expression<R, ? extends Number> gap;
+
+ /**
+ * Whether labels are aligned to the line geometry, or {@code null} for the default value.
+ * If false, labels are drawn horizontally.
+ *
+ * @see #isAligned()
+ * @see #setAligned(Expression)
+ *
+ * @todo Needs an adapter from expression to plain boolean.
+ */
+ @XmlElement(name = "IsAligned")
+ protected Expression<R,Boolean> isAligned;
+
+ /**
+ * Whether to allow the geometry to be generalized, or {@code null} for the default value.
+ *
+ * @see #getGeneralizeLine()
+ * @see #setGeneralizeLine(Expression)
+ *
+ * @todo Needs an adapter from expression to plain boolean.
+ */
+ @XmlElement(name = "GeneralizeLine")
+ protected Expression<R,Boolean> generalizeLine;
+
+ /**
+ * For JAXB unmarshalling only.
+ */
+ private LinePlacement() {
+ // Thread-local factory will be used.
+ }
+
+ /**
+ * Creates a line placement initialized to no offset, no repetition and no gap.
+ *
+ * @param factory the factory to use for creating expressions and child elements.
+ */
+ public LinePlacement(final StyleFactory<R> factory) {
+ super(factory);
+ }
+
+ /**
+ * Creates a shallow copy of the given object.
+ * For a deep copy, see {@link #clone()} instead.
+ *
+ * @param source the object to copy.
+ */
+ public LinePlacement(final LinePlacement<R> source) {
+ super(source);
+ perpendicularOffset = source.perpendicularOffset;
+ isRepeated = source.isRepeated;
+ initialGap = source.initialGap;
+ gap = source.gap;
+ isAligned = source.isAligned;
+ generalizeLine = source.generalizeLine;
+ }
+
+ /**
+ * Returns a perpendicular distance away from a line where to draw a label.
+ * The distance units of measurement is given by {@link Symbolizer#getUnitOfMeasure()}.
+ * The value is positive to the left-hand side of the line string.
+ * Negative numbers mean right. The default offset is 0.
+ *
+ * @return perpendicular distance away from a line where to draw a label.
+ */
+ public Expression<R, ? extends Number> getPerpendicularOffset() {
+ return defaultToZero(perpendicularOffset);
+ }
+
+ /**
+ * Sets a perpendicular distance away from a line where to draw a label.
+ * If this method is never invoked, then the default value is literal 0.
+ * That default value is standardized by OGC 05-077r4.
+ *
+ * @param value new distance to apply for drawing label, or {@code null} for resetting the default value.
+ */
+ public void setPerpendicularOffset(final Expression<R, ? extends Number> value) {
+ perpendicularOffset = value;
+ }
+
+ /**
+ * Returns whether the label will be repeatedly drawn along the line.
+ * If {@code true}, the repetition will use the {@linkplain #getInitialGap() initial gap}
+ * and {@linkplain #getGap() gap} for defining the spaces at the beginning and between labels.
+ *
+ * @return whether the label will be repeatedly drawn along the line.
+ */
+ public Expression<R,Boolean> isRepeated() {
+ return defaultToFalse(isRepeated);
+ }
+
+ /**
+ * Sets whether the label will be repeatedly drawn along the line.
+ * If this method is never invoked, then the default value is literal false.
+ *
+ * @param value whether the label will be repeated, or {@code null} for resetting the default value.
+ */
+ public void setRepeated(final Expression<R,Boolean> value) {
+ isRepeated = value;
+ }
+
+ /**
+ * Returns how far away the first label will be drawn relative to the start of the rendering line.
+ *
+ * @return distance of first label relative to the rendering start.
+ */
+ public Expression<R, ? extends Number> getInitialGap() {
+ return defaultToZero(initialGap);
+ }
+
+ /**
+ * Sets how far away the first label will be drawn relative to the start of the rendering line.
+ * If this method is never invoked, then the default value is literal 0.
+ *
+ * @param value new distance relative to rendering start, or {@code null} for resetting the default value.
+ */
+ public void setInitialGap(final Expression<R, ? extends Number> value) {
+ initialGap = value;
+ }
+
+ /**
+ * Returns the distance between two labels.
+ *
+ * @return distance between two labels.
+ */
+ public Expression<R, ? extends Number> getGap() {
+ return defaultToZero(gap);
+ }
+
+ /**
+ * Sets the distance between two labels.
+ * If this method is never invoked, then the default value is literal 0.
+ *
+ * @param value new distance between two labels, or {@code null} for resetting the default value.
+ */
+ public void setGap(final Expression<R, ? extends Number> value) {
+ gap = value;
+ }
+
+ /**
+ * Returns whether labels are aligned to the line geometry or drawn horizontally.
+ *
+ * @return whether labels are aligned to the line geometry or drawn horizontally.
+ */
+ public Expression<R,Boolean> isAligned() {
+ return defaultToTrue(isAligned);
+ }
+
+ /**
+ * Sets whether labels are aligned to the line geometry or drawn horizontally.
+ * If this method is never invoked, then the default value is literal true.
+ * That default value is standardized by OGC 05-077r4.
+ *
+ * @param value whether labels are aligned to the line geometry, or {@code null} for resetting the default value.
+ */
+ public void setAligned(final Expression<R,Boolean> value) {
+ isAligned = value;
+ }
+
+ /**
+ * Returns whether to allow the geometry to be generalized for label placement.
+ *
+ * @return whether to allow the geometry to be generalized for label placement.
+ */
+ public Expression<R,Boolean> getGeneralizeLine() {
+ return defaultToFalse(generalizeLine);
+ }
+
+ /**
+ * Sets whether to allow the geometry to be generalized for label placement.
+ * If this method is never invoked, then the default value is literal false.
+ *
+ * @param value whether to allow the geometry to be generalized, or {@code null} for resetting the default value.
+ */
+ public void setGeneralizeLine(final Expression<R,Boolean> value) {
+ generalizeLine = value;
+ }
+
+ /**
+ * Returns all properties contained in this class.
+ * This is used for {@link #equals(Object)} and {@link #hashCode()} implementations.
+ */
+ @Override
+ final Object[] properties() {
+ return new Object[] {perpendicularOffset, isRepeated, initialGap, gap, isAligned, generalizeLine};
+ }
+
+ /**
+ * Returns a deep clone of this object. All style elements are cloned,
+ * but expressions are not on the assumption that they are immutable.
+ *
+ * @return deep clone of all style elements.
+ */
+ @Override
+ public LinePlacement<R> clone() {
+ return (LinePlacement<R>) super.clone();
+ }
+ }
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/LineSymbolizer.java
index 0000000000,2119f059dc..8445f212db
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/LineSymbolizer.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/LineSymbolizer.java
@@@ -1,0 -1,173 +1,173 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import jakarta.xml.bind.annotation.XmlType;
+ import jakarta.xml.bind.annotation.XmlElement;
+ import jakarta.xml.bind.annotation.XmlRootElement;
+
+ // Branch-dependent imports
-import org.opengis.filter.Expression;
++import org.apache.sis.filter.Expression;
+
+
+ /**
+ * Instructions about how to draw on a map the lines of a geometry.
+ *
+ * <!-- Following list of authors contains credits to OGC GeoAPI 2 contributors. -->
+ * @author Johann Sorel (Geomatys)
+ * @author Chris Dillard (SYS Technologies)
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
+ *
+ * @since 1.5
+ */
+ @XmlType(name = "LineSymbolizerType", propOrder = {
+ "stroke",
+ "perpendicularOffset"
+ })
+ @XmlRootElement(name = "LineSymbolizer")
+ public class LineSymbolizer<R> extends Symbolizer<R> {
+ /**
+ * Information about how to draw lines, or {@code null} for lazily constructed default.
+ *
+ * @see #getStroke()
+ * @see #setStroke(Stroke)
+ */
+ @XmlElement(name = "Stroke")
+ protected Stroke<R> stroke;
+
+ /**
+ * Distance to apply for drawing lines in parallel to geometry, or {@code null} for the default value.
+ *
+ * @see #getPerpendicularOffset()
+ * @see #setPerpendicularOffset(Expression)
+ */
+ @XmlElement(name = "PerpendicularOffset")
+ protected Expression<R, ? extends Number> perpendicularOffset;
+
+ /**
+ * For JAXB unmarshalling only.
+ */
+ private LineSymbolizer() {
+ // Thread-local factory will be used.
+ }
+
+ /**
+ * Creates a line symbolizer with the default stroke and no perpendicular offset.
+ *
+ * @param factory the factory to use for creating expressions and child elements.
+ */
+ public LineSymbolizer(final StyleFactory<R> factory) {
+ super(factory);
+ }
+
+ /**
+ * Creates a shallow copy of the given object.
+ * For a deep copy, see {@link #clone()} instead.
+ *
+ * @param source the object to copy.
+ */
+ public LineSymbolizer(final LineSymbolizer<R> source) {
+ super(source);
+ stroke = source.stroke;
+ perpendicularOffset = source.perpendicularOffset;
+ }
+
+ /**
+ * Returns the object containing all the information necessary to draw styled lines.
+ * The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this symbolizer, and conversely.
+ *
+ * @return information about how to draw lines.
+ */
+ public Stroke<R> getStroke() {
+ if (stroke == null) {
+ stroke = factory.createStroke();
+ }
+ return stroke;
+ }
+
+ /**
+ * Sets the object containing all the information necessary to draw styled lines.
+ * The given instance is stored by reference, it is not cloned.
+ * If this method is never invoked, then the default value is a {@linkplain Stroke#Stroke() default instance}.
+ *
+ * @param value new information about how to draw lines, or {@code null} for resetting the default value.
+ */
+ public void setStroke(final Stroke<R> value) {
+ stroke = value;
+ }
+
+ /**
+ * Returns a distance to apply for drawing lines in parallel to the original geometry.
+ * These parallel lines have to be constructed so that the distance between
+ * original geometry and drawn line stays equal.
+ * This construction may result in drawn lines that are
+ * actually smaller or longer than the original geometry.
+ *
+ * <p>The distance units of measurement is given by {@link #getUnitOfMeasure()}.
+ * The value is positive to the left-hand side of the line string.
+ * Negative numbers mean right. The default offset is 0.</p>
+ *
+ * @return distance to apply for drawing lines in parallel to the original geometry.
+ */
+ public Expression<R, ? extends Number> getPerpendicularOffset() {
+ return defaultToZero(perpendicularOffset);
+ }
+
+ /**
+ * Sets a distance to apply for drawing lines in parallel to the original geometry.
+ * If this method is never invoked, then the default value is literal 0.
+ * That default value is standardized by OGC 05-077r4.
+ *
+ * @param value new distance to apply for drawing lines, or {@code null} for resetting the default value.
+ */
+ public void setPerpendicularOffset(final Expression<R, ? extends Number> value) {
+ perpendicularOffset = value;
+ }
+
+ /**
+ * Returns all properties contained in this class.
+ * This is used for {@link #equals(Object)} and {@link #hashCode()} implementations.
+ */
+ @Override
+ final Object[] properties() {
+ return new Object[] {perpendicularOffset, stroke};
+ }
+
+ /**
+ * Returns a deep clone of this object. All style elements are cloned,
+ * but expressions are not on the assumption that they are immutable.
+ *
+ * @return deep clone of all style elements.
+ */
+ @Override
+ public LineSymbolizer<R> clone() {
+ final var clone = (LineSymbolizer<R>) super.clone();
+ clone.selfClone();
+ return clone;
+ }
+
+ /**
+ * Clones the mutable style fields of this element.
+ */
+ private void selfClone() {
+ stroke = stroke.clone();
+ }
+ }
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Mark.java
index 0000000000,bb97c23182..c155f61788
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Mark.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Mark.java
@@@ -1,0 -1,293 +1,293 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import java.util.Optional;
+ import jakarta.xml.bind.Marshaller;
+ import jakarta.xml.bind.annotation.XmlType;
+ import jakarta.xml.bind.annotation.XmlElement;
+ import jakarta.xml.bind.annotation.XmlRootElement;
+
+ // Branch-dependent imports
-import org.opengis.filter.Expression;
++import org.apache.sis.filter.Expression;
+
+
+ /**
+ * Predefined shapes that can be drawn at the points of the geometry.
+ * This is an alternative to {@link ExternalGraphic} for
+ * {@linkplain Graphic#graphicalSymbols graphical symbols}.
+ * When marks are provided in the bottom of the graphical symbol list,
+ * it allows a style to be specified that can produce a usable result in a best-effort basis.
+ *
+ * <!-- Following list of authors contains credits to OGC GeoAPI 2 contributors. -->
+ * @author Johann Sorel (Geomatys)
+ * @author Chris Dillard (SYS Technologies)
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
+ *
+ * @since 1.5
+ */
+ @XmlType(name = "MarkType", propOrder = {
+ "wellKnownName",
+ // "onlineResource", // XML encoding not yet available.
+ // "inlineContent", // Idem.
+ "format",
+ "markIndex",
+ "fill",
+ "stroke"
+ })
+ @XmlRootElement(name = "Mark")
+ public class Mark<R> extends GraphicalSymbol<R> {
+ /**
+ * Expression whose value will indicate the symbol to draw, or {@code null} for the default value.
+ *
+ * @see #getWellKnownName()
+ * @see #setWellKnownName(Expression)
+ */
+ @XmlElement(name = "WellKnownName")
+ protected Expression<R,String> wellKnownName;
+
+ /**
+ * Information about how the interior of marks should be filled, or {@code null} for no fill.
+ * If no value has been explicitly set (including null value),
+ * then a default fill will be lazily created when first requested.
+ *
+ * @see #getFill()
+ * @see #setFill(Fill)
+ */
+ @XmlElement(name = "Fill")
+ protected Fill<R> fill;
+
+ /**
+ * Whether {@link #fill} has been explicitly set to some value, including null.
+ * If {@code false}, then a default fill will be created when first needed.
+ */
+ private boolean isFillSet;
+
+ /**
+ * Information about styled lines, or {@code null} if mark lines should not be drawn.
+ * If no value has been explicitly set (including null value),
+ * then a default stroke will be lazily created when first needed.
+ *
+ * @see #getStroke()
+ * @see #setStroke(Stroke)
+ */
+ @XmlElement(name = "Stroke")
+ protected Stroke<R> stroke;
+
+ /**
+ * Whether {@link #stroke} has been explicitly set to some value, including null.
+ * If {@code false}, then a default stroke will be created when first requested.
+ */
+ private boolean isStrokeSet;
+
+ /**
+ * Individual mark to select in a mark archive, or {@code null} if none.
+ *
+ * @see #getMarkIndex()
+ * @see #setMarkIndex(Expression)
+ */
+ @XmlElement(name = "MarkIndex")
+ protected Expression<R,Integer> markIndex;
+
+ /**
+ * Invoked by JAXB before marshalling this mark.
+ * Creates the default fill and stroke if needed.
+ */
+ private void beforeMarshal(Marshaller caller) {
+ if (fill == null && !isFillSet) fill = factory.createFill();
+ if (stroke == null && !isStrokeSet) stroke = factory.createStroke();
+ }
+
+ /**
+ * For JAXB unmarshalling only. This constructor disables the lazy creation of default values.
+ * This is because OGC 05-077r4 said that if the fill or the stroke is not specified,
+ * then no fill or stroke should be applied.
+ */
+ private Mark() {
+ // Thread-local factory will be used.
+ isFillSet = true;
+ isStrokeSet = true;
+ }
+
+ /**
+ * Creates a mark initialized to a gray square with black outline.
+ * The size is specified by {@link Graphic#getSize()} and should be 6 pixels by default.
+ *
+ * @param factory the factory to use for creating expressions and child elements.
+ */
+ public Mark(final StyleFactory<R> factory) {
+ super(factory);
+ }
+
+ /**
+ * Creates a shallow copy of the given object.
+ * For a deep copy, see {@link #clone()} instead.
+ *
+ * @param source the object to copy.
+ */
+ public Mark(final Mark<R> source) {
+ super(source);
+ wellKnownName = source.wellKnownName;
+ fill = source.fill;
+ stroke = source.stroke;
+ }
+
+ /**
+ * Returns the expression whose value will indicate the symbol to draw.
+ * Allowed values include at least "square", "circle", "triangle", "star", "cross", and "x".
+ * Renderings of these marks may be made solid or hollow depending on
+ * {@linkplain #getFill() fill} and {@linkplain #getStroke() stroke} elements.
+ *
+ * <p>The well-known name may be ignored if the mark is also provided
+ * by {@linkplain #getInlineContent() inline content}
+ * or {@linkplain #getOnlineResource() online resource}.</p>
+ *
+ * @return well-known name of the mark to render.
+ *
+ * @see #getOnlineResource()
+ * @see #getInlineContent()
+ */
+ public Expression<R,String> getWellKnownName() {
+ final var value = wellKnownName;
+ return (value != null) ? value : factory.square;
+ }
+
+ /**
+ * Sets the expression whose value will indicate the symbol to draw.
+ * If this method is never invoked, then the default value is literal "square".
+ *
+ * @param value well-known name of the mark to render, or {@code null} for resetting the default.
+ */
+ public void setWellKnownName(final Expression<R,String> value) {
+ wellKnownName = value;
+ }
+
+ /**
+ * Returns the object that indicates how the mark should be filled.
+ * If absent, then the marks are not to be filled at all.
+ *
+ * <p>The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this mark, and conversely.</p>
+ *
+ * @return information about how the interior of polygons should be filled.
+ *
+ * @see #getStroke()
+ */
+ public Optional<Fill<R>> getFill() {
+ if (!isFillSet) {
+ isFillSet = true;
+ fill = factory.createFill();
+ }
+ return Optional.ofNullable(fill);
+ }
+
+ /**
+ * Sets information about how the interior of marks should be filled.
+ * The given instance is stored by reference, it is not cloned.
+ * If this method is never invoked, then the default value is the {@linkplain Fill#Fill() default fill}.
+ *
+ * @param value new information about the fill, or {@code null} for no fill.
+ */
+ public void setFill(final Fill<R> value) {
+ isFillSet = true;
+ fill = value;
+ }
+
+ /**
+ * Returns the object that indicates how the edges of the mark will be drawn.
+ * This is used for the edges of marks.
+ * Absent means that the edges will not be drawn at all.
+ *
+ * <p>The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this mark, and conversely.</p>
+ *
+ * @return information about styled lines, or {@code null} if lines should not be drawn.
+ *
+ * @see #getFill()
+ */
+ public Optional<Stroke<R>> getStroke() {
+ if (!isStrokeSet) {
+ isStrokeSet = true;
+ stroke = factory.createStroke();
+ }
+ return Optional.ofNullable(stroke);
+ }
+
+ /**
+ * Sets information about styled lines.
+ * The given instance is stored by reference, it is not cloned.
+ * If this method is never invoked, then the default value is the {@linkplain Stroke#Stroke() default stroke}.
+ *
+ * @param value new information about styled lines, or {@code null} if lines should not be drawn.
+ */
+ public void setStroke(final Stroke<R> value) {
+ isStrokeSet = true;
+ stroke = value;
+ }
+
+ /**
+ * Returns an individual mark to select in a mark archive.
+ * For example it can be the index of a glyph to select in a TrueType fond file.
+ *
+ * @return individual mark to select in a mark archive.
+ */
+ public Optional<Expression<R,Integer>> getMarkIndex() {
+ return Optional.ofNullable(markIndex);
+ }
+
+ /**
+ * Sets an individual mark to select in a mark archive.
+ *
+ * @param value new index of an individual mark to select, or {@code null} if none.
+ */
+ public void setMarkIndex(final Expression<R,Integer> value) {
+ markIndex = value;
+ }
+
+ /**
+ * Returns all properties contained in this class.
+ * This is used for {@link #equals(Object)} and {@link #hashCode()} implementations.
+ */
+ @Override
+ final Object[] properties() {
+ return new Object[] {wellKnownName, fill, isFillSet, stroke, isStrokeSet, markIndex};
+ }
+
+ /**
+ * Returns a deep clone of this object. All style elements are cloned,
+ * but expressions are not on the assumption that they are immutable.
+ *
+ * @return deep clone of all style elements.
+ */
+ @Override
+ public Mark<R> clone() {
+ final var clone = (Mark<R>) super.clone();
+ clone.selfClone();
+ return clone;
+ }
+
+ /**
+ * Clones the mutable style fields of this element.
+ */
+ private void selfClone() {
+ if (fill != null) fill = fill.clone();
+ if (stroke != null) stroke = stroke.clone();
+ }
+ }
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/PointPlacement.java
index 0000000000,6efa32f95b..e98fcad4b5
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/PointPlacement.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/PointPlacement.java
@@@ -1,0 -1,209 +1,209 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import jakarta.xml.bind.annotation.XmlType;
+ import jakarta.xml.bind.annotation.XmlElement;
+ import jakarta.xml.bind.annotation.XmlRootElement;
+
+ // Branch-dependent imports
-import org.opengis.filter.Expression;
++import org.apache.sis.filter.Expression;
+
+
+ /**
+ * Instructions about how a text label is positioned relative to a point.
+ *
+ * <!-- Following list of authors contains credits to OGC GeoAPI 2 contributors. -->
+ * @author Johann Sorel (Geomatys)
+ * @author Ian Turton (CCG)
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
+ *
+ * @since 1.5
+ */
+ @XmlType(name = "PointPlacementType", propOrder = {
+ "anchorPoint",
+ "displacement",
+ "rotation"
+ })
+ @XmlRootElement(name = "PointPlacement")
+ public class PointPlacement<R> extends LabelPlacement<R> {
+ /**
+ * Location to use for anchoring the label to the point, or {@code null} for lazily constructed default.
+ *
+ * @see #getAnchorPoint()
+ * @see #setAnchorPoint(AnchorPoint)
+ */
+ @XmlElement(name = "AnchorPoint")
+ protected AnchorPoint<R> anchorPoint;
+
+ /**
+ * Two-dimensional displacements from the "hot-spot" point, or {@code null} for lazily constructed default.
+ *
+ * @see #getDisplacement()
+ * @see #setDisplacement(Displacement)
+ */
+ @XmlElement(name = "Displacement")
+ protected Displacement<R> displacement;
+
+ /**
+ * Expression fetching the rotation of the label when it is drawn.
+ *
+ * @see #getRotation()
+ * @see #setRotation(Expression)
+ */
+ @XmlElement(name = "Rotation")
+ protected Expression<R, ? extends Number> rotation;
+
+ /**
+ * For JAXB unmarshalling only.
+ */
+ private PointPlacement() {
+ // Thread-local factory will be used.
+ }
+
+ /**
+ * Creates a point placement initialized to anchor at the middle and no displacement.
+ *
+ * @param factory the factory to use for creating expressions and child elements.
+ */
+ public PointPlacement(final StyleFactory<R> factory) {
+ super(factory);
+ }
+
+ /**
+ * Creates a shallow copy of the given object.
+ * For a deep copy, see {@link #clone()} instead.
+ *
+ * @param source the object to copy.
+ */
+ public PointPlacement(final PointPlacement<R> source) {
+ super(source);
+ anchorPoint = source.anchorPoint;
+ displacement = source.displacement;
+ rotation = source.rotation;
+ }
+
+ /**
+ * Returns the location inside of a label to use for anchoring the label to the point.
+ * The coordinates are given as (<var>x</var>,<var>y</var>) floating-point numbers
+ * relative the the label bounding box, where (0,0) is the lower-left corner and
+ * (1,1) is the upper-right corner.
+ *
+ * <p>The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this graphic, and conversely.</p>
+ *
+ * @return the anchor point.
+ */
+ public AnchorPoint<R> getAnchorPoint() {
+ if (anchorPoint == null) {
+ anchorPoint = factory.createAnchorPoint();
+ }
+ return anchorPoint;
+ }
+
+ /**
+ * Sets the location inside of a label to use for anchoring the label to the point.
+ * The given instance is stored by reference, it is not cloned. If this method is never invoked,
+ * then the default value is a {@linkplain AnchorPoint#AnchorPoint() default anchor point}.
+ *
+ * @param value new anchor point, or {@code null} for resetting the default value.
+ */
+ public void setAnchorPoint(final AnchorPoint<R> value) {
+ anchorPoint = value;
+ }
+
+ /**
+ * Returns the two-dimensional displacements from the "hot-spot" point.
+ * The displacements are in units of pixels above and to the right of the point.
+ *
+ * @return two-dimensional displacements from the "hot-spot" point.
+ *
+ * @see Graphic#getDisplacement()
+ * @see PolygonSymbolizer#getDisplacement()
+ */
+ public Displacement<R> getDisplacement() {
+ if (displacement == null) {
+ displacement = factory.createDisplacement();
+ }
+ return displacement;
+ }
+
+ /**
+ * Sets the two-dimensional displacements from the "hot-spot" point.
+ * The given instance is stored by reference, it is not cloned. If this method is never invoked,
+ * then the default value is a {@linkplain Displacement#Displacement() default displacement}.
+ *
+ * @param value new displacements from the "hot-spot" point, or {@code null} for resetting the default value.
+ */
+ public void setDisplacement(final Displacement<R> value) {
+ displacement = value;
+ }
+
+ /**
+ * Returns the expression fetching the rotation of the label when it is drawn.
+ * This is the clockwise rotation of the label in degrees from the normal direction for a font
+ * (left-to-right for Latin languages).
+ *
+ * @return rotation of the label when it is drawn.
+ */
+ public Expression<R, ? extends Number> getRotation() {
+ return defaultToZero(rotation);
+ }
+
+ /**
+ * Sets the expression fetching the rotation of the label when it is drawn.
+ * If this method is never invoked, then the default value is literal zero.
+ *
+ * @param value new rotation of the label, or {@code null} for resetting the default value.
+ */
+ public void setRotation(final Expression<R, ? extends Number> value) {
+ rotation = value;
+ }
+
+ /**
+ * Returns all properties contained in this class.
+ * This is used for {@link #equals(Object)} and {@link #hashCode()} implementations.
+ */
+ @Override
+ final Object[] properties() {
+ return new Object[] {anchorPoint, displacement, rotation};
+ }
+
+ /**
+ * Returns a deep clone of this object. All style elements are cloned,
+ * but expressions are not on the assumption that they are immutable.
+ *
+ * @return deep clone of all style elements.
+ */
+ @Override
+ public PointPlacement<R> clone() {
+ final var clone = (PointPlacement<R>) super.clone();
+ clone.selfClone();
+ return clone;
+ }
+
+ /**
+ * Clones the mutable style fields of this element.
+ */
+ private void selfClone() {
+ if (anchorPoint != null) anchorPoint = anchorPoint .clone();
+ if (displacement != null) displacement = displacement.clone();
+ }
+ }
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/PolygonSymbolizer.java
index 0000000000,2a3e6acbc2..6b2474f044
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/PolygonSymbolizer.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/PolygonSymbolizer.java
@@@ -1,0 -1,324 +1,324 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import java.util.Optional;
+ import jakarta.xml.bind.Marshaller;
+ import jakarta.xml.bind.annotation.XmlType;
+ import jakarta.xml.bind.annotation.XmlElement;
+ import jakarta.xml.bind.annotation.XmlRootElement;
+
+ // Branch-dependent imports
-import org.opengis.filter.Expression;
++import org.apache.sis.filter.Expression;
+
+
+ /**
+ * Instructions about how to draw on a map the lines and the interior of polygons.
+ * Holes are not filled but borders around the holes are stroked.
+ * Islands within holes are filled and stroked, and so on.
+ *
+ * <p>If the geometry is not a polygon, then
+ * line strings should be closed for filling but not for stroking.
+ * Points should be rendered as small squares, and
+ * rasters should be rendered using the coverage extent as the polygon.</p>
+ *
+ * <p>The fill should be rendered first, then the stroke should be rendered on top of the fill.
+ * A missing stroke element means that the geometry will not be stroked.</p>
+ *
+ * <!-- Following list of authors contains credits to OGC GeoAPI 2 contributors. -->
+ * @author Johann Sorel (Geomatys)
+ * @author Chris Dillard (SYS Technologies)
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
+ *
+ * @since 1.5
+ */
+ @XmlType(name = "PolygonSymbolizerType", propOrder = {
+ "fill",
+ "stroke",
+ "displacement",
+ "perpendicularOffset"
+ })
+ @XmlRootElement(name = "PolygonSymbolizer")
+ public class PolygonSymbolizer<R> extends Symbolizer<R> {
+ /**
+ * Information about how the interior of polygons should be filled, or {@code null} for no fill.
+ * If no value has been explicitly set (including null value),
+ * then a default fill will be lazily created when first requested.
+ *
+ * @see #getFill()
+ * @see #setFill(Fill)
+ */
+ @XmlElement(name = "Fill")
+ protected Fill<R> fill;
+
+ /**
+ * Whether {@link #fill} has been explicitly set to some value, including null.
+ * If {@code false}, then a default fill will be created when first needed.
+ */
+ private boolean isFillSet;
+
+ /**
+ * Information about styled lines, or {@code null} if lines should not be drawn.
+ * If no value has been explicitly set (including null value),
+ * then a default stroke will be lazily created when first needed.
+ *
+ * @see #getStroke()
+ * @see #setStroke(Stroke)
+ */
+ @XmlElement(name = "Stroke")
+ protected Stroke<R> stroke;
+
+ /**
+ * Whether {@link #stroke} has been explicitly set to some value, including null.
+ * If {@code false}, then a default stroke will be created when first requested.
+ */
+ private boolean isStrokeSet;
+
+ /**
+ * Displacement from the "hot-spot" point, or {@code null} for lazily constructed default.
+ *
+ * @see #getDisplacement()
+ * @see #setDisplacement(Displacement)
+ */
+ @XmlElement(name = "Displacement")
+ protected Displacement<R> displacement;
+
+ /**
+ * Distance to apply for drawing lines in parallel to geometry, or {@code null} for the default value.
+ *
+ * @see #getPerpendicularOffset()
+ * @see #setPerpendicularOffset(Expression)
+ */
+ @XmlElement(name = "PerpendicularOffset")
+ protected Expression<R, ? extends Number> perpendicularOffset;
+
+ /**
+ * Invoked by JAXB before marshalling this polyogon symbolizer.
+ * Creates the default fill and stroke if needed.
+ */
+ private void beforeMarshal(Marshaller caller) {
+ if (fill == null && !isFillSet) fill = factory.createFill();
+ if (stroke == null && !isStrokeSet) stroke = factory.createStroke();
+ }
+
+ /**
+ * For JAXB unmarshalling only. This constructor disables the lazy creation of default values.
+ * This is because OGC 05-077r4 said that if the fill or the stroke is not specified,
+ * then no fill or stroke should be applied.
+ */
+ private PolygonSymbolizer() {
+ // Thread-local factory will be used.
+ isFillSet = true;
+ isStrokeSet = true;
+ }
+
+ /**
+ * Creates a polygon symbolizer initialized to the default fill and default stroke.
+ *
+ * @param factory the factory to use for creating expressions and child elements.
+ */
+ public PolygonSymbolizer(final StyleFactory<R> factory) {
+ super(factory);
+ }
+
+ /**
+ * Creates a shallow copy of the given object.
+ * For a deep copy, see {@link #clone()} instead.
+ *
+ * @param source the object to copy.
+ */
+ public PolygonSymbolizer(final PolygonSymbolizer<R> source) {
+ super(source);
+ fill = source.fill;
+ stroke = source.stroke;
+ isFillSet = source.isFillSet;
+ isStrokeSet = source.isStrokeSet;
+ displacement = source.displacement;
+ perpendicularOffset = source.perpendicularOffset;
+ }
+
+ /**
+ * Returns information about how the interior of polygons should be filled.
+ * If absent, then the polygons are not to be filled at all.
+ *
+ * <p>The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this stroke, and conversely.</p>
+ *
+ * @return information about how the interior of polygons should be filled.
+ *
+ * @see #getStroke()
+ * @see #isVisible()
+ */
+ public Optional<Fill<R>> getFill() {
+ if (!isFillSet) {
+ isFillSet = true;
+ fill = factory.createFill();
+ }
+ return Optional.ofNullable(fill);
+ }
+
+ /**
+ * Sets information about how the interior of polygons should be filled.
+ * The given instance is stored by reference, it is not cloned.
+ * If this method is never invoked, then the default value is the {@linkplain Fill#Fill() default fill}.
+ *
+ * @param value new information about the fill, or {@code null} for no fill.
+ */
+ public void setFill(final Fill<R> value) {
+ isFillSet = true;
+ fill = value;
+ }
+
+ /**
+ * Returns information about styled lines.
+ * This is used for the edges of polygons.
+ * Absent means that lines should not be drawn
+ *
+ * <p>The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this stroke, and conversely.</p>
+ *
+ * @return information about styled lines.
+ *
+ * @see #getFill()
+ * @see #isVisible()
+ */
+ public Optional<Stroke<R>> getStroke() {
+ if (!isStrokeSet) {
+ isStrokeSet = true;
+ stroke = factory.createStroke();
+ }
+ return Optional.ofNullable(stroke);
+ }
+
+ /**
+ * Sets information about styled lines.
+ * The given instance is stored by reference, it is not cloned.
+ * If this method is never invoked, then the default value is the {@linkplain Stroke#Stroke() default stroke}.
+ *
+ * @param value new information about styled lines, or {@code null} if lines should not be drawn.
+ */
+ public void setStroke(final Stroke<R> value) {
+ isStrokeSet = true;
+ stroke = value;
+ }
+
+ /**
+ * Returns the two-dimensional displacement from the "hot-spot" point.
+ * This element may be used to avoid over-plotting of multiple polygons for one geometry.
+ * It may also be used for creating shadows of polygon geometries.
+ * The unit of measurement is given by {@link Symbolizer#getUnitOfMeasure()}.
+ * Positive values are to the right of the point.
+ * The default displacement is <var>x</var> = 0, <var>y</var> = 0,
+ *
+ * <p>The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this stroke, and conversely.</p>
+ *
+ * @return displacement from the "hot-spot" point.
+ *
+ * @see Graphic#getDisplacement()
+ * @see PointPlacement#getDisplacement()
+ */
+ public Displacement<R> getDisplacement() {
+ if (displacement == null) {
+ displacement = factory.createDisplacement();
+ }
+ return displacement;
+ }
+
+ /**
+ * Sets the two-dimensional displacement from the "hot-spot" point.
+ * The given instance is stored by reference, it is not cloned. If this method is never invoked,
+ * then the default value is a {@linkplain Displacement#Displacement() default displacement}.
+ *
+ * @param value new displacement from the "hot-spot" point, or {@code null} for resetting the default value.
+ */
+ public void setDisplacement(final Displacement<R> value) {
+ displacement = value;
+ }
+
+ /**
+ * Returns a distance to apply for drawing lines in parallel to the original polygon.
+ * This property allow to draw polygons smaller or larger than their actual geometry.
+ * The distance units of measurement is given by {@link #getUnitOfMeasure()}.
+ * The value is positive outside the polygon.
+ *
+ * @return distance to apply for drawing lines in parallel to the original geometry.
+ */
+ public Expression<R, ? extends Number> getPerpendicularOffset() {
+ return defaultToZero(perpendicularOffset);
+ }
+
+ /**
+ * Sets a distance to apply for drawing lines in parallel to the original geometry.
+ * If this method is never invoked, then the default value is literal 0.
+ * That default value is standardized by OGC 05-077r4.
+ *
+ * @param value new distance to apply for drawing lines, or {@code null} for resetting the default value.
+ */
+ public void setPerpendicularOffset(final Expression<R, ? extends Number> value) {
+ perpendicularOffset = value;
+ }
+
+ /**
+ * Returns {@code true} if this symbolizer has a fill and/or a stroke.
+ * If both {@code setFill(null)} and {@code setStroke(null)} have been
+ * explicitly invoked with a null argument value, then this method returns {@code false}.
+ *
+ * @return whether this symbolizer has a fill and/or a stroke.
+ *
+ * @see #setFill(Fill)
+ * @see #setStroke(Stroke)
+ */
+ @Override
+ public boolean isVisible() {
+ return fill != null || stroke != null || !(isFillSet | isStrokeSet);
+ }
+
+ /**
+ * Returns all properties contained in this class.
+ * This is used for {@link #equals(Object)} and {@link #hashCode()} implementations.
+ */
+ @Override
+ final Object[] properties() {
+ return new Object[] {fill, isFillSet, stroke, isStrokeSet, displacement, perpendicularOffset};
+ }
+
+ /**
+ * Returns a deep clone of this object. All style elements are cloned,
+ * but expressions are not on the assumption that they are immutable.
+ *
+ * @return deep clone of all style elements.
+ */
+ @Override
+ public PolygonSymbolizer<R> clone() {
+ final var clone = (PolygonSymbolizer<R>) super.clone();
+ clone.selfClone();
+ return clone;
+ }
+
+ /**
+ * Clones the mutable style fields of this element.
+ */
+ private void selfClone() {
+ if (stroke != null) stroke = stroke.clone();
+ if (fill != null) fill = fill.clone();
+ if (displacement != null) displacement = displacement.clone();
+ }
+ }
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/RasterSymbolizer.java
index 0000000000,2cff799d4f..ba873fcce6
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/RasterSymbolizer.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/RasterSymbolizer.java
@@@ -1,0 -1,358 +1,358 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import java.util.Optional;
+ import jakarta.xml.bind.annotation.XmlType;
+ import jakarta.xml.bind.annotation.XmlElement;
+ import jakarta.xml.bind.annotation.XmlRootElement;
+
+ // Branch-dependent imports
-import org.opengis.filter.Expression;
++import org.apache.sis.filter.Expression;
+
+
+ /**
+ * Instructions about how to render raster, matrix or coverage data.
+ * It may be satellite photos or DEMs for example.
+ *
+ * <p>In the particular case of raster symbolizer, {@link #getGeometry()}
+ * should return a {@link org.apache.sis.coverage.BandedCoverage} instead
+ * of a geometry.</p>
+ *
+ * <!-- Following list of authors contains credits to OGC GeoAPI 2 contributors. -->
+ * @author Ian Turton (CCG)
+ * @author Johann Sorel (Geomatys)
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
+ *
+ * @since 1.5
+ */
+ @XmlType(name = "RasterSymbolizerType", propOrder = {
+ "opacity",
+ "channelSelection",
+ "overlapBehavior",
+ "colorMap",
+ "contrastEnhancement",
+ "shadedRelief",
+ "imageOutline"
+ })
+ @XmlRootElement(name = "RasterSymbolizer")
+ public class RasterSymbolizer<R> extends Symbolizer<R> implements Translucent<R> {
+ /**
+ * Level of translucency as a floating point number between 0 and 1 (inclusive), or {@code null} the default value.
+ * The default value specified by OGC 05-077r4 standard is 1.
+ *
+ * @see #getOpacity()
+ * @see #setOpacity(Expression)
+ */
+ @XmlElement(name = "Opacity")
+ protected Expression<R, ? extends Number> opacity;
+
+ /**
+ * Selection of false-color channels for a multi-spectral raster source, or {@code null} if none.
+ *
+ * @see #getChannelSelection()
+ * @see #setChannelSelection(ChannelSelection)
+ */
+ @XmlElement(name = "ChannelSelection")
+ protected ChannelSelection<R> channelSelection;
+
+ /**
+ * Behavior when multiple raster images in a layer overlap each other, or {@code null} if unspecified.
+ * The default value is implementation-dependent.
+ *
+ * @see #getOverlapBehavior()
+ * @see #setOverlapBehavior(OverlapBehavior)
+ */
+ @XmlElement(name = "OverlapBehavior")
+ protected OverlapBehavior overlapBehavior;
+
+ /**
+ * Mapping of fixed-numeric pixel values to colors, or {@code null} if none.
+ *
+ * @see #getColorMap()
+ * @see #setColorMap(ColorMap)
+ */
+ @XmlElement(name = "ColorMap")
+ protected ColorMap<R> colorMap;
+
+ /**
+ * Contrast enhancement for the whole image, or {@code null} if none.
+ *
+ * @see #getContrastEnhancement()
+ * @see #setContrastEnhancement(ContrastEnhancement)
+ */
+ @XmlElement(name = "ContrastEnhancement")
+ protected ContrastEnhancement<R> contrastEnhancement;
+
+ /**
+ * Relief shading (or “hill shading”) to apply to the image for a three-dimensional visual effect.
+ *
+ * @see #getShadedRelief()
+ * @see #setShadedRelief(ShadedRelief)
+ */
+ @XmlElement(name = "ShadedRelief")
+ protected ShadedRelief<R> shadedRelief;
+
+ /**
+ * Line or polygon symbolizer to use for outlining source rasters, or {@code null} if none.
+ *
+ * @see #getImageOutline()
+ * @see #setImageOutline(Symbolizer)
+ */
+ @XmlElement(name = "ImageOutline")
+ protected Symbolizer<R> imageOutline;
+
+ /**
+ * For JAXB unmarshalling only.
+ */
+ private RasterSymbolizer() {
+ // Thread-local factory will be used.
+ }
+
+ /**
+ * Creates an initially opaque raster symbolizer with no contrast enhancement, shaded relief or outline.
+ *
+ * @param factory the factory to use for creating expressions and child elements.
+ */
+ public RasterSymbolizer(final StyleFactory<R> factory) {
+ super(factory);
+ }
+
+ /**
+ * Creates a shallow copy of the given object.
+ * For a deep copy, see {@link #clone()} instead.
+ *
+ * @param source the object to copy.
+ */
+ public RasterSymbolizer(final RasterSymbolizer<R> source) {
+ super(source);
+ opacity = source.opacity;
+ channelSelection = source.channelSelection;
+ overlapBehavior = source.overlapBehavior;
+ colorMap = source.colorMap;
+ contrastEnhancement = source.contrastEnhancement;
+ shadedRelief = source.shadedRelief;
+ imageOutline = source.imageOutline;
+ }
+
+ /**
+ * Indicates the level of translucency as a floating point number between 0 and 1 (inclusive).
+ * A value of zero means completely transparent. A value of 1.0 means completely opaque.
+ *
+ * @return the level of translucency as a floating point number between 0 and 1 (inclusive).
+ *
+ * @see Fill#getOpacity()
+ * @see Stroke#getOpacity()
+ * @see Graphic#getOpacity()
+ */
+ @Override
+ public Expression<R, ? extends Number> getOpacity() {
+ return defaultToOne(opacity);
+ }
+
+ /**
+ * Sets the level of translucency as a floating point number between 0 and 1 (inclusive).
+ * If this method is never invoked, then the default value is literal 1 (totally opaque).
+ * That default value is standardized by OGC 05-077r4.
+ *
+ * @param value new level of translucency, or {@code null} for resetting the default value.
+ */
+ @Override
+ public void setOpacity(final Expression<R, ? extends Number> value) {
+ opacity = value;
+ }
+
+ /**
+ * Returns the selection of false-color channels for a multi-spectral raster source.
+ * Either red, green, and blue channels are selected, or a single grayscale channel is selected.
+ * Contrast enhancement may be applied to each channel in isolation.
+ *
+ * <p>The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this fill, and conversely.</p>
+ *
+ * @return the selection of channels.
+ */
+ public Optional<ChannelSelection<R>> getChannelSelection() {
+ return Optional.ofNullable(channelSelection);
+ }
+
+ /**
+ * Sets the selection of false-color channels for a multi-spectral raster source.
+ * The given instance is stored by reference, it is not cloned.
+ * If this method is never invoked, then the default value is absence.
+ *
+ * @param value new selection of channels, or {@code null} for none.
+ */
+ public void setChannelSelection(final ChannelSelection<R> value) {
+ channelSelection = value;
+ }
+
+ /**
+ * Returns the behavior when multiple raster images in a layer overlap each other.
+ *
+ * @return behavior when multiple raster images in a layer overlap each other.
+ */
+ public OverlapBehavior getOverlapBehavior() {
+ final var value = overlapBehavior;
+ return (value != null) ? value : OverlapBehavior.LATEST_ON_TOP;
+ // Default value is unspecified, we use LATEST_ON_TOP for now.
+ }
+
+ /**
+ * Set the behavior when multiple raster images in a layer overlap each other.
+ *
+ * @param value new behavior, or {@code null} for resetting the default value.
+ */
+ public void setOverlapBehavior(final OverlapBehavior value) {
+ overlapBehavior = value;
+ }
+
+ /**
+ * Returns the mapping of fixed-numeric pixel values to colors.
+ * It can be used for defining the olors of a palette-type raster source.
+ * For example, a DEM raster giving elevations in meters above sea level
+ * can be translated to a colored image.
+ *
+ * <p>The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this fill, and conversely.</p>
+ *
+ * @return color map for the raster.
+ */
+ public Optional<ColorMap<R>> getColorMap() {
+ return Optional.ofNullable(colorMap);
+ }
+
+ /**
+ * Sets the mapping of fixed-numeric pixel values to colors.
+ * The given instance is stored by reference, it is not cloned.
+ * If this method is never invoked, then the default value is absence.
+ *
+ * @param value new color map for the raster, or {@code null} if none.
+ */
+ public void setColorMap(final ColorMap<R> value) {
+ colorMap = value;
+ }
+
+ /**
+ * Returns the contrast enhancement for the whole image.
+ * The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this stroke, and conversely.
+ *
+ * @return contrast enhancement for the whole image.
+ *
+ * @see SelectedChannel#getContrastEnhancement()
+ */
+ public Optional<ContrastEnhancement<R>> getContrastEnhancement() {
+ return Optional.ofNullable(contrastEnhancement);
+ }
+
+ /**
+ * Sets the contrast enhancement applied to the whole image.
+ * The given instance is stored by reference, it is not cloned.
+ * If this method is never invoked, then the default value is absence.
+ *
+ * @param value new contrast enhancement, or {@code null} if none.
+ *
+ * @see SelectedChannel#setContrastEnhancement(ContrastEnhancement)
+ */
+ public void setContrastEnhancement(final ContrastEnhancement<R> value) {
+ contrastEnhancement = value;
+ }
+
+ /**
+ * Returns the relief shading to apply to the image for a three-dimensional visual effect.
+ * The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this stroke, and conversely.
+ *
+ * @return the relief shading to apply.
+ */
+ public Optional<ShadedRelief<R>> getShadedRelief() {
+ return Optional.ofNullable(shadedRelief);
+ }
+
+ /**
+ * Sets the relief shading to apply to the image for a three-dimensional visual effect.
+ * The given instance is stored by reference, it is not cloned.
+ * If this method is never invoked, then the default value is absence.
+ *
+ * @param value new relief shading to apply, or {@code null} if none.
+ */
+ public void setShadedRelief(final ShadedRelief<R> value) {
+ shadedRelief = value;
+ }
+
+ /**
+ * How to outline individual source rasters in a multi-raster set.
+ * The value should be either a {@link LineSymbolizer} or {@link PolygonSymbolizer}.
+ *
+ * <p>The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this stroke, and conversely.</p>
+ *
+ * @return Line or polygon symbolizer to use for outlining source rasters.
+ */
+ public Optional<Symbolizer<R>> getImageOutline() {
+ return Optional.ofNullable(imageOutline);
+ }
+
+ /**
+ * Sets how to outline individual source rasters in a multi-raster set.
+ * The given instance is stored by reference, it is not cloned.
+ * If this method is never invoked, then the default value is absence.
+ *
+ * @param value new line or polygon symbolizer to use, or {@code null} if none.
+ */
+ public void setImageOutline(final Symbolizer<R> value) {
+ imageOutline = value;
+ }
+
+ /**
+ * Returns all properties contained in this class.
+ * This is used for {@link #equals(Object)} and {@link #hashCode()} implementations.
+ */
+ @Override
+ final Object[] properties() {
+ return new Object[] {opacity, channelSelection, overlapBehavior,
+ colorMap, contrastEnhancement, shadedRelief, imageOutline};
+ }
+
+ /**
+ * Returns a deep clone of this object. All style elements are cloned,
+ * but expressions are not on the assumption that they are immutable.
+ *
+ * @return deep clone of all style elements.
+ */
+ @Override
+ public RasterSymbolizer<R> clone() {
+ final var clone = (RasterSymbolizer<R>) super.clone();
+ clone.selfClone();
+ return clone;
+ }
+
+ /**
+ * Clones the mutable style fields of this element.
+ */
+ private void selfClone() {
+ if (channelSelection != null) channelSelection = channelSelection.clone();
+ if (colorMap != null) colorMap = colorMap.clone();
+ if (contrastEnhancement != null) contrastEnhancement = contrastEnhancement.clone();
+ if (shadedRelief != null) shadedRelief = shadedRelief.clone();
+ if (imageOutline != null) imageOutline = imageOutline.clone();
+ }
+ }
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Rule.java
index 0000000000,36055b9671..e90163ae6b
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Rule.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Rule.java
@@@ -1,0 -1,513 +1,513 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import java.util.List;
+ import java.util.ArrayList;
+ import java.util.Optional;
+ import jakarta.xml.bind.annotation.XmlType;
+ import jakarta.xml.bind.annotation.XmlElement;
+ import jakarta.xml.bind.annotation.XmlElementRef;
+ import jakarta.xml.bind.annotation.XmlRootElement;
+ import org.opengis.metadata.citation.OnlineResource;
+ import org.apache.sis.internal.jaxb.Context;
+ import org.apache.sis.util.resources.Errors;
+ import org.apache.sis.util.ArgumentChecks;
+
+ // Branch-dependent imports
-import org.opengis.filter.Filter;
++import org.apache.sis.filter.Filter;
+
+
+ /**
+ * Rendering instructions grouped by feature-property conditions and map scales.
+ * A rule consists of two important parts: a {@linkplain Filter filter} and a list of symbols.
+ * When drawing a given feature, the rendering engine examines each rule in the style,
+ * first checking its filter. If the feature is accepted by the filter,
+ * then all {@link Symbolizer} for that rule are applied to the given feature.
+ *
+ * <!-- Following list of authors contains credits to OGC GeoAPI 2 contributors. -->
+ * @author Johann Sorel (Geomatys)
+ * @author Chris Dillard (SYS Technologies)
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
+ *
+ * @since 1.5
+ */
+ @XmlType(name = "RuleType", propOrder = {
+ "name",
+ "description",
+ "legend",
+ // "filter", // XML encoding not yet available.
+ "elseFilter",
+ "minScale",
+ "maxScale",
+ "symbolizers"
+ })
+ @XmlRootElement(name = "Rule")
+ public class Rule<R> extends StyleElement<R> {
+ /**
+ * Name for this rule, or {@code null} if none.
+ *
+ * @see #getName()
+ * @see #setName(String)
+ */
+ @XmlElement(name = "Name")
+ protected String name;
+
+ /**
+ * Information for user interfaces, or {@code null} if none.
+ *
+ * @see #getDescription()
+ * @see #setDescription(Description)
+ */
+ @XmlElement(name = "Description")
+ protected Description<R> description;
+
+ /**
+ * Small graphic to draw in a legend window, or {@code null} if none.
+ *
+ * @see #getLegend()
+ * @see #setLegend(LegendGraphic)
+ */
+ @XmlElement(name = "LegendGraphic")
+ protected LegendGraphic<R> legend;
+
+ /**
+ * Filter that will limit the features, or {@code null} if none.
+ *
+ * @see #getFilter()
+ * @see #setFilter(Filter)
+ */
+ // @XmlElement(name = "Filter", namespace = "http://www.opengis.net/ogc")
+ protected Filter<R> filter;
+
+ /**
+ * Whether this {@code Rule} will be applied only if no other rules in the containing style apply.
+ *
+ * @see #isElseFilter()
+ * @see #setElseFilter(boolean)
+ */
+ protected boolean isElseFilter;
+
+ /**
+ * Minimum value (inclusive) in the denominator of map scale at which this rule will apply.
+ *
+ * @see #getMinScaleDenominator()
+ * @see #setMaxScaleDenominator(double)
+ */
+ protected double minScale;
+
+ /**
+ * Maximum value (exclusive) in the denominator of map scale at which this rule will apply.
+ *
+ * @see #getMaxScaleDenominator()
+ * @see #setMaxScaleDenominator(double)
+ */
+ protected double maxScale;
+
+ /**
+ * Description of how a feature is to appear on a map.
+ *
+ * @see #symbolizers()
+ */
+ @XmlElementRef(name = "Symbolizer")
+ private List<Symbolizer<R>> symbolizers;
+
+ /**
+ * If the style comes from an external XML file, the original source. Otherwise {@code null}.
+ *
+ * @see #getOnlineResource()
+ * @see #setOnlineSource(OnlineResource)
+ */
+ protected OnlineResource onlineSource;
+
+ /**
+ * For JAXB unmarshalling only.
+ */
+ private Rule() {
+ // Thread-local factory will be used.
+ }
+
+ /**
+ * Creates an initially empty rule.
+ *
+ * @param factory the factory to use for creating expressions and child elements.
+ */
+ public Rule(final StyleFactory<R> factory) {
+ super(factory);
+ maxScale = Double.POSITIVE_INFINITY;
+ symbolizers = new ArrayList<>();
+ }
+
+ /**
+ * Creates a shallow copy of the given object.
+ * For a deep copy, see {@link #clone()} instead.
+ *
+ * @param source the object to copy.
+ */
+ public Rule(final Rule<R> source) {
+ super(source);
+ name = source.name;
+ description = source.description;
+ legend = source.legend;
+ filter = source.filter;
+ minScale = source.minScale;
+ maxScale = source.maxScale;
+ onlineSource = source.onlineSource;
+ symbolizers = new ArrayList<>(source.symbolizers);
+ }
+
+ /**
+ * Returns the name for this rule.
+ * This can be any string that uniquely identifies this rule within a given canvas.
+ * It is not meant to be human-friendly. For a human-friendly label,
+ * see the {@linkplain Description#getTitle() title} instead.
+ *
+ * @return a name for this rule.
+ */
+ public Optional<String> getName() {
+ return Optional.ofNullable(name);
+ }
+
+ /**
+ * Sets a name for this rule.
+ * If this method is never invoked, then the default value is absence.
+ *
+ * @param value new name for this rule, or {@code null} if none.
+ */
+ public void setName(final String value) {
+ name = value;
+ }
+
+ /**
+ * Returns the description of this rule.
+ * The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this rule, and conversely.
+ *
+ * @return information for user interfaces.
+ */
+ public Optional<Description<R>> getDescription() {
+ return Optional.ofNullable(description);
+ }
+
+ /**
+ * Sets a description of this rule.
+ * The given instance is stored by reference, it is not cloned.
+ * If this method is never invoked, then the default value is absence.
+ *
+ * @param value new information for user interfaces, or {@code null} if none.
+ */
+ public void setDescription(final Description<R> value) {
+ description = value;
+ }
+
+ /**
+ * Returns a small graphic that could be used by the rendering engine to draw a legend window.
+ * User interfaces may present the user with a legend that indicates how features of a given type are being portrayed.
+ * Through its {@code LegendGraphic} property, a {@code Rule} can provide a custom picture to be used in such a legend window.
+ *
+ * <p>The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this rule, and conversely.</p>
+ *
+ * @return small graphic to draw in a legend window.
+ */
+ public Optional<LegendGraphic<R>> getLegend() {
+ return Optional.ofNullable(legend);
+ }
+
+ /**
+ * Sets a small graphic that could be used by the rendering engine to draw a legend window.
+ * The given instance is stored by reference, it is not cloned.
+ * If this method is never invoked, then the default value is absence.
+ *
+ * @param value new legend graphic, or {@code null} if none.
+ */
+ public void setLegend(final LegendGraphic<R> value) {
+ legend = value;
+ }
+
+ /**
+ * Returns the filter that will limit the features for which this rule will apply.
+ * This value should be used only if {@link #isElseFilter()} returns {@code false},
+ * in which case this rule applies to all features.
+ *
+ * @return the filter that will limit the features.
+ */
+ public Filter<R> getFilter() {
+ if (isElseFilter) {
+ return Filter.exclude();
+ }
+ final var value = filter;
+ return (value != null) ? value : Filter.include();
+ }
+
+ /**
+ * Sets the filter that will limit the features for which this rule will apply.
+ * If this method is never invoked, then the default value is {@link Filter#include()}.
+ * Invoking this method forces {@link #isElseFilter()} to {@code false}.
+ *
+ * @param value new filter that will limit the features, or {@code null} if none.
+ */
+ public void setFilter(final Filter<R> value) {
+ isElseFilter = false;
+ filter = value;
+ }
+
+ /**
+ * Returns true if this {@code Rule} will be applied only if no other rules in the containing style apply.
+ * If this is true, then the {@linkplain #getFilter() filter} should be ignored.
+ *
+ * <p>The "Else Filter" is implicitly a filter with a condition that depends on the enclosing style.
+ * Consequently, it can not be expressed as a standalone {@code Filter} expression in this rule.</p>
+ *
+ * @return true if the filter is an else filter.
+ */
+ public boolean isElseFilter() {
+ return isElseFilter;
+ }
+
+ /**
+ * Sets or unset this filter to an else filter.
+ *
+ * @param value whether the filter is the "else" filter.
+ */
+ public void setElseFilter(final boolean value) {
+ isElseFilter = value;
+ }
+
+ /**
+ * Invoked by JAXB at marshalling time for expressing the boolean {@code isElseFilter} value as an XML element.
+ */
+ @XmlElement(name = "ElseFilter")
+ private ElseFilter getElseFilter() {
+ return isElseFilter ? ElseFilter.INSTANCE : null;
+ }
+
+ /**
+ * Invoked at JAXB unmarshalling time when an {@code <ElseFilter/>} element is found.
+ */
+ private void setElseFilter(final ElseFilter value) {
+ isElseFilter = (value != null);
+ }
+
+ /**
+ * Returns the minimum value (inclusive) in the denominator of map scale at which this rule will apply.
+ * If, for example, this value was 10000, then this rule would only apply at scales of 1:<var>X</var>
+ * where <var>X</var> is greater than 10000. A value of zero indicates that there is no minimum.
+ *
+ * <h4>Relationship with real world lengths</h4>
+ * The values used are scale denominators relative to a “standardized rendering pixel size”.
+ * That size is defined as a square with sides of 0.28 millimeters. If the real pixel size
+ * is different or if the CRS uses angular units instead than linear, then the renderer shall
+ * take those information in account as described in OGC 05-077r4 §10.2.
+ *
+ * @return minimum scale value, inclusive.
+ */
+ public double getMinScaleDenominator() {
+ return minScale;
+ }
+
+ /**
+ * Invoked by JAXB for marshalling the minimum scale denominator.
+ * If both the minimum and maximum are the default values, then this property is omitted.
+ * If a maximum value exists, then the zero minimum is explicitly written for clarity.
+ */
+ @XmlElement(name = "MinScaleDenominator")
+ private Double getMinScale() {
+ final var value = minScale;
+ return (value > 0 || maxScale != Double.POSITIVE_INFINITY) ? value : null;
+ }
+
+ /**
+ * Sets the minimum value (inclusive) in the denominator of map scale at which this rule will apply.
+ * If the given value is greater than the maximum scale, then that maximum is discarded.
+ * If this method is never invoked, then the default value is 0.
+ *
+ * @param value new minimum scale value (inclusive).
+ */
+ public void setMinScaleDenominator(final double value) {
+ ArgumentChecks.ensurePositive("MinScaleDenominator", value);
+ minScale = value;
+ if (value > maxScale) {
+ maxScale = Double.POSITIVE_INFINITY;
+ }
+ }
+
+ /**
+ * Invoked by JAXB for unmarshalling the minimum scale denominator.
+ * The argument validity check assumes that this method is invoked only once.
+ * If this assumption is violated, the check allows range restriction but not expansion.
+ * If the given value is invalid, a warning is reported but the unmarshalling continue.
+ */
+ private void setMinScale(final Double value) {
+ if (isValidScale("MinScaleDenominator", value)) {
+ minScale = value;
+ }
+ }
+
+ /**
+ * Returns the maximum value (exclusive) in the denominator of map scale at which this rule will apply.
+ * If, for example, this value was 10000, then this rule would only apply at scales of 1:<var>X</var>
+ * where <var>X</var> is less than 10000.
+ * An {@linkplain Double#POSITIVE_INFINITY infinite} value indicates that there is no maximum.
+ *
+ * <h4>Relationship with real world lengths</h4>
+ * The same discussion than {@link #getMinScaleDenominator()} applies also to the maximum scale value.
+ *
+ * @return maximum scale value, exclusive.
+ */
+ public double getMaxScaleDenominator() {
+ return maxScale;
+ }
+
+ /**
+ * Invoked by JAXB for marshalling the maximum scale denominator.
+ * If the value is positive infinity, then the property is omitted.
+ */
+ @XmlElement(name = "MaxScaleDenominator")
+ private Double getMaxScale() {
+ final var value = maxScale;
+ return (value != Double.POSITIVE_INFINITY) ? value : null;
+ }
+
+ /**
+ * Sets the maximum value (exclusive) in the denominator of map scale at which this rule will apply.
+ * If the given value is less than the minimum scale, then that minimum is discarded.
+ * If this method is never invoked, then the default value is {@link Double#POSITIVE_INFINITY}.
+ *
+ * @param value new maximum scale value (exclusive).
+ */
+ public void setMaxScaleDenominator(final double value) {
+ ArgumentChecks.ensureStrictlyPositive("MaxScaleDenominator", value);
+ maxScale = value;
+ if (value < minScale) {
+ minScale = 0;
+ }
+ }
+
+ /**
+ * Invoked by JAXB for unmarshalling the maximum scale denominator.
+ * The argument validity check assumes that this method is invoked only once.
+ * If this assumption is violated, the check allows range restriction but not expansion.
+ * If the given value is invalid, a warning is reported but the unmarshalling continue.
+ */
+ private void setMaxScale(final Double value) {
+ if (isValidScale("MaxScaleDenominator", value)) {
+ maxScale = value;
+ }
+ }
+
+ /**
+ * Indicates whether an unmarshalled minimum or maximum scale denominator is inside the expected range of values.
+ * If the given value is invalid, a warning is emitted to the JAXB unmarshaller and the caller will keep the default value.
+ */
+ private boolean isValidScale(final String name, final Double value) {
+ boolean isValidScale = (value != null);
+ if (isValidScale) {
+ isValidScale = (value >= minScale && value <= maxScale);
+ if (!isValidScale) {
+ Context.warningOccured(Context.current(), Rule.class, "set".concat(name), Errors.class,
+ Errors.Keys.ValueOutOfRange_4, name, minScale, maxScale, value);
+ }
+ }
+ return isValidScale;
+ }
+
+ /**
+ * Returns the description of how a feature is to appear on a map.
+ * Each symbolizer describes how the shape should appear,
+ * together with graphical properties such as color and opacity.
+ * The predefined type of symbolizers are
+ * {@linkplain LineSymbolizer line},
+ * {@linkplain PolygonSymbolizer polygon},
+ * {@linkplain PointSymbolizer point},
+ * {@linkplain TextSymbolizer text}, and
+ * {@linkplain RasterSymbolizer raster} symbolizers.
+ *
+ * <p>The returned collection is <em>live</em>:
+ * changes in that collection are reflected into this object, and conversely.</p>
+ *
+ * @return the list of symbolizers, as a live collection.
+ */
+ @SuppressWarnings("ReturnOfCollectionOrArrayField")
+ public List<Symbolizer<R>> symbolizers() {
+ return symbolizers;
+ }
+
+ /**
+ * If the style comes from an external XML file, the original source.
+ * This property may be non-null if a XML document specified this rule
+ * by a link to another XML document.
+ *
+ * @return the original source of this rule.
+ */
+ public Optional<OnlineResource> getOnlineSource() {
+ return Optional.ofNullable(onlineSource);
+ }
+
+ /**
+ * If the style comes from an external XML file, the original source.
+ * If this method is never invoked, then the default value is absence.
+ *
+ * <h4>Effect on XML marshalling</h4>
+ * Setting this property to a non-null value has the following effect:
+ * When this rule is written in a XML document, then instead of writing
+ * the XML elements describing this rule,
+ * the specified link will be written instead.
+ *
+ * @todo Above-describing marshalling is not yet implemented.
+ *
+ * @param value new source of this rule, or {@code null} if none.
+ */
+ public void setOnlineSource(final OnlineResource value) {
+ onlineSource = value;
+ }
+
+ /**
+ * Returns all properties contained in this class.
+ * This is used for {@link #equals(Object)} and {@link #hashCode()} implementations.
+ */
+ @Override
+ final Object[] properties() {
+ return new Object[] {name, description, legend, filter, minScale, maxScale, symbolizers, onlineSource};
+ }
+
+ /**
+ * Returns a deep clone of this object. All style elements are cloned,
+ * but expressions are not on the assumption that they are immutable.
+ *
+ * @return deep clone of all style elements.
+ */
+ @Override
+ public Rule<R> clone() {
+ final var clone = (Rule<R>) super.clone();
+ clone.selfClone();
+ return clone;
+ }
+
+ /**
+ * Clones the mutable style fields of this element.
+ */
+ private void selfClone() {
+ if (description != null) description = description.clone();
+ if (legend != null) legend = legend.clone();
+ symbolizers = new ArrayList<>(symbolizers);
+ symbolizers.replaceAll(Symbolizer::clone);
+ }
+ }
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/SelectedChannel.java
index 0000000000,48298dd9cb..cb946149bb
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/SelectedChannel.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/SelectedChannel.java
@@@ -1,0 -1,173 +1,173 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import java.util.Optional;
+ import jakarta.xml.bind.annotation.XmlType;
+ import jakarta.xml.bind.annotation.XmlElement;
+
+ // Branch-dependent imports
-import org.opengis.filter.Expression;
++import org.apache.sis.filter.Expression;
+
+
+ /**
+ * Information about a channel to use in a multi-spectral source.
+ * Channels are identified by data-dependent character identifiers.
+ * Commonly, channels will be labelled as "1", "2", <i>etc</i>.
+ * A set of selected channels is contained in {@link ChannelSelection}.
+ *
+ * <!-- Following list of authors contains credits to OGC GeoAPI 2 contributors. -->
+ * @author Ian Turton (CCG)
+ * @author Johann Sorel (Geomatys)
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
+ *
+ * @since 1.5
+ */
+ @XmlType(name = "SelectedChannelType", propOrder = {
+ "sourceChannelName",
+ "contrastEnhancement"
+ })
+ // No root element is specified in OGC 05-077r4.
+ public class SelectedChannel<R> extends StyleElement<R> {
+ /**
+ * The channel's name, or {@code null} if unspecified.
+ *
+ * @see #getSourceChannelName()
+ * @see #setSourceChannelName(Expression)
+ *
+ * @todo Needs an adapter from expression to plain string.
+ */
+ @XmlElement(name = "SourceChannelName", required = true)
+ protected Expression<R,String> sourceChannelName;
+
+ /**
+ * Contrast enhancement applied to the selected channel in isolation, or {@code null} if none.
+ *
+ * @see #getContrastEnhancement()
+ * @see #setContrastEnhancement(ContrastEnhancement)
+ */
+ @XmlElement(name = "ContrastEnhancement")
+ protected ContrastEnhancement<R> contrastEnhancement;
+
+ /**
+ * For JAXB unmarshalling only.
+ */
+ private SelectedChannel() {
+ // Thread-local factory will be used.
+ }
+
+ /**
+ * Creates an initially empty selected channel.
+ *
+ * @param factory the factory to use for creating expressions and child elements.
+ */
+ public SelectedChannel(final StyleFactory<R> factory) {
+ super(factory);
+ }
+
+ /**
+ * Creates a shallow copy of the given object.
+ * For a deep copy, see {@link #clone()} instead.
+ *
+ * @param source the object to copy.
+ */
+ public SelectedChannel(final SelectedChannel<R> source) {
+ super(source);
+ sourceChannelName = source.sourceChannelName;
+ contrastEnhancement = source.contrastEnhancement;
+ }
+
+ /**
+ * Returns the channel's name.
+ *
+ * @return the channel's name, or {@code null} if unspecified.
+ *
+ * @todo Shall never be {@code null}. We need to think about some default value.
+ */
+ public Expression<R,String> getSourceChannelName() {
+ return sourceChannelName;
+ }
+
+ /**
+ * Sets the channel's name.
+ *
+ * @param value the channel's name, or {@code null} if unspecified.
+ */
+ public void setSourceChannelName(final Expression<R,String> value) {
+ sourceChannelName = value;
+ }
+
+ /**
+ * Returns the contrast enhancement applied to the selected channel in isolation.
+ * The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this stroke, and conversely.
+ *
+ * @return contrast enhancement for the selected channel.
+ *
+ * @see RasterSymbolizer#getContrastEnhancement()
+ */
+ public Optional<ContrastEnhancement<R>> getContrastEnhancement() {
+ return Optional.ofNullable(contrastEnhancement);
+ }
+
+ /**
+ * Sets the contrast enhancement applied to the selected channel in isolation.
+ * The given instance is stored by reference, it is not cloned.
+ * If this method is never invoked, then the default value is absence.
+ *
+ * @param value new contrast enhancement, or {@code null} if none.
+ *
+ * @see RasterSymbolizer#setContrastEnhancement(ContrastEnhancement)
+ */
+ public void setContrastEnhancement(final ContrastEnhancement<R> value) {
+ contrastEnhancement = value;
+ }
+
+ /**
+ * Returns all properties contained in this class.
+ * This is used for {@link #equals(Object)} and {@link #hashCode()} implementations.
+ */
+ @Override
+ final Object[] properties() {
+ return new Object[] {sourceChannelName, contrastEnhancement};
+ }
+
+ /**
+ * Returns a deep clone of this object. All style elements are cloned,
+ * but expressions are not on the assumption that they are immutable.
+ *
+ * @return deep clone of all style elements.
+ */
+ @Override
+ public SelectedChannel<R> clone() {
+ final var clone = (SelectedChannel<R>) super.clone();
+ clone.selfClone();
+ return clone;
+ }
+
+ /**
+ * Clones the mutable style fields of this element.
+ */
+ private void selfClone() {
+ if (contrastEnhancement != null) {
+ contrastEnhancement = contrastEnhancement.clone();
+ }
+ }
+ }
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/ShadedRelief.java
index 0000000000,a01dea558c..82e171e0fd
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/ShadedRelief.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/ShadedRelief.java
@@@ -1,0 -1,155 +1,155 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import jakarta.xml.bind.annotation.XmlType;
+ import jakarta.xml.bind.annotation.XmlElement;
+ import jakarta.xml.bind.annotation.XmlRootElement;
+
+ // Branch-dependent imports
-import org.opengis.filter.Expression;
++import org.apache.sis.filter.Expression;
+
+
+ /**
+ * Relief shading (or “hill shading”) applied to an image for a three-dimensional visual effect.
+ *
+ * <!-- Following list of authors contains credits to OGC GeoAPI 2 contributors. -->
+ * @author Ian Turton (CCG)
+ * @author Johann Sorel (Geomatys)
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
+ *
+ * @since 1.5
+ */
+ @XmlType(name = "ShadedReliefType", propOrder = {
+ "brightnessOnly",
+ "reliefFactor"
+ })
+ @XmlRootElement(name = "ShadedRelief")
+ public class ShadedRelief<R> extends StyleElement<R> {
+ /**
+ * Whether to apply the shading to the image generated so far by other layers.
+ *
+ * @see #isBrightnessOnly()
+ * @see #setBrightnessOnly(Expression)
+ *
+ * @todo Needs an adapter from expression to plain boolean.
+ */
+ @XmlElement(name = "BrightnessOnly")
+ protected Expression<R,Boolean> brightnessOnly;
+
+ /**
+ * Amount of exaggeration to use for the height of the hills, or {@code null} for the default value.
+ *
+ * @see #getReliefFactor()
+ * @see #setReliefFactor(Expression)
+ */
+ @XmlElement(name = "ReliefFactor")
+ protected Expression<R, ? extends Number> reliefFactor;
+
+ /**
+ * For JAXB unmarshalling only.
+ */
+ private ShadedRelief() {
+ // Thread-local factory will be used.
+ }
+
+ /**
+ * Creates a shaded relief initialized to implementation-specific default values.
+ *
+ * @param factory the factory to use for creating expressions and child elements.
+ */
+ public ShadedRelief(final StyleFactory<R> factory) {
+ super(factory);
+ }
+
+ /**
+ * Creates a shallow copy of the given object.
+ * For a deep copy, see {@link #clone()} instead.
+ *
+ * @param source the object to copy.
+ */
+ public ShadedRelief(final ShadedRelief<R> source) {
+ super(source);
+ brightnessOnly = source.brightnessOnly;
+ reliefFactor = source.reliefFactor;
+ }
+
+ /**
+ * Returns whether to apply the shading to the image generated so far by other layers.
+ * If {@code false}, then the shading is applied only on the layer being rendered by
+ * the current {@link RasterSymbolizer}.
+ *
+ * @return whether to apply the shading to the image generated so far by other layers.
+ */
+ public Expression<R,Boolean> isBrightnessOnly() {
+ return defaultToFalse(brightnessOnly);
+ }
+
+ /**
+ * Sets whether to apply the shading to the image generated so far by other layers.
+ * If this method is never invoked, then the default value is literal false.
+ *
+ * @param value new policy, or {@code null} for resetting the default value.
+ */
+ public void setBrightnessOnly(final Expression<R,Boolean> value) {
+ brightnessOnly = value;
+ }
+
+ /**
+ * Returns the amount of exaggeration to use for the height of the hills.
+ * A value of around 55 gives reasonable results for Earth-based DEMs.
+ *
+ * @return amount of exaggeration to use for the height of the hills.
+ */
+ public Expression<R, ? extends Number> getReliefFactor() {
+ final var value = reliefFactor;
+ return (value != null) ? value : factory.relief;
+ }
+
+ /**
+ * Sets the amount of exaggeration to use for the height of the hills.
+ * If this method is never invoked, then the default value is implementation-specific.
+ *
+ * @param value new amount of exaggeration, or {@code null} for resetting the default value.
+ */
+ public void setReliefFactor(final Expression<R, ? extends Number> value) {
+ reliefFactor = value;
+ }
+
+ /**
+ * Returns all properties contained in this class.
+ * This is used for {@link #equals(Object)} and {@link #hashCode()} implementations.
+ */
+ @Override
+ final Object[] properties() {
+ return new Object[] {brightnessOnly, reliefFactor};
+ }
+
+ /**
+ * Returns a deep clone of this object. All style elements are cloned,
+ * but expressions are not on the assumption that they are immutable.
+ *
+ * @return deep clone of all style elements.
+ */
+ @Override
+ public ShadedRelief<R> clone() {
+ return (ShadedRelief<R>) super.clone();
+ }
+ }
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Stroke.java
index 0000000000,70f52769f3..431741425b
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Stroke.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Stroke.java
@@@ -1,0 -1,492 +1,492 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import java.awt.Color;
+ import java.util.Optional;
+ import jakarta.xml.bind.annotation.XmlType;
+ import jakarta.xml.bind.annotation.XmlElement;
+ import jakarta.xml.bind.annotation.XmlRootElement;
+
+ // Branch-dependent imports
-import org.opengis.filter.Expression;
++import org.apache.sis.filter.Expression;
+
+
+ /**
+ * Instructions about how to draw styled lines.
+ * Stroke objects are contained by {@link LineSymbolizer} and {@link PolygonSymbolizer}.
+ * There are three basic types of strokes: solid-color, {@link GraphicFill} (stipple),
+ * and repeated linear {@link GraphicStroke}.
+ * A repeated linear graphic is plotted linearly and has its graphic symbol bent around the curves
+ * of the line string, and a graphic fill has the pixels of the line rendered with a repeating area-fill pattern.
+ * If neither a {@linkplain #getGraphicFill() graphic fill} nor {@linkplain #getGraphicStroke() graphic stroke}
+ * element is given, then the line symbolizer will render a solid color.
+ *
+ * <!-- Following list of authors contains credits to OGC GeoAPI 2 contributors. -->
+ * @author Johann Sorel (Geomatys)
+ * @author Chris Dillard (SYS Technologies)
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
+ *
+ * @since 1.5
+ */
+ @XmlType(name = "StrokeType", propOrder = {
+ "graphicFill",
+ "graphicStroke",
+ // "svgParameter"
+ })
+ @XmlRootElement(name = "Stroke")
+ public class Stroke<R> extends StyleElement<R> implements Translucent<R> {
+ /**
+ * Graphic for tiling the (thin) area of the line, or {@code null} if none.
+ * This property and {@link #graphicStroke} are mutually exclusive.
+ *
+ * @see #getGraphicFill()
+ * @see #setGraphicFill(GraphicFill)
+ */
+ @XmlElement(name = "GraphicFill")
+ protected GraphicFill<R> graphicFill;
+
+ /**
+ * Graphic to repeat along the path of the lines, or {@code null} if none.
+ * This property and {@link #graphicFill} are mutually exclusive.
+ *
+ * @see #getGraphicStroke()
+ * @see #setGraphicStroke(GraphicStroke)
+ */
+ @XmlElement(name = "GraphicStroke")
+ protected GraphicStroke<R> graphicStroke;
+
+ /**
+ * Color of the line if it is to be solid-color filled, or {@code null} for the default value.
+ * The default value specified by OGC 05-077r4 standard is black.
+ *
+ * <p>This property is used when both {@link #graphicFill} and {@link #graphicStroke} are null.
+ * In XML documents, this is encoded inside a {@code <SvgParameter name="stroke">} element.</p>
+ *
+ * @see #getColor()
+ * @see #setColor(Expression)
+ */
+ protected Expression<R,Color> color;
+
+ /**
+ * Level of translucency as a floating point number between 0 and 1 (inclusive), or {@code null} the default value.
+ * The default value specified by OGC 05-077r4 standard is 1.
+ *
+ * <p>In XML documents, this is encoded inside a {@code <SvgParameter name="stroke-opacity">} element.</p>
+ *
+ * @see #getOpacity()
+ * @see #setOpacity(Expression)
+ */
+ protected Expression<R, ? extends Number> opacity;
+
+ /**
+ * Absolute width of the line stroke as a positive floating point number, or {@code null} for the default value.
+ * The default value specified by OGC 05-077r4 standard is 1.
+ *
+ * <p>In XML documents, this is encoded inside a {@code <SvgParameter name="stroke-width">} element.</p>
+ *
+ * @see #getWidth()
+ * @see #setWidth(Expression)
+ */
+ protected Expression<R, ? extends Number> width;
+
+ /**
+ * How the various segments of a (thick) line string should be joined, or {@code null} the default value.
+ * The default value is implementation-specific.
+ *
+ * <p>In XML documents, this is encoded inside a {@code <SvgParameter name="stroke-linejoin">} element.</p>
+ *
+ * @see #getLineJoin()
+ * @see #setLineJoin(Expression)
+ */
+ protected Expression<R,String> lineJoin;
+
+ /**
+ * How the beginning and ending segments of a line string will be terminated, or {@code null} the default value.
+ * The default value is implementation-specific.
+ *
+ * <p>In XML documents, this is encoded inside a {@code <SvgParameter name="stroke-linecap">} element.</p>
+ *
+ * @see #getLineCap()
+ * @see #setLineCap(Expression)
+ */
+ protected Expression<R,String> lineCap;
+
+ /**
+ * Dash pattern as a space-separated sequence of numbers, or {@code null} for a solid line.
+ *
+ * <p>In XML documents, this is encoded inside a {@code <SvgParameter name="stroke-dasharray">} element.</p>
+ *
+ * @see #getDashArray()
+ * @see #setDashArray(Expression)
+ */
+ protected Expression<R,float[]> dashArray;
+
+ /**
+ * Distance offset into the dash array to begin drawing, or {@code null} for the default value.
+ * The default value is zero.
+ *
+ * <p>In XML documents, this is encoded inside a {@code <SvgParameter name="stroke-dashoffset">} element.</p>
+ *
+ * @see #getDashOffset()
+ * @see #setDashOffset(Expression)
+ */
+ protected Expression<R,Integer> dashOffset;
+
+ /**
+ * For JAXB unmarshalling only.
+ */
+ private Stroke() {
+ // Thread-local factory will be used.
+ }
+
+ /**
+ * Creates a stroke initialized to solid line of black opaque color, 1 pixel width.
+ *
+ * @param factory the factory to use for creating expressions and child elements.
+ */
+ public Stroke(final StyleFactory<R> factory) {
+ super(factory);
+ }
+
+ /**
+ * Creates a shallow copy of the given object.
+ * For a deep copy, see {@link #clone()} instead.
+ *
+ * @param source the object to copy.
+ */
+ public Stroke(final Stroke<R> source) {
+ super(source);
+ graphicFill = source.graphicFill;
+ graphicStroke = source.graphicStroke;
+ color = source.color;
+ opacity = source.opacity;
+ width = source.width;
+ lineJoin = source.lineJoin;
+ lineCap = source.lineCap;
+ dashArray = source.dashArray;
+ dashOffset = source.dashOffset;
+ }
+
+ /**
+ * Indicates that line should be drawn by tiling the (thin) area of the line with the given graphic.
+ * Between {@code getGraphicFill()} and {@link #getGraphicStroke()}, only one may return a non-null value
+ * because a {@code Stroke} can have a {@code GraphicFill} or a {@code GraphicStroke}, but not both.
+ *
+ * <p>The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this stroke, and conversely.</p>
+ *
+ * @return graphic for tiling the (thin) area of the line.
+ *
+ * @see Fill#getGraphicFill()
+ */
+ public Optional<GraphicFill<R>> getGraphicFill() {
+ return Optional.ofNullable(graphicFill);
+ }
+
+ /**
+ * Specifies that line should be drawn by tiling the (thin) area of the line with the given graphic.
+ * The given instance is stored by reference, it is not cloned.
+ * If this method is never invoked, then the default value is absence.
+ *
+ * <p>Setting a non-null value causes {@link #getGraphicStroke()} to
+ * return {@code null} because those two properties are mutually exclusive.</p>
+ *
+ * @param value new graphic for tiling the (thin) area of the line, or {@code null} if none.
+ *
+ * @see Fill#setGraphicFill(GraphicFill)
+ */
+ public void setGraphicFill(final GraphicFill<R> value) {
+ graphicFill = value;
+ if (value != null) {
+ graphicStroke = null;
+ }
+ }
+
+ /**
+ * Indicates that lines should be drawn by repeatedly plotting the given graphic.
+ * The graphic is repeated along the path of the lines, rotating it according to the orientation of the line.
+ * Between {@link #getGraphicFill()} and {@code getGraphicStroke()}, only one may return a non-null value
+ * because a {@code Stroke} can have a {@link GraphicFill} or a {@link GraphicStroke}, but not both.
+ *
+ * <p>The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this stroke, and conversely.</p>
+ *
+ * @return graphic to repeat along the path of the lines.
+ */
+ public Optional<GraphicStroke<R>> getGraphicStroke() {
+ return Optional.ofNullable(graphicStroke);
+ }
+
+ /**
+ * Specifies that lines should be drawn by repeatedly plotting the given graphic.
+ * The given instance is stored by reference, it is not cloned.
+ * If this method is never invoked, then the default value is absence.
+ *
+ * <p>Setting a non-null value causes {@link #getGraphicFill()} to
+ * return {@code null} because those two properties are mutually exclusive.</p>
+ *
+ * @param value new graphic to repeat along the path of the lines, or {@code null} if none.
+ */
+ public void setGraphicStroke(final GraphicStroke<R> value) {
+ graphicStroke = value;
+ if (value != null) {
+ graphicFill = null;
+ }
+ }
+
+ /**
+ * Indicates the color of the line if it is to be solid-color filled.
+ * This is used when both {@linkplain #getGraphicFill() graphic fill}
+ * and {@linkplain #getGraphicStroke() graphic stroke} are null.
+ *
+ * @return color of the line if it is to be solid-color filled.
+ *
+ * @see Fill#getColor()
+ */
+ public Expression<R,Color> getColor() {
+ final var value = color;
+ return (value != null) ? value : factory.black;
+ }
+
+ /**
+ * Sets the color of the line if it is to be solid-color filled.
+ * If this method is never invoked, then the default value is {@link Fill#BLACK}.
+ * That default value is standardized by OGC 05-077r4.
+ *
+ * <p>Setting a non-null value clears the {@linkplain #getGraphicFill() graphic fill} and the
+ * {@linkplain #getGraphicStroke() graphic stroke} because those three properties are mutually exclusive.</p>
+ *
+ * @param value color of the line if solid-color filled, or {@code null} for resetting the default value.
+ *
+ * @see Fill#setColor(Expression)
+ */
+ public void setColor(final Expression<R,Color> value) {
+ color = value;
+ if (value != null) {
+ graphicFill = null;
+ graphicStroke = null;
+ }
+ }
+
+ /**
+ * Sets the color and opacity together.
+ * The opacity is derived from the alpha value of the given color.
+ *
+ * @param value new color and opacity, or {@code null} for resetting the defaults.
+ */
+ public void setColorAndOpacity(Color value) {
+ if (value == null) {
+ color = null;
+ opacity = null;
+ } else {
+ if ((opacity = opacity(value)) != null) {
+ value = new Color(value.getRGB() | 0xFF000000);
+ }
+ color = literal(value);
+ }
+ }
+
+ /**
+ * Indicates the level of translucency as a floating point number between 0 and 1 (inclusive).
+ * A value of zero means completely transparent. A value of 1.0 means completely opaque.
+ *
+ * @return the level of translucency as a floating point number between 0 and 1 (inclusive).
+ *
+ * @see Fill#getOpacity()
+ * @see Graphic#getOpacity()
+ * @see RasterSymbolizer#getOpacity()
+ */
+ @Override
+ public Expression<R, ? extends Number> getOpacity() {
+ return defaultToOne(opacity);
+ }
+
+ /**
+ * Sets the level of translucency as a floating point number between 0 and 1 (inclusive).
+ * If this method is never invoked, then the default value is literal 1 (totally opaque).
+ * That default value is standardized by OGC 05-077r4.
+ *
+ * @param value new level of translucency, or {@code null} for resetting the default value.
+ */
+ @Override
+ public void setOpacity(final Expression<R, ? extends Number> value) {
+ opacity = value;
+ }
+
+ /**
+ * Gives the absolute width of the line stroke as a floating point number.
+ * The unit of measurement is given by {@link Symbolizer#getUnitOfMeasure()}.
+ * Fractional numbers are allowed, but negative numbers are not.
+ *
+ * @return absolute width of the line stroke as a positive floating point number.
+ */
+ public Expression<R, ? extends Number> getWidth() {
+ return defaultToOne(width);
+ }
+
+ /**
+ * Sets the absolute width of the line stroke as a floating point number.
+ * If this method is never invoked, then the default value 1.0.
+ * That default value is standardized by OGC 05-077r4.
+ *
+ * @param value new width of the line stroke, or {@code null} for resetting the default value.
+ */
+ public void setWidth(final Expression<R, ? extends Number> value) {
+ width = value;
+ }
+
+ /**
+ * Indicates how the various segments of a (thick) line string should be joined.
+ * Valid values are "miter", "round", and "bevel".
+ *
+ * @return how segments of a (thick) line string should be joined.
+ */
+ public Expression<R,String> getLineJoin() {
+ final var value = lineJoin;
+ return (value != null) ? value : factory.bevel;
+ }
+
+ /**
+ * Sets how the various segments of a (thick) line string should be joined.
+ * If this method is never invoked, then the default value is literal "bevel".
+ * That default value is implementation-specific.
+ *
+ * @param value how segments of a line string should be joined, or {@code null} for resetting the default value.
+ */
+ public void setLineJoin(final Expression<R,String> value) {
+ lineJoin = value;
+ }
+
+ /**
+ * Indicates how the beginning and ending segments of a line string will be terminated.
+ * Valid values are "butt", "round", and "square".
+ *
+ * @return how the beginning and ending segments of a line string will be terminated.
+ */
+ public Expression<R,String> getLineCap() {
+ final var value = lineCap;
+ return (value != null) ? value : factory.square;
+ }
+
+ /**
+ * Sets how the beginning and ending segments of a line string will be terminated.
+ * If this method is never invoked, then the default value is literal "square".
+ * That default value is implementation-specific.
+ *
+ * @param value how a line string should be terminated, or {@code null} for resetting the default value.
+ */
+ public void setLineCap(final Expression<R,String> value) {
+ lineCap = value;
+ }
+
+ /**
+ * Indicates the dash pattern as a space-separated sequence of floating point numbers.
+ * The first number represents the length of the first dash to draw.
+ * The second number represents the length of space to leave.
+ * This continues to the end of the list then repeats.
+ * If {@code null}, then lines will be drawn as solid and unbroken.
+ *
+ * @return dash pattern as a space-separated sequence of numbers, or empty for a solid line.
+ */
+ public Optional<Expression<R,float[]>> getDashArray() {
+ return Optional.ofNullable(dashArray);
+ }
+
+ /**
+ * Sets the dash pattern as a space-separated sequence of floating point numbers.
+ * If this method is never invoked, then the default value is {@code null} (solid line).
+ *
+ * @param value new dash pattern as a space-separated sequence of numbers, or {@code null} for a solid line.
+ */
+ public void setDashArray(final Expression<R,float[]> value) {
+ dashArray = value;
+ }
+
+ /**
+ * Indicates the distance offset into the dash array to begin drawing.
+ *
+ * @return distance offset into the dash array to begin drawing.
+ */
+ public Expression<R,Integer> getDashOffset() {
+ final var value = dashOffset;
+ return (value != null) ? value : factory.zeroAsInt;
+ }
+
+ /**
+ * Sets the distance offset into the dash array to begin drawing.
+ * If this method is never invoked, then the default value is 0.
+ *
+ * @param value new distance offset into the dash array, or {@code null} for resetting the default value.
+ */
+ public void setDashOffset(final Expression<R,Integer> value) {
+ dashOffset = value;
+ }
+
+ /*
+ * TODO: we need a private method like below for formatting above SVG parameters:
+ *
+ * @XmlElement(name = "SvgParameter")
+ * private List<SvgParameter> svgParameters();
+ *
+ * Where:
+ *
+ * class SvgParameter {
+ * @XmlAttribute(required = true)
+ * private String name;
+ *
+ * @XmlMixed
+ * @XmlElementRef(name = "expression", namespace = "http://www.opengis.net/ogc")
+ * private List<Expression<?,?>> content;
+ * }
+ *
+ * See 05-077r4 §11.1.3.
+ */
+
+ /**
+ * Returns all properties contained in this class.
+ * This is used for {@link #equals(Object)} and {@link #hashCode()} implementations.
+ */
+ @Override
+ final Object[] properties() {
+ return new Object[] {graphicFill, graphicStroke, color, opacity, width, lineJoin, lineCap, dashArray, dashOffset};
+ }
+
+ /**
+ * Returns a deep clone of this object. All style elements are cloned,
+ * but expressions are not on the assumption that they are immutable.
+ *
+ * @return deep clone of all style elements.
+ */
+ @Override
+ public Stroke<R> clone() {
+ final var clone = (Stroke<R>) super.clone();
+ clone.selfClone();
+ return clone;
+ }
+
+ /**
+ * Clones the mutable style fields of this element.
+ */
+ private void selfClone() {
+ if (graphicFill != null) graphicFill = graphicFill.clone();
+ if (graphicStroke != null) graphicStroke = graphicStroke.clone();
+ }
+ }
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/StyleElement.java
index 0000000000,55df55df04..bb245cacac
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/StyleElement.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/StyleElement.java
@@@ -1,0 -1,220 +1,219 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import java.awt.Color;
+ import java.util.Arrays;
+ import jakarta.xml.bind.annotation.XmlTransient;
+ import org.opengis.util.InternationalString;
+ import org.apache.sis.util.ArgumentChecks;
+
+ // Branch-dependent imports
-import org.opengis.filter.Expression;
-import org.opengis.filter.Literal;
++import org.apache.sis.filter.Expression;
+
+
+ /**
+ * Base class of all style objects.
+ * This base class can not be extended directly.
+ * Instead, one of the subclasses can be extended.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
+ *
+ * @since 1.5
+ */
+ @XmlTransient
+ public abstract class StyleElement<R> implements Cloneable {
+ /**
+ * The factory to use for creating expressions and child elements.
+ * This is typically the same factory than the one used for creating this element.
+ *
+ * @see FeatureTypeStyle#FACTORY
+ * @see CoverageStyle#FACTORY
+ */
+ protected final StyleFactory<R> factory;
+
+ /**
+ * Creates a new style element.
+ * Intentionally restricted to this package because {@link #properties()} is package-private.
+ *
+ * @param factory the factory to use for creating expressions and child elements.
+ */
+ StyleElement(final StyleFactory<R> factory) {
+ ArgumentChecks.ensureNonNull("factory", factory);
+ this.factory = factory;
+ }
+
+ /**
+ * Creates a shallow copy of the given object.
+ * For a deep copy, see {@link #clone()} instead.
+ *
+ * @param source the object to copy.
+ */
+ StyleElement(final StyleElement<R> source) {
+ factory = source.factory;
+ }
+
+ /**
+ * Creates a style element for XML unmarshalling.
+ * <em>This constructor is unsafe</em> and should be used only by JAXB reflection.
+ *
+ * @todo Allow the factory to be set according the parent {@link AbstractStyle} being unmarshalled.
+ * We will need to use {@link ThreadLocal}.
+ */
+ StyleElement() {
+ factory = null; // TODO
+ }
+
+ /**
+ * Returns a literal for the given value.
+ * This is a convenience method for use with setter methods that expect an expression.
+ *
+ * @param <E> type of value.
+ * @param value the value for which to return a literal, or {@code null} if none.
+ * @return literal for the given value, or {@code null} if the given value was null.
+ */
- public final <E> Literal<R,E> literal(final E value) {
++ public final <E> Expression<R,E> literal(final E value) {
+ return (value == null) ? null : factory.filterFactory.literal(value);
+ }
+
+ /**
+ * Returns the given expression if non-null, or literal {@code true} otherwise.
+ * This is a convenience method for the implementation of getter methods when
+ * a default value exists.
+ *
+ * @param value the value for which to apply a default value if {@code null}.
+ * @return the given value if non-null, or {@code true} literal otherwise.
+ */
+ protected final Expression<R,Boolean> defaultToTrue(final Expression<R,Boolean> value) {
+ return (value != null) ? value : factory.enabled;
+ }
+
+ /**
+ * Returns the given expression if non-null, or literal {@code false} otherwise.
+ * This is a convenience method for the implementation of getter methods when
+ * a default value exists.
+ *
+ * @param value the value for which to apply a default value if {@code null}.
+ * @return the given value if non-null, or a {@code false} literal otherwise.
+ */
+ protected final Expression<R,Boolean> defaultToFalse(final Expression<R,Boolean> value) {
+ return (value != null) ? value : factory.disabled;
+ }
+
+ /**
+ * Returns the given expression if non-null, or literal {@code 0.0} otherwise.
+ * This is a convenience method for the implementation of getter methods when
+ * a default value exists.
+ *
+ * @param value the value for which to apply a default value if {@code null}.
+ * @return the given value if non-null, or {@code 0.0} literal otherwise.
+ */
+ protected final Expression<R, ? extends Number> defaultToZero(final Expression<R, ? extends Number> value) {
+ return (value != null) ? value : factory.zero;
+ }
+
+ /**
+ * Returns the given expression if non-null, or literal {@code 0.5} otherwise.
+ * This is a convenience method for the implementation of getter methods when
+ * a default value exists.
+ *
+ * @param value the value for which to apply a default value if {@code null}.
+ * @return the given value if non-null, or {@code 0.5} literal otherwise.
+ */
+ protected final Expression<R, ? extends Number> defaultToHalf(final Expression<R, ? extends Number> value) {
+ return (value != null) ? value : factory.half;
+ }
+
+ /**
+ * Returns the given expression if non-null, or literal {@code 1.0} otherwise.
+ * This is a convenience method for the implementation of getter methods when
+ * a default value exists.
+ *
+ * @param value the value for which to apply a default value if {@code null}.
+ * @return the given value if non-null, or {@code 1.0} literal otherwise.
+ */
+ protected final Expression<R, ? extends Number> defaultToOne(final Expression<R, ? extends Number> value) {
+ return (value != null) ? value : factory.one;
+ }
+
+ /**
+ * Returns the opacity of the alpha value of the given color.
+ * If the color is totally opaque, then this method returns {@code null}.
+ *
+ * @param color color from which to get the opacity.
+ * @return opacity derived from the alpha value of the color, or {@code null} if totally opaque.
+ */
+ final Expression<R, ? extends Number> opacity(final Color color) {
+ final int alpha = color.getAlpha();
+ return (alpha != 255) ? literal(alpha / 256d) : null;
+ // Divide by 256 instead of 255 in order to get round numbers for alpha values 64, 128, etc.
+ }
+
+ /**
+ * Returns all properties contained in the subclasses.
+ * This is used for {@link #equals(Object)} and {@link #hashCode()} implementations.
+ *
+ * @return all properties.
+ */
+ abstract Object[] properties();
+
+ /**
+ * Returns a hash code value for this object.
+ *
+ * @return a hash code value for this object.
+ */
+ @Override
+ public int hashCode() {
+ return getClass().hashCode() + Arrays.hashCode(properties());
+ }
+
+ /**
+ * Compares this element with the given object for equality.
+ *
+ * @param obj the other object to compare with this.
+ * @return whether the other object is equal to this.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ return (obj != null) && (obj.getClass() == getClass()) &&
+ Arrays.equals(properties(), ((StyleElement) obj).properties());
+ }
+
+ /**
+ * Returns a deep clone of this object. All style elements are cloned,
+ * but expressions are not on the assumption that they are immutable.
+ * ISO 19115 metadata and {@link InternationalString} members, if any,
+ * are not cloned neither in current Apache SIS version.
+ *
+ * @return a clone of this element.
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ public StyleElement<R> clone() {
+ try {
+ return (StyleElement<R>) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError(e); // Should never happen since we are cloneable.
+ }
+ }
+ }
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/StyleFactory.java
index 0000000000,51137d965e..777b150fc4
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/StyleFactory.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/StyleFactory.java
@@@ -1,0 -1,534 +1,534 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import java.net.URI;
+ import java.awt.Color;
+ import org.apache.sis.util.ArgumentChecks;
+ import org.apache.sis.internal.feature.AttributeConvention;
+ import org.apache.sis.metadata.iso.citation.DefaultOnlineResource;
+
+ // Branch-dependent imports
-import org.opengis.filter.Literal;
-import org.opengis.filter.FilterFactory;
-import org.opengis.filter.ValueReference;
++import org.apache.sis.filter.Expression;
++import org.apache.sis.filter.DefaultFilterFactory;
+
+
+ /**
+ * Factory of style elements.
+ * Style factory uses a {@link FilterFactory} instance that depends on the type of data to be styled.
+ * That type of data is specified by the parameterized type {@code <R>}.
- * The two main types are {@link org.opengis.feature.Feature} and {@link org.apache.sis.coverage.BandedCoverage}.
++ * The two main types are {@link org.apache.sis.feature.AbstractFeature}
++ * and {@link org.apache.sis.coverage.BandedCoverage}.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
+ *
+ * @since 1.5
+ */
+ public class StyleFactory<R> {
+ /**
+ * The factory to use for creating expressions.
+ */
- final FilterFactory<R,?,?> filterFactory;
++ final DefaultFilterFactory<R,?,?> filterFactory;
+
+ /**
+ * Literal commonly used as a default value.
+ *
+ * @see StyleElement#defaultToTrue(Expression)
+ * @see StyleElement#defaultToFalse(Expression)
+ */
- final Literal<R,Boolean> enabled, disabled;
++ final Expression<R,Boolean> enabled, disabled;
+
+ /**
+ * Literal commonly used as a default value.
+ */
- final Literal<R,Integer> zeroAsInt;
++ final Expression<R,Integer> zeroAsInt;
+
+ /**
+ * Literal commonly used as a default value.
+ *
+ * @see StyleElement#defaultToZero(Expression)
+ * @see StyleElement#defaultToHalf(Expression)
+ * @see StyleElement#defaultToOne(Expression)
+ */
- final Literal<R,Double> zero, half, one, six, ten;
++ final Expression<R,Double> zero, half, one, six, ten;
+
+ /**
+ * Default factor for shaded relief.
+ * This is an arbitrary suggested but not standardized by OGC 05-077r4.
+ */
- final Literal<R,Double> relief;
++ final Expression<R,Double> relief;
+
+ /**
+ * Literal commonly used as a default value.
+ */
- final Literal<R,String> normal, square, bevel;
++ final Expression<R,String> normal, square, bevel;
+
+ /**
+ * Literal for a predefined color which can be used as fill color.
+ */
- final Literal<R,Color> black, gray, white;
++ final Expression<R,Color> black, gray, white;
+
+ /**
+ * An expression for fetching the default geometry.
+ *
+ * @todo According SE specification, the default expression in the context of some symbolizers should
+ * fetch all geometries, not only a default one. The default seems to depend on the symbolizer type.
+ */
- final ValueReference<R,?> defaultGeometry;
++ final Expression<R,?> defaultGeometry;
+
+ /**
+ * Creates a new style factory.
+ *
+ * @param filterFactory the factory to use for creating expressions.
+ */
- public StyleFactory(final FilterFactory<R,?,?> filterFactory) {
++ public StyleFactory(final DefaultFilterFactory<R,?,?> filterFactory) {
+ ArgumentChecks.ensureNonNull("filterFactory", filterFactory);
+ this.filterFactory = filterFactory;
+ enabled = filterFactory.literal(Boolean.TRUE);
+ disabled = filterFactory.literal(Boolean.FALSE);
+ zeroAsInt = filterFactory.literal(0);
+ zero = filterFactory.literal(0.0);
+ half = filterFactory.literal(0.5);
+ one = filterFactory.literal(1.0);
+ six = filterFactory.literal(6.0);
+ ten = filterFactory.literal(10.0);
+ relief = filterFactory.literal(55.0);
+ normal = filterFactory.literal("normal");
+ square = filterFactory.literal("square");
+ bevel = filterFactory.literal("bevel");
+ black = filterFactory.literal(Color.BLACK);
+ gray = filterFactory.literal(Color.GRAY);
+ white = filterFactory.literal(Color.WHITE);
+
+ defaultGeometry = filterFactory.property(AttributeConvention.GEOMETRY);
+ }
+
+ /**
+ * Creates a new style factory with the same literals than the given factory.
+ * This constructor shall not be public because it assumes that all literals
+ * are implementations that ignore the type {@code <R>} of data to style,
+ * in which case the unchecked cast is safe.
+ *
+ * @param source the style factory to copy.
+ */
+ @SuppressWarnings("unchecked")
+ StyleFactory(final StyleFactory<?> source) {
- enabled = (Literal<R,Boolean>) source.enabled;
- disabled = (Literal<R,Boolean>) source.disabled;
- zeroAsInt = (Literal<R,Integer>) source.zeroAsInt;
- zero = (Literal<R,Double>) source.zero;
- half = (Literal<R,Double>) source.half;
- one = (Literal<R,Double>) source.one;
- six = (Literal<R,Double>) source.six;
- ten = (Literal<R,Double>) source.ten;
- relief = (Literal<R,Double>) source.relief;
- normal = (Literal<R,String>) source.normal;
- square = (Literal<R,String>) source.square;
- bevel = (Literal<R,String>) source.bevel;
- black = (Literal<R,Color>) source.black;
- gray = (Literal<R,Color>) source.gray;
- white = (Literal<R,Color>) source.white;
++ enabled = (Expression<R,Boolean>) source.enabled;
++ disabled = (Expression<R,Boolean>) source.disabled;
++ zeroAsInt = (Expression<R,Integer>) source.zeroAsInt;
++ zero = (Expression<R,Double>) source.zero;
++ half = (Expression<R,Double>) source.half;
++ one = (Expression<R,Double>) source.one;
++ six = (Expression<R,Double>) source.six;
++ ten = (Expression<R,Double>) source.ten;
++ relief = (Expression<R,Double>) source.relief;
++ normal = (Expression<R,String>) source.normal;
++ square = (Expression<R,String>) source.square;
++ bevel = (Expression<R,String>) source.bevel;
++ black = (Expression<R,Color>) source.black;
++ gray = (Expression<R,Color>) source.gray;
++ white = (Expression<R,Color>) source.white;
+
+ filterFactory = null; // TODO: FilterFactory for coverage is not yet available.
+ defaultGeometry = null; // Idem.
+ }
+
+ /**
+ * Creates an initially empty rule.
+ * A rule is a set of tendering instructions grouped by feature-property conditions and map scales.
+ *
+ * @return new initially empty rule.
+ */
+ public Rule<R> createRule() {
+ return new Rule<>(this);
+ }
+
+ /**
+ * Creates a point symbolizer initialized to a default graphic.
+ * A point symbolizer is a set of instructions about how to draw a graphic at a point.
+ *
+ * @return new point symbolizer initialized to a default graphic.
+ */
+ public PointSymbolizer<R> createPointSymbolizer() {
+ return new PointSymbolizer<>(this);
+ }
+
+ /**
+ * Creates a line symbolizer with the default stroke and no perpendicular offset.
+ * A line symbolizer is a set of instructions about how to draw on a map the lines of a geometry.
+ *
+ * @return new ine symbolizer with the default stroke and no perpendicular offset.
+ */
+ public LineSymbolizer<R> createLineSymbolizer() {
+ return new LineSymbolizer<>(this);
+ }
+
+ /**
+ * Creates a polygon symbolizer initialized to the default fill and default stroke.
+ * A polygon symbolizer is a set of instructions about how to draw on a map the lines and the interior of polygons.
+ *
+ * @return new polygon symbolizer initialized to the default fill and default stroke.
+ */
+ public PolygonSymbolizer<R> createPolygonSymbolizer() {
+ return new PolygonSymbolizer<>(this);
+ }
+
+ /**
+ * Creates a text symbolizer with default placement and default font.
+ * A text symbolizer is a set of instructions about how to drawn text on a map.
+ * The new symbolizer has no initial label.
+ *
+ * @return new text symbolizer with default placement and default font.
+ *
+ * @see #createTextSymbolizer(String)
+ */
+ public TextSymbolizer<R> createTextSymbolizer() {
+ return new TextSymbolizer<>(this);
+ }
+
+ /**
+ * Creates a text symbolizer initialized with the specified label literal.
+ *
+ * @param label initial label literal, or {@code null} if none.
+ * @return new text symbolizer with default placement and default font.
+ */
+ public TextSymbolizer<R> createTextSymbolizer(final String label) {
+ final var s = createTextSymbolizer();
+ s.label = s.literal(label);
+ return s;
+ }
+
+ /**
+ * Creates an initially opaque raster symbolizer with no contrast enhancement, shaded relief or outline.
+ * A raster symbolizer is a set of instructions about how to render raster, matrix or coverage data.
+ *
+ * @return new initially opaque raster symbolizer.
+ */
+ public RasterSymbolizer<R> createRasterSymbolizer() {
+ return new RasterSymbolizer<>(this);
+ }
+
+ /**
+ * Creates an initially empty description.
+ * A description is a set of human-readable information about a style object being defined.
+ *
+ * @return new initially empty description.
+ */
+ public Description<R> createDescription() {
+ return new Description<>(this);
+ }
+
+ /**
+ * Creates a point placement initialized to anchor at the middle and no displacement.
+ * A point placement is a set of instructions about how a text label is positioned relative to a point.
+ *
+ * @return new point placement initialized to anchor at the middle and no displacement.
+ */
+ public PointPlacement<R> createPointPlacement() {
+ return new PointPlacement<>(this);
+ }
+
+ /**
+ * Creates a line placement initialized to no offset, no repetition and no gap.
+ * A line placement is a set of instructions about where and how a text label should be rendered relative to a line.
+ *
+ * @return new line placement initialized to no offset, no repetition and no gap.
+ */
+ public LinePlacement<R> createLinePlacement() {
+ return new LinePlacement<>(this);
+ }
+
+ /**
+ * Creates an anchor point initialized to <var>x</var> = 0.5 and <var>y</var> = 0.5.
+ * An anchor point is the location inside a graphic or label to use as an "anchor"
+ * for positioning it relative to a point.
+ *
+ * @return new anchor point initialized to center.
+ *
+ * @see #createAnchorPoint(double, double)
+ */
+ public AnchorPoint<R> createAnchorPoint() {
+ return new AnchorPoint<>(this);
+ }
+
+ /**
+ * Creates an anchor point initialized to the given position.
+ * This is a convenience method for a frequently used operation.
+ *
+ * @param x the initial <var>x</var> position.
+ * @param y the initial <var>y</var> position.
+ * @return new anchor point initialized to the given position.
+ */
+ public AnchorPoint<R> createAnchorPoint(final double x, final double y) {
+ final var s = createAnchorPoint();
+ s.anchorPointX = filterFactory.literal(x);
+ s.anchorPointY = filterFactory.literal(y);
+ return s;
+ }
+
+ /**
+ * Creates a displacement initialized to zero offsets.
+ * A displacement is the two-dimensional offsets from the original geometry.
+ *
+ * @return new displacement initialized to zero offsets.
+ *
+ * @see #createDisplacement(double, double)
+ */
+ public Displacement<R> createDisplacement() {
+ return new Displacement<>(this);
+ }
+
+ /**
+ * Creates a displacement initialized to the given offsets.
+ * This is a convenience method for a frequently used operation.
+ *
+ * @param x the <var>x</var> displacement.
+ * @param y the <var>y</var> displacement.
+ * @return new displacement initialized to the given offsets.
+ */
+ public Displacement<R> createDisplacement(final double x, final double y) {
+ final var s = createDisplacement();
+ s.displacementX = filterFactory.literal(x);
+ s.displacementY = filterFactory.literal(y);
+ return s;
+ }
+
+ /**
+ * Creates a mark initialized to a gray square with black outline.
+ * A mark is a predefined shape that can be drawn at the points of the geometry.
+ *
+ * @return new mark initialized to a gray square with black outline.
+ */
+ public Mark<R> createMark() {
+ return new Mark<>(this);
+ }
+
+ /**
+ * Creates an initially empty external graphic.
+ * An external graphic is a reference to an external file that contains an image of some kind,
+ * such as a PNG or SVG.
+ *
+ * @return new initially empty external graphic.
+ *
+ * @see #createExternalGraphic(URI, String)
+ */
+ public ExternalGraphic<R> createExternalGraphic() {
+ return new ExternalGraphic<>(this);
+ }
+
+ /**
+ * Creates an external graphic initialized to the given URI.
+ *
+ * @param linkage URI to the external graphic, or {@code null} if none.
+ * @param format MIME type of the external graphic, or {@code null} if unspecified.
+ * @return new external graphic initialized to the given URI.
+ */
+ public ExternalGraphic<R> createExternalGraphic(final URI linkage, final String format) {
+ final var s = createExternalGraphic();
+ s.format = format;
+ if (linkage != null) {
+ s.onlineResource = new DefaultOnlineResource(linkage);
+ }
+ return s;
+ }
+
+ /**
+ * Creates a stroke initialized to solid line of black opaque color, 1 pixel width.
+ * A stroke is a set of instructions about how to draw styled lines.
+ *
+ * @return new stroke initialized to solid line of black opaque color, 1 pixel width.
+ *
+ * @see #createStroke(Color)
+ */
+ public Stroke<R> createStroke() {
+ return new Stroke<>(this);
+ }
+
+ /**
+ * Creates a stroke initialized to the given color and opacity.
+ * The alpha channel of the given color is used for determining the opacity.
+ *
+ * @param color the initial color, or {@code null} if none.
+ * @return new stroke initialized to the given color and opacity.
+ */
+ public Stroke<R> createStroke(final Color color) {
+ final var s = createStroke();
+ s.setColorAndOpacity(color);
+ return s;
+ }
+
+ /**
+ * Creates an opaque fill initialized to the gray color.
+ * A fill is a set of instructions about how to fill the interior of polygons.
+ *
+ * @return new opaque fill initialized to the gray color.
+ *
+ * @see #createFill(Color)
+ */
+ public Fill<R> createFill() {
+ return new Fill<>(this);
+ }
+
+ /**
+ * Creates a fill initialized to the given color and opacity.
+ * The alpha channel of the given color is used for determining the opacity.
+ *
+ * @param color the initial color, or {@code null} if none.
+ * @return new fill initialized to the given color and opacity.
+ */
+ public Fill<R> createFill(final Color color) {
+ final var s = createFill();
+ s.setColorAndOpacity(color);
+ return s;
+ }
+
+ /**
+ * Creates an halo initialized to a white color and a radius of 1 pixel.
+ * A halo is a fill that are applied to the backgrounds of font glyphs.
+ *
+ * @return new halo initialized to a white color and a radius of 1 pixel.
+ */
+ public Halo<R> createHalo() {
+ return new Halo<>(this);
+ }
+
+ /**
+ * Creates a font initialized to normal style, normal weight and a size of 10 pixels.
+ * A font is the identification of a font of a certain family, style, and size.
+ *
+ * @return new font initialized to normal style, normal weight and a size of 10 pixels.
+ */
+ public Font<R> createFont() {
+ return new Font<>(this);
+ }
+
+ /**
+ * Creates a graphic initialized to opaque default mark, default size and no rotation.
+ * A graphic is a symbol with an inherent shape, color(s), and possibly size.
+ *
+ * @return new graphic initialized to opaque default mark, default size and no rotation.
+ */
+ public Graphic<R> createGraphic() {
+ return new Graphic<>(this);
+ }
+
+ /**
+ * Creates a graphic fill initialized to a default graphic.
+ * A graphic fill is a stipple-fill repeated graphic.
+ *
+ * @return new graphic fill initialized to a default graphic.
+ */
+ public GraphicFill<R> createGraphicFill() {
+ return new GraphicFill<>(this);
+ }
+
+ /**
+ * Creates a graphic stroke initialized to a default graphic and no gap.
+ * A graphic stroke is a repeated-linear-graphic stroke.
+ *
+ * @return new graphic stroke initialized to a default graphic and no gap.
+ */
+ public GraphicStroke<R> createGraphicStroke() {
+ return new GraphicStroke<>(this);
+ }
+
+ /**
+ * Creates a legend initialized to the default graphic.
+ * A legend graphic is a graphic to do displayed in a legend for a rule.
+ *
+ * @return new legend initialized to the default graphic.
+ */
+ public LegendGraphic<R> createLegendGraphic() {
+ return new LegendGraphic<>(this);
+ }
+
+ /**
+ * Creates an initially empty color replacement.
+ * A color replacement defines the replacement of a color in an external graphic.
+ *
+ * @return new initially empty color replacement.
+ */
+ public ColorReplacement<R> createColorReplacement() {
+ return new ColorReplacement<>(this);
+ }
+
+ /**
+ * Creates an initially empty color map.
+ * A color map is the mapping of fixed-numeric pixel values to colors.
+ *
+ * @return new initially empty color map.
+ */
+ public ColorMap<R> createColorMap() {
+ return new ColorMap<>(this);
+ }
+
+ /**
+ * Creates an initially empty channel selection.
+ * A channel selection specifies the false-color channel selection for a multi-spectral raster source.
+ *
+ * @return new initially empty channel selection.
+ */
+ public ChannelSelection<R> createChannelSelection() {
+ return new ChannelSelection<>(this);
+ }
+
+ /**
+ * Creates an initially empty selected channel.
+ * A selected channel is information about a channel to use in a multi-spectral source.
+ *
+ * @return new initially empty selected channel.
+ *
+ * @see #createSelectedChannel(String)
+ */
+ public SelectedChannel<R> createSelectedChannel() {
+ return new SelectedChannel<>(this);
+ }
+
+ /**
+ * Creates a selected channel initialized to the given channel name.
+ *
+ * @param sourceChannelName the channel's name, or {@code null} if unspecified.
+ * @return new selected channel for the given name.
+ */
+ public SelectedChannel<R> createSelectedChannel(final String sourceChannelName) {
+ final var s = createSelectedChannel();
+ s.sourceChannelName = s.literal(sourceChannelName);
+ return s;
+ }
+
+ /**
+ * Creates a contrast enhancement initialized to no operation.
+ *
+ * @return new contrast enhancement initialized to no operation.
+ */
+ public ContrastEnhancement<R> createContrastEnhancement() {
+ return new ContrastEnhancement<> (this);
+ }
+
+ /**
+ * Creates a shaded relief initialized to implementation-specific default values.
+ * A shaded relief is a “hill shading” applied to an image for a three-dimensional visual effect.
+ *
+ * @return new shaded relief initialized to implementation-specific default values.
+ */
+ public ShadedRelief<R> createShadedRelief() {
+ return new ShadedRelief<>(this);
+ }
+ }
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Symbolizer.java
index 0000000000,743e2d87a1..7c98bcd330
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Symbolizer.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Symbolizer.java
@@@ -1,0 -1,314 +1,314 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import java.util.Objects;
+ import java.util.Optional;
+ import javax.measure.Unit;
+ import jakarta.xml.bind.annotation.XmlType;
+ import jakarta.xml.bind.annotation.XmlElement;
+ import jakarta.xml.bind.annotation.XmlRootElement;
+ import jakarta.xml.bind.annotation.XmlSchemaType;
+ import jakarta.xml.bind.annotation.XmlAttribute;
+ import jakarta.xml.bind.annotation.XmlSeeAlso;
+ import org.apache.sis.measure.Units;
+ import org.apache.sis.util.resources.Errors;
-import org.opengis.filter.Expression;
-import org.opengis.filter.ValueReference;
++
++// Branch-depend imports
++import org.apache.sis.filter.Expression;
+
+
+ /**
+ * Description of how a feature is to appear on a map.
+ * A symbolizer describes how the shape should appear,
+ * together with graphical properties such as color and opacity.
+ * A symbolizer is obtained by specifying one of a small number of different types
+ * and then supplying parameters to override its default behavior.
+ * The predefined type of symbolizers are
+ * {@linkplain LineSymbolizer line},
+ * {@linkplain PolygonSymbolizer polygon},
+ * {@linkplain PointSymbolizer point},
+ * {@linkplain TextSymbolizer text}, and
+ * {@linkplain RasterSymbolizer raster} symbolizers.
+ *
+ * <!-- Following list of authors contains credits to OGC GeoAPI 2 contributors. -->
+ * @author Johann Sorel (Geomatys)
+ * @author Chris Dillard (SYS Technologies)
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
+ *
+ * @since 1.5
+ */
+ @XmlType(name = "SymbolizerType", propOrder = {
+ "name",
+ "description",
+ "geometry"
+ })
+ @XmlSeeAlso({
+ LineSymbolizer.class,
+ PointSymbolizer.class,
+ PolygonSymbolizer.class,
+ TextSymbolizer.class,
+ RasterSymbolizer.class
+ })
+ @XmlRootElement(name = "Symbolizer")
+ public abstract class Symbolizer<R> extends StyleElement<R> {
+ /**
+ * Name for this style, or {@code null} if none.
+ *
+ * @see #getName()
+ * @see #setName(String)
+ */
+ @XmlElement(name = "Name")
+ protected String name;
+
+ /**
+ * Information for user interfaces, or {@code null} if none.
+ *
+ * @see #getDescription()
+ * @see #setDescription(Description)
+ */
+ @XmlElement(name = "Description")
+ protected Description<R> description;
+
+ /**
+ * Expression fetching the geometry to draw, or {@code null} for the default geometries.
+ * The Symbology Encoding restrict the XML representation to {@code <ogc:PropertyName>}
+ * (defined in the Filter Specification), but Apache SIS accepts any expression.
+ *
+ * @see #getGeometry()
+ * @see #setGeometry(Expression)
+ */
+ @XmlElement(name = "Geometry")
+ protected Expression<R,?> geometry;
+
+ /**
+ * Unit of measurement for all lengths inside this symbolizer, or {@code null} for the default value.
+ * The recommended XML representations of this attribute are like below:
+ *
+ * <ul>
+ * <li>uom="http://www.opengeospatial.org/se/units/metre"</li>
+ * <li>uom="http://www.opengeospatial.org/se/units/foot"</li>
+ * <li>uom="http://www.opengeospatial.org/se/units/pixel"</li>
+ * </ul>
+ *
+ * @todo Recommended XML representation is not yet implemented.
+ *
+ * @see #getUnitOfMeasure()
+ * @see #setUnitOfMeasure(Unit)
+ */
+ @XmlAttribute(name = "uom")
+ @XmlSchemaType(name = "anyURI")
+ protected Unit<?> unit;
+
+ /**
+ * For JAXB unmarshalling only.
+ */
+ Symbolizer() {
+ // Thread-local factory will be used.
+ }
+
+ /**
+ * Creates a symbolizer initialized to default geometries and pixel unit of measurement.
+ *
+ * @param factory the factory to use for creating expressions and child elements.
+ */
+ public Symbolizer(final StyleFactory<R> factory) {
+ super(factory);
+ }
+
+ /**
+ * Creates a shallow copy of the given object.
+ * For a deep copy, see {@link #clone()} instead.
+ *
+ * @param source the object to copy.
+ */
+ protected Symbolizer(final Symbolizer<R> source) {
+ super(source);
+ name = source.name;
+ geometry = source.geometry;
+ description = source.description;
+ unit = source.unit;
+ }
+
+ /**
+ * Returns the name for this symbolizer.
+ * This can be any string that uniquely identifies this symbolizer within a given canvas.
+ * It is not meant to be human-friendly. For a human-friendly label,
+ * see the {@linkplain Description#getTitle() title} instead.
+ *
+ * @return a name for this symbolizer.
+ */
+ public Optional<String> getName() {
+ return Optional.ofNullable(name);
+ }
+
+ /**
+ * Sets a name for this symbolizer.
+ * If this method is never invoked, then the default value is absence.
+ *
+ * @param value new name for this symbolizer, or {@code null} if none.
+ */
+ public void setName(final String value) {
+ name = value;
+ }
+
+ /**
+ * Returns the description of this style.
+ * The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this symbolizer, and conversely.
+ *
+ * @return information for user interfaces.
+ */
+ public Optional<Description<R>> getDescription() {
+ return Optional.ofNullable(description);
+ }
+
+ /**
+ * Sets a description of this style.
+ * The given instance is stored by reference, it is not cloned.
+ * If this method is never invoked, then the default value is absence.
+ *
+ * @param value new information for user interfaces, or {@code null} if none.
+ */
+ public void setDescription(final Description<R> value) {
+ description = value;
+ }
+
+ /**
+ * Returns the unit of measurement for all lengths inside this symbolizer.
+ * The unit applies to stroke width, size, gap, initial gap, displacement and perpendicular offset.
+ * Two types of units are allowed:
+ *
+ * <ul>
+ * <li>{@link Units#PIXEL} is interpreted as a paper unit, referring to the size of the map.</li>
+ * <li>{@linkplain Units#isLinear(Unit) Linear units} such as metre and foot are “ground” units
+ * referring to the actual size of real-world objects.</li>
+ * </ul>
+ *
+ * @return unit of measurement for all lengths inside this symbolizer.
+ */
+ public Unit<?> getUnitOfMeasure() {
+ final var value = unit;
+ return (value != null) ? value : Units.PIXEL;
+ }
+
+ /**
+ * Sets the unit of measurement for all lengths inside this symbolizer.
+ * If this method is never invoked, then the default value is {@link Units#PIXEL}.
+ * That default value is standardized by OGC 05-077r4.
+ *
+ * @param value new unit of measurement for lengths, or {@code null} for resetting the default value.
+ * @throws IllegalArgumentException if the specified value is neither {@link Units#PIXEL} or a linear unit.
+ */
+ public void setUnitOfMeasure(Unit<?> value) {
+ if (value != null && value != Units.PIXEL && !Units.isLinear(value)) {
+ throw new IllegalArgumentException(Errors.format(Errors.Keys.NonLinearUnit_1, value));
+ }
+ unit = value;
+ }
+
+ /**
+ * Returns the expression used for fetching the geometry to draw.
- * This expression is often a {@link ValueReference}.
+ * The value in the referenced feature property should be a geometry
+ * from a {@linkplain org.apache.sis.setup.GeometryLibrary supported library},
+ * expect in the particular case of {@link RasterSymbolizer} where the value
+ * should be a {@link org.apache.sis.coverage.BandedCoverage}.
+ *
+ * @return expression fetching the geometry or the coverage to draw.
+ */
+ public Expression<R,?> getGeometry() {
+ final var value = geometry;
+ return (value != null) ? value : factory.defaultGeometry;
+ }
+
+ /**
+ * Sets the expression used for fetching the geometry to draw.
+ * If this method is never invoked, then the default value is
- * a {@link ValueReference} fetching {@code "sis:geometry"}.
++ * a {@code ValueReference} fetching {@code "sis:geometry"}.
+ *
+ * @todo The default expression may change in a future version.
+ * According SE specification, the default should fetch all geometries.
+ *
+ * @param value new expression fetching the geometry to draw, or {@code null} for resetting the default value.
+ */
+ public void setGeometry(final Expression<R,?> value) {
+ geometry = value;
+ }
+
+ /**
+ * Returns {@code true} if this symbolizer has enough information for displaying something.
+ *
+ * @return whether this symbolizer can display something.
+ */
+ public boolean isVisible() {
+ return true;
+ }
+
+ /**
+ * Returns a hash code value for this symbolizer.
+ *
+ * @return a hash code value for this symbolizer.
+ */
+ @Override
+ public int hashCode() {
+ return super.hashCode() + Objects.hash(name, description, geometry, unit);
+ }
+
+ /**
+ * Compares this symbolizer with the given object for equality.
+ *
+ * @param obj the other object to compare with this.
+ * @return whether the other object is equal to this.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ final var other = (Symbolizer) obj;
+ return Objects.equals(name, other.name)
+ && Objects.equals(description, other.description)
+ && Objects.equals(geometry, other.geometry)
+ && Objects.equals(unit, other.unit);
+ }
+
+ /**
+ * Returns a deep clone of this object. All style elements are cloned,
+ * but expressions are not on the assumption that they are immutable.
+ *
+ * @return deep clone of all style elements.
+ */
+ @Override
+ public Symbolizer<R> clone() {
+ final var clone = (Symbolizer<R>) super.clone();
+ clone.selfClone();
+ return clone;
+ }
+
+ /**
+ * Clones the mutable style fields of this element.
+ */
+ private void selfClone() {
+ if (description != null) description = description.clone();
+ }
+ }
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/TextSymbolizer.java
index 0000000000,6a47d5c873..ffc46e799a
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/TextSymbolizer.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/TextSymbolizer.java
@@@ -1,0 -1,288 +1,288 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import java.util.Optional;
+ import jakarta.xml.bind.annotation.XmlType;
+ import jakarta.xml.bind.annotation.XmlElement;
+ import jakarta.xml.bind.annotation.XmlElementRef;
+ import jakarta.xml.bind.annotation.XmlRootElement;
+
+ // Branch-dependent imports
-import org.opengis.filter.Expression;
++import org.apache.sis.filter.Expression;
+
+
+ /**
+ * Instructions about how to drawn text on a map.
+ * The {@linkplain #getGeometry() geometry} is interpreted as being either a point
+ * or a line as needed by the {@linkplain #getLabelPlacement() label placement}.
+ * If a given geometry is not of point or line, it shall be transformed into the appropriate type.
+ *
+ * <!-- Following list of authors contains credits to OGC GeoAPI 2 contributors. -->
+ * @author Johann Sorel (Geomatys)
+ * @author Chris Dillard (SYS Technologies)
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
+ *
+ * @since 1.5
+ */
+ @XmlType(name = "TextSymbolizerType", propOrder = {
+ "label",
+ "font",
+ "labelPlacement",
+ "halo",
+ "fill"
+ })
+ @XmlRootElement(name = "TextSymbolizer")
+ public class TextSymbolizer<R> extends Symbolizer<R> {
+ /**
+ * Text to display, or {@code null} if none.
+ *
+ * @see #getLabel()
+ * @see #setLabel(Expression)
+ */
+ @XmlElement(name = "Label")
+ protected Expression<R,String> label;
+
+ /**
+ * Font to apply on the text, or {@code null} for lazily constructed default.
+ *
+ * @see #getFont()
+ * @see #setFont(Font)
+ */
+ @XmlElement(name = "Font")
+ protected Font<R> font;
+
+ /**
+ * Indications about how the text should be placed with respect to the feature geometry.
+ * If {@code null}, it will be lazily created when first needed.
+ *
+ * @see #getLabelPlacement()
+ * @see #setLabelPlacement(LabelPlacement)
+ */
+ @XmlElementRef(name = "LabelPlacement")
+ protected LabelPlacement<R> labelPlacement;
+
+ /**
+ * Indication about a halo to draw around the text, or {@code null} if none.
+ *
+ * @see #getHalo()
+ * @see #setHalo(Halo)
+ */
+ @XmlElement(name = "Halo")
+ protected Halo<R> halo;
+
+ /**
+ * Graphic, color and opacity of the text to draw, or {@code null} for lazily constructed default.
+ *
+ * @see #getFill()
+ * @see #setFill(Fill)
+ */
+ @XmlElement(name = "Fill")
+ protected Fill<R> fill;
+
+ /**
+ * For JAXB unmarshalling only.
+ */
+ private TextSymbolizer() {
+ // Thread-local factory will be used.
+ }
+
+ /**
+ * Creates a text symbolizer with default placement and default font.
+ * The new symbolizer has no initial label.
+ *
+ * @param factory the factory to use for creating expressions and child elements.
+ */
+ public TextSymbolizer(final StyleFactory<R> factory) {
+ super(factory);
+ }
+
+ /**
+ * Creates a shallow copy of the given object.
+ * For a deep copy, see {@link #clone()} instead.
+ *
+ * @param source the object to copy.
+ */
+ public TextSymbolizer(final TextSymbolizer<R> source) {
+ super(source);
+ label = source.label;
+ font = source.font;
+ labelPlacement = source.labelPlacement;
+ halo = source.halo;
+ fill = source.fill;
+ }
+
+ /**
+ * Returns the expression that will be evaluated to determine what text is displayed.
+ * If {@code null}, then no text will be rendered.
+ *
+ * @return text to display, or {@code null} if none.
+ *
+ * @todo Replace {@code null} by a default expression searching for a default text property in the feature.
+ */
+ public Expression<R,String> getLabel() {
+ return label;
+ }
+
+ /**
+ * Sets the expression that will be evaluated to determine what text is displayed.
+ * If this method is never invoked, then the default value is {@code null}.
+ *
+ * @param value new text to display, or {@code null} if none.
+ */
+ public void setLabel(final Expression<R,String> value) {
+ label = value;
+ }
+
+ /**
+ * Returns the font to apply on the text.
+ * The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this symbolizer, and conversely.
+ *
+ * @return font to apply on the text.
+ */
+ public Font<R> getFont() {
+ if (font == null) {
+ font = factory.createFont();
+ }
+ return font;
+ }
+
+ /**
+ * Sets the font to apply on the text.
+ * The given instance is stored by reference, it is not cloned. If this method is never invoked,
+ * then the default value is a {@linkplain Font#Font() default font}.
+ *
+ * @param value new font to apply on the text, or {@code null} for resetting the default value.
+ */
+ public void setFont(final Font<R> value) {
+ font = value;
+ }
+
+ /**
+ * Returns indications about how the text should be placed with respect to the feature geometry.
+ * This object will either be an instance of {@link LinePlacement} or {@link PointPlacement}.
+ *
+ * <p>The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this symbolizer, and conversely.</p>
+ *
+ * @return how the text should be placed with respect to the feature geometry.
+ */
+ public LabelPlacement<R> getLabelPlacement() {
+ if (labelPlacement == null) {
+ labelPlacement = factory.createPointPlacement();
+ }
+ return labelPlacement;
+ }
+
+ /**
+ * Sets indications about how the text should be placed with respect to the feature geometry.
+ * The given instance is stored by reference, it is not cloned.
+ * If this method is never invoked, then the default value is a
+ * {@linkplain PointPlacement#PointPlacement() default point placement}.
+ *
+ * @param value new indications about text placement, or {@code null} for resetting the default value.
+ */
+ public void setLabelPlacement(final LabelPlacement<R> value) {
+ labelPlacement = value;
+ }
+
+ /**
+ * Returns indication about a halo to draw around the text.
+ *
+ * <p>The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this symbolizer, and conversely.</p>
+ *
+ * @return indication about a halo to draw around the text.
+ */
+ public Optional<Halo<R>> getHalo() {
+ return Optional.ofNullable(halo);
+ }
+
+ /**
+ * Sets indication about a halo to draw around the text.
+ * The given instance is stored by reference, it is not cloned.
+ * If this method is never invoked, then the default value is absence.
+ *
+ * @param value new indication about a halo to draw around the text, or {@code null} if none.
+ */
+ public void setHalo(final Halo<R> value) {
+ halo = value;
+ }
+
+ /**
+ * Returns the graphic, color and opacity of the text to draw.
+ * The returned object is <em>live</em>:
+ * changes in the returned instance will be reflected in this symbolizer, and conversely.
+ *
+ * @return graphic, color and opacity of the text to draw.
+ */
+ public Fill<R> getFill() {
+ if (fill == null) {
+ fill = factory.createFill();
+ fill.setColor(factory.black);
+ }
+ return fill;
+ }
+
+ /**
+ * Sets the graphic, color and opacity of the text to draw.
+ * The given instance is stored by reference, it is not cloned.
+ * If this method is never invoked, then the default value is a solid black color.
+ * That default value is standardized by OGC 05-077r4.
+ *
+ * @param value new fill of the text to draw, or {@code null} for resetting the default value.
+ */
+ public void setFill(final Fill<R> value) {
+ fill = value;
+ }
+
+ /**
+ * Returns all properties contained in this class.
+ * This is used for {@link #equals(Object)} and {@link #hashCode()} implementations.
+ */
+ @Override
+ final Object[] properties() {
+ return new Object[] {label, font, labelPlacement, halo, fill};
+ }
+
+ /**
+ * Returns a deep clone of this object. All style elements are cloned,
+ * but expressions are not on the assumption that they are immutable.
+ *
+ * @return deep clone of all style elements.
+ */
+ @Override
+ public TextSymbolizer<R> clone() {
+ final var clone = (TextSymbolizer<R>) super.clone();
+ clone.selfClone();
+ return clone;
+ }
+
+ /**
+ * Clones the mutable style fields of this element.
+ */
+ private void selfClone() {
+ if (font != null) font = font.clone();
+ if (labelPlacement != null) labelPlacement = labelPlacement.clone();
+ if (halo != null) halo = halo.clone();
+ if (fill != null) fill = fill.clone();
+ }
+ }
diff --cc core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Translucent.java
index 0000000000,f8d9e11624..5d90081d36
mode 000000,100644..100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Translucent.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/style/se1/Translucent.java
@@@ -1,0 -1,49 +1,49 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
-import org.opengis.filter.Expression;
++import org.apache.sis.filter.Expression;
+
+
+ /**
+ * Object which can be rendered with various levels of transparency.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ *
+ * @param <R> the type of data to style, such as {@code Feature} or {@code Coverage}.
+ *
+ * @since 1.5
+ */
+ public interface Translucent<R> {
+ /**
+ * Indicates the level of translucency as a floating point number between 0 and 1 (inclusive).
+ * A value of zero means completely transparent. A value of 1.0 means completely opaque.
+ *
+ * @return the level of translucency as a floating point number between 0 and 1 (inclusive).
+ */
+ Expression<R, ? extends Number> getOpacity();
+
+ /**
+ * Sets the level of translucency as a floating point number between 0 and 1 (inclusive).
+ * If this method is never invoked, then the default value is literal 1 (totally opaque).
+ * That default value is standardized by OGC 05-077r4.
+ *
+ * @param value new level of translucency, or {@code null} for resetting the default value.
+ */
+ void setOpacity(Expression<R, ? extends Number> value);
+ }
diff --cc core/sis-portrayal/src/test/java/org/apache/sis/style/se1/HaloTest.java
index 0000000000,a0e2fe0f62..3e925ff721
mode 000000,100644..100644
--- a/core/sis-portrayal/src/test/java/org/apache/sis/style/se1/HaloTest.java
+++ b/core/sis-portrayal/src/test/java/org/apache/sis/style/se1/HaloTest.java
@@@ -1,0 -1,73 +1,73 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import org.junit.Test;
+ import static org.junit.Assert.*;
+
+ // Branch-dependent imports
-import org.opengis.feature.Feature;
++import org.apache.sis.feature.AbstractFeature;
+
+
+ /**
+ * Tests for {@link Halo}.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 1.5
+ * @since 1.5
+ */
+ public final class HaloTest extends StyleTestCase {
+ /**
+ * Creates a new test case.
+ */
+ public HaloTest() {
+ }
+
+ /**
+ * Test of {@code Fill} property.
+ */
+ @Test
+ public void testFill() {
+ final var cdt = factory.createHalo();
+
+ // Check default
- Fill<Feature> value = cdt.getFill();
++ Fill<AbstractFeature> value = cdt.getFill();
+ assertEquals(factory.white, value.getColor());
+ assertLiteralEquals(1.0, value.getOpacity());
+
+ // Check get/set
+ value.setColor(anyColor());
+ value.setOpacity(literal(0.8));
+ cdt.setFill(value);
+ assertEquals(value, cdt.getFill());
+ }
+
+ /**
+ * Test of {@code Radius} property.
+ */
+ @Test
+ public void testRadius() {
+ final var cdt = factory.createHalo();
+
+ // Check default
+ assertLiteralEquals(1.0, cdt.getRadius());
+
+ // Check get/set
+ cdt.setRadius(literal(40));
+ assertLiteralEquals(40, cdt.getRadius());
+ }
+ }
diff --cc core/sis-portrayal/src/test/java/org/apache/sis/style/se1/LineSymbolizerTest.java
index 0000000000,9a71c7282e..11ddf31d3a
mode 000000,100644..100644
--- a/core/sis-portrayal/src/test/java/org/apache/sis/style/se1/LineSymbolizerTest.java
+++ b/core/sis-portrayal/src/test/java/org/apache/sis/style/se1/LineSymbolizerTest.java
@@@ -1,0 -1,72 +1,72 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import java.awt.Color;
+ import org.junit.Test;
+ import static org.junit.Assert.*;
+
+ // Branch-dependent imports
-import org.opengis.feature.Feature;
++import org.apache.sis.feature.AbstractFeature;
+
+
+ /**
+ * Tests for {@link LineSymbolizer}.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 1.5
+ * @since 1.5
+ */
+ public final class LineSymbolizerTest extends StyleTestCase {
+ /**
+ * Creates a new test case.
+ */
+ public LineSymbolizerTest() {
+ }
+
+ /**
+ * Test of {@code Stroke} property.
+ */
+ @Test
+ public void testStroke() {
+ final var cdt = factory.createLineSymbolizer();
+
+ // Check default
- Stroke<Feature> value = cdt.getStroke();
++ Stroke<AbstractFeature> value = cdt.getStroke();
+ assertLiteralEquals(Color.BLACK, value.getColor());
+
+ // Check get/set
+ value = factory.createStroke();
+ cdt.setStroke(value);
+ assertEquals(value, cdt.getStroke());
+ }
+
+ /**
+ * Test of {@code PerpendicularOffset} property.
+ */
+ @Test
+ public void testPerpendicularOffset() {
+ final var cdt = factory.createLineSymbolizer();
+
+ // Check default
+ assertLiteralEquals(0.0, cdt.getPerpendicularOffset());
+
+ // Check get/set
+ cdt.setPerpendicularOffset(literal(20));
+ assertLiteralEquals(20, cdt.getPerpendicularOffset());
+ }
+ }
diff --cc core/sis-portrayal/src/test/java/org/apache/sis/style/se1/PointPlacementTest.java
index 0000000000,3b4a8729ab..7ce1a72408
mode 000000,100644..100644
--- a/core/sis-portrayal/src/test/java/org/apache/sis/style/se1/PointPlacementTest.java
+++ b/core/sis-portrayal/src/test/java/org/apache/sis/style/se1/PointPlacementTest.java
@@@ -1,0 -1,90 +1,90 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import org.junit.Test;
+ import static org.junit.Assert.*;
+
+ // Branch-dependent imports
-import org.opengis.feature.Feature;
++import org.apache.sis.feature.AbstractFeature;
+
+
+ /**
+ * Tests for {@link PointPlacement}.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 1.5
+ * @since 1.5
+ */
+ public final class PointPlacementTest extends StyleTestCase {
+ /**
+ * Creates a new test case.
+ */
+ public PointPlacementTest() {
+ }
+
+ /**
+ * Test of {@code AnchorPoint} property.
+ */
+ @Test
+ public void testAnchorPoint() {
+ final var cdt = factory.createPointPlacement();
+
+ // Check default
- AnchorPoint<Feature> value = cdt.getAnchorPoint();
++ AnchorPoint<AbstractFeature> value = cdt.getAnchorPoint();
+ assertLiteralEquals(0.5, value.getAnchorPointX());
+ assertLiteralEquals(0.5, value.getAnchorPointY());
+
+ // Check get/set
+ value = factory.createAnchorPoint(3, 1);
+ cdt.setAnchorPoint(value);
+ assertEquals(value, cdt.getAnchorPoint());
+ }
+
+ /**
+ * Test of {@code Displacement} property.
+ */
+ @Test
+ public void testDisplacement() {
+ final var cdt = factory.createPointPlacement();
+
+ // Check default
- Displacement<Feature> value = cdt.getDisplacement();
++ Displacement<AbstractFeature> value = cdt.getDisplacement();
+ assertLiteralEquals(0.0, value.getDisplacementX());
+ assertLiteralEquals(0.0, value.getDisplacementY());
+
+ // Check get/set
+ value = factory.createDisplacement(1, 2);
+ cdt.setDisplacement(value);
+ assertEquals(value, cdt.getDisplacement());
+ }
+
+ /**
+ * Test of {@code Rotation} property.
+ */
+ @Test
+ public void testRotation() {
+ final var cdt = factory.createPointPlacement();
+
+ // Check default
+ assertLiteralEquals(0.0, cdt.getRotation());
+
+ // Check get/set
+ cdt.setRotation(literal(180));
+ assertLiteralEquals(180, cdt.getRotation());
+ }
+ }
diff --cc core/sis-portrayal/src/test/java/org/apache/sis/style/se1/PointSymbolizerTest.java
index 0000000000,44598828eb..d72d63ba2a
mode 000000,100644..100644
--- a/core/sis-portrayal/src/test/java/org/apache/sis/style/se1/PointSymbolizerTest.java
+++ b/core/sis-portrayal/src/test/java/org/apache/sis/style/se1/PointSymbolizerTest.java
@@@ -1,0 -1,57 +1,57 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import org.junit.Test;
+ import static org.junit.Assert.*;
+
+ // Branch-dependent imports
-import org.opengis.feature.Feature;
++import org.apache.sis.feature.AbstractFeature;
+
+
+ /**
+ * Tests for {@link PointSymbolizer}.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 1.5
+ * @since 1.5
+ */
+ public final class PointSymbolizerTest extends StyleTestCase {
+ /**
+ * Creates a new test case.
+ */
+ public PointSymbolizerTest() {
+ }
+
+ /**
+ * Test of {@code Graphic} property.
+ */
+ @Test
+ public void testGraphic() {
+ final var cdt = factory.createPointSymbolizer();
+
+ // Check default
- Graphic<Feature> value = cdt.getGraphic();
++ Graphic<AbstractFeature> value = cdt.getGraphic();
+ assertLiteralEquals(1.0, value.getOpacity());
+
+ // Check get/set
+ value = factory.createGraphic();
+ value.setOpacity(literal(0.8));
+ cdt.setGraphic(value);
+ assertEquals(value, cdt.getGraphic());
+ }
+ }
diff --cc core/sis-portrayal/src/test/java/org/apache/sis/style/se1/PolygonSymbolizerTest.java
index 0000000000,08fc95a730..073f449607
mode 000000,100644..100644
--- a/core/sis-portrayal/src/test/java/org/apache/sis/style/se1/PolygonSymbolizerTest.java
+++ b/core/sis-portrayal/src/test/java/org/apache/sis/style/se1/PolygonSymbolizerTest.java
@@@ -1,0 -1,109 +1,109 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import java.awt.Color;
+ import org.junit.Test;
+ import static org.junit.Assert.*;
+
+ // Branch-dependent imports
-import org.opengis.feature.Feature;
++import org.apache.sis.feature.AbstractFeature;
+
+
+ /**
+ * Tests for {@link PolygonSymbolizer}.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 1.5
+ * @since 1.5
+ */
+ public final class PolygonSymbolizerTest extends StyleTestCase {
+ /**
+ * Creates a new test case.
+ */
+ public PolygonSymbolizerTest() {
+ }
+
+ /**
+ * Test of {@code Stroke} property.
+ */
+ @Test
+ public void testStroke() {
+ final var cdt = factory.createPolygonSymbolizer();
+
+ // Check default
+ var value = cdt.getStroke().orElseThrow();
+ assertLiteralEquals("bevel", value.getLineJoin());
+ assertLiteralEquals("square", value.getLineCap());
+
+ // Check get/set
+ value = factory.createStroke();
+ cdt.setStroke(value);
+ assertOptionalEquals(value, cdt.getStroke());
+ }
+
+ /**
+ * Test of {@code Fill} property.
+ */
+ @Test
+ public void testFill() {
+ final var cdt = factory.createPolygonSymbolizer();
+
+ // Check default
- Fill<Feature> value = cdt.getFill().orElseThrow();
++ Fill<AbstractFeature> value = cdt.getFill().orElseThrow();
+ assertLiteralEquals(Color.GRAY, value.getColor());
+
+ // Check get/set
+ value = factory.createFill();
+ value.setColorAndOpacity(ANY_COLOR);
+ cdt.setFill(value);
+ assertOptionalEquals(value, cdt.getFill());
+ }
+
+ /**
+ * Test of {@code Displacement} property.
+ */
+ @Test
+ public void testDisplacement() {
+ final var cdt = factory.createPolygonSymbolizer();
+
+ // Check default
- Displacement<Feature> value = cdt.getDisplacement();
++ Displacement<AbstractFeature> value = cdt.getDisplacement();
+ assertLiteralEquals(0.0, value.getDisplacementX());
+ assertLiteralEquals(0.0, value.getDisplacementY());
+
+ // Check get/set
+ value = factory.createDisplacement(4, 1);
+ cdt.setDisplacement(value);
+ assertEquals(value, cdt.getDisplacement());
+ }
+
+ /**
+ * Test of {@code PerpendicularOffset} property.
+ */
+ @Test
+ public void testPerpendicularOffset() {
+ final var cdt = factory.createPolygonSymbolizer();
+
+ // Check default
+ assertLiteralEquals(0.0, cdt.getPerpendicularOffset());
+
+ // Check get/set
+ cdt.setPerpendicularOffset(literal(10));
+ assertLiteralEquals(10, cdt.getPerpendicularOffset());
+ }
+ }
diff --cc core/sis-portrayal/src/test/java/org/apache/sis/style/se1/RuleTest.java
index 0000000000,c4ccfc7da2..cfd0c69de3
mode 000000,100644..100644
--- a/core/sis-portrayal/src/test/java/org/apache/sis/style/se1/RuleTest.java
+++ b/core/sis-portrayal/src/test/java/org/apache/sis/style/se1/RuleTest.java
@@@ -1,0 -1,184 +1,184 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import java.util.List;
+ import org.junit.Test;
+ import org.apache.sis.metadata.iso.citation.DefaultOnlineResource;
+
+ import static org.junit.Assert.*;
+
+ // Branch-dependent imports
-import org.opengis.filter.Filter;
++import org.apache.sis.filter.Filter;
+
+
+ /**
+ * Tests for {@link Rule}.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 1.5
+ * @since 1.5
+ */
+ public final class RuleTest extends StyleTestCase {
+ /**
+ * Creates a new test case.
+ */
+ public RuleTest() {
+ }
+
+ /**
+ * Test of {@code Name} property.
+ */
+ @Test
+ public void testGetName() {
+ final var cdt = factory.createRule();
+
+ // Check defaults
+ assertEmpty(cdt.getName());
+
+ // Check get/set
+ String value = "A random name";
+ cdt.setName(value);
+ assertOptionalEquals(value, cdt.getName());
+ }
+
+ /**
+ * Test of {@code Description} property.
+ */
+ @Test
+ public void testDescription() {
+ final var cdt = factory.createRule();
+
+ // Check defaults
+ assertEmpty(cdt.getDescription());
+
+ // Check get/set
+ var desc = anyDescription();
+ cdt.setDescription(desc);
+ assertOptionalEquals(desc, cdt.getDescription());
+ }
+
+ /**
+ * Test of {@code Legend} property.
+ */
+ @Test
+ public void testLegend() {
+ final var cdt = factory.createRule();
+
+ // Check defaults
+ assertEmpty(cdt.getLegend());
+
+ // Check get/set
+ var value = factory.createLegendGraphic();
+ cdt.setLegend(value);
+ assertOptionalEquals(value, cdt.getLegend());
+ }
+
+ /**
+ * Test of {@code Filter} property.
+ */
+ @Test
+ public void testFilter() {
+ final var cdt = factory.createRule();
+
+ // Check defaults
+ assertEquals(Filter.include(), cdt.getFilter());
+
+ // Check get/set
+ var value = factory.filterFactory.equal(literal("A"), literal("B"));
+ cdt.setFilter(value);
+ assertEquals(value, cdt.getFilter());
+ }
+
+ /**
+ * Test of {@code ElseFilter} property.
+ */
+ @Test
+ public void testIsElseFilter() {
+ final var cdt = factory.createRule();
+
+ // Check defaults
+ assertFalse(cdt.isElseFilter());
+
+ // Check get/set
+ cdt.setElseFilter(true);
+ assertTrue(cdt.isElseFilter());
+ }
+
+ /**
+ * Test of {@code MinScaleDenominator} property.
+ */
+ @Test
+ public void testMinScaleDenominator() {
+ final var cdt = factory.createRule();
+
+ // Check defaults
+ assertEquals(0.0, cdt.getMinScaleDenominator(), 0.0);
+
+ // Check get/set
+ cdt.setMinScaleDenominator(10.0);
+ assertEquals(10.0, cdt.getMinScaleDenominator(), 0.0);
+ }
+
+ /**
+ * Test of {@code MaxScaleDenominator} property.
+ */
+ @Test
+ public void testGetMaxScaleDenominator() {
+ final var cdt = factory.createRule();
+
+ // Check defaults
+ assertEquals(Double.POSITIVE_INFINITY, cdt.getMaxScaleDenominator(), 0.0);
+
+ // Check get/set
+ cdt.setMaxScaleDenominator(10.0);
+ assertEquals(10.0, cdt.getMaxScaleDenominator(), 0.0);
+ }
+
+ /**
+ * Test of {@code Symbolizer} property.
+ */
+ @Test
+ public void testSymbolizers() {
+ final var cdt = factory.createRule();
+
+ // Check defaults
+ assertTrue(cdt.symbolizers().isEmpty());
+
+ // Check get/set
+ var value = factory.createLineSymbolizer();
+ cdt.symbolizers().add(value);
+ assertEquals(List.of(value), cdt.symbolizers());
+ }
+
+ /**
+ * Test of {@code OnlineSource} property.
+ */
+ @Test
+ public void testGetOnlineSource() {
+ final var cdt = factory.createRule();
+
+ // Check defaults
+ assertEmpty(cdt.getOnlineSource());
+
+ // Check get/set
+ var r = new DefaultOnlineResource();
+ r.setProtocol("HTTP");
+ cdt.setOnlineSource(r);
+ assertOptionalEquals(r, cdt.getOnlineSource());
+ }
+ }
diff --cc core/sis-portrayal/src/test/java/org/apache/sis/style/se1/StyleTestCase.java
index 0000000000,bd2fe221ca..5737ee6722
mode 000000,100644..100644
--- a/core/sis-portrayal/src/test/java/org/apache/sis/style/se1/StyleTestCase.java
+++ b/core/sis-portrayal/src/test/java/org/apache/sis/style/se1/StyleTestCase.java
@@@ -1,0 -1,120 +1,120 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import java.awt.Color;
+ import java.util.Optional;
+ import org.apache.sis.util.SimpleInternationalString;
+ import org.apache.sis.test.TestCase;
+
+ import static org.junit.Assert.*;
+ import static org.opengis.test.Assert.assertInstanceOf;
+
+ // Branch-dependent imports
-import org.opengis.feature.Feature;
-import org.opengis.filter.Literal;
-import org.opengis.filter.Expression;
++import org.apache.sis.filter.Expression;
++import org.apache.sis.feature.AbstractFeature;
++import org.apache.sis.internal.geoapi.filter.Literal;
+
+
+ /**
+ * Base class of all tests of style elements.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 1.5
+ * @since 1.5
+ */
+ abstract class StyleTestCase extends TestCase {
+ /**
+ * The factory to use for creating style elements.
+ */
- final StyleFactory<Feature> factory;
++ final StyleFactory<AbstractFeature> factory;
+
+ /**
+ * Creates a new test case.
+ */
+ StyleTestCase() {
+ factory = FeatureTypeStyle.FACTORY;
+ }
+
+ /**
+ * Returns a literal for the given value.
+ *
+ * @param <E> type of value.
+ * @param value the value for which to return a literal.
+ * @return literal for the given value.
+ */
- final <E> Literal<Feature,E> literal(final E value) {
++ final <E> Expression<AbstractFeature,E> literal(final E value) {
+ return factory.filterFactory.literal(value);
+ }
+
+ /**
+ * Creates a dummy description with arbitrary title and abstract.
+ */
- final Description<Feature> anyDescription() {
++ final Description<AbstractFeature> anyDescription() {
+ final var value = factory.createDescription();
+ value.setTitle(new SimpleInternationalString("A random title"));
+ value.setAbstract(new SimpleInternationalString("A random abstract"));
+ return value;
+ }
+
+ /**
+ * Returns an expression with a random color.
+ * The color is {@link #ANY_COLOR}.
+ */
- final Expression<Feature,Color> anyColor() {
++ final Expression<AbstractFeature,Color> anyColor() {
+ return literal(ANY_COLOR);
+ }
+
+ /**
+ * The color used by {@link #anyColor()}.
+ * Should be different than all default colors.
+ * Provided for verification with {@link #assertLiteralEquals(Object, Expression)}.
+ */
+ static final Color ANY_COLOR = Color.YELLOW;
+
+ /**
+ * Asserts that the given optional is empty.
+ *
+ * @param opt the optional to check.
+ */
+ static void assertEmpty(final Optional<?> opt) {
+ assertTrue(opt.isEmpty());
+ }
+
+ /**
+ * Asserts that the value of the given optional is equals to the expected value.
+ *
+ * @param <E> type of object to compare.
+ * @param expected the expected value.
+ * @param opt the actual value in an optional.
+ */
+ static <E> void assertOptionalEquals(final E expected, final Optional<E> opt) {
+ assertEquals(expected, opt.orElseThrow());
+ }
+
+ /**
+ * Asserts that the given expression is a literal with the given value.
+ *
+ * @param <E> the expression value type.
+ * @param expected the expected expression value.
+ * @param actual the expression from which to test the value.
+ */
+ static <E> void assertLiteralEquals(final E expected, final Expression<?, ? extends E> actual) {
+ assertInstanceOf("expression", Literal.class, actual);
+ assertEquals(expected, actual.apply(null));
+ }
+ }
diff --cc core/sis-portrayal/src/test/java/org/apache/sis/style/se1/SymbolizerTest.java
index 0000000000,d9889903b0..39012eb3b6
mode 000000,100644..100644
--- a/core/sis-portrayal/src/test/java/org/apache/sis/style/se1/SymbolizerTest.java
+++ b/core/sis-portrayal/src/test/java/org/apache/sis/style/se1/SymbolizerTest.java
@@@ -1,0 -1,103 +1,103 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import org.apache.sis.measure.Units;
+ import org.junit.Test;
+ import static org.junit.Assert.*;
+ import static org.opengis.test.Assert.assertInstanceOf;
+
+ // Branch-dependent imports
-import org.opengis.filter.ValueReference;
++import org.apache.sis.internal.geoapi.filter.ValueReference;
+
+
+ /**
+ * Tests for {@link Symbolizer}.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 1.5
+ * @since 1.5
+ */
+ public final class SymbolizerTest extends StyleTestCase {
+ /**
+ * Creates a new test case.
+ */
+ public SymbolizerTest() {
+ }
+
+ /**
+ * Test of {@code UnitOfMeasure} property.
+ */
+ @Test
+ public void testUnitOfMeasure() {
+ final var cdt = factory.createLineSymbolizer();
+
+ // Check default
+ assertEquals(Units.PIXEL, cdt.getUnitOfMeasure());
+
+ // Check get/set
+ cdt.setUnitOfMeasure(Units.INCH);
+ assertEquals(Units.INCH, cdt.getUnitOfMeasure());
+ }
+
+ /**
+ * Test of {@code Geometry} property.
+ */
+ @Test
+ public void testGeometry() {
+ final var cdt = factory.createLineSymbolizer();
+
+ // Check default
+ assertInstanceOf("geometry", ValueReference.class, cdt.getGeometry());
+
+ // Check get/set
+ cdt.setGeometry(literal(8));
+ assertLiteralEquals(8, cdt.getGeometry());
+ }
+
+ /**
+ * Test of {@code Name} property.
+ */
+ @Test
+ public void testName() {
+ final var cdt = factory.createLineSymbolizer();
+
+ // Check defaults
+ assertEmpty(cdt.getName());
+
+ // Check get/set
+ String value = "A random name";
+ cdt.setName(value);
+ assertOptionalEquals(value, cdt.getName());
+ }
+
+ /**
+ * Test of {@code getDescription} property.
+ */
+ @Test
+ public void testDescription() {
+ final var cdt = factory.createLineSymbolizer();
+
+ // Check defaults
+ assertEmpty(cdt.getDescription());
+
+ // Check get/set
+ var desc = anyDescription();
+ cdt.setDescription(desc);
+ assertOptionalEquals(desc, cdt.getDescription());
+ }
+ }
diff --cc core/sis-portrayal/src/test/java/org/apache/sis/style/se1/TextSymbolizerTest.java
index 0000000000,5954377dcd..1e23bd6342
mode 000000,100644..100644
--- a/core/sis-portrayal/src/test/java/org/apache/sis/style/se1/TextSymbolizerTest.java
+++ b/core/sis-portrayal/src/test/java/org/apache/sis/style/se1/TextSymbolizerTest.java
@@@ -1,0 -1,122 +1,122 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package org.apache.sis.style.se1;
+
+ import org.junit.Test;
+ import static org.junit.Assert.*;
+
+ // Branch-dependent imports
-import org.opengis.feature.Feature;
++import org.apache.sis.feature.AbstractFeature;
+
+
+ /**
+ * Tests for {@link TextSymbolizer}.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 1.5
+ * @since 1.5
+ */
+ public final class TextSymbolizerTest extends StyleTestCase {
+ /**
+ * Creates a new test case.
+ */
+ public TextSymbolizerTest() {
+ }
+
+ /**
+ * Test of {@code Label} property.
+ */
+ @Test
+ public void testLabel() {
+ final var cdt = factory.createTextSymbolizer();
+
+ // Check default
+ assertNull(cdt.getLabel());
+
+ // Check get/set
+ var value = literal("A random label");
+ cdt.setLabel(value);
+ assertEquals(value, cdt.getLabel());
+ }
+
+ /**
+ * Test of {@code Font} property.
+ */
+ @Test
+ public void testFont() {
+ final var cdt = factory.createTextSymbolizer();
+
+ // Check default
+ var value = cdt.getFont();
+ assertLiteralEquals("normal", value.getStyle());
+
+ // Check get/set
+ value = factory.createFont();
+ value.setStyle(literal("italic"));
+ cdt.setFont(value);
+ assertEquals(value, cdt.getFont());
+ }
+
+ /**
+ * Test of {@code LabelPlacement} property.
+ */
+ @Test
+ public void testLabelPlacement() {
+ final var cdt = factory.createTextSymbolizer();
+
+ // Check default
- LabelPlacement<Feature> value = cdt.getLabelPlacement();
++ LabelPlacement<AbstractFeature> value = cdt.getLabelPlacement();
+ assertNotNull(value);
+
+ // Check get/set
+ value = factory.createPointPlacement();
+ cdt.setLabelPlacement(value);
+ assertEquals(value, cdt.getLabelPlacement());
+ }
+
+ /**
+ * Test of {@code Halo} property.
+ */
+ @Test
+ public void testHalo() {
+ final var cdt = factory.createTextSymbolizer();
+
+ // Check default
+ assertEmpty(cdt.getHalo());
+
+ // Check get/set
+ var value = factory.createHalo();
+ cdt.setHalo(value);
+ assertOptionalEquals(value, cdt.getHalo());
+ }
+
+ /**
+ * Test of {@code Fill} property.
+ */
+ @Test
+ public void testFill() {
+ final var cdt = factory.createTextSymbolizer();
+
+ // Check default
+ assertNotNull(cdt.getFill());
+
+ // Check get/set
+ var value = factory.createFill();
+ cdt.setFill(value);
+ assertEquals(value, cdt.getFill());
+ }
+ }