geotools从地图上删除选择要素图层

时间:2018-10-02 14:10:06

标签: javafx geotools

我想从地图中选择要素图层以将其删除。

首先,我以图形方式添加一些类似于此图像的多边形:

enter image description here

现在,我想使用MOUSE_MOVED事件选择任何红色要素,然后右键单击一个多边形并显示带有删除项的contextMenu:

mapCanvas.addEventHandler(MouseEvent.MOUSE_MOVED, e -> {
            Point2D.Double current = transformScrToWrl(e.getX(), e.getY());
            selectLayer(current.getX(), current.getY());
});

...

MenuItem item1 = new MenuItem(LangHelper.getString("delete"));
    item1.setOnAction(event -> {
        Point2D.Double pnt = transformScrToWrl(x, y);
        deletePolygon(pnt.getX(), pnt.getY());
    });

ContextMenu menu = new ContextMenu(item1);

mapCanvas.setOnContextMenuRequested(e -> {
    x = e.getX();
    y = e.getY();
    menu.show(mapCanvas, e.getScreenX(), e.getScreenY());
    e.consume();
});

...

private void deletePolygon(double x, double y) {
    //Todo complete delete polygon

    map.removeLayer(selectedLayer.get());
    refreshMap();
}

我将来自geotools的相同SelectionLab selectFeature()代码放在selectLayer(x, y)上,进行了一些更改。但是代码无法正常工作。

enter image description here

selectLayer选择青色的要素地图,而不选择黄色的先前插入的红色多边形。是否有任何代码可以选择以图形方式插入的多边形之一?对不起,我的英语。

我的代码。 DistribTabPageController

import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKTReader;
import com.vividsolutions.jts.io.WKTWriter;
import cu.edu.cujae.guatini.Main;
import cu.edu.cujae.guatini.dao.DistributionDAO;
import cu.edu.cujae.guatini.helper.LangHelper;
import cu.edu.cujae.guatini.model.Distribution;
import cu.edu.cujae.guatini.model.Species;
import javafx.application.Platform;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.util.Duration;
import org.geotools.data.FileDataStore;
import org.geotools.data.FileDataStoreFinder;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.DefaultFeatureCollection;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geometry.DirectPosition2D;
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.GTRenderer;
import org.geotools.renderer.lite.StreamingRenderer;
import org.geotools.styling.*;
import org.geotools.styling.Stroke;
import org.jfree.fx.FXGraphics2D;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.identity.FeatureId;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.Polygon;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;

public class DistribTabPageController {
    @FXML
    private Canvas mapCanvas;

    private MapContent map;
    private GraphicsContext gc;

    private GeometryFactory factory;

    private ObservableList<Coordinate> coordList;
    private ObservableList<Layer> pointList;
    private ObservableList<Geometry> polygonListInsert, polygonListUpdate, polygonListDelete;

    private Style stylePolygon, stylePoint;

    private String geometryAttributeName = "the_geom";

    private Species species;

    private boolean repaint = true;

    private double baseDrageX, baseDrageY, x, y;

    private static final double PAINT_HZ = 60.0;

    private WKTReader reader;
    private WKTWriter writer;

    private ObjectProperty<Layer> selectedLayer;

    /*
     * Factories that we will use to create style and filter objects
     */
    private StyleFactory sf;
    private FilterFactory2 ff;

    /*
     * Convenient constants for the type of feature geometry in the shapefile
     */
    private enum GeomType {
        POINT,
        LINE,
        POLYGON
    }

    /*
     * Some default style variables
     */
    private static final Color LINE_COLOUR = Color.BLUE, FILL_COLOUR = Color.CYAN, SELECTED_COLOUR = Color.YELLOW;
    private static final float OPACITY = 1.0f, LINE_WIDTH = 1.0f, POINT_SIZE = 10.0f;

    private SimpleFeatureSource featureSource;

    private GeomType geometryType;

    @FXML
    private void initialize() {
        sf = CommonFactoryFinder.getStyleFactory();
        ff = CommonFactoryFinder.getFilterFactory2();

        selectedLayer = new SimpleObjectProperty<>();

        writer = new WKTWriter();
        reader = new WKTReader();

        coordList = FXCollections.observableArrayList();
        pointList = FXCollections.observableArrayList();

        polygonListInsert = FXCollections.observableArrayList();
        polygonListUpdate = FXCollections.observableArrayList();
        polygonListDelete = FXCollections.observableArrayList();

        stylePolygon = SLD.createPolygonStyle(Color.RED, Color.RED, 0.5f);
        stylePoint = SLD.createPointStyle("Circle", Color.RED, Color.RED, 0.5f, 10);

        factory = JTSFactoryFinder.getGeometryFactory();
        gc = mapCanvas.getGraphicsContext2D();
        initMap();
        drawMap(gc);
        initEvent();
        initPaintThread();
    }

    public void setDistributions(Species species) {
        this.species = species;

        ObservableList<Distribution> dist = DistributionDAO.getDistributionsBySpecies(species.getId());

        if (!dist.isEmpty()) {
            dist.forEach(distr -> {
                try {
                    Geometry geometry = reader.read(distr.getGeometry());
                    addGeometry(geometry, stylePolygon);
                    polygonListUpdate.add(geometry);
                } catch (ParseException e) {
                    e.printStackTrace();
                }

            });
        }
    }

    private void convertAndInsert(double x, double y) {
        Point2D.Double pointD = transformScrToWrl(x, y);
        addPoint(pointD.getX(), pointD.getY());
    }

    private Point2D.Double transformScrToWrl(double x, double y) {
        Point2D.Double pointS = new Point2D.Double(x, y);
        Point2D.Double pointD = new Point2D.Double();
        map.getViewport().getScreenToWorld().transform(pointS, pointD);
        return pointD;
    }

    private Layer addGeometry(Geometry geom, Style style) {
        SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
        builder.setName("Location");
        builder.setCRS(DefaultGeographicCRS.WGS84);
        builder.add(geometryAttributeName, Geometry.class);

        SimpleFeatureType type = builder.buildFeatureType();
        SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(type);
        featureBuilder.add(geom);

        SimpleFeature feature = featureBuilder.buildFeature(null);

        DefaultFeatureCollection simpleFeatures = new DefaultFeatureCollection();
        simpleFeatures.add(feature);

        Layer layer = new FeatureLayer(simpleFeatures, style);
        map.addLayer(layer);
        refreshMap();
        return layer;
    }

    private void addPoint(double x, double y) {
        Coordinate coordinate = new Coordinate(x, y);
        Layer layer = addGeometry(factory.createPoint(coordinate), stylePoint);
        coordList.add(coordinate);
        pointList.add(layer);
    }

    private void addPolygon(Coordinate[] coordinates) {
        if (coordList.size() > 3) {
            Geometry polygon = factory.createPolygon(coordinates);
            addGeometry(polygon, stylePolygon);
            polygonListInsert.add(polygon);
        }
        map.layers().removeAll(pointList);
        pointList.clear();
        coordList.clear();
    }

    private void deletePolygon(double x, double y) {
        //Todo completar delete polygon

        map.removeLayer(selectedLayer.get());
        refreshMap();
    }

    private void initMap() {
        try {
            map = loadMap();
            map.getViewport().setScreenArea(new Rectangle((int) mapCanvas.getWidth(), (int) mapCanvas.getHeight()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void drawMap(GraphicsContext gc) {
        if (!repaint) {
            return;
        }
        repaint = false;
        StreamingRenderer draw = new StreamingRenderer();
        draw.setMapContent(map);
        FXGraphics2D graphics = new FXGraphics2D(gc);
        graphics.setBackground(Color.WHITE);
        graphics.clearRect(0, 0, (int) mapCanvas.getWidth(), (int) mapCanvas.getHeight());
        Rectangle rectangle = new Rectangle((int) mapCanvas.getWidth(), (int) mapCanvas.getHeight());
        draw.paint(graphics, rectangle, map.getViewport().getBounds());
    }

    /*
     *initial for mouse event
     */
    private void initEvent() {
        /*
         * setting the original coordinate
         */

        mapCanvas.addEventFilter(MouseEvent.ANY, e -> mapCanvas.requestFocus());

        mapCanvas.addEventHandler(MouseEvent.MOUSE_MOVED, e -> {
            Point2D.Double current = transformScrToWrl(e.getX(), e.getY());
            selectLayer(current.getX(), current.getY());
        });

        mapCanvas.addEventHandler(MouseEvent.MOUSE_PRESSED, e -> {
            baseDrageX = e.getSceneX();
            baseDrageY = e.getSceneY();
            e.consume();
        });

        MenuItem item1 = new MenuItem(LangHelper.getString("delete"));
        item1.setOnAction(event -> {

            //saveImage(map, file, (int) mapCanvas.getWidth());
            Point2D.Double pnt = transformScrToWrl(x, y);
            deletePolygon(pnt.getX(), pnt.getY());

        });
        ContextMenu menu = new ContextMenu(item1);

        mapCanvas.setOnContextMenuRequested(e -> {
            x = e.getX();
            y = e.getY();
            //show only if selectedLayer is not null
            if (selectedLayer.get() != null){
                menu.show(mapCanvas, e.getScreenX(), e.getScreenY());
            }

            e.consume();
        });

        mapCanvas.addEventHandler(KeyEvent.KEY_RELEASED, e -> {
            System.out.println("key released");
            if (e.getCode() == KeyCode.CONTROL && !coordList.isEmpty()) {

                coordList.add(coordList.get(0));

                addPolygon(coordList.toArray(new Coordinate[]{}));

                refreshMap();
            }
        });

        /*
         * translate according to the mouse drag
         */
        mapCanvas.addEventHandler(MouseEvent.MOUSE_DRAGGED, e -> {
            if (e.isControlDown()) {
                convertAndInsert(e.getX(), e.getY());
            } else {
                double difX = e.getSceneX() - baseDrageX;
                double difY = e.getSceneY() - baseDrageY;
                baseDrageX = e.getSceneX();
                baseDrageY = e.getSceneY();
                DirectPosition2D newPos = new DirectPosition2D(difX, difY);
                DirectPosition2D result = new DirectPosition2D();
                map.getViewport().getScreenToWorld().transform(newPos, result);
                ReferencedEnvelope env = new ReferencedEnvelope(map.getViewport().getBounds());
                env.translate(env.getMinimum(0) - result.x, env.getMaximum(1) - result.y);
                doSetDisplayArea(env);
            }
            e.consume();

        });
        /*
         * double clicks to restore to original map
         */
        mapCanvas.addEventHandler(MouseEvent.MOUSE_CLICKED, t -> {
            menu.hide();
            if (t.getClickCount() > 1) {
                doSetDisplayArea(map.getMaxBounds());
            }
            t.consume();
        });

        mapCanvas.addEventHandler(MouseEvent.MOUSE_RELEASED, e -> {
            if (e.isControlDown()) {
                convertAndInsert(e.getX(), e.getY());
            }
            e.consume();
        });

        /*
         * scroll for zoom in and out
         */
        mapCanvas.addEventHandler(ScrollEvent.SCROLL, e -> {
            ReferencedEnvelope envelope = map.getViewport().getBounds();
            double percent = -e.getDeltaY() / mapCanvas.getWidth();
            double width = envelope.getWidth();
            double height = envelope.getHeight();
            double deltaW = width * percent;
            double deltaH = height * percent;
            envelope.expandBy(deltaW, deltaH);
            doSetDisplayArea(envelope);
            e.consume();
        });
    }

    private void selectLayer(double x, double y) {

        //Todo SelectionLab code from geotools.org doesnt work

        System.out.println("Mouse moved at: " + x + " : " + y);

        /*
         * Construct a 5x5 pixel rectangle centred on the mouse click position
         */
        Rectangle screenRect = new Rectangle(((int) x - 2), ((int) x - 2), 5, 5);

        /*
         * Transform the screen rectangle into bounding box in the coordinate
         * reference system of our map context. Note: we are using a naive method
         * here but GeoTools also offers other, more accurate methods.
         */
        AffineTransform screenToWorld = map.getViewport().getScreenToWorld();
        Rectangle2D worldRect = screenToWorld.createTransformedShape(screenRect).getBounds2D();
        ReferencedEnvelope bbox =
                new ReferencedEnvelope(
                        worldRect, map.getCoordinateReferenceSystem());

        /*
         * Create a Filter to select features that intersect with
         * the bounding box
         */
        Filter filter = ff.intersects(ff.property(geometryAttributeName), ff.literal(bbox));

        /*
         * Use the filter to identify the selected features
         */
        try {
            SimpleFeatureCollection selectedFeatures = featureSource.getFeatures(filter);

            Set<FeatureId> IDs = new HashSet<>();
            try (SimpleFeatureIterator iter = selectedFeatures.features()) {
                while (iter.hasNext()) {
                    SimpleFeature feature = iter.next();
                    IDs.add(feature.getIdentifier());

                    System.out.println("   " + feature.getIdentifier());
                }
            }

            if (IDs.isEmpty()) {
                System.out.println("   no feature selected");
            }

            displaySelectedFeatures(IDs);

        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }

    private void refreshMap() {
        ReferencedEnvelope env = new ReferencedEnvelope(map.getViewport().getBounds());
        doSetDisplayArea(env);
    }

    private void initPaintThread() {
        ScheduledService<Boolean> svc = new ScheduledService<Boolean>() {
            protected Task<Boolean> createTask() {
                return new Task<Boolean>() {
                    protected Boolean call() {
                        Platform.runLater(() -> drawMap(gc));
                        return true;
                    }
                };
            }
        };
        svc.setPeriod(Duration.millis(1000.0 / PAINT_HZ));
        svc.start();
    }

    private void doSetDisplayArea(ReferencedEnvelope envelope) {
        map.getViewport().setBounds(envelope);
        repaint = true;
    }

    public void saveImage(MapContent map, File file, int imageWidth) {
        GTRenderer renderer = new StreamingRenderer();
        renderer.setMapContent(map);

        Rectangle imageBounds;
        ReferencedEnvelope mapBounds;
        try {
            mapBounds = map.getMaxBounds();
            double heightToWidth = mapBounds.getSpan(1) / mapBounds.getSpan(0);
            imageBounds = new Rectangle(
                    0, 0, imageWidth, (int) Math.round(imageWidth * heightToWidth));

        } catch (Exception e) {
            // failed to access map layers
            throw new RuntimeException(e);
        }

        BufferedImage image = new BufferedImage(imageBounds.width, imageBounds.height, BufferedImage.TYPE_INT_RGB);

        Graphics2D gr = image.createGraphics();
        gr.setPaint(Color.WHITE);
        gr.fill(imageBounds);

        try {
            renderer.paint(gr, imageBounds, mapBounds);
            ImageIO.write(image, "jpeg", file);

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public ObservableList<Distribution> getDistributionsInsert() {
        return list(writer, polygonListInsert);
    }

    public ObservableList<Distribution> getDistributionsUpdate() {
        return list(writer, polygonListUpdate);
    }

    public ObservableList<Distribution> getDistributionsDelete() {
        return list(writer, polygonListDelete);
    }

    private ObservableList<Distribution> list(WKTWriter writer, ObservableList<Geometry> polygonList) {
        return polygonList.stream()
                .map(geometry -> {
                    Distribution distribution = new Distribution();
                    distribution.setSpecies(species.getId());
                    distribution.setGeometry(writer.write(geometry));
                    return distribution;
                })
                .collect(Collectors.toCollection(FXCollections::observableArrayList));
    }


    // docs end select features

    // docs start display selected

    /**
     * Sets the display to paint selected features yellow and unselected features in the default
     * style.
     *
     * @param IDs identifiers of currently selected features
     */
    public void displaySelectedFeatures(Set<FeatureId> IDs) {
        Style style;

        if (IDs.isEmpty()) {
            style = createDefaultStyle();

        } else {
            style = createSelectedStyle(IDs);
        }

        //here i put selected layer
        selectedLayer.set(map.layers().get(0));
        ((FeatureLayer) selectedLayer.get()).setStyle(style);
        refreshMap();
    }
    // docs end display selected

    // docs start default style

    /**
     * Create a default Style for feature display
     */
    private Style createDefaultStyle() {
        Rule rule = createRule(LINE_COLOUR, FILL_COLOUR);

        FeatureTypeStyle fts = sf.createFeatureTypeStyle();
        fts.rules().add(rule);

        Style style = sf.createStyle();
        style.featureTypeStyles().add(fts);
        return style;
    }
    // docs end default style

    // docs start selected style

    /**
     * Create a Style where features with given IDs are painted yellow, while others are painted
     * with the default colors.
     */
    private Style createSelectedStyle(Set<FeatureId> IDs) {
        Rule selectedRule = createRule(SELECTED_COLOUR, SELECTED_COLOUR);
        selectedRule.setFilter(ff.id(IDs));

        Rule otherRule = createRule(LINE_COLOUR, FILL_COLOUR);
        otherRule.setElseFilter(true);

        FeatureTypeStyle fts = sf.createFeatureTypeStyle();
        fts.rules().add(selectedRule);
        fts.rules().add(otherRule);

        Style style = sf.createStyle();
        style.featureTypeStyles().add(fts);
        return style;
    }
    // docs end selected style

    // docs start create rule

    /**
     * Helper for createXXXStyle methods. Creates a new Rule containing a Symbolizer tailored to the
     * geometry type of the features that we are displaying.
     */
    private Rule createRule(Color outlineColor, Color fillColor) {
        Symbolizer symbolizer = null;
        Fill fill;
        Stroke stroke = sf.createStroke(ff.literal(outlineColor), ff.literal(LINE_WIDTH));

        switch (geometryType) {
            case POLYGON:
                fill = sf.createFill(ff.literal(fillColor), ff.literal(OPACITY));
                symbolizer = sf.createPolygonSymbolizer(stroke, fill, geometryAttributeName);
                break;

            case LINE:
                symbolizer = sf.createLineSymbolizer(stroke, geometryAttributeName);
                break;

            case POINT:
                fill = sf.createFill(ff.literal(fillColor), ff.literal(OPACITY));

                Mark mark = sf.getCircleMark();
                mark.setFill(fill);
                mark.setStroke(stroke);

                Graphic graphic = sf.createDefaultGraphic();
                graphic.graphicalSymbols().clear();
                graphic.graphicalSymbols().add(mark);
                graphic.setSize(ff.literal(POINT_SIZE));

                symbolizer = sf.createPointSymbolizer(graphic, geometryAttributeName);
        }

        Rule rule = sf.createRule();
        rule.symbolizers().add(symbolizer);
        return rule;
    }

    private MapContent loadMap() throws IOException {
        if (map == null || map.layers().isEmpty()) {
            map = new MapContent();
            URL source = Main.class.getResource("resource/map/CUB_adm0.shp");
            FileDataStore store = FileDataStoreFinder.getDataStore(source);
            featureSource = store.getFeatureSource();

            setGeometry();

            Style style = SLD.createSimpleStyle(featureSource.getSchema());
            Layer layer = new FeatureLayer(featureSource, style);
            map.addLayer(layer);
        }
        return map;
    }

    private void setGeometry() {
        GeometryDescriptor geomDesc = featureSource.getSchema().getGeometryDescriptor();
        geometryAttributeName = geomDesc.getLocalName();

        Class<?> clazz = geomDesc.getType().getBinding();

        if (Polygon.class.isAssignableFrom(clazz) || MultiPolygon.class.isAssignableFrom(clazz)) {
            geometryType = GeomType.POLYGON;

        } else if (LineString.class.isAssignableFrom(clazz)
                || MultiLineString.class.isAssignableFrom(clazz)) {

            geometryType = GeomType.LINE;

        } else {
            geometryType = GeomType.POINT;
        }
    }

}

还有fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.canvas.Canvas?>
<?import javafx.scene.layout.Pane?>
<Pane xmlns="http://javafx.com/javafx"
      xmlns:fx="http://javafx.com/fxml"
      fx:controller="cu.edu.cujae.guatini.controller.speciesTabController.DistribTabPageController">
    <Canvas fx:id="mapCanvas" height="300.0" width="700.0"/>
</Pane>

我正在使用geotools 19.2并在fxml中显示mapCanvas需要ejml依赖性。谢谢你,我的英语很抱歉。

1 个答案:

答案 0 :(得分:0)

您看到的行为正是您要求程序执行的操作。在selectLayer中,您将在featureSource中搜索与单击周围的框相交的要素。 featureSource包含CUB_adm0.shp中的功能(已加载到loadMap中)。

如果要从多边形图层中选择(并删除)要素,则需要在该图层中搜索相交的多边形。看着addGeometry,似乎您为每个多边形创建了一个新图层,但没有跟踪它们。我将使用单个全局层来存储多边形,然后在selectLayer中使用它来查找要删除的多边形。