/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2014, Geomatys * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotoolkit.gui.javafx.render2d.shape; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.MultiPolygon; import com.vividsolutions.jts.geom.Polygon; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.logging.Level; import javafx.application.Platform; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.scene.paint.Paint; import javafx.scene.shape.Circle; import javafx.scene.shape.Rectangle; import org.apache.sis.util.Utilities; import org.apache.sis.referencing.CRS; import org.apache.sis.util.Utilities; import org.geotoolkit.display2d.canvas.J2DCanvas; import org.geotoolkit.geometry.jts.JTS; import org.geotoolkit.gui.javafx.render2d.FXMap; import org.geotoolkit.gui.javafx.render2d.FXMapDecoration; import org.geotoolkit.gui.javafx.render2d.edition.EditionHelper; import org.geotoolkit.internal.Loggers; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.CoordinateOperation; /** * * @author Johann Sorel (Geomatys) */ public class FXGeometryLayer extends Pane implements FXMapDecoration{ private FXMap map; private final ObservableList<Geometry> geoms = FXCollections.observableArrayList(); private final PropertyChangeListener canvasListener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { final String propName = evt.getPropertyName(); if(J2DCanvas.ENVELOPE_KEY.equals(propName) || J2DCanvas.OBJECTIVE_CRS_KEY.equals(propName) || J2DCanvas.TRANSFORM_KEY.equals(propName)){ updateGraphics(); } } }; private Paint fill = new Color(0, 0, 0, 0.4); private EditionHelper.EditionGeometry editGeom; public FXGeometryLayer() { geoms.addListener((ListChangeListener.Change<? extends Geometry> c) -> { updateGraphics(); }); //clip content widthProperty().addListener(new ChangeListener(){ public void changed(ObservableValue observable, Object oldValue, Object newValue) { setClip(new Rectangle(getWidth(), getHeight())); } }); heightProperty().addListener(new ChangeListener(){ public void changed(ObservableValue observable, Object oldValue, Object newValue) { setClip(new Rectangle(getWidth(), getHeight())); } }); //disable cache, may have many and large geometries setCache(false); setCacheShape(false); } public void setFill(Paint fill) { this.fill = fill; } public Paint getFill() { return fill; } public ObservableList<Geometry> getGeometries() { return geoms; } private synchronized void updateGraphics(){ final CoordinateReferenceSystem dispCrs = (map==null) ? null : map.getCanvas().getDisplayCRS(); final List<Node> shapes = new ArrayList<>(); final List<Coordinate> coords = new ArrayList<>(); //defensive copy for(Geometry geom : geoms.toArray(new Geometry[0])){ if(geom==null) continue; try{ final CoordinateReferenceSystem geomcrs = CRS.getHorizontalComponent(JTS.findCoordinateReferenceSystem(geom)); if(dispCrs==null || geomcrs==null || Utilities.equalsIgnoreMetadata(geomcrs, dispCrs)){ //do nothing }else{ geom = JTS.transform(geom, CRS.findOperation(geomcrs, dispCrs, null).getMathTransform()); } coords.addAll(Arrays.asList(geom.getCoordinates())); final FXGeometry fxgeom = new FXGeometry(geom); if(geom instanceof Polygon || geom instanceof MultiPolygon){ fxgeom.setFill(fill); } shapes.add(fxgeom); }catch(Exception ex){ Loggers.JAVAFX.log(Level.WARNING, ex.getMessage(), ex); } } //create circles where there are points if(!coords.isEmpty()){ final Group group = new Group(); group.setCache(false); for(Coordinate c : coords){ final Node vertice = createVerticeNode(c,false); if(vertice!=null){ group.getChildren().add(vertice); } } shapes.add(group); } //paint the selected node selectedNode : if(editGeom != null && editGeom.hasNodeSelected()){ Geometry candidate = editGeom.geometry.get(); try{ final CoordinateReferenceSystem geomcrs = CRS.getHorizontalComponent(JTS.findCoordinateReferenceSystem(candidate)); if(dispCrs==null || geomcrs==null || Utilities.equalsIgnoreMetadata(geomcrs, dispCrs)){ //do nothing }else{ CoordinateOperation op = CRS.findOperation(geomcrs, dispCrs, null); candidate = JTS.transform(editGeom.geometry.get(), op.getMathTransform()); } }catch(Exception ex){ Loggers.JAVAFX.log(Level.WARNING, ex.getMessage(), ex); } final Geometry geo = candidate.getGeometryN(editGeom.numSubGeom); if(editGeom.numHole < 0){ final Coordinate coord = geo.getCoordinates()[editGeom.selectedNode[0]]; shapes.add(createVerticeNode(coord, true)); }else{ final Polygon poly = (Polygon) geo; final LineString ls = poly.getInteriorRingN(editGeom.numHole); final Coordinate coord = ls.getCoordinates()[editGeom.selectedNode[0]]; shapes.add(createVerticeNode(coord, true)); } } //JAVAFX BUG : //getChildren().setAll(shapes); Platform.runLater(() -> { getChildren().setAll(shapes); }); } protected Node createVerticeNode(Coordinate c, boolean selected){ final Circle circle = new Circle(c.x, c.y, 5); circle.setFill(selected ? Color.BLUE : Color.WHITE); circle.setStroke(Color.BLACK); circle.setStrokeWidth(1); return circle; } @Override public void refresh() { } @Override public void dispose() { } @Override public void setMap2D(FXMap map) { if(this.map!=null){ this.map.getCanvas().removePropertyChangeListener(canvasListener); } this.map = map; if(this.map!=null){ this.map.getCanvas().addPropertyChangeListener(canvasListener); } } @Override public FXMap getMap2D() { return map; } @Override public Node getComponent() { return this; } public void setNodeSelection(EditionHelper.EditionGeometry editGeom) { this.editGeom = editGeom; } }