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 256182d4bf feat(Renderer): reorganize renderer, cleaning code, add
doc, start test cases
256182d4bf is described below
commit 256182d4bf4c1c60733bccea50676532b812cfaf
Author: jsorel <[email protected]>
AuthorDate: Mon Jan 29 17:00:28 2024 +0100
feat(Renderer): reorganize renderer, cleaning code, add doc, start test
cases
---
.../org.apache.sis.feature/main/module-info.java | 1 +
.../main/module-info.java | 13 ++
.../main/org/apache/sis/map/Presentation.java | 2 +
.../apache/sis/map/service/GraphicsPortrayer.java | 201 +++++++++++++++++++
.../sis/map/service/LineSymbolizerPainter.java | 39 ----
.../org/apache/sis/map/service/MapPortrayer.java | 148 --------------
.../sis/map/service/PointSymbolizerPainter.java | 39 ----
.../sis/map/service/RasterSymbolizerPainter.java | 39 ----
...rayalException.java => RenderingException.java} | 14 +-
.../main/org/apache/sis/map/service/SEPainter.java | 113 -----------
...{PolygonSymbolizerPainter.java => Scene2D.java} | 31 ++-
.../org/apache/sis/map/service/StylePainter.java | 10 +-
.../map/service/SymbolizerPainterDescription.java | 43 -----
.../sis/map/service/TextSymbolizerPainter.java | 39 ----
.../apache/sis/map/service/se1/LineToScene2D.java | 120 ++++++++++++
.../apache/sis/map/service/se1/PointToScene2D.java | 73 +++++++
.../sis/map/service/se1/PolygonToScene2D.java | 73 +++++++
.../sis/map/service/se1/PresentationToScene2D.java | 214 +++++++++++++++++++++
.../sis/map/service/se1/RasterToScene2D.java | 111 +++++++++++
.../apache/sis/map/service/se1/RenderedShape.java | 112 +++++++++++
.../org/apache/sis/map/service/se1/SEPainter.java | 88 +++++++++
.../SymbolizerCache.java} | 21 +-
.../sis/map/service/se1/SymbolizerToScene2D.java | 192 ++++++++++++++++++
.../apache/sis/map/service/se1/TextToScene2D.java | 73 +++++++
.../sis/map/service/GraphicsPortrayerTest.java | 156 +++++++++++++++
25 files changed, 1477 insertions(+), 488 deletions(-)
diff --git a/endorsed/src/org.apache.sis.feature/main/module-info.java
b/endorsed/src/org.apache.sis.feature/main/module-info.java
index 9badc7e6ba..d850d06278 100644
--- a/endorsed/src/org.apache.sis.feature/main/module-info.java
+++ b/endorsed/src/org.apache.sis.feature/main/module-info.java
@@ -70,6 +70,7 @@ module org.apache.sis.feature {
org.apache.sis.gui; // In the "optional"
sub-project.
exports org.apache.sis.geometry.wrapper.jts to
+ org.apache.sis.portrayal.map, // In the "incubator"
sub-project.
org.apache.sis.cql; // In the "incubator"
sub-project.
exports org.apache.sis.coverage.internal to
diff --git a/incubator/src/org.apache.sis.portrayal.map/main/module-info.java
b/incubator/src/org.apache.sis.portrayal.map/main/module-info.java
index 8254cbf98d..128b11c4f8 100644
--- a/incubator/src/org.apache.sis.portrayal.map/main/module-info.java
+++ b/incubator/src/org.apache.sis.portrayal.map/main/module-info.java
@@ -25,4 +25,17 @@ module org.apache.sis.portrayal.map {
requires static org.locationtech.jts;
exports org.apache.sis.map;
+ exports org.apache.sis.map.service;
+
+ uses org.apache.sis.map.service.StylePainter;
+ uses org.apache.sis.map.service.se1.SymbolizerToScene2D.Spi;
+
+ provides org.apache.sis.map.service.StylePainter
+ with org.apache.sis.map.service.se1.SEPainter;
+ provides org.apache.sis.map.service.se1.SymbolizerToScene2D.Spi
+ with org.apache.sis.map.service.se1.PointToScene2D.Spi,
+ org.apache.sis.map.service.se1.LineToScene2D.Spi,
+ org.apache.sis.map.service.se1.PolygonToScene2D.Spi,
+ org.apache.sis.map.service.se1.TextToScene2D.Spi,
+ org.apache.sis.map.service.se1.RasterToScene2D.Spi;
}
diff --git
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/Presentation.java
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/Presentation.java
index 9b544aec0f..040173e2f2 100644
---
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/Presentation.java
+++
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/Presentation.java
@@ -43,6 +43,8 @@ import org.apache.sis.storage.Resource;
*
* @todo Consider renaming as {@code Graphic} for emphasis that this is a
graphical representation of something.
* This would be consistent with legacy GO-1 specification (even if
retired, it still have worthy material).
+ * @todo Consider renaming as {@code Instruction} for emphasis that this is a
rendering operation.
+ * IHO S-52 and S-100 specification. S-100 has been build guided by
ISO-19117.
*
* @author Johann Sorel (Geomatys)
*/
diff --git
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/GraphicsPortrayer.java
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/GraphicsPortrayer.java
new file mode 100644
index 0000000000..feb4597426
--- /dev/null
+++
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/GraphicsPortrayer.java
@@ -0,0 +1,201 @@
+/*
+ * 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.map.service;
+
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.image.BufferedImage;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.stream.Stream;
+import org.apache.sis.coverage.grid.GridCoverage2D;
+import org.apache.sis.coverage.grid.GridGeometry;
+import org.apache.sis.map.MapItem;
+import org.apache.sis.map.MapLayer;
+import org.apache.sis.map.MapLayers;
+import org.apache.sis.map.Presentation;
+import org.apache.sis.map.SEPortrayer;
+import org.apache.sis.style.Style;
+import org.apache.sis.util.ArgumentChecks;
+
+/**
+ * Produce rendered image of styled resources.
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public final class GraphicsPortrayer {
+
+ /**
+ * Reference all painters.
+ */
+ private static final Map<Class<? extends Style>,StylePainter> PAINTERS =
new HashMap<>();
+ static {
+ final ServiceLoader<StylePainter> loader =
ServiceLoader.load(StylePainter.class, StylePainter.class.getClassLoader());
+ for (StylePainter painter : loader) {
+ PAINTERS.put(painter.getStyleClass(), painter);
+ }
+ }
+
+ private Graphics2D graphics;
+ private GridGeometry domain;
+ private BufferedImage image;
+
+ public GraphicsPortrayer(){}
+
+ /**
+ * Set the output image to render into.
+ *
+ * @param image not null
+ * @return this portrayer
+ */
+ public GraphicsPortrayer setCanvas(BufferedImage image) {
+ ArgumentChecks.ensureNonNull("image", image);
+ this.image = image;
+ this.graphics = image.createGraphics();
+ return this;
+ }
+
+ /**
+ * Set the output Graphics2D to render into.
+ *
+ * @param graphics not null
+ * @return this portrayer
+ */
+ public GraphicsPortrayer setCanvas(Graphics2D graphics) {
+ ArgumentChecks.ensureNonNull("graphics", graphics);
+ this.graphics = graphics;
+ return this;
+ }
+
+ /**
+ * Set the GridGeometry which is rendered.
+ *
+ * @param domain not null, lower extent coordinates must be on 0.
+ */
+ public GraphicsPortrayer setDomain(GridGeometry domain) {
+ ArgumentChecks.ensureNonNull("domain", domain);
+ long[] low = domain.getExtent().getLow().getCoordinateValues();
+ for (long l : low) {
+ ArgumentChecks.ensureValidIndex(1, (int) l);
+ }
+
+ this.domain = domain;
+ return this;
+ }
+
+ /**
+ *
+ * @return created image, may be null
+ */
+ public BufferedImage getImage() {
+ return image;
+ }
+
+ /**
+ * Get the rendering image as a coverage.
+ * @return coverage, never null
+ */
+ public GridCoverage2D toCoverage() {
+ return new GridCoverage2D(domain, null, getImage());
+ }
+
+ /**
+ * Validate parameters and create image if needed.
+ */
+ private Scene2D init() {
+ ArgumentChecks.ensureNonNull("domain", domain);
+ if (image == null) {
+ setCanvas(new BufferedImage(
+ (int) domain.getExtent().getSize(0),
+ (int) domain.getExtent().getSize(1),
+ BufferedImage.TYPE_INT_ARGB));
+ }
+
+ return new Scene2D(domain, graphics);
+ }
+
+ /**
+ * Paint given map.
+ *
+ * An image is created if not defined.
+ *
+ * @param map to paint, not null
+ * @throws IllegalArgumentException if canvas is not property configured
+ * @throws RenderingException if a rendering procedure fails.
+ */
+ public synchronized GraphicsPortrayer portray(MapItem map) throws
RenderingException {
+ portray(init(), map);
+ return this;
+ }
+
+ private void portray(Scene2D scene, MapItem map) throws RenderingException
{
+ if (map == null || !map.isVisible()) return;
+ if (map instanceof MapLayer) {
+ portray(scene, (MapLayer) map);
+ } else if (map instanceof MapLayers) {
+ final MapLayers layers = (MapLayers) map;
+ for (MapItem item : layers.getComponents()) {
+ portray(scene, item);
+ }
+ }
+ }
+
+ private void portray(Scene2D scene, MapLayer layer) throws
RenderingException {
+ final Style style = layer.getStyle();
+ if (style == null) return;
+ final StylePainter painter = PAINTERS.get(style.getClass());
+ if (painter == null) return;
+ painter.paint(scene, layer);
+ }
+
+ /**
+ * Compute visual intersection of given map.
+ *
+ * @param mapItem to be processed, not null.
+ * @param mask intersection mask, not null.
+ * @return intersecting stream of presentations.
+ * @throws IllegalArgumentException if canvas is not property configured
+ * @throws RenderingException if a rendering procedure fails.
+ */
+ public Stream<Presentation> intersects(MapItem mapItem, Shape mask) throws
RenderingException {
+ return intersects(init(), mapItem, mask);
+ }
+
+ private Stream<Presentation> intersects(Scene2D scene, MapItem map, Shape
mask) throws RenderingException{
+ Stream<Presentation> results = Stream.empty();
+ if (map == null || !map.isVisible()) return results;
+ if (map instanceof MapLayer) {
+ results = Stream.concat(results, intersects(scene, (MapLayer) map,
mask));
+ } else if (map instanceof MapLayers) {
+ final MapLayers layers = (MapLayers) map;
+ for (MapItem item : layers.getComponents()) {
+ results = Stream.concat(results, intersects(scene, item,
mask));
+ }
+ }
+ return results;
+ }
+
+ private Stream<Presentation> intersects(Scene2D scene, MapLayer layer,
Shape mask) throws RenderingException {
+ final Style style = layer.getStyle();
+ if (style == null) return Stream.empty();
+ final StylePainter painter = PAINTERS.get(style.getClass());
+ if (painter == null) return Stream.empty();
+ return painter.intersects(scene, layer, mask);
+ }
+
+}
diff --git
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/LineSymbolizerPainter.java
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/LineSymbolizerPainter.java
deleted file mode 100644
index d5aa93c596..0000000000
---
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/LineSymbolizerPainter.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.map.service;
-
-import java.awt.Graphics2D;
-import org.apache.sis.coverage.grid.GridGeometry;
-import org.apache.sis.map.MapLayer;
-import org.apache.sis.style.se1.Symbolizer;
-
-/**
- *
- * @author Johann Sorel (Geomatys)
- */
-final class LineSymbolizerPainter extends SymbolizerPainter {
-
- public LineSymbolizerPainter(Symbolizer<?> symbolizer) {
- super(symbolizer);
- }
-
- @Override
- public void paint(Graphics2D g, GridGeometry gridGeometry, MapLayer layer)
{
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
-}
diff --git
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/MapPortrayer.java
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/MapPortrayer.java
deleted file mode 100644
index 9555b133e4..0000000000
---
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/MapPortrayer.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * 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.map.service;
-
-import java.awt.Graphics2D;
-import java.awt.RenderingHints;
-import java.awt.image.BufferedImage;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.ServiceLoader;
-import org.apache.sis.coverage.grid.GridExtent;
-import org.apache.sis.coverage.grid.GridGeometry;
-import org.apache.sis.coverage.grid.GridOrientation;
-import org.apache.sis.map.MapItem;
-import org.apache.sis.map.MapLayer;
-import org.apache.sis.map.MapLayers;
-import org.apache.sis.referencing.CRS;
-import org.apache.sis.referencing.CommonCRS;
-import org.apache.sis.style.Style;
-import org.apache.sis.util.ArgumentChecks;
-
-/**
- * Draft.
- * Class used to render a map using Java2D API.
- *
- * @author Johann Sorel (Geomatys)
- */
-public final class MapPortrayer {
-
- /**
- * Reference all painters.
- */
- private static final Map<Class<? extends Style>,StylePainter> PAINTERS =
new HashMap<>();
-
- private GridGeometry grid = new GridGeometry(new GridExtent(360, 180),
CRS.getDomainOfValidity(CommonCRS.WGS84.normalizedGeographic()),
GridOrientation.REFLECTION_Y);
- private BufferedImage image = new
BufferedImage((int)grid.getExtent().getSize(0),
(int)grid.getExtent().getSize(0), BufferedImage.TYPE_INT_ARGB);
- private Graphics2D graphics = image.createGraphics();
- private boolean valid = false;
-
- static {
- final ServiceLoader<StylePainter> loader =
ServiceLoader.load(StylePainter.class, StylePainter.class.getClassLoader());
- for (StylePainter painter : loader) {
- PAINTERS.put(painter.getStyleClass(), painter);
- }
- }
-
- public MapPortrayer() {
- }
-
- /**
- * Update canvas.
- *
- * @param image image to write into, if null the graphics must be defined
- * @param graphics graphics to paint into, if null the image must be
defined
- */
- public synchronized void setCanvas(BufferedImage image, Graphics2D
graphics) {
- this.image = image;
- if (graphics == null) {
- graphics = image.createGraphics();
- graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
- }
- this.graphics = graphics;
- valid = false;
- }
-
- /**
- * Update canvas area.
- *
- * @param grid GridGeometry of the rendered area
- */
- public synchronized void setGridGeometry(GridGeometry grid) {
- ArgumentChecks.ensureNonNull("grid", grid);
- this.grid = grid;
- valid = false;
- }
-
- /**
- * @return currently used Graphics2D, can be null
- */
- public synchronized Graphics2D getGraphics() {
- return graphics;
- }
-
- /**
- * @return currently used Image, can be null
- */
- public BufferedImage getImage() {
- return image;
- }
-
- /**
- * Paint given map.
- *
- * An image is created if not defined.
- *
- * @param map to paint, not null
- * @throws PortrayalException
- * @throws IllegalArgumentException if canvas is not property configured
- */
- public synchronized void portray(MapItem map) throws PortrayalException {
-
- if (!valid) {
- //check canvas configuration
- if (grid == null) {
- throw new IllegalArgumentException("Grid geometry has not been
initialize");
- }
- if (graphics == null) {
-
- }
- valid = true;
- }
-
- //render given map
- if (map == null || !map.isVisible()) return;
- if (map instanceof MapLayer) {
- portray((MapLayer) map);
- } else if (map instanceof MapLayers) {
- final MapLayers layers = (MapLayers) map;
- for (MapItem item : layers.getComponents()) {
- portray(item);
- }
- }
- }
-
- private void portray(MapLayer layer) throws PortrayalException {
- final Style style = layer.getStyle();
- if (style == null) return;
- final StylePainter painter = PAINTERS.get(style.getClass());
- if (painter == null) return;
- painter.paint(graphics, image, grid, layer);
- }
-
-
-}
diff --git
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/PointSymbolizerPainter.java
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/PointSymbolizerPainter.java
deleted file mode 100644
index 081913a433..0000000000
---
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/PointSymbolizerPainter.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.map.service;
-
-import java.awt.Graphics2D;
-import org.apache.sis.coverage.grid.GridGeometry;
-import org.apache.sis.map.MapLayer;
-import org.apache.sis.style.se1.Symbolizer;
-
-/**
- *
- * @author Johann Sorel (Geomatys)
- */
-final class PointSymbolizerPainter extends SymbolizerPainter {
-
- public PointSymbolizerPainter(Symbolizer<?> symbolizer) {
- super(symbolizer);
- }
-
- @Override
- public void paint(Graphics2D g, GridGeometry gridGeometry, MapLayer layer)
{
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
-}
diff --git
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/RasterSymbolizerPainter.java
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/RasterSymbolizerPainter.java
deleted file mode 100644
index 28778a405c..0000000000
---
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/RasterSymbolizerPainter.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.map.service;
-
-import java.awt.Graphics2D;
-import org.apache.sis.coverage.grid.GridGeometry;
-import org.apache.sis.map.MapLayer;
-import org.apache.sis.style.se1.Symbolizer;
-
-/**
- *
- * @author Johann Sorel (Geomatys)
- */
-final class RasterSymbolizerPainter extends SymbolizerPainter {
-
- public RasterSymbolizerPainter(Symbolizer<?> symbolizer) {
- super(symbolizer);
- }
-
- @Override
- public void paint(Graphics2D g, GridGeometry gridGeometry, MapLayer layer)
{
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
-}
diff --git
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/PortrayalException.java
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/RenderingException.java
similarity index 70%
rename from
incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/PortrayalException.java
rename to
incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/RenderingException.java
index ca6ecd581b..0257342767 100644
---
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/PortrayalException.java
+++
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/RenderingException.java
@@ -17,9 +17,21 @@
package org.apache.sis.map.service;
/**
+ * Exception that may be thrown by a portraying operation.
*
* @author Johann Sorel (Geomatys)
*/
-public class PortrayalException extends Exception {
+public final class RenderingException extends Exception {
+ public RenderingException(String message) {
+ super(message);
+ }
+
+ public RenderingException(Throwable cause) {
+ super(cause);
+ }
+
+ public RenderingException(String message, Throwable cause) {
+ super(message, cause);
+ }
}
diff --git
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/SEPainter.java
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/SEPainter.java
deleted file mode 100644
index f93f9e23a0..0000000000
---
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/SEPainter.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * 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.map.service;
-
-import java.awt.Graphics2D;
-import java.awt.image.BufferedImage;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.ServiceLoader;
-import java.util.stream.Stream;
-import org.apache.sis.coverage.grid.GridGeometry;
-import org.apache.sis.map.ExceptionPresentation;
-import org.apache.sis.map.MapLayer;
-import org.apache.sis.map.Presentation;
-import org.apache.sis.map.SEPortrayer;
-import org.apache.sis.map.SEPresentation;
-import org.apache.sis.style.Style;
-import org.apache.sis.style.se1.Symbolizer;
-import org.apache.sis.style.se1.Symbology;
-
-/**
- *
- * @author Johann Sorel (Geomatys)
- */
-public final class SEPainter implements StylePainter {
-
- /**
- * Reference all painters.
- */
- private static final List<SymbolizerPainterDescription<?>> PAINTERS = new
ArrayList<>();
-
- static {
- final ServiceLoader<SymbolizerPainterDescription> loader =
ServiceLoader.load(SymbolizerPainterDescription.class,
SymbolizerPainterDescription.class.getClassLoader());
- for (SymbolizerPainterDescription painter : loader) {
- PAINTERS.add(painter);
- }
- }
-
- private static final SymbolizerPainter NONE = new SymbolizerPainter(null) {
- @Override
- public void paint(Graphics2D g, GridGeometry gridGeometry, MapLayer
layer) {
- }
- };
-
- @Override
- public Class<? extends Style> getStyleClass() {
- return Symbology.class;
- }
-
- @Override
- public void paint(Graphics2D g, BufferedImage image, GridGeometry
gridGeometry, MapLayer layer) {
-
- final Map<Symbolizer, SymbolizerPainter> subPainters = new HashMap<>();
-
- final SEPortrayer p = new SEPortrayer();
- try (Stream<Presentation> presentations = p.present(gridGeometry,
layer)) {
-
- //process presentations in order
- final Iterator<Presentation> ite = presentations.iterator();
- while (ite.hasNext()) {
- final Presentation presentation = ite.next();
- if (presentation instanceof SEPresentation) {
- final SEPresentation sepre = (SEPresentation) presentation;
- final Symbolizer<?> symbolizer = sepre.getSymbolizer();
- SymbolizerPainter subPainter = subPainters.get(symbolizer);
-
- creation:
- if (subPainter == null) {
- subPainter = NONE;
- for (SymbolizerPainterDescription s : PAINTERS) {
- if (s.getSymbolizerClass().isInstance(symbolizer))
{
- subPainter = s.createRenderer(symbolizer);
- break;
- }
- }
- subPainters.put(symbolizer, subPainter);
- }
-
- if (subPainter == NONE) {
- continue;
- }
-
- subPainter.paint(g, gridGeometry, layer);
-
- } else if (presentation instanceof ExceptionPresentation) {
- //todo
- } else {
- //todo
- }
- }
-
- }
-
- }
-
-}
diff --git
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/PolygonSymbolizerPainter.java
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/Scene2D.java
similarity index 56%
rename from
incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/PolygonSymbolizerPainter.java
rename to
incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/Scene2D.java
index 4e22be7cba..e034f4467f 100644
---
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/PolygonSymbolizerPainter.java
+++
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/Scene2D.java
@@ -17,23 +17,38 @@
package org.apache.sis.map.service;
import java.awt.Graphics2D;
+import java.util.logging.Logger;
import org.apache.sis.coverage.grid.GridGeometry;
-import org.apache.sis.map.MapLayer;
-import org.apache.sis.style.se1.Symbolizer;
+import org.apache.sis.util.ArgumentChecks;
/**
+ * Holds the rendering properties.
*
* @author Johann Sorel (Geomatys)
*/
-final class PolygonSymbolizerPainter extends SymbolizerPainter {
+public final class Scene2D {
- public PolygonSymbolizerPainter(Symbolizer<?> symbolizer) {
- super(symbolizer);
+ public static final Logger LOGGER =
Logger.getLogger("org.apache.sis.internal.renderer");
+
+ /**
+ * The rendering grid geometry.
+ */
+ public final GridGeometry grid;
+ /**
+ * Graphics to render into.
+ * When modified by renderers, it must be reset accordingly.
+ */
+ public final Graphics2D graphics;
+
+ public Scene2D(GridGeometry grid, Graphics2D graphics) {
+ ArgumentChecks.ensureNonNull("grid", grid);
+ ArgumentChecks.ensureNonNull("graphics", graphics);
+ this.grid = grid;
+ this.graphics = graphics;
}
- @Override
- public void paint(Graphics2D g, GridGeometry gridGeometry, MapLayer layer)
{
- throw new UnsupportedOperationException("Not supported yet.");
+ public Graphics2D getGraphics() {
+ return graphics;
}
}
diff --git
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/StylePainter.java
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/StylePainter.java
index 699a110d85..df01ef9047 100644
---
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/StylePainter.java
+++
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/StylePainter.java
@@ -16,10 +16,10 @@
*/
package org.apache.sis.map.service;
-import java.awt.Graphics2D;
-import java.awt.image.BufferedImage;
-import org.apache.sis.coverage.grid.GridGeometry;
+import java.awt.Shape;
+import java.util.stream.Stream;
import org.apache.sis.map.MapLayer;
+import org.apache.sis.map.Presentation;
import org.apache.sis.style.Style;
/**
@@ -30,6 +30,8 @@ public interface StylePainter {
Class<? extends Style> getStyleClass();
- void paint(Graphics2D g, BufferedImage image, GridGeometry gridGeometry,
MapLayer layer);
+ void paint(Scene2D scene, MapLayer layer);
+
+ Stream<Presentation> intersects(Scene2D scene, MapLayer layer, Shape mask);
}
diff --git
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/SymbolizerPainterDescription.java
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/SymbolizerPainterDescription.java
deleted file mode 100644
index 3022c94347..0000000000
---
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/SymbolizerPainterDescription.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.map.service;
-
-import org.apache.sis.style.se1.Symbolizer;
-
-/**
- *
- * @author Johann Sorel (Geomatys)
- */
-public interface SymbolizerPainterDescription<T extends Symbolizer> {
-
- /**
- * @return The symbolizer class handle by this renderer.
- */
- Class<T> getSymbolizerClass();
-
- /**
- * Create a renderer fixed for a symbol and a context.
- *
- * @param symbol : cached symbolizer
- * @param context : rendering context
- * @return SymbolizerRenderer or null if symbol is never visible.
- */
- SymbolizerPainter createRenderer(T symbol);
-
- //TODO Glyph methods for legend
-
-}
diff --git
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/TextSymbolizerPainter.java
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/TextSymbolizerPainter.java
deleted file mode 100644
index f6aff9a3cd..0000000000
---
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/TextSymbolizerPainter.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.map.service;
-
-import java.awt.Graphics2D;
-import org.apache.sis.coverage.grid.GridGeometry;
-import org.apache.sis.map.MapLayer;
-import org.apache.sis.style.se1.Symbolizer;
-
-/**
- *
- * @author Johann Sorel (Geomatys)
- */
-final class TextSymbolizerPainter extends SymbolizerPainter {
-
- public TextSymbolizerPainter(Symbolizer<?> symbolizer) {
- super(symbolizer);
- }
-
- @Override
- public void paint(Graphics2D g, GridGeometry gridGeometry, MapLayer layer)
{
- throw new UnsupportedOperationException("Not supported yet.");
- }
-
-}
diff --git
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/LineToScene2D.java
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/LineToScene2D.java
new file mode 100644
index 0000000000..e28f4dd8de
--- /dev/null
+++
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/LineToScene2D.java
@@ -0,0 +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.map.service.se1;
+
+import org.apache.sis.map.service.Scene2D;
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Shape;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+import org.apache.sis.feature.internal.AttributeConvention;
+import org.apache.sis.geometry.wrapper.Geometries;
+import org.apache.sis.geometry.wrapper.GeometryWrapper;
+import org.apache.sis.geometry.wrapper.jts.JTS;
+import org.apache.sis.map.Presentation;
+import org.apache.sis.map.SEPresentation;
+import org.apache.sis.map.service.RenderingException;
+import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.operation.transform.MathTransforms;
+import org.apache.sis.style.se1.LineSymbolizer;
+import org.locationtech.jts.geom.Geometry;
+import org.opengis.feature.Feature;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.datum.PixelInCell;
+import org.opengis.referencing.operation.CoordinateOperation;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.util.FactoryException;
+
+/**
+ * Support for LineSymbolizer rendering.
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public final class LineToScene2D extends
SymbolizerToScene2D<LineSymbolizer<?>> {
+
+ private LineToScene2D(Scene2D state, LineSymbolizer<?> symbolizer) {
+ super(state, symbolizer);
+ }
+
+ @Override
+ public void paint(SEPresentation presentation,
Consumer<Stream<Presentation>> callback) throws RenderingException {
+ final RenderedShape visual = createVisual(presentation);
+ if (visual != null) {
+ visual.paint(state.getGraphics());
+ }
+ }
+
+ @Override
+ public boolean intersects(SEPresentation presentation, Shape mask,
Consumer<Stream<Presentation>> callback) throws RenderingException {
+ final RenderedShape visual = createVisual(presentation);
+ if (visual != null) {
+ return visual.intersects(mask);
+ }
+ return false;
+ }
+
+ private RenderedShape createVisual(SEPresentation presentation) throws
RenderingException {
+ final Feature feature = presentation.getCandidate();
+ Object geometry =
feature.getPropertyValue(AttributeConvention.GEOMETRY);
+
+ if (geometry instanceof Geometry) {
+
+ final MathTransform gridToCRS =
state.grid.getGridToCRS(PixelInCell.CELL_CENTER);
+
+ final GeometryWrapper geomWrap = Geometries.wrap(geometry).get();
+ final CoordinateReferenceSystem geomCrs =
geomWrap.getCoordinateReferenceSystem();
+
+ final Geometry jts;
+ try {
+ final CoordinateOperation coop = CRS.findOperation(geomCrs,
state.grid.getCoordinateReferenceSystem(), null);
+ final MathTransform geomToGrid =
MathTransforms.concatenate(coop.getMathTransform(), gridToCRS.inverse());
+
+ jts = JTS.transform((Geometry) geometry, geomToGrid);
+ } catch (FactoryException | TransformException ex) {
+ throw new RenderingException(ex);
+ }
+
+ Shape shape = JTS.asShape(jts);
+
+ //TODO geometry world wrap and styling
+
+ RenderedShape rs = new RenderedShape();
+ rs.shape = shape;
+ rs.stroke = new BasicStroke(1);
+ rs.strokePaint = Color.BLACK;
+
+ return rs;
+ }
+
+ return null;
+ }
+
+ public static final class Spi implements
SymbolizerToScene2D.Spi<LineSymbolizer> {
+
+ @Override
+ public Class<LineSymbolizer> getSymbolizerType() {
+ return LineSymbolizer.class;
+ }
+
+ @Override
+ public SymbolizerToScene2D create(Scene2D state, LineSymbolizer
symbolizer) throws RenderingException {
+ return new LineToScene2D(state, symbolizer);
+ }
+ }
+}
diff --git
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/PointToScene2D.java
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/PointToScene2D.java
new file mode 100644
index 0000000000..4faa39b287
--- /dev/null
+++
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/PointToScene2D.java
@@ -0,0 +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.map.service.se1;
+
+import org.apache.sis.map.service.Scene2D;
+import java.awt.Shape;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+import org.apache.sis.map.Presentation;
+import org.apache.sis.map.SEPresentation;
+import org.apache.sis.map.service.RenderingException;
+import org.apache.sis.style.se1.PointSymbolizer;
+
+/**
+ * Support for PointSymbolizer rendering.
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public final class PointToScene2D extends
SymbolizerToScene2D<PointSymbolizer<?>> {
+
+ private PointToScene2D(Scene2D state, PointSymbolizer<?> symbolizer) {
+ super(state, symbolizer);
+ }
+
+ @Override
+ public void paint(SEPresentation presentation,
Consumer<Stream<Presentation>> callback) throws RenderingException {
+ final RenderedShape visual = createVisual(presentation);
+ if (visual != null) {
+ visual.paint(state.getGraphics());
+ }
+ }
+
+ @Override
+ public boolean intersects(SEPresentation presentation, Shape mask,
Consumer<Stream<Presentation>> callback) throws RenderingException {
+ final RenderedShape visual = createVisual(presentation);
+ if (visual != null) {
+ return visual.intersects(mask);
+ }
+ return false;
+ }
+
+ private RenderedShape createVisual(SEPresentation presentation) throws
RenderingException {
+ //todo
+ return null;
+ }
+
+ public static final class Spi implements
SymbolizerToScene2D.Spi<PointSymbolizer> {
+
+ @Override
+ public Class<PointSymbolizer> getSymbolizerType() {
+ return PointSymbolizer.class;
+ }
+
+ @Override
+ public SymbolizerToScene2D create(Scene2D state, PointSymbolizer
symbolizer) throws RenderingException {
+ return new PointToScene2D(state, symbolizer);
+ }
+ }
+}
diff --git
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/PolygonToScene2D.java
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/PolygonToScene2D.java
new file mode 100644
index 0000000000..626424df5d
--- /dev/null
+++
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/PolygonToScene2D.java
@@ -0,0 +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.map.service.se1;
+
+import org.apache.sis.map.service.Scene2D;
+import java.awt.Shape;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+import org.apache.sis.map.Presentation;
+import org.apache.sis.map.SEPresentation;
+import org.apache.sis.map.service.RenderingException;
+import org.apache.sis.style.se1.PolygonSymbolizer;
+
+/**
+ * Support for PointSymbolizer rendering.
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public final class PolygonToScene2D extends
SymbolizerToScene2D<PolygonSymbolizer<?>> {
+
+ private PolygonToScene2D(Scene2D state, PolygonSymbolizer<?> symbolizer) {
+ super(state, symbolizer);
+ }
+
+ @Override
+ public void paint(SEPresentation presentation,
Consumer<Stream<Presentation>> callback) throws RenderingException {
+ final RenderedShape visual = createVisual(presentation);
+ if (visual != null) {
+ visual.paint(state.getGraphics());
+ }
+ }
+
+ @Override
+ public boolean intersects(SEPresentation presentation, Shape mask,
Consumer<Stream<Presentation>> callback) throws RenderingException {
+ final RenderedShape visual = createVisual(presentation);
+ if (visual != null) {
+ return visual.intersects(mask);
+ }
+ return false;
+ }
+
+ private RenderedShape createVisual(SEPresentation presentation) throws
RenderingException {
+ //todo
+ return null;
+ }
+
+ public static final class Spi implements
SymbolizerToScene2D.Spi<PolygonSymbolizer> {
+
+ @Override
+ public Class<PolygonSymbolizer> getSymbolizerType() {
+ return PolygonSymbolizer.class;
+ }
+
+ @Override
+ public SymbolizerToScene2D create(Scene2D state, PolygonSymbolizer
symbolizer) throws RenderingException {
+ return new PolygonToScene2D(state, symbolizer);
+ }
+ }
+}
diff --git
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/PresentationToScene2D.java
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/PresentationToScene2D.java
new file mode 100644
index 0000000000..baa991628c
--- /dev/null
+++
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/PresentationToScene2D.java
@@ -0,0 +1,214 @@
+/*
+ * 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.map.service.se1;
+
+import org.apache.sis.map.service.Scene2D;
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.logging.Level;
+import java.util.stream.Stream;
+import org.apache.sis.coverage.grid.GridGeometry;
+import org.apache.sis.map.Presentation;
+import org.apache.sis.map.SEPresentation;
+import org.apache.sis.map.service.RenderingException;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.style.se1.Symbolizer;
+import org.apache.sis.util.ArgumentChecks;
+import org.opengis.geometry.MismatchedDimensionException;
+import org.opengis.referencing.operation.NoninvertibleTransformException;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.util.FactoryException;
+
+
+/**
+ * Generate a 2D scene from canvas and graphic presentations.
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+final class PresentationToScene2D {
+
+ /**
+ * Flag instance for missing symbolizers converters.
+ */
+ private static final SymbolizerToScene2D<?> NONE = new
SymbolizerToScene2D<Symbolizer<?>>(null, null){
+ @Override
+ public void paint(SEPresentation presentation,
Consumer<Stream<Presentation>> callback) throws RenderingException {
+ throw new UnsupportedOperationException("Should not called.");
+ }
+ };
+ private static final SymbolizerCache NO_CACHE = new SymbolizerCache(){};
+
+ private final Scene2D state;
+
+ private final Map<Symbolizer<?>,SymbolizerToScene2D<?>> cache = new
HashMap<>();
+ private Map<Symbolizer<?>,SymbolizerCache> symbolizerCaches;
+
+ /**
+ * Prepare parameters for scene creation.The grid geometry will provide
the base coordinate system and scale informations.
+ * All further presentations must have been build with given grid as base.
+ *
+ * @param grid, not null
+ */
+ public PresentationToScene2D(GridGeometry grid, Graphics2D graphics)
throws FactoryException, MismatchedDimensionException, TransformException {
+ ArgumentChecks.ensureNonNull("grid", grid);
+
+ state = new Scene2D(grid, graphics);
+ }
+
+ /**
+ * Create converter from an existing scene state.
+ *
+ * @param state, not null
+ */
+ public PresentationToScene2D(Scene2D state) {
+ ArgumentChecks.ensureNonNull("state", state);
+
+ this.state = state;
+ }
+
+ /**
+ * Define global shared cache map instance.
+ *
+ * @param symbolizerCaches
+ */
+ public void setSymbolizerCaches(Map<Symbolizer<?>, SymbolizerCache>
symbolizerCaches) {
+ this.symbolizerCaches = symbolizerCaches;
+ }
+
+ /**
+ * Convert and add given presentation to the scene.
+ * Exceptions will be logged.
+ *
+ * @param presentations, not null, will be closed.
+ */
+ public void render(Stream<Presentation> presentations) {
+ try {
+ presentations.parallel().forEach(new Consumer<Presentation>() {
+ @Override
+ public void accept(Presentation t) {
+ try {
+ render(t);
+ } catch (Exception ex) {
+ Scene2D.LOGGER.log(Level.INFO, ex.getMessage(), ex);
+ }
+ }
+ });
+ } finally {
+ presentations.close();
+ }
+ }
+
+ private void render(Presentation presentation) throws RenderingException,
IOException, NoninvertibleTransformException, TransformException,
URISyntaxException, FactoryException, DataStoreException {
+ //standard presentation types
+ if (presentation instanceof SEPresentation) {
+ render((SEPresentation) presentation);
+ } else {
+ //unknown type
+ }
+ }
+
+ private void render(SEPresentation presentation) throws
MismatchedDimensionException, TransformException, FactoryException,
DataStoreException, IOException, RenderingException {
+ final SymbolizerToScene2D<?> sts =
getRenderer(presentation.getSymbolizer());
+
+ if (sts != null) {
+ sts.paint(presentation, this::render);
+ } else {
+ Scene2D.LOGGER.log(Level.INFO, "Unnowned symbolizer {0}",
presentation.getSymbolizer().getClass().getName());
+ }
+ }
+
+ /**
+ * Process given presentations and retain only thos who intersects the
requested shape.
+ *
+ * @param presentations, not null.
+ */
+ public Stream<Presentation> intersects(Stream<Presentation> presentations,
Shape mask) {
+
+ final Consumer<Stream<Presentation>> callback = (Stream<Presentation>
t) -> {intersects(t, mask);};
+
+ return presentations.parallel().filter(new Predicate<Presentation>() {
+ @Override
+ public boolean test(Presentation t) {
+ try {
+ return intersects(t, mask, callback);
+ } catch (RenderingException ex) {
+ Scene2D.LOGGER.log(Level.WARNING, ex.getMessage(), ex);
+ }
+ return false;
+ }
+ });
+ }
+
+ private boolean intersects(Presentation presentation, Shape mask,
Consumer<Stream<Presentation>> callback) throws RenderingException {
+ //standard presentation types
+ if (presentation instanceof SEPresentation) {
+ return intersects((SEPresentation) presentation, mask, callback);
+ } else {
+ //unknown type
+ return false;
+ }
+ }
+
+ private boolean intersects(SEPresentation presentation, Shape mask,
Consumer<Stream<Presentation>> callback) throws RenderingException {
+ final SymbolizerToScene2D<?> sts =
getRenderer(presentation.getSymbolizer());
+
+ if (sts != null) {
+ return sts.intersects(presentation, mask, callback);
+ } else {
+ Scene2D.LOGGER.log(Level.INFO, "Unnowned symbolizer {0}",
presentation.getSymbolizer().getClass().getName());
+ return false;
+ }
+ }
+
+ private SymbolizerToScene2D<?> getRenderer(Symbolizer<?> symbolizer)
throws RenderingException {
+ SymbolizerToScene2D<?> sts;
+ synchronized (cache) {
+ sts = cache.get(symbolizer);
+ if (sts == NONE) {
+ sts = null;
+ } else if (sts == null) {
+ sts = SymbolizerToScene2D.create(state, symbolizer);
+ if (sts == null) sts = NONE;
+ cache.put(symbolizer, sts);
+ if (sts == NONE) {
+ sts = null;
+ } else if (symbolizerCaches != null) {
+ // get or create shared cache
+ SymbolizerCache cache = symbolizerCaches.get(symbolizer);
+ if (cache == null) {
+ cache = SymbolizerToScene2D.createCache(symbolizer);
+ if (cache == null) {
+ cache = NO_CACHE;
+ }
+ symbolizerCaches.put(symbolizer, cache);
+ }
+ if (cache != NO_CACHE) {
+ sts.sharedCache(cache);
+ }
+ }
+ }
+ }
+ return sts;
+ }
+
+}
diff --git
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/RasterToScene2D.java
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/RasterToScene2D.java
new file mode 100644
index 0000000000..4a2560ff01
--- /dev/null
+++
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/RasterToScene2D.java
@@ -0,0 +1,111 @@
+/*
+ * 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.map.service.se1;
+
+import org.apache.sis.map.service.Scene2D;
+import java.awt.Shape;
+import java.awt.image.RenderedImage;
+import java.util.function.Consumer;
+import java.util.logging.Level;
+import java.util.stream.Stream;
+import org.apache.sis.coverage.grid.GridCoverage;
+import org.apache.sis.coverage.grid.GridCoverageBuilder;
+import org.apache.sis.coverage.grid.GridCoverageProcessor;
+import org.apache.sis.coverage.grid.GridDerivation;
+import org.apache.sis.coverage.grid.GridExtent;
+import org.apache.sis.coverage.grid.GridGeometry;
+import org.apache.sis.map.Presentation;
+import org.apache.sis.map.SEPresentation;
+import org.apache.sis.map.service.RenderingException;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.GridCoverageResource;
+import org.apache.sis.storage.NoSuchDataException;
+import org.apache.sis.storage.Resource;
+import org.apache.sis.style.se1.RasterSymbolizer;
+import org.opengis.referencing.operation.TransformException;
+
+/**
+ * Support for RasterSymbolizer rendering.
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public final class RasterToScene2D extends
SymbolizerToScene2D<RasterSymbolizer<?>> {
+
+ private RasterToScene2D(Scene2D state, RasterSymbolizer<?> symbolizer) {
+ super(state, symbolizer);
+ }
+
+ @Override
+ public void paint(SEPresentation presentation,
Consumer<Stream<Presentation>> callback) throws RenderingException {
+ Resource resource = presentation.getResource();
+
+ if (resource instanceof GridCoverageResource) {
+ final GridCoverageResource gcr = (GridCoverageResource) resource;
+
+ try {
+ GridCoverage coverage = gcr.read(state.grid);
+
+ // naive inefficient implementation to be improved
+ // keep only a 2D slice for rendering
+ final GridGeometry gridGeometry = coverage.getGridGeometry();
+ final GridDerivation sliceGridBuilder =
gridGeometry.derive().sliceByRatio(0.5, 0, 1);
+ final GridExtent intersection =
sliceGridBuilder.getIntersection();
+ final GridGeometry sliceGrid =
sliceGridBuilder.build().selectDimensions(0,1);
+ final RenderedImage image = coverage.render(intersection);
+
+ final GridCoverageBuilder gcb = new GridCoverageBuilder();
+ gcb.setValues(image);
+ gcb.setDomain(sliceGrid);
+ gcb.setRanges(coverage.getSampleDimensions());
+ final GridCoverage coverage2d = gcb.build();
+
+ final GridCoverageProcessor gcp = new GridCoverageProcessor();
+ final GridCoverage resampled = gcp.resample(coverage2d,
state.grid);
+
+ //TODO handle raster symbolizer parameters.
+
+ state.graphics.drawRenderedImage(resampled.render(null), null);
+
+ } catch (NoSuchDataException ex) {
+ //do nothing
+ } catch (DataStoreException ex) {
+ LOGGER.log(Level.WARNING, ex.getMessage(), ex);
+ } catch (TransformException ex) {
+ LOGGER.log(Level.WARNING, ex.getMessage(), ex);
+ }
+
+ }
+ }
+
+ @Override
+ public boolean intersects(SEPresentation presentation, Shape mask,
Consumer<Stream<Presentation>> callback) throws RenderingException {
+ return false;
+ }
+
+ public static final class Spi implements
SymbolizerToScene2D.Spi<RasterSymbolizer> {
+
+ @Override
+ public Class<RasterSymbolizer> getSymbolizerType() {
+ return RasterSymbolizer.class;
+ }
+
+ @Override
+ public SymbolizerToScene2D create(Scene2D state, RasterSymbolizer
symbolizer) throws RenderingException {
+ return new RasterToScene2D(state, symbolizer);
+ }
+ }
+}
diff --git
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/RenderedShape.java
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/RenderedShape.java
new file mode 100644
index 0000000000..cc8cebed55
--- /dev/null
+++
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/RenderedShape.java
@@ -0,0 +1,112 @@
+/*
+ * 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.map.service.se1;
+
+import java.awt.AlphaComposite;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.geom.Area;
+
+/**
+ * Combine an AWT Shape and it's rendering options.
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+final class RenderedShape {
+
+ public static final AlphaComposite ALPHA_COMPOSITE_0F =
AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f);
+ public static final AlphaComposite ALPHA_COMPOSITE_1F =
AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f);
+
+ public AlphaComposite fillComposite = ALPHA_COMPOSITE_1F;
+ public AlphaComposite strokeComposite = ALPHA_COMPOSITE_1F;
+
+ /**
+ * Shape to render.
+ * If null, paint and hit methods will always return false.
+ */
+ public Shape shape;
+ /**
+ * The fill paint of the shape.
+ * If null, no fill rendering is made.
+ */
+ public Paint fillPaint;
+ /**
+ * The stroke paint of the shape.
+ * If null, no stroke rendering is made.
+ */
+ public Paint strokePaint;
+ /**
+ * The stroke of the shape.
+ * If null, no stroke rendering is made.
+ */
+ public Stroke stroke;
+
+ /**
+ * Paint this shape with given Graphics2D.
+ *
+ * @param g2d not null
+ * @return true if something was painted.
+ */
+ public boolean paint(Graphics2D g2d) {
+ if (shape == null) return false;
+
+ boolean painted = false;
+ if (fillPaint != null) {
+ g2d.setComposite(fillComposite);
+ g2d.setPaint(fillPaint);
+ g2d.fill(shape);
+ painted = true;
+ }
+ if (stroke != null && strokePaint != null) {
+ g2d.setComposite(strokeComposite);
+ g2d.setPaint(strokePaint);
+ g2d.setStroke(stroke);
+ g2d.draw(shape);
+ painted = true;
+ }
+ return painted;
+ }
+
+ /**
+ * Test intersection of the shape and it's style options with the searched
mask.
+ * @param mask not null.
+ * @return true if rendered shape intersects the mask.
+ */
+ public boolean intersects(Shape mask) {
+
+ //test intersection with fill if defined
+ if (fillPaint != null) {
+ final Area maskArea = new Area(mask);
+ final Area shapeArea = new Area(shape);
+ maskArea.intersect(shapeArea);
+ if (!maskArea.isEmpty()) {
+ return true;
+ }
+ }
+ //test intersection with stroke if defined
+ if (stroke != null && strokePaint != null) {
+ final Area maskArea = new Area(mask);
+ final Area shapeArea = new Area(stroke.createStrokedShape(shape));
+ maskArea.intersect(shapeArea);
+ return !maskArea.isEmpty();
+ }
+ return false;
+ }
+
+}
diff --git
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/SEPainter.java
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/SEPainter.java
new file mode 100644
index 0000000000..7e669636bc
--- /dev/null
+++
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/SEPainter.java
@@ -0,0 +1,88 @@
+/*
+ * 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.map.service.se1;
+
+import java.awt.Shape;
+import java.util.stream.Stream;
+import org.apache.sis.map.MapLayer;
+import org.apache.sis.map.Presentation;
+import org.apache.sis.map.SEPortrayer;
+import org.apache.sis.map.service.Scene2D;
+import org.apache.sis.map.service.StylePainter;
+import org.apache.sis.style.Style;
+import org.apache.sis.style.se1.Symbology;
+
+/**
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public final class SEPainter implements StylePainter {
+
+
+ @Override
+ public Class<? extends Style> getStyleClass() {
+ return Symbology.class;
+ }
+
+ /**
+ * Render the given map using default SEPortrayer configuration.
+ *
+ * @param mapItem to be rendered, not null.
+ * @return this portrayer
+ */
+ public void paint(Scene2D scene, MapLayer mapItem) {
+ try (Stream<Presentation> stream = new
SEPortrayer().present(scene.grid, mapItem)) {
+ paint(scene, stream);
+ }
+ }
+
+ /**
+ * Render the given stream of Presentations.
+ *
+ * @param presentations to be rendered, not null.
+ * @return this portrayer
+ */
+ public void paint(Scene2D scene, Stream<Presentation> presentations) {
+ final PresentationToScene2D pts = new PresentationToScene2D(scene);
+ pts.render(presentations);
+ }
+
+ /**
+ * Compute visual intersection of given map using default SEPortrayer
configuration.
+ *
+ * @param layer to be processed, not null.
+ * @param mask intersection mask, not null.
+ * @return intersecting stream of presentations.
+ */
+ @Override
+ public Stream<Presentation> intersects(Scene2D scene, MapLayer layer,
Shape mask) {
+ Stream<Presentation> stream = new SEPortrayer().present(scene.grid,
layer);
+ return intersects(scene, stream, mask);
+ }
+
+ /**
+ * Compute visual intersection of given map using default SEPortrayer
configuration.
+ *
+ * @param presentations to be processed, not null.
+ * @param mask intersection mask, not null.
+ * @return intersecting stream of presentations.
+ */
+ public Stream<Presentation> intersects(Scene2D scene, Stream<Presentation>
presentations, Shape mask) {
+ final PresentationToScene2D pts = new PresentationToScene2D(scene);
+ return pts.intersects(presentations, mask);
+ }
+}
diff --git
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/SymbolizerPainter.java
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/SymbolizerCache.java
similarity index 64%
rename from
incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/SymbolizerPainter.java
rename to
incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/SymbolizerCache.java
index c954bca315..6057cd4679 100644
---
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/SymbolizerPainter.java
+++
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/SymbolizerCache.java
@@ -14,25 +14,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.sis.map.service;
-
-import java.awt.Graphics2D;
-import org.apache.sis.coverage.grid.GridGeometry;
-import org.apache.sis.map.MapLayer;
-import org.apache.sis.style.se1.Symbolizer;
+package org.apache.sis.map.service.se1;
/**
+ * Subclassed by symbolizer who have resource caches.
+ * This could contain images or models.
+ * The cache instance may be shared by multiple {@link SymbolizerToScene2D}
instances
+ * using method {@link
SymbolizerToScene2D#sharedCache(org.apache.sis.internal.renderer.SymbolizerCache)
}.
*
* @author Johann Sorel (Geomatys)
*/
-public abstract class SymbolizerPainter {
-
- protected final Symbolizer<?> symbolizer;
-
- public SymbolizerPainter(Symbolizer<?> symbolizer) {
- this.symbolizer = symbolizer;
- }
-
- public abstract void paint(Graphics2D g, GridGeometry gridGeometry,
MapLayer layer);
+public abstract class SymbolizerCache {
}
diff --git
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/SymbolizerToScene2D.java
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/SymbolizerToScene2D.java
new file mode 100644
index 0000000000..c0e02476fc
--- /dev/null
+++
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/SymbolizerToScene2D.java
@@ -0,0 +1,192 @@
+/*
+ * 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.map.service.se1;
+
+import org.apache.sis.map.service.Scene2D;
+import java.awt.Shape;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.function.Consumer;
+import java.util.logging.Logger;
+import java.util.stream.Stream;
+import org.apache.sis.map.Presentation;
+import org.apache.sis.map.SEPresentation;
+import org.apache.sis.map.service.RenderingException;
+import org.apache.sis.style.se1.Symbolizer;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.ObjectConverters;
+import org.opengis.feature.Feature;
+import org.opengis.filter.Expression;
+
+/**
+ * Transforms a {@link Presentation} to Java2D graphics.
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public abstract class SymbolizerToScene2D<S extends Symbolizer> {
+
+ private static final List<SymbolizerToScene2D.Spi<?>> SPIS;
+ private static final Map<Class<?>,SymbolizerToScene2D.Spi<?>> SPI_MAP =
new HashMap<>();
+ static {
+ //collect all symbolizer SPI
+ List<SymbolizerToScene2D.Spi<?>> spis = new ArrayList<>();
+
ServiceLoader.load(SymbolizerToScene2D.Spi.class).iterator().forEachRemaining(spis::add);
+ SPIS = Collections.unmodifiableList(spis);
+ for (SymbolizerToScene2D.Spi<?> spi : SPIS) {
+ if (SPI_MAP.put(spi.getSymbolizerType(), spi) != null) {
+ throw new IllegalStateException("More then one
SymbolizerToScene2D.Spi is registered for type " +
spi.getSymbolizerType().getName());
+ }
+ }
+ }
+
+ protected static final Logger LOGGER =
Logger.getLogger("com.examind.sdk.render");
+ protected final Scene2D state;
+ protected final S symbolizer;
+
+ protected SymbolizerToScene2D(Scene2D state, S symbolizer) {
+ this.state = state;
+ this.symbolizer = symbolizer;
+ }
+
+ /**
+ * Define a shared cache instance.
+ * Shared caches avoid multiple loading of a resource (example : images,
models)
+ *
+ * @param cache not null
+ */
+ public void sharedCache(SymbolizerCache cache) {
+ }
+
+ /**
+ * Paint the given {@link SEPresentation}.
+ *
+ * @param presentation not null
+ * @param callback not null, can be used to create new presentation
treated in the rendering loop.
+ * @throws RenderingException
+ */
+ public abstract void paint(SEPresentation presentation,
Consumer<Stream<Presentation>> callback) throws RenderingException;
+
+ /**
+ * Test intersection of the given {@link SEPresentation}.
+ *
+ * @param presentation not null
+ * @param callback not null, can be used to test new presentations treated
in the rendering loop.
+ * @throws RenderingException
+ */
+ public boolean intersects(SEPresentation presentation, Shape mask,
Consumer<Stream<Presentation>> callback) throws RenderingException {
+ return false;
+ }
+
+ static <T> T evaluate(Feature feature, Expression<? super Feature,?> exp,
Expression<? super Feature,?> fallback, Class<T> clazz) {
+ T value = null;
+ if (exp != null) {
+ value = ObjectConverters.convert(exp.apply(feature), clazz);
+ }
+ if (value == null && fallback != null) {
+ value = ObjectConverters.convert(fallback.apply(feature), clazz);
+ }
+ return value;
+ }
+
+ /**
+ * Create a symbolizer to scene processor.
+ *
+ * @param state not null
+ * @param symbolizer not null
+ * @return may be null if no Spi support this symbolizer.
+ * @throws RenderingException if the symbolizer is incorrectly defined or
some assets can not be resolved.
+ */
+ public static SymbolizerToScene2D<?> create(Scene2D state, Symbolizer<?>
symbolizer) throws RenderingException {
+ ArgumentChecks.ensureNonNull("symbolizer", symbolizer);
+ final Spi<Symbolizer> spi = (Spi<Symbolizer>)
getSpi(symbolizer.getClass());
+ return spi == null ? null : spi.create(state, symbolizer);
+ }
+
+ /**
+ * Create a symbolizer cache.
+ *
+ * @param symbolizer not null
+ * @return may be null if no Spi support this symbolizer.
+ * @throws RenderingException if the symbolizer is incorrectly defined or
some assets can not be resolved.
+ */
+ public static SymbolizerCache createCache(Symbolizer symbolizer) throws
RenderingException {
+ for (SymbolizerToScene2D.Spi spi : SPIS) {
+ final SymbolizerCache sts = spi.createCache(symbolizer);
+ if (sts != null) {
+ return sts;
+ }
+ }
+ return null;
+ }
+ /**
+ * Get the Spi capable to handle given symbolizer.
+ *
+ * @return may be null if no Spi support this symbolizer.
+ */
+ public static synchronized <T extends Symbolizer>
SymbolizerToScene2D.Spi<T> getSpi(Class<T> clazz) {
+ Spi<T> cdt = (Spi<T>) SPI_MAP.get(clazz);
+ if (cdt == null) {
+ for (SymbolizerToScene2D.Spi spi : SPIS) {
+ if (spi.getSymbolizerType().isAssignableFrom(clazz)) {
+ SPI_MAP.put(clazz, spi);
+ cdt = spi;
+ break;
+ }
+ }
+ }
+ return cdt;
+ }
+
+ /**
+ * Factory to create new transformation instances.
+ *
+ * @param <T> symbolizer type supported
+ */
+ public interface Spi<T extends Symbolizer> {
+
+ /**
+ * Returns the support symbolizer class.
+ * @return supported symbolizer class, not null.
+ */
+ Class<T> getSymbolizerType();
+
+ /**
+ * Create a cache for given {@link Symbolizer}.
+ *
+ * @param symbolizer not null
+ * @return cache or null if symbolizer is not supported or no cache is
needed.
+ * @throws RenderingException if symbolizer declaration contains
errors.
+ */
+ default SymbolizerCache createCache(T symbolizer) throws
RenderingException {
+ return null;
+ }
+
+ /**
+ * Create a transformation instance.
+ *
+ * @param state scene state, not null
+ * @param symbolizer not null
+ * @return instance or null if symbolizer is not supported
+ * @throws RenderingException if symbolizer declaration contains
errors.
+ */
+ SymbolizerToScene2D create(Scene2D state, T symbolizer) throws
RenderingException;
+ }
+}
diff --git
a/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/TextToScene2D.java
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/TextToScene2D.java
new file mode 100644
index 0000000000..4a282ff820
--- /dev/null
+++
b/incubator/src/org.apache.sis.portrayal.map/main/org/apache/sis/map/service/se1/TextToScene2D.java
@@ -0,0 +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.map.service.se1;
+
+import org.apache.sis.map.service.Scene2D;
+import java.awt.Shape;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+import org.apache.sis.map.Presentation;
+import org.apache.sis.map.SEPresentation;
+import org.apache.sis.map.service.RenderingException;
+import org.apache.sis.style.se1.TextSymbolizer;
+
+/**
+ * Support for PointSymbolizer rendering.
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public final class TextToScene2D extends
SymbolizerToScene2D<TextSymbolizer<?>> {
+
+ private TextToScene2D(Scene2D state, TextSymbolizer<?> symbolizer) {
+ super(state, symbolizer);
+ }
+
+ @Override
+ public void paint(SEPresentation presentation,
Consumer<Stream<Presentation>> callback) throws RenderingException {
+ final RenderedShape visual = createVisual(presentation);
+ if (visual != null) {
+ visual.paint(state.getGraphics());
+ }
+ }
+
+ @Override
+ public boolean intersects(SEPresentation presentation, Shape mask,
Consumer<Stream<Presentation>> callback) throws RenderingException {
+ final RenderedShape visual = createVisual(presentation);
+ if (visual != null) {
+ return visual.intersects(mask);
+ }
+ return false;
+ }
+
+ private RenderedShape createVisual(SEPresentation presentation) throws
RenderingException {
+ //todo
+ return null;
+ }
+
+ public static final class Spi implements
SymbolizerToScene2D.Spi<TextSymbolizer> {
+
+ @Override
+ public Class<TextSymbolizer> getSymbolizerType() {
+ return TextSymbolizer.class;
+ }
+
+ @Override
+ public SymbolizerToScene2D create(Scene2D state, TextSymbolizer
symbolizer) throws RenderingException {
+ return new TextToScene2D(state, symbolizer);
+ }
+ }
+}
diff --git
a/incubator/src/org.apache.sis.portrayal.map/test/org/apache/sis/map/service/GraphicsPortrayerTest.java
b/incubator/src/org.apache.sis.portrayal.map/test/org/apache/sis/map/service/GraphicsPortrayerTest.java
new file mode 100644
index 0000000000..c584b16741
--- /dev/null
+++
b/incubator/src/org.apache.sis.portrayal.map/test/org/apache/sis/map/service/GraphicsPortrayerTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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 andz
+ * limitations under the License.
+ */
+package org.apache.sis.map.service;
+
+import java.awt.Color;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+import java.util.Arrays;
+import java.util.stream.Stream;
+import org.apache.sis.coverage.grid.GridExtent;
+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.map.MapLayer;
+import org.apache.sis.map.Presentation;
+import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.CommonCRS;
+import org.apache.sis.storage.FeatureSet;
+import org.apache.sis.storage.base.MemoryFeatureSet;
+import org.apache.sis.style.se1.FeatureTypeStyle;
+import org.apache.sis.style.se1.LineSymbolizer;
+import org.apache.sis.style.se1.Rule;
+import org.apache.sis.style.se1.Symbolizer;
+import org.apache.sis.style.se1.Symbology;
+import static org.junit.Assert.*;
+import org.junit.Test;
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.LineString;
+import org.opengis.feature.Feature;
+import org.opengis.feature.FeatureType;
+
+/**
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public class GraphicsPortrayerTest {
+
+ private static final GridGeometry WORLD = new GridGeometry(
+ new GridExtent(360, 180),
+ CRS.getDomainOfValidity(CommonCRS.WGS84.normalizedGeographic()),
+ GridOrientation.REFLECTION_Y);
+ private static final GeometryFactory GF = new GeometryFactory();
+
+ /**
+ * Creates a new test case.
+ */
+ public GraphicsPortrayerTest() {
+ }
+
+ /**
+ * Sanity test for rendering.
+ */
+ @Test
+ public void testRendering() throws RenderingException {
+
+ final FeatureTypeBuilder ftb = new FeatureTypeBuilder();
+ ftb.setName("test");
+
ftb.addAttribute(Geometry.class).setName("geom").addRole(AttributeRole.DEFAULT_GEOMETRY);
+ final FeatureType ft = ftb.build();
+
+ final Feature feature = ft.newInstance();
+ LineString geom = GF.createLineString(new Coordinate[]{new
Coordinate(0,0), new Coordinate(0,90)});
+ geom.setUserData(WORLD.getCoordinateReferenceSystem());
+ feature.setPropertyValue("geom", geom);
+
+ final FeatureSet featureSet = new MemoryFeatureSet(null, ft,
Arrays.asList(feature));
+
+ final LineSymbolizer<Feature> symbolizer = new
LineSymbolizer<>(FeatureTypeStyle.FACTORY);
+ final Symbology style = createStyle(symbolizer);
+
+ final MapLayer item = new MapLayer();
+ item.setData(featureSet);
+ item.setStyle(style);
+
+ BufferedImage image = new GraphicsPortrayer()
+ .setDomain(WORLD)
+ .portray(item)
+ .getImage();
+
+ int color1 = image.getRGB(180, 45);
+ int color2 = image.getRGB(179, 45);
+ assertTrue(color1 == Color.BLACK.getRGB());
+ assertTrue(color2 == new Color(0,0,0,0).getRGB());
+ }
+
+ /**
+ * Sanity test for intersection.
+ */
+ @Test
+ public void testIntersects() throws RenderingException {
+
+ final FeatureTypeBuilder ftb = new FeatureTypeBuilder();
+ ftb.setName("test");
+
ftb.addAttribute(Geometry.class).setName("geom").addRole(AttributeRole.DEFAULT_GEOMETRY);
+ final FeatureType ft = ftb.build();
+
+ final Feature feature = ft.newInstance();
+ LineString geom = GF.createLineString(new Coordinate[]{new
Coordinate(0,0), new Coordinate(0,90)});
+ geom.setUserData(WORLD.getCoordinateReferenceSystem());
+ feature.setPropertyValue("geom", geom);
+
+ final FeatureSet featureSet = new MemoryFeatureSet(null, ft,
Arrays.asList(feature));
+
+ final LineSymbolizer<Feature> symbolizer = new
LineSymbolizer<>(FeatureTypeStyle.FACTORY);
+ final Symbology style = createStyle(symbolizer);
+
+
+ final MapLayer item = new MapLayer();
+ item.setData(featureSet);
+ item.setStyle(style);
+
+ //rectangle outside, no intersection
+ try (Stream<Presentation> result = new GraphicsPortrayer()
+ .setDomain(WORLD)
+ .intersects(item, new Rectangle(7,50,4,4))) {
+ assertEquals(0, result.count());
+ }
+
+ //rectangle overlaps, intersects
+ try (Stream<Presentation> result = new GraphicsPortrayer()
+ .setDomain(WORLD)
+ .intersects(item, new Rectangle(178,50,4,4))) {
+ assertEquals(1, result.count());
+ }
+ }
+
+ private static Symbology createStyle(Symbolizer symbolizer) {
+ final Symbology style = new Symbology();
+ final FeatureTypeStyle fts = new FeatureTypeStyle();
+ style.featureTypeStyles().add(fts);
+
+ final Rule<Feature> rule = new Rule<>(FeatureTypeStyle.FACTORY);
+ rule.symbolizers().add(symbolizer);
+ fts.rules().add(rule);
+
+ return style;
+ }
+
+}
\ No newline at end of file