GeoTools 25-SNAPSHOT(Java11):无刷新在栅格地图添加标记
解决GeoTools添加标记不刷新地图的问题
我明白你的问题了——每次点击添加标记都调用addLayer()导致整个地图重载,这确实影响体验。核心问题是你每次都新建了一个FeatureLayer并加入地图,这会触发MapContent的图层变更事件,进而导致全图重绘。咱们换个思路,复用同一个标记图层,只更新它里面的点数据就可以了,这样地图就不会重载啦!
解决思路
- 提前创建一个全局的
DefaultFeatureCollection(用来存储所有标记点)和对应的FeatureLayer,并把这个图层添加到MapContent中,只做一次。 - 鼠标点击时,不再新建图层,而是直接往已有的
DefaultFeatureCollection里添加新的点Feature。 - 调用
featureCollection.fireFeaturesChanged()触发数据更新事件,让地图只刷新标记图层的内容,而不重载整个栅格底图。 - 如果需要用图片作为标记,只需要把SLD的点样式替换为图片符号器即可。
修改后的完整代码
import java.awt.Color; import java.io.File; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JToolBar; import org.geotools.coverage.grid.GridCoverage2D; import org.geotools.coverage.grid.io.AbstractGridFormat; import org.geotools.coverage.grid.io.GridCoverage2DReader; import org.geotools.coverage.grid.io.GridFormatFinder; import org.geotools.data.DataUtilities; import org.geotools.feature.DefaultFeatureCollection; import org.geotools.feature.SchemaException; import org.geotools.feature.simple.SimpleFeatureBuilder; import org.geotools.geometry.DirectPosition2D; import org.geotools.geometry.jts.JTSFactoryFinder; import org.geotools.map.FeatureLayer; import org.geotools.map.GridCoverageLayer; import org.geotools.map.MapContent; import org.geotools.styling.SLD; import org.geotools.styling.Style; import org.geotools.styling.StyleBuilder; import org.geotools.swing.JMapFrame; import org.geotools.swing.event.MapMouseEvent; import org.geotools.swing.tool.CursorTool; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.Point; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; public class Test { // 全局的标记点集合和对应的图层,复用这两个对象 private static DefaultFeatureCollection markerCollection; private static SimpleFeatureType MARKER_TYPE; public static void main(String[] args) throws Exception { try { JMapFrame mapFrame; File file = new File("mapfile.tif"); GridFormatFinder.scanForPlugins(); AbstractGridFormat format = GridFormatFinder.findFormat(file); GridCoverage2DReader reader = format.getReader(file); GridCoverage2D coverage = (GridCoverage2D) reader.read(null); // 创建地图内容并添加栅格底图 MapContent map = new MapContent(); map.setTitle("Marker test"); StyleBuilder sb = new StyleBuilder(); Style baseStyle = sb.createStyle(sb.createRasterSymbolizer()); map.addLayer(new GridCoverageLayer(coverage, baseStyle)); // ---------------------- 关键改动:提前创建标记图层 ---------------------- // 定义标记点的FeatureType MARKER_TYPE = DataUtilities.createType("MarkerLayer", "the_geom:Point"); // 创建全局的FeatureCollection存储所有标记点 markerCollection = new DefaultFeatureCollection(); // 创建标记图层的样式(这里用红圈,后面可以改成图片) Style markerStyle = SLD.createPointStyle("Circle", Color.BLACK, Color.RED, 1f, 15f); // 创建标记图层并添加到地图,只做一次 FeatureLayer markerLayer = new FeatureLayer(markerCollection, markerStyle); map.addLayer(markerLayer); // --------------------------------------------------------------------- mapFrame = new JMapFrame(map); mapFrame.enableToolBar(true); mapFrame.enableStatusBar(true); // 工具栏按钮 JToolBar toolBar = mapFrame.getToolBar(); Icon icon = new ImageIcon("icon.png"); JButton btn = new JButton(icon); btn.setToolTipText("Add mining manually"); toolBar.addSeparator(); toolBar.add(btn); // 鼠标点击添加标记的逻辑,现在只更新已有集合 btn.addActionListener(e -> mapFrame.getMapPane().setCursorTool(new CursorTool() { @Override public void onMouseClicked(MapMouseEvent ev) { try { DirectPosition2D p = ev.getWorldPos(); System.out.println(p.getX() + " -- " + p.getY()); // 调用方法往全局集合里添加新标记点 addMarkerToCollection(new Coordinate(p.getX(), p.getY())); } catch (Exception ex) { ex.printStackTrace(); } } })); mapFrame.setTitle("Aeron GIS Utility"); mapFrame.setSize(2030,830); mapFrame.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } /** * 往全局的标记集合里添加新的点,并触发更新事件 */ static void addMarkerToCollection(Coordinate coords) throws SchemaException { GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(); Point point = geometryFactory.createPoint(coords); SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(MARKER_TYPE); featureBuilder.add(point); // 生成唯一的Feature ID,避免重复 SimpleFeature feature = featureBuilder.buildFeature("marker_" + System.currentTimeMillis()); // 添加到全局集合 markerCollection.add(feature); // 触发集合变更事件,让地图刷新标记层 markerCollection.fireFeaturesChanged(); } // 如果需要用图片作为标记,替换上面的markerStyle为下面的方法创建的样式 static Style createImageMarkerStyle(String imagePath) { StyleBuilder sb = new StyleBuilder(); // 创建图片符号器,设置图片路径和大小 return sb.createStyle( sb.createPointSymbolizer( sb.createExternalGraphic(imagePath, "image/png"), 20 // 图片大小 ) ); } }
关键改动说明
- 把原来每次点击创建新图层的逻辑,改成提前创建全局的FeatureCollection和FeatureLayer,只添加一次到地图。
- 点击时只往这个集合里加新点,然后调用
fireFeaturesChanged()通知地图更新这个图层,这样只会刷新标记层,不会重载整个栅格底图。 - 新增了
createImageMarkerStyle方法,如果你需要用图片作为标记,只需要把创建markerStyle的代码替换成调用这个方法即可。
这样修改后,你点击添加标记时,地图就不会重载了,只会在指定位置显示新的标记点~
内容的提问来源于stack exchange,提问作者code_poetry
相关产品推荐
相关产品推荐

