使用FXGraphics2D与StreamingRenderer调整Canvas时渲染裁剪问题求助
GeoTools + JavaFX Canvas 放大渲染裁剪问题解决方案
问题描述
开发基于GeoTools(采用WMS+WFS实现网格功能)的地图应用时,JavaFX Canvas在窗口缩小时渲染正常,但放大窗口(Canvas同步自适应)时,地图内容被裁剪,无法显示完整的十字图形(Canvas本身已验证可正常自适应)。
复现代码
import java.awt.Color; import java.awt.Rectangle; import org.geotools.feature.DefaultFeatureCollection; import org.geotools.feature.simple.SimpleFeatureBuilder; import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.geotools.geometry.jts.JTSFactoryFinder; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.map.FeatureLayer; import org.geotools.map.Layer; import org.geotools.map.MapContent; import org.geotools.referencing.crs.DefaultGeographicCRS; import org.geotools.renderer.lite.StreamingRenderer; import org.geotools.styling.SLD; import org.geotools.styling.Style; import org.jfree.fx.FXGraphics2D; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.LineString; import javafx.animation.AnimationTimer; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; import javafx.scene.layout.BorderPane; import javafx.stage.Stage; public class ResizingTest extends Application { @Override public void start(Stage stage) { Canvas canvas = new Canvas(640, 480); BorderPane root = new BorderPane(canvas); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); // Create bindings for resizing. canvas.widthProperty().bind(root.widthProperty()); canvas.heightProperty().bind(root.heightProperty()); SimpleFeatureTypeBuilder lineFeatureTypeBuilder = new SimpleFeatureTypeBuilder(); lineFeatureTypeBuilder.setName("LineFeatureType"); lineFeatureTypeBuilder.setCRS(DefaultGeographicCRS.WGS84); lineFeatureTypeBuilder.add("the_geom", LineString.class, DefaultGeographicCRS.WGS84); SimpleFeatureType lineFeatureType = lineFeatureTypeBuilder.buildFeatureType(); SimpleFeatureBuilder lineFeatureBuilder = new SimpleFeatureBuilder(lineFeatureType); DefaultFeatureCollection lines = new DefaultFeatureCollection(); Coordinate[][] cs = { { new Coordinate(-1, 42), new Coordinate(4, 46) }, { new Coordinate(-1, 46), new Coordinate(4, 42) } }; GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(); for(Coordinate [] c : cs) { LineString line = geometryFactory.createLineString(c); lineFeatureBuilder.add(line); SimpleFeature feature = lineFeatureBuilder.buildFeature(null); lines.add(feature); } MapContent map = new MapContent(); Style style = SLD.createLineStyle(Color.RED, 1); Layer layer = new FeatureLayer(lines, style); map.addLayer(layer); //map.getViewport().setBounds(new ReferencedEnvelope(-1, 4, 42, 46, DefaultGeographicCRS.WGS84)); AnimationTimer loop = new AnimationTimer() { @Override public void handle(long now) { GraphicsContext g = canvas.getGraphicsContext2D(); FXGraphics2D graphics = new FXGraphics2D(g); graphics.setBackground(java.awt.Color.BLUE); Rectangle rectangle = new Rectangle( (int) canvas.getWidth(), (int) canvas.getHeight()); graphics.clearRect(0, 0, (int) rectangle.getWidth(), (int) rectangle.getHeight()); graphics.drawRect(100, 100, 100, 100); map.getViewport().setScreenArea(rectangle); // Necessary ? StreamingRenderer renderer = new StreamingRenderer(); renderer.setMapContent(map); renderer.paint(graphics, rectangle, map.getViewport().getBounds()); System.out.println("ScreenArea: " + map.getViewport().getScreenArea() + " - Viewport: " + map.getViewport().getBounds()); } }; loop.start(); } public static void main(String[] args) { launch(args); } }
问题现象
放大窗口时,Canvas可正常延伸,但地图渲染内容被裁剪,十字图形仅部分显示,裁剪区域无任何渲染内容:

解决方案
问题根源在于每次渲染时重复创建StreamingRenderer实例,以及未正确维护视口的地理边界与屏幕区域的同步。修改如下:
- 将
StreamingRenderer移到AnimationTimer外部初始化,避免重复创建:
// 在AnimationTimer之前创建渲染器 StreamingRenderer renderer = new StreamingRenderer(); renderer.setMapContent(map);
- 显式设置视口的地理边界,确保缩放时地图范围正确匹配:
取消注释代码中视口边界设置的行,初始化时固定地理范围:
// 初始化时设置视口地理边界 ReferencedEnvelope viewBounds = new ReferencedEnvelope(-1, 4, 42, 46, DefaultGeographicCRS.WGS84); map.getViewport().setBounds(viewBounds);
- 优化渲染逻辑,调整后的
AnimationTimer代码:
AnimationTimer loop = new AnimationTimer() { @Override public void handle(long now) { GraphicsContext g = canvas.getGraphicsContext2D(); FXGraphics2D graphics = new FXGraphics2D(g); graphics.setBackground(java.awt.Color.BLUE); Rectangle rectangle = new Rectangle( (int) canvas.getWidth(), (int) canvas.getHeight()); graphics.clearRect(0, 0, (int) rectangle.getWidth(), (int) rectangle.getHeight()); graphics.drawRect(100, 100, 100, 100); // 更新视口屏幕区域 map.getViewport().setScreenArea(rectangle); // 渲染地图 renderer.paint(graphics, rectangle, map.getViewport().getBounds()); System.out.println("ScreenArea: " + map.getViewport().getScreenArea() + " - Viewport: " + map.getViewport().getBounds()); } }; loop.start();
核心原理:
StreamingRenderer是重量级对象,重复创建会导致渲染状态异常,尤其是缩放时的坐标转换逻辑出错- 显式设置视口地理边界,确保GeoTools在计算渲染范围时能正确映射到Canvas的全部区域,避免裁剪
内容的提问来源于stack exchange,提问作者Elyas Tigre
相关产品推荐
相关产品推荐

