This is an automated email from the ASF dual-hosted git repository.
jsorel pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
new fb27a92 Portrayal : add portrayal rule and query tests, implements
events on MapLayers components
fb27a92 is described below
commit fb27a9201e97441515d1e6ad4f8bbec814cc52de
Author: jsorel <[email protected]>
AuthorDate: Mon Jan 18 14:59:39 2021 +0100
Portrayal : add portrayal rule and query tests, implements events on
MapLayers components
---
.../apache/sis/internal/map/ListChangeEvent.java | 98 +++++++++
.../org/apache/sis/internal/map/NotifiedList.java | 72 +++++++
.../org/apache/sis/internal/map/SEPortrayer.java | 4 +-
.../java/org/apache/sis/portrayal/MapLayers.java | 44 +++-
.../java/org/apache/sis/portrayal/Observable.java | 30 ++-
.../java/org/apache/sis/internal/map/MockRule.java | 2 +-
.../apache/sis/internal/map/SEPortrayerTest.java | 234 ++++++++++++++++++++-
.../org/apache/sis/portrayal/MapLayersTest.java | 99 +++++++++
.../apache/sis/test/suite/PortrayalTestSuite.java | 1 +
9 files changed, 570 insertions(+), 14 deletions(-)
diff --git
a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/ListChangeEvent.java
b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/ListChangeEvent.java
new file mode 100644
index 0000000..311c830
--- /dev/null
+++
b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/ListChangeEvent.java
@@ -0,0 +1,98 @@
+/*
+ * 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.internal.map;
+
+import java.beans.PropertyChangeEvent;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.apache.sis.measure.NumberRange;
+
+/**
+ * Event generated by modified list properties.
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public final class ListChangeEvent<T> extends PropertyChangeEvent {
+
+ public enum Type {
+ ADDED,
+ REMOVED,
+ CHANGED
+ }
+
+ private final NumberRange<Integer> range;
+ private final Type type;
+ private final List<T> items;
+
+ private ListChangeEvent(final Object source, String propertyName, List<T>
originalList, final List<? extends T> items, final NumberRange<Integer> range,
final Type type){
+ super(source, propertyName, originalList, originalList);
+ this.range = range;
+ this.type = type;
+ this.items = (items != null) ? Collections.unmodifiableList(new
ArrayList<>(items)) : null;
+ }
+
+ /**
+ * Returns the range index of the affected items.
+ * If the event type is Added, the range correspond to the index range
after insertion.
+ * If the event type is Removed, the range correspond to the index before
deletion.
+ * @return NumberRange added or removed range.
+ */
+ public NumberRange<Integer> getRange(){
+ return range;
+ }
+
+ /**
+ * Returns event type.
+ */
+ public Type getType(){
+ return type;
+ }
+
+ /**
+ * Returns the affected items of this event.
+ * This property is set if event is of type added or removed.
+ *
+ * @return List
+ */
+ public List<T> getItems(){
+ return items;
+ }
+
+ public static <T> ListChangeEvent<T> added(Object source, String
propertyName, List<T> originalList, T newItem, final int index) {
+ return added(source, propertyName, originalList,
Arrays.asList(newItem),
+ NumberRange.create(index, true, index, true));
+ }
+
+ public static <T> ListChangeEvent<T> added(Object source, String
propertyName, List<T> originalList, List<T> newItems, final
NumberRange<Integer> range) {
+ return new ListChangeEvent<>(source, propertyName, originalList,
newItems, range, Type.ADDED);
+ }
+
+ public static <T> ListChangeEvent<T> removed(Object source, String
propertyName, List<T> originalList, T newItem, final int index) {
+ return removed(source, propertyName, originalList,
Arrays.asList(newItem),
+ NumberRange.create(index, true, index, true));
+ }
+
+ public static <T> ListChangeEvent<T> removed(Object source, String
propertyName, List<T> originalList, List<T> oldItems, final
NumberRange<Integer> range) {
+ return new ListChangeEvent<>(source, propertyName, originalList,
oldItems, range, Type.REMOVED);
+ }
+
+ public static <T> ListChangeEvent<T> changed(Object source, String
propertyName, List<T> originalList) {
+ return new ListChangeEvent<>(source, propertyName, originalList, null,
null, Type.CHANGED);
+ }
+}
diff --git
a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/NotifiedList.java
b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/NotifiedList.java
new file mode 100644
index 0000000..20fe558
--- /dev/null
+++
b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/NotifiedList.java
@@ -0,0 +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.internal.map;
+
+import java.util.AbstractList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import org.apache.sis.measure.NumberRange;
+
+/**
+ * Decorate a CopyOnWriteArrayList and notify changes when elements are added
or removed.
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public abstract class NotifiedList<T> extends AbstractList<T> {
+
+ private final CopyOnWriteArrayList<T> parent = new
CopyOnWriteArrayList<>();
+
+ @Override
+ public T get(int index) {
+ return parent.get(index);
+ }
+
+ @Override
+ public int size() {
+ return parent.size();
+ }
+
+ @Override
+ public T set(int index, T element) {
+ final T old = parent.set(index, element);
+ notifyReplace(old, element, index);
+ return old;
+ }
+
+ @Override
+ public void add(int index, T element) {
+ parent.add(index, element);
+ notifyAdd(element, index);
+ }
+
+ @Override
+ public T remove(int index) {
+ final T old = parent.remove(index);
+ notifyRemove(old, index);
+ return old;
+ }
+
+ protected abstract void notifyAdd(final T item, int index);
+
+ protected abstract void notifyAdd(final List<T> items,
NumberRange<Integer> range);
+
+ protected abstract void notifyRemove(final T item, int index);
+
+ protected abstract void notifyRemove(final List<T> items,
NumberRange<Integer> range);
+
+ protected abstract void notifyReplace(final T olditem, final T newitem,
int index);
+}
diff --git
a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/SEPortrayer.java
b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/SEPortrayer.java
index 29c951f..19283c0 100644
---
a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/SEPortrayer.java
+++
b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/SEPortrayer.java
@@ -98,8 +98,8 @@ import org.opengis.util.GenericName;
* </ul>
*
* @author Johann Sorel (Geomatys)
- * @version 2.0
- * @since 2.0
+ * @version 1.1
+ * @since 1.1
* @module
*/
public final class SEPortrayer {
diff --git
a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/MapLayers.java
b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/MapLayers.java
index e5f8429..d58303c 100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/MapLayers.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/MapLayers.java
@@ -16,13 +16,15 @@
*/
package org.apache.sis.portrayal;
-import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
-import org.opengis.geometry.Envelope;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.geometry.ImmutableEnvelope;
+import org.apache.sis.internal.map.ListChangeEvent;
+import org.apache.sis.internal.map.NotifiedList;
+import org.apache.sis.measure.NumberRange;
import org.apache.sis.storage.DataSet;
+import org.opengis.geometry.Envelope;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
/**
@@ -55,11 +57,44 @@ public class MapLayers extends MapItem {
public static final String AREA_OF_INTEREST_PROPERTY = "areaOfInterest";
/**
+ * The {@value} property name, used for notifications about changes in map
item components.
+ *
+ * @see #getComponents()
+ * @see #addPropertyChangeListener(String, PropertyChangeListener)
+ */
+ public static final String COMPONENTS_PROPERTY = "components";
+
+ /**
* The components in this group, or an empty list if none.
*
* @todo Should be an observable list with event sent when an element is
added/removed/modified.
*/
- private final List<MapItem> components;
+ private final List<MapItem> components = new NotifiedList<MapItem>() {
+ @Override
+ protected void notifyAdd(MapItem item, int index) {
+ firePropertyChange(ListChangeEvent.added(MapLayers.this,
COMPONENTS_PROPERTY, components, item, index));
+ }
+
+ @Override
+ protected void notifyAdd(List<MapItem> items, NumberRange<Integer>
range) {
+ firePropertyChange(ListChangeEvent.added(MapLayers.this,
COMPONENTS_PROPERTY, components, items, range));
+ }
+
+ @Override
+ protected void notifyRemove(MapItem item, int index) {
+ firePropertyChange(ListChangeEvent.removed(MapLayers.this,
COMPONENTS_PROPERTY, components, item, index));
+ }
+
+ @Override
+ protected void notifyRemove(List<MapItem> items, NumberRange<Integer>
range) {
+ firePropertyChange(ListChangeEvent.removed(MapLayers.this,
COMPONENTS_PROPERTY, components, items, range));
+ }
+
+ @Override
+ protected void notifyReplace(MapItem olditem, MapItem newitem, int
index) {
+ firePropertyChange(ListChangeEvent.changed(MapLayers.this,
COMPONENTS_PROPERTY, components));
+ }
+ };
/**
* The area of interest, or {@code null} is unspecified.
@@ -70,7 +105,6 @@ public class MapLayers extends MapItem {
* Creates an initially empty group of layers.
*/
public MapLayers() {
- components = new ArrayList<>();
}
/**
diff --git
a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/Observable.java
b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/Observable.java
index 67ca9c7..e68e4af 100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/Observable.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/portrayal/Observable.java
@@ -16,12 +16,12 @@
*/
package org.apache.sis.portrayal;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.Arrays;
-import java.util.ConcurrentModificationException;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
+import java.util.Arrays;
+import java.util.ConcurrentModificationException;
+import java.util.HashMap;
+import java.util.Map;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.ArraysExt;
@@ -150,6 +150,28 @@ abstract class Observable {
}
/**
+ * Notifies all registered listeners that a property of the given name
changed its value.
+ * It is caller responsibility to verify that the event source and
property name are valid.
+ *
+ * @param event the event to forward, can not be null.
+ *
+ * @see PropertyChangeEvent
+ * @see PropertyChangeListener
+ */
+ protected void firePropertyChange(final PropertyChangeEvent event) {
+ ArgumentChecks.ensureNonNull("event", event);
+
+ if (listeners != null) {
+ final PropertyChangeListener[] list =
listeners.get(event.getPropertyName());
+ if (list != null) {
+ for (final PropertyChangeListener listener : list) {
+ listener.propertyChange(event);
+ }
+ }
+ }
+ }
+
+ /**
* Returns {@code true} if the given property has at least one listener.
*
* @param propertyName name of the property.
diff --git
a/core/sis-portrayal/src/test/java/org/apache/sis/internal/map/MockRule.java
b/core/sis-portrayal/src/test/java/org/apache/sis/internal/map/MockRule.java
index d8f38c0..f3eecee 100644
--- a/core/sis-portrayal/src/test/java/org/apache/sis/internal/map/MockRule.java
+++ b/core/sis-portrayal/src/test/java/org/apache/sis/internal/map/MockRule.java
@@ -65,7 +65,7 @@ public final class MockRule implements Rule {
return legend;
}
- public void setLegeng(GraphicLegend legend) {
+ public void setLegend(GraphicLegend legend) {
this.legend = legend;
}
diff --git
a/core/sis-portrayal/src/test/java/org/apache/sis/internal/map/SEPortrayerTest.java
b/core/sis-portrayal/src/test/java/org/apache/sis/internal/map/SEPortrayerTest.java
index 02ae357..b7fdb2e 100644
---
a/core/sis-portrayal/src/test/java/org/apache/sis/internal/map/SEPortrayerTest.java
+++
b/core/sis-portrayal/src/test/java/org/apache/sis/internal/map/SEPortrayerTest.java
@@ -30,8 +30,12 @@ import org.apache.sis.coverage.grid.GridGeometry;
import org.apache.sis.coverage.grid.GridOrientation;
import org.apache.sis.feature.builder.AttributeRole;
import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.filter.DefaultFilterFactory;
+import org.apache.sis.geometry.GeneralEnvelope;
import org.apache.sis.internal.feature.AttributeConvention;
import org.apache.sis.internal.storage.MemoryFeatureSet;
+import org.apache.sis.internal.storage.query.SimpleQuery;
+import org.apache.sis.internal.system.DefaultFactories;
import org.apache.sis.portrayal.MapItem;
import org.apache.sis.portrayal.MapLayer;
import org.apache.sis.portrayal.MapLayers;
@@ -48,6 +52,11 @@ import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.Polygon;
import org.opengis.feature.Feature;
import org.opengis.feature.FeatureType;
+import org.opengis.filter.Filter;
+import org.opengis.filter.FilterFactory;
+import org.opengis.filter.FilterFactory2;
+import org.opengis.filter.identity.Identifier;
+import org.opengis.geometry.Envelope;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.style.SemanticType;
import org.opengis.style.Symbolizer;
@@ -58,11 +67,18 @@ import org.opengis.style.Symbolizer;
*/
public class SEPortrayerTest extends TestCase {
+ private final FilterFactory2 filterFactory;
private final FeatureSet fishes;
private final FeatureSet boats;
public SEPortrayerTest() {
+ FilterFactory filterFactory =
DefaultFactories.forClass(FilterFactory.class);
+ if (!(filterFactory instanceof FilterFactory2)) {
+ filterFactory = new DefaultFilterFactory();
+ }
+ this.filterFactory = (FilterFactory2) filterFactory;
+
final GeometryFactory gf = new GeometryFactory();
final CoordinateReferenceSystem crs =
CommonCRS.WGS84.normalizedGeographic();
@@ -78,7 +94,7 @@ public class SEPortrayerTest extends TestCase {
fish1.setPropertyValue("id", "1");
fish1.setPropertyValue("geom", point1);
- final Point point2 = gf.createPoint(new Coordinate(1, 1));
+ final Point point2 = gf.createPoint(new Coordinate(10, 20));
point2.setUserData(crs);
final Feature fish2 = fishType.newInstance();
fish2.setPropertyValue("id", "2");
@@ -109,7 +125,11 @@ public class SEPortrayerTest extends TestCase {
}
private Set<Entry<String, Symbolizer>> present(MapItem item) {
- final GridGeometry grid = new GridGeometry(new GridExtent(360, 180),
CRS.getDomainOfValidity(CommonCRS.WGS84.normalizedGeographic()),
GridOrientation.REFLECTION_Y);
+ return present(item,
CRS.getDomainOfValidity(CommonCRS.WGS84.normalizedGeographic()));
+ }
+
+ private Set<Entry<String, Symbolizer>> present(MapItem item, Envelope env)
{
+ final GridGeometry grid = new GridGeometry(new GridExtent(360, 180),
env, GridOrientation.REFLECTION_Y);
final SEPortrayer portrayer = new SEPortrayer();
final Stream<Presentation> stream = portrayer.present(grid, item);
final List<Presentation> presentations =
stream.collect(Collectors.toList());
@@ -164,6 +184,81 @@ public class SEPortrayerTest extends TestCase {
}
/**
+ * Test portrayer includes the bounding box of the canvas while querying
features.
+ * Only fish feature with identifier "2" matches in this test.
+ */
+ @Test
+ public void testCanvasBboxfilter() {
+
+ final GeneralEnvelope env = new
GeneralEnvelope(CommonCRS.WGS84.normalizedGeographic());
+ env.setRange(0, 9, 11);
+ env.setRange(1, 19, 21);
+
+ final MockStyle style = new MockStyle();
+ final MockFeatureTypeStyle fts = new MockFeatureTypeStyle();
+ final MockRule rule = new MockRule();
+ final MockLineSymbolizer symbolizer = new MockLineSymbolizer();
+ style.featureTypeStyles().add(fts);
+ fts.rules().add(rule);
+ rule.symbolizers().add(symbolizer);
+
+
+ final MapLayer fishLayer = new MapLayer();
+ fishLayer.setData(fishes);
+ fishLayer.setStyle(style);
+ final MapLayer boatLayer = new MapLayer();
+ boatLayer.setData(boats);
+ boatLayer.setStyle(style);
+ final MapLayers layers = new MapLayers();
+ layers.getComponents().add(fishLayer);
+ layers.getComponents().add(boatLayer);
+
+ final Set<Entry<String, Symbolizer>> presentations = present(layers,
env);
+ assertEquals(1, presentations.size());
+ assertTrue(presentations.contains(new AbstractMap.SimpleEntry<>("2",
symbolizer)));
+ }
+
+ /**
+ * Test portrayer uses the user defined query when portraying.
+ * Only fish feature with identifier "1" and boat feature with identifier
"20" matches in this test.
+ */
+ @Test
+ public void testUserQuery() {
+
+ final MockStyle style = new MockStyle();
+ final MockFeatureTypeStyle fts = new MockFeatureTypeStyle();
+ final MockRule rule = new MockRule();
+ final MockLineSymbolizer symbolizer = new MockLineSymbolizer();
+ style.featureTypeStyles().add(fts);
+ fts.rules().add(rule);
+ rule.symbolizers().add(symbolizer);
+
+ final Set<Identifier> ids = new HashSet<>();
+ ids.add(filterFactory.featureId("1"));
+ ids.add(filterFactory.featureId("20"));
+ final Filter filter = filterFactory.id(ids);
+ final SimpleQuery query = new SimpleQuery();
+ query.setFilter(filter);
+
+ final MapLayer fishLayer = new MapLayer();
+ fishLayer.setData(fishes);
+ fishLayer.setStyle(style);
+ fishLayer.setQuery(query);
+ final MapLayer boatLayer = new MapLayer();
+ boatLayer.setData(boats);
+ boatLayer.setStyle(style);
+ boatLayer.setQuery(query);
+ final MapLayers layers = new MapLayers();
+ layers.getComponents().add(fishLayer);
+ layers.getComponents().add(boatLayer);
+
+ final Set<Entry<String, Symbolizer>> presentations = present(layers);
+ assertEquals(2, presentations.size());
+ assertTrue(presentations.contains(new AbstractMap.SimpleEntry<>("1",
symbolizer)));
+ assertTrue(presentations.contains(new AbstractMap.SimpleEntry<>("20",
symbolizer)));
+ }
+
+ /**
* Portray using defined type names.
* Test expect only boat type features to be rendered.
*/
@@ -228,4 +323,139 @@ public class SEPortrayerTest extends TestCase {
assertTrue(presentations.contains(new AbstractMap.SimpleEntry<>("1",
symbolizer)));
assertTrue(presentations.contains(new AbstractMap.SimpleEntry<>("2",
symbolizer)));
}
+
+ /**
+ * Portray using defined rule filter
+ * Test expect only features with identifier equals "2" to match.
+ */
+ @Test
+ public void testRuleFilter() {
+
+ final Set<Identifier> ids = new HashSet<>();
+ ids.add(filterFactory.featureId("2"));
+ final Filter filter = filterFactory.id(ids);
+
+ final MockStyle style = new MockStyle();
+ final MockFeatureTypeStyle fts = new MockFeatureTypeStyle();
+ final MockRule rule = new MockRule();
+ rule.setFilter(filter);
+ final MockLineSymbolizer symbolizer = new MockLineSymbolizer();
+ style.featureTypeStyles().add(fts);
+ fts.rules().add(rule);
+ rule.symbolizers().add(symbolizer);
+
+
+ final MapLayer fishLayer = new MapLayer();
+ fishLayer.setData(fishes);
+ fishLayer.setStyle(style);
+ final MapLayer boatLayer = new MapLayer();
+ boatLayer.setData(boats);
+ boatLayer.setStyle(style);
+ final MapLayers layers = new MapLayers();
+ layers.getComponents().add(fishLayer);
+ layers.getComponents().add(boatLayer);
+
+ final Set<Entry<String, Symbolizer>> presentations = present(layers);
+ assertEquals(1, presentations.size());
+ assertTrue(presentations.contains(new AbstractMap.SimpleEntry<>("2",
symbolizer)));
+ }
+
+ /**
+ * Portray using defined rule scale filter.
+ * Test expect only matching scale rule symbolizer to be portrayed.
+ */
+ @Test
+ public void testRuleScale() {
+
+ final MockLineSymbolizer symbolizerAbove = new MockLineSymbolizer();
+ final MockLineSymbolizer symbolizerUnder = new MockLineSymbolizer();
+ final MockLineSymbolizer symbolizerMatch = new MockLineSymbolizer();
+
+ //Symbology rendering scale here is 3.944391406060875E8
+ final MockRule ruleAbove = new MockRule();
+ ruleAbove.symbolizers().add(symbolizerAbove);
+ ruleAbove.setMinScaleDenominator(4e8);
+ ruleAbove.setMaxScaleDenominator(Double.MAX_VALUE);
+ final MockRule ruleUnder = new MockRule();
+ ruleUnder.symbolizers().add(symbolizerUnder);
+ ruleUnder.setMinScaleDenominator(0.0);
+ ruleUnder.setMaxScaleDenominator(3e8);
+ final MockRule ruleMatch = new MockRule();
+ ruleMatch.symbolizers().add(symbolizerMatch);
+ ruleMatch.setMinScaleDenominator(3e8);
+ ruleMatch.setMaxScaleDenominator(4e8);
+
+ final MockStyle style = new MockStyle();
+ final MockFeatureTypeStyle fts = new MockFeatureTypeStyle();
+ style.featureTypeStyles().add(fts);
+ fts.rules().add(ruleAbove);
+ fts.rules().add(ruleUnder);
+ fts.rules().add(ruleMatch);
+
+
+ final MapLayer fishLayer = new MapLayer();
+ fishLayer.setData(fishes);
+ fishLayer.setStyle(style);
+ final MapLayer boatLayer = new MapLayer();
+ boatLayer.setData(boats);
+ boatLayer.setStyle(style);
+ final MapLayers layers = new MapLayers();
+ layers.getComponents().add(fishLayer);
+ layers.getComponents().add(boatLayer);
+
+ final Set<Entry<String, Symbolizer>> presentations = present(layers);
+ assertEquals(4, presentations.size());
+ assertTrue(presentations.contains(new AbstractMap.SimpleEntry<>("1",
symbolizerMatch)));
+ assertTrue(presentations.contains(new AbstractMap.SimpleEntry<>("2",
symbolizerMatch)));
+ assertTrue(presentations.contains(new AbstractMap.SimpleEntry<>("10",
symbolizerMatch)));
+ assertTrue(presentations.contains(new AbstractMap.SimpleEntry<>("20",
symbolizerMatch)));
+ }
+
+ /**
+ * Portray using defined rule 'is else' property.
+ * Test expect only feature with identifier "10" to be rendered with the
base rule
+ * and other features to rendered with the fallback rule.
+ */
+ @Test
+ public void testRuleElseCondition() {
+
+ final Set<Identifier> ids = new HashSet<>();
+ ids.add(filterFactory.featureId("10"));
+ final Filter filter = filterFactory.id(ids);
+
+ final MockLineSymbolizer symbolizerBase = new MockLineSymbolizer();
+ final MockLineSymbolizer symbolizerElse = new MockLineSymbolizer();
+
+ final MockRule ruleBase = new MockRule();
+ ruleBase.symbolizers().add(symbolizerBase);
+ ruleBase.setFilter(filter);
+ final MockRule ruleOther = new MockRule();
+ ruleOther.setIsElseFilter(true);
+ ruleOther.symbolizers().add(symbolizerElse);
+
+ final MockStyle style = new MockStyle();
+ final MockFeatureTypeStyle fts = new MockFeatureTypeStyle();
+ style.featureTypeStyles().add(fts);
+ fts.rules().add(ruleBase);
+ fts.rules().add(ruleOther);
+
+ final MapLayer fishLayer = new MapLayer();
+ fishLayer.setData(fishes);
+ fishLayer.setStyle(style);
+ final MapLayer boatLayer = new MapLayer();
+ boatLayer.setData(boats);
+ boatLayer.setStyle(style);
+ final MapLayers layers = new MapLayers();
+ layers.getComponents().add(fishLayer);
+ layers.getComponents().add(boatLayer);
+
+ final Set<Entry<String, Symbolizer>> presentations = present(layers);
+ assertEquals(4, presentations.size());
+ assertTrue(presentations.contains(new AbstractMap.SimpleEntry<>("1",
symbolizerElse)));
+ assertTrue(presentations.contains(new AbstractMap.SimpleEntry<>("2",
symbolizerElse)));
+ assertTrue(presentations.contains(new AbstractMap.SimpleEntry<>("10",
symbolizerBase)));
+ assertTrue(presentations.contains(new AbstractMap.SimpleEntry<>("20",
symbolizerElse)));
+ }
+
+
}
diff --git
a/core/sis-portrayal/src/test/java/org/apache/sis/portrayal/MapLayersTest.java
b/core/sis-portrayal/src/test/java/org/apache/sis/portrayal/MapLayersTest.java
new file mode 100644
index 0000000..3276f1f
--- /dev/null
+++
b/core/sis-portrayal/src/test/java/org/apache/sis/portrayal/MapLayersTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.portrayal;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.sis.internal.map.ListChangeEvent;
+import org.apache.sis.measure.NumberRange;
+import org.apache.sis.test.TestCase;
+import static org.junit.Assert.*;
+import org.junit.Test;
+
+/**
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 1.1
+ * @since 1.1
+ */
+public class MapLayersTest extends TestCase {
+
+ /**
+ * Test the maplayers components list events.
+ */
+ @Test
+ public void testListEvents() {
+
+ final MapLayers layers = new MapLayers();
+ final MapLayer layer1 = new MapLayer();
+ final MapLayer layer2 = new MapLayer();
+
+ final AtomicInteger eventNum = new AtomicInteger();
+ layers.addPropertyChangeListener(MapLayers.COMPONENTS_PROPERTY, new
PropertyChangeListener() {
+ @Override
+ public void propertyChange(PropertyChangeEvent evt) {
+ assertTrue(evt instanceof ListChangeEvent);
+ final ListChangeEvent levt = (ListChangeEvent) evt;
+ assertEquals(layers.getComponents(), levt.getOldValue());
+ assertEquals(layers.getComponents(), levt.getNewValue());
+ assertEquals(MapLayers.COMPONENTS_PROPERTY,
levt.getPropertyName());
+ assertEquals(layers, levt.getSource());
+ int eventId = eventNum.incrementAndGet();
+ switch (eventId) {
+ case 1 :
+ assertEquals(ListChangeEvent.Type.ADDED,
levt.getType());
+ assertEquals(NumberRange.create(0, true, 0, true),
levt.getRange());
+ assertEquals(1, levt.getItems().size());
+ assertEquals(layer1, levt.getItems().get(0));
+ break;
+ case 2 :
+ assertEquals(ListChangeEvent.Type.ADDED,
levt.getType());
+ assertEquals(NumberRange.create(1, true, 1, true),
levt.getRange());
+ assertEquals(1, levt.getItems().size());
+ assertEquals(layer2, levt.getItems().get(0));
+ break;
+ case 3 :
+ assertEquals(ListChangeEvent.Type.REMOVED,
levt.getType());
+ assertEquals(NumberRange.create(1, true, 1, true),
levt.getRange());
+ assertEquals(1, levt.getItems().size());
+ assertEquals(layer2, levt.getItems().get(0));
+ break;
+ case 4 :
+ assertEquals(ListChangeEvent.Type.CHANGED,
levt.getType());
+ assertEquals(null, levt.getRange());
+ assertEquals(null, levt.getItems());
+ break;
+ }
+
+ }
+ });
+
+ layers.getComponents().add(layer1);
+ assertEquals(1, eventNum.get());
+
+ layers.getComponents().add(layer2);
+ assertEquals(2, eventNum.get());
+
+ layers.getComponents().remove(layer2);
+ assertEquals(3, eventNum.get());
+
+ layers.getComponents().set(0, layer2);
+ assertEquals(4, eventNum.get());
+ }
+
+}
diff --git
a/core/sis-portrayal/src/test/java/org/apache/sis/test/suite/PortrayalTestSuite.java
b/core/sis-portrayal/src/test/java/org/apache/sis/test/suite/PortrayalTestSuite.java
index 0538531..e22488f 100644
---
a/core/sis-portrayal/src/test/java/org/apache/sis/test/suite/PortrayalTestSuite.java
+++
b/core/sis-portrayal/src/test/java/org/apache/sis/test/suite/PortrayalTestSuite.java
@@ -30,6 +30,7 @@ import org.junit.runners.Suite;
* @module
*/
@Suite.SuiteClasses({
+ org.apache.sis.portrayal.MapLayersTest.class,
org.apache.sis.internal.map.SEPortrayerTest.class,
})
public final strictfp class PortrayalTestSuite extends TestSuite {