我想从地图中选择要素图层以将其删除。
首先,我以图形方式添加一些类似于此图像的多边形:
现在,我想使用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)
上,进行了一些更改。但是代码无法正常工作。
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依赖性。谢谢你,我的英语很抱歉。
答案 0 :(得分:0)
您看到的行为正是您要求程序执行的操作。在selectLayer
中,您将在featureSource
中搜索与单击周围的框相交的要素。 featureSource
包含CUB_adm0.shp
中的功能(已加载到loadMap
中)。
如果要从多边形图层中选择(并删除)要素,则需要在该图层中搜索相交的多边形。看着addGeometry
,似乎您为每个多边形创建了一个新图层,但没有跟踪它们。我将使用单个全局层来存储多边形,然后在selectLayer
中使用它来查找要删除的多边形。