/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2009-2011, Johann Sorel
* (C) 2011, 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.swing.render2d.control.edition;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPoint;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.operation.buffer.BufferParameters;
import com.vividsolutions.jts.operation.distance.DistanceOp;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.sis.feature.FeatureExt;
import org.geotoolkit.factory.FactoryFinder;
import org.geotoolkit.factory.Hints;
import org.geotoolkit.data.FeatureCollection;
import org.geotoolkit.data.FeatureIterator;
import org.geotoolkit.data.memory.GenericFilterFeatureIterator;
import org.geotoolkit.data.query.QueryBuilder;
import org.geotoolkit.geometry.jts.JTS;
import org.geotoolkit.gui.swing.render2d.JMap2D;
import org.geotoolkit.gui.swing.propertyedit.JFeatureOutLine;
import org.geotoolkit.map.FeatureMapLayer;
import org.apache.sis.referencing.CRS;
import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
import org.geotoolkit.util.StringUtilities;
import org.apache.sis.util.logging.Logging;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.expression.Expression;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.util.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.apache.sis.geometry.Envelopes;
import org.apache.sis.internal.feature.AttributeConvention;
import static org.apache.sis.util.ArgumentChecks.*;
import org.opengis.feature.AttributeType;
import org.opengis.feature.Feature;
import org.opengis.feature.FeatureType;
/**
*
* @author Johann Sorel
* @module
*/
public class EditionHelper {
private static final Logger LOGGER = Logging.getLogger("org.geotoolkit.gui.swing.render2d.control.edition");
private static final GeometryFactory GEOMETRY_FACTORY = new GeometryFactory();
private static final FilterFactory2 FF = (FilterFactory2) FactoryFinder.getFilterFactory(
new Hints(Hints.FILTER_FACTORY, FilterFactory2.class));
public static final Coordinate[] EMPTY_COORDINATE_ARRAY = new Coordinate[0];
public static class EditionGeometry{
public Geometry geometry = null;
public int numSubGeom = -1;
public int numHole = -1;
public final int[] selectedNode = new int[]{-1,-1};
public void reset() {
geometry = null;
numSubGeom = -1;
numHole = -1;
selectedNode[0] = -1;
selectedNode[1] = -1;
}
public void moveSelectedNode(final Coordinate newCoordinate){
if(geometry == null) return;
if(numSubGeom < 0) return;
if(selectedNode[0] < 0) return;
final Geometry geo = geometry.getGeometryN(numSubGeom);
if(numHole < 0){
final Coordinate[] coords = geo.getCoordinates();
coords[selectedNode[0]].setCoordinate(newCoordinate);
coords[selectedNode[1]].setCoordinate(newCoordinate);
geo.geometryChanged();
}else{
final Polygon p = (Polygon) geo;
final LineString ls = p.getInteriorRingN(numHole);
final Coordinate[] coords = ls.getCoordinates();
coords[selectedNode[0]].setCoordinate(newCoordinate);
coords[selectedNode[1]].setCoordinate(newCoordinate);
geo.geometryChanged();
}
geo.geometryChanged();
geometry.geometryChanged();
}
public void deleteSelectedNode() {
if(geometry == null) return;
if(numSubGeom < 0) return;
if(selectedNode[0] < 0) return;
final Geometry geo = geometry.getGeometryN(numSubGeom);
if(numHole < 0){
final List<Coordinate> newCoords = new ArrayList<Coordinate>();
newCoords.addAll(Arrays.asList(geo.getCoordinates()));
if(geo instanceof Polygon && selectedNode[0] == 0){
//remove first and last point
newCoords.remove(newCoords.size()-1);
newCoords.remove(0);
}else{
newCoords.remove(selectedNode[0]);
}
if(geometry instanceof Point){
//can not delete node from a Point
}else if(geometry instanceof MultiPoint){
//we have deleted the given subgeometry
final MultiPoint mp = (MultiPoint) geometry;
final List<Geometry> parts = new ArrayList<Geometry>();
for(int i=0,n=mp.getNumGeometries();i<n;i++){
parts.add(mp.getGeometryN(i));
}
if(parts.size()>1){
//remove only if we have more then one point
parts.remove(numSubGeom);
geometry = createMultiPoint(parts);
}
}else if(geometry instanceof LineString){
geometry = createLine(newCoords);
}else if(geometry instanceof MultiLineString){
final MultiLineString ml = (MultiLineString) geometry;
final List<Geometry> strs = new ArrayList<Geometry>();
for(int i=0,n=ml.getNumGeometries();i<n;i++){
if(i==numSubGeom){
final LineString str = createLine(newCoords);
strs.add(str);
}else{
strs.add(ml.getGeometryN(i));
}
}
geometry = createMultiLine(strs);
}else if(geometry instanceof Polygon){
final Polygon poly = (Polygon) geometry;
final List<LinearRing> holes = new ArrayList<LinearRing>();
for(int i=0,n=poly.getNumInteriorRing();i<n;i++){
holes.add((LinearRing) poly.getInteriorRingN(i));
}
geometry = createPolygon(newCoords, holes.toArray(new LinearRing[holes.size()]));
}else if(geometry instanceof MultiPolygon){
final MultiPolygon mp = (MultiPolygon) geometry;
final List<Geometry> polys = new ArrayList<Geometry>();
for(int i=0,n=mp.getNumGeometries();i<n;i++){
Polygon poly = (Polygon) mp.getGeometryN(i);
if(i==numSubGeom){
final List<LinearRing> holes = new ArrayList<LinearRing>();
for(int j=0,k=poly.getNumInteriorRing();j<k;j++){
holes.add((LinearRing) poly.getInteriorRingN(j));
}
poly = createPolygon(newCoords, holes.toArray(new LinearRing[holes.size()]));
}
polys.add(poly);
}
geometry = createMultiPolygon(polys);
}else{
throw new IllegalArgumentException("Unexpected geometry type :" + geometry.getClass());
}
selectedNode[0] = selectedNode[1] = -1;
}else{
throw new UnsupportedOperationException("not yet implemented");
}
}
@Override
public String toString() {
return "Selection\n"+ StringUtilities.toStringTree(geometry,numSubGeom,numHole,selectedNode[0]);
}
}
private final JMap2D map;
private final FeatureMapLayer editedLayer;
private boolean showAtributeditor;
/**
*
* @param map
* @param editedLayer
* @param showAtributeditor display the feature attribute editor after creation.
*/
public EditionHelper(final JMap2D map, final FeatureMapLayer editedLayer) {
this.map = map;
this.editedLayer = editedLayer;
this.showAtributeditor = true;
}
public boolean isShowAtributeditor() {
return showAtributeditor;
}
public void setShowAtributeditor(boolean showAtributeditor) {
this.showAtributeditor = showAtributeditor;
}
/**
* transform a mouse coordinate in JTS Geometry using the CRS of the map context
* @param mx : x coordinate of the mouse on the map (in pixel)
* @param my : y coordinate of the mouse on the map (in pixel)
* @return JTS geometry (corresponding to a square of 6x6 pixel around mouse coordinate)
*/
public Polygon mousePositionToGeometry(final int mx, final int my) throws NoninvertibleTransformException {
final Coordinate[] coord = new Coordinate[5];
int taille = 4;
coord[0] = toCoord(mx - taille, my - taille);
coord[1] = toCoord(mx - taille, my + taille);
coord[2] = toCoord(mx + taille, my + taille);
coord[3] = toCoord(mx + taille, my - taille);
coord[4] = coord[0];
final LinearRing lr1 = GEOMETRY_FACTORY.createLinearRing(coord);
return GEOMETRY_FACTORY.createPolygon(lr1, null);
}
public Point toJTS(final int x, final int y){
final Coordinate coord = toCoord(x, y);
final Point geom = GEOMETRY_FACTORY.createPoint(coord);
return geom;
}
public Coordinate toCoord(final int x, final int y){
final AffineTransform2D trs = map.getCanvas().getObjectiveToDisplay();
AffineTransform dispToObj;
try {
dispToObj = trs.createInverse();
} catch (NoninvertibleTransformException ex) {
dispToObj = new AffineTransform();
LOGGER.log(Level.WARNING, null, ex);
}
final double[] crds = new double[]{x,y};
dispToObj.transform(crds, 0, crds, 0, 1);
return new Coordinate(crds[0], crds[1]);
}
public Feature grabFeature(final int mx, final int my, final boolean style) {
if(editedLayer == null) return null;
Feature candidate = null;
FeatureCollection editgeoms = null;
FeatureIterator fi = null;
try {
final Polygon geo = mousePositionToGeometry(mx, my);
Filter flt = toFilter(geo, editedLayer);
//concatenate with temporal range if needed ----------------------------
for (final FeatureMapLayer.DimensionDef def : editedLayer.getExtraDimensions()) {
final CoordinateReferenceSystem crs = def.getCrs();
final org.opengis.geometry.Envelope canvasEnv = map.getCanvas().getVisibleEnvelope();
final org.opengis.geometry.Envelope dimEnv;
try {
dimEnv = Envelopes.transform(canvasEnv, crs);
} catch (TransformException ex) {
continue;
}
final Filter dimFilter = FF.and(
FF.lessOrEqual(FF.literal(dimEnv.getMinimum(0)), def.getLower()),
FF.greaterOrEqual(FF.literal(dimEnv.getMaximum(0)), def.getUpper()));
flt = FF.and(flt, dimFilter);
}
QueryBuilder qb = new QueryBuilder(editedLayer.getCollection().getFeatureType().getName().toString());
//we filter in the map CRS
qb.setCRS(map.getCanvas().getObjectiveCRS2D());
editgeoms = editedLayer.getCollection().subCollection(qb.buildQuery());
//we filter ourself since we want the filter to occure after the reprojection
editgeoms = GenericFilterFeatureIterator.wrap(editgeoms, flt);
fi = editgeoms.iterator();
if (fi.hasNext()) {
Feature sf = fi.next();
//get the original, in it's data crs
flt = FF.id(Collections.singleton(FeatureExt.getId(sf)));
sf = null;
fi.close();
qb.reset();
qb.setTypeName(editedLayer.getCollection().getFeatureType().getName());
qb.setFilter(flt);
editgeoms = editedLayer.getCollection().subCollection(qb.buildQuery());
fi = editgeoms.iterator();
if (fi.hasNext()){
sf = fi.next();
}
return sf;
}
}catch(Exception ex){
LOGGER.log(Level.WARNING, ex.getLocalizedMessage(),ex);
}finally{
if(fi != null){
fi.close();
}
}
return candidate;
}
public boolean grabGeometrynode(final Point pt, final int mx, final int my){
try{
//transform our mouse in a geometry
final Geometry mouseGeo = mousePositionToGeometry(mx, my);
return pt.intersects(mouseGeo);
}catch(Exception ex){
LOGGER.log(Level.WARNING, ex.getLocalizedMessage(),ex);
return false;
}
}
public void grabGeometryNode(final int mx, final int my,final EditionGeometry edited) {
try{
//transform our mouse in a geometry
final Geometry mouseGeo = mousePositionToGeometry(mx, my);
grabGeometryNode(mouseGeo, edited);
}catch(final Exception ex){
LOGGER.log(Level.WARNING, ex.getLocalizedMessage(),ex);
}
}
public void grabGeometryNode(final Geometry mouseGeo, final EditionGeometry edited) {
//reset selection
edited.numSubGeom = -1;
edited.numHole = -1;
edited.selectedNode[0] = -1;
edited.selectedNode[1] = -1;
for (int i=0,n=edited.geometry.getNumGeometries(); i<n; i++) {
final Geometry subgeo = edited.geometry.getGeometryN(i);
if (subgeo.intersects(mouseGeo)) {
//this geometry intersect the mouse
edited.numSubGeom = i;
//this far it can only be a point, linestring or polygon
if(subgeo instanceof Point || subgeo instanceof LineString){
simpleIntersect(mouseGeo, subgeo, edited.selectedNode);
break;
}else if(subgeo instanceof Polygon){
final Polygon poly = (Polygon) subgeo;
LineString ring = poly.getExteriorRing();
simpleIntersect(mouseGeo, ring,edited.selectedNode);
if(edited.selectedNode[0] != -1){
break;
}
for(int j=0,k=poly.getNumInteriorRing(); j<k; j++){
ring = poly.getInteriorRingN(j);
simpleIntersect(mouseGeo, ring,edited.selectedNode);
if(edited.selectedNode[0] != -1){
edited.numHole = j;
break;
}
}
}else{
throw new IllegalArgumentException("Was expecting a Point, LineString or Polygon, but was : " + subgeo.getClass());
}
break;
}
}
}
private void simpleIntersect(final Geometry mouseGeo, final Geometry geom, final int[] indexes){
final Coordinate[] coos = geom.getCoordinates();
for (int j=0,m=coos.length; j<m; j++) {
final Coordinate coo = coos[j];
final Point p = createPoint(coo);
if (p.intersects(mouseGeo)) {
if(geom instanceof LinearRing && (j==0 || j == m-1)){
indexes[0] = 0;
indexes[1] = m-1;
}else{
indexes[0] = indexes[1] = j;
}
return;
}
}
}
public void moveGeometry(final Geometry geo, final int dx, final int dy) {
try{
final Point2D pt0 = map.getCanvas().getObjectiveToDisplay().inverseTransform(new Point2D.Double(0, 0), null);
final Point2D pt = map.getCanvas().getObjectiveToDisplay().inverseTransform(new Point2D.Double(dx, dy), null);
pt.setLocation(pt.getX()-pt0.getX(), pt.getY()-pt0.getY());
for (int i=0,n=geo.getNumGeometries(); i<n; i++) {
final Geometry subgeo = geo.getGeometryN(i);
final Coordinate[] coos = subgeo.getCoordinates();
for (int j=0,m=coos.length; j<m; j++) {
final Coordinate coo = coos[j];
coo.x += pt.getX();
coo.y += pt.getY();
}
subgeo.geometryChanged();
}
}catch(final Exception ex){
LOGGER.log(Level.WARNING, ex.getLocalizedMessage(),ex);
}
geo.geometryChanged();
}
public void moveSubGeometry(final Geometry geo, final int indice, final int dx, final int dy) {
try{
final Point2D pt0 = map.getCanvas().getObjectiveToDisplay().inverseTransform(new Point2D.Double(0, 0), null);
final Point2D pt = map.getCanvas().getObjectiveToDisplay().inverseTransform(new Point2D.Double(dx, dy), null);
pt.setLocation(pt.getX()-pt0.getX(), pt.getY()-pt0.getY());
final Geometry subgeo = geo.getGeometryN(indice);
final Coordinate[] coos = subgeo.getCoordinates();
for (int j=0,m=coos.length; j<m; j++) {
final Coordinate coo = coos[j];
coo.x += pt.getX();
coo.y += pt.getY();
}
subgeo.geometryChanged();
}catch(final Exception ex){
LOGGER.log(Level.WARNING, ex.getLocalizedMessage(),ex);
}
geo.geometryChanged();
}
public Geometry insertNode(final Polygon geo, final int mx, final int my) {
try{
//transform our mouse in a geometry
final Polygon mouseGeo = mousePositionToGeometry(mx, my);
final Point mousePoint = toJTS(mx, my);
if (geo.intersects(mouseGeo)) {
//this geometry intersect the mouse
final Coordinate[] coos = geo.getCoordinates();
for (int j=0,m=coos.length-1; j<m; j++) {
//find the segment that intersect
final Coordinate coo1 = coos[j];
final Coordinate coo2 = coos[j+1];
final LineString segment = createLine(coo1,coo2);
if(mouseGeo.intersects(segment) && isOnLine(mouseGeo,mousePoint,segment)){
//we must add the new node on this segment
final List<Coordinate> ncs = new ArrayList<Coordinate>();
for (int d=0,p=coos.length; d<p; d++) {
ncs.add(coos[d]);
if(d==j){
//we must add the new node here
ncs.add(mousePoint.getCoordinate());
}
}
return createPolygon(ncs);
}
}
}
}catch(final Exception ex){
LOGGER.log(Level.WARNING, ex.getLocalizedMessage(),ex);
}
return geo;
}
public Geometry insertNode(final LineString geo, final int mx, final int my) {
try{
//transform our mouse in a geometry
final Polygon mouseGeo = mousePositionToGeometry(mx, my);
final Point mousePoint = toJTS(mx, my);
if (geo.intersects(mouseGeo)) {
//this geometry intersect the mouse
final Coordinate[] coos = geo.getCoordinates();
for (int j=0,m=coos.length-1; j<m; j++) {
//find the segment that intersect
final Coordinate coo1 = coos[j];
final Coordinate coo2 = coos[j+1];
final LineString segment = createLine(coo1,coo2);
if(mouseGeo.intersects(segment) && isOnLine(mouseGeo,mousePoint,segment)){
//we must add the new node on this segment
final List<Coordinate> ncs = new ArrayList<Coordinate>();
for (int d=0,p=coos.length; d<p; d++) {
ncs.add(coos[d]);
if(d==j){
//we must add the new node here
ncs.add(mousePoint.getCoordinate());
}
}
return createLine(ncs);
}
}
}
}catch(final Exception ex){
LOGGER.log(Level.WARNING, ex.getLocalizedMessage(),ex);
}
return geo;
}
private static boolean isOnLine(final Polygon candidate, final Point center, final LineString line){
final Envelope env = line.getEnvelopeInternal();
final Coordinate coord = center.getCoordinate();
if(env.contains(coord)){
//get the nearest point on the line to avoid deformations
final Coordinate[] cnds = DistanceOp.nearestPoints(line, center);
coord.setCoordinate(cnds[0]);
return true;
}else{
//make a more accurate test, envelope might have a width or hight
//of zero which will return false on intersection wit the point
final Polygon buffer = (Polygon) line.buffer(candidate.getEnvelopeInternal().getWidth()/2, 10, BufferParameters.CAP_FLAT);
if(buffer.contains(center)){
//get the nearest point on the line to avoid deformations
final Coordinate[] cnds = DistanceOp.nearestPoints(line, center);
coord.setCoordinate(cnds[0]);
return true;
}else{
return false;
}
}
}
public Geometry insertNode(final GeometryCollection geo, final int mx, final int my) {
try{
//transform our mouse in a geometry
final Polygon mouseGeo = mousePositionToGeometry(mx, my);
final Point mousePoint = toJTS(mx, my);
for (int i=0,n=geo.getNumGeometries(); i<n; i++) {
final Geometry subgeo = geo.getGeometryN(i);
if (subgeo.intersects(mouseGeo)) {
//this geometry intersect the mouse
final Coordinate[] coos = subgeo.getCoordinates();
for (int j=0,m=coos.length-1; j<m; j++) {
//find the segment that intersect
final Coordinate coo1 = coos[j];
final Coordinate coo2 = coos[j+1];
final LineString segment = createLine(coo1,coo2);
if(mouseGeo.intersects(segment) && isOnLine(mouseGeo,mousePoint,segment)){
//we must add the new node on this segment
final List<Geometry> subs = new ArrayList<Geometry>();
for (int k=0,l=geo.getNumGeometries(); k<l; k++) {
if(k==i){
//this subgeo must be changed
final List<Coordinate> ncs = new ArrayList<Coordinate>();
for (int d=0,p=coos.length; d<p; d++) {
ncs.add(coos[d]);
if(d==j){
//we must add the new node here
ncs.add(mousePoint.getCoordinate());
}
}
if(geo instanceof MultiLineString && ncs.size() >1){
subs.add(createLine(ncs));
}else if(geo instanceof MultiPolygon && ncs.size() >2){
subs.add(createPolygon(ncs));
}
}else{
subs.add(geo.getGeometryN(k));
}
}
if(geo instanceof MultiLineString && !subs.isEmpty()){
return createMultiLine(subs);
}else if(geo instanceof MultiPolygon && !subs.isEmpty()){
return createMultiPolygon(subs);
}else{
return null;
}
}
}
break;
}
}
}catch(final Exception ex){
LOGGER.log(Level.WARNING, ex.getLocalizedMessage(),ex);
}
return geo;
}
public Geometry deleteNode(final Polygon geo, final int mx, final int my) {
try{
//transform our mouse in a geometry
final Geometry mouseGeo = mousePositionToGeometry(mx, my);
if (geo.intersects(mouseGeo)) {
//this geometry intersect the mouse
final Coordinate[] coos = geo.getCoordinates();
for (int j=0,m=coos.length; j<m; j++) {
final Coordinate coo = coos[j];
final Point p = createPoint(coo);
if (p.intersects(mouseGeo)) {
//delete this node
final List<Coordinate> ncs = new ArrayList<Coordinate>();
for (int d=0,z=coos.length; d<z; d++) {
if(d!=j){
ncs.add(coos[d]);
}
}
if(ncs.size() > 2){
return createPolygon(ncs);
}
break;
}
}
}
}catch(final Exception ex){
LOGGER.log(Level.WARNING, ex.getLocalizedMessage(),ex);
}
return geo;
}
public Geometry deleteNode(final LineString geo, final int mx, final int my) {
try{
//transform our mouse in a geometry
final Geometry mouseGeo = mousePositionToGeometry(mx, my);
if (geo.intersects(mouseGeo)) {
//this geometry intersect the mouse
final Coordinate[] coos = geo.getCoordinates();
for (int j=0,m=coos.length; j<m; j++) {
final Coordinate coo = coos[j];
final Point p = createPoint(coo);
if (p.intersects(mouseGeo)) {
//delete this node
final List<Coordinate> ncs = new ArrayList<Coordinate>();
for (int d=0,z=coos.length; d<z; d++) {
if(d!=j){
ncs.add(coos[d]);
}
}
if(ncs.size() > 1){
return createLine(ncs);
}
break;
}
}
}
}catch(final Exception ex){
LOGGER.log(Level.WARNING, ex.getLocalizedMessage(),ex);
}
return geo;
}
public Geometry deleteNode(final GeometryCollection geo, final int mx, final int my) {
try{
//transform our mouse in a geometry
final Geometry mouseGeo = mousePositionToGeometry(mx, my);
for (int i=0,n=geo.getNumGeometries(); i<n; i++) {
final Geometry subgeo = geo.getGeometryN(i);
if (subgeo.intersects(mouseGeo)) {
//this geometry intersect the mouse
final Coordinate[] coos = subgeo.getCoordinates();
for (int j=0,m=coos.length; j<m; j++) {
final Coordinate coo = coos[j];
final Point p = createPoint(coo);
if (p.intersects(mouseGeo)) {
//delete this node
final List<Geometry> subs = new ArrayList<Geometry>();
for (int k=0,l=geo.getNumGeometries(); k<l; k++) {
if(k==i){
//this subgeo must be changed
final List<Coordinate> ncs = new ArrayList<Coordinate>();
for (int d=0,z=coos.length; d<z; d++) {
if(d!=j){
ncs.add(coos[d]);
}
}
if(geo instanceof MultiLineString && ncs.size() >1){
subs.add(createLine(ncs));
}else if(geo instanceof MultiPolygon && ncs.size() >2){
subs.add(createPolygon(ncs));
}
}else{
subs.add(geo.getGeometryN(k));
}
}
if(geo instanceof MultiLineString && !subs.isEmpty()){
return createMultiLine(subs);
}else if(geo instanceof MultiPolygon && !subs.isEmpty()){
return createMultiPolygon(subs);
}else{
return null;
}
}
}
break;
}
}
}catch(final Exception ex){
LOGGER.log(Level.WARNING, ex.getLocalizedMessage(),ex);
}
return geo;
}
public Geometry deleteSubGeometry(final GeometryCollection geo, final int mx, final int my) {
try{
//transform our mouse in a geometry
final Geometry mouseGeo = mousePositionToGeometry(mx, my);
for (int i=0,n=geo.getNumGeometries(); i<n; i++) {
final Geometry subgeo = geo.getGeometryN(i);
if (subgeo.intersects(mouseGeo)) {
//this geometry intersect the mouse
final List<Geometry> subs = new ArrayList<Geometry>();
for (int k=0,l=geo.getNumGeometries(); k<l; k++) {
if(k!=i){
subs.add(geo.getGeometryN(k));
}
}
if(geo instanceof MultiLineString && !subs.isEmpty()){
return createMultiLine(subs);
}else if(geo instanceof MultiPolygon && !subs.isEmpty()){
return createMultiPolygon(subs);
}else if(geo instanceof MultiPoint && !subs.isEmpty()){
return createMultiPoint(subs);
}else{
return null;
}
}
}
}catch(final Exception ex){
LOGGER.log(Level.WARNING, ex.getLocalizedMessage(),ex);
}
return geo;
}
public Geometry deleteHole(final Geometry geo, final int mx, final int my) {
if(!(geo instanceof Polygon || geo instanceof MultiPolygon)){
//this method only works on polygon or multipolygon
return geo;
}
try{
//transform our mouse in a geometry
final Geometry mouseGeo = mousePositionToGeometry(mx, my);
final List<Polygon> subGeometries = new ArrayList<Polygon>();
final List<LinearRing> holes = new ArrayList<LinearRing>();
for (int i=0,n=geo.getNumGeometries(); i<n; i++) {
//subgeo is a Polygon
subGeometries.add((Polygon) geo.getGeometryN(i));
}
for (int i=0,n=subGeometries.size();i<n;i++) {
final Polygon subgeo = subGeometries.get(i);
//find the hole to remove
int toRemove = -1;
holes.clear();
for(int j=0,k=subgeo.getNumInteriorRing(); j<k; j++){
final LinearRing ring = (LinearRing) subgeo.getInteriorRingN(j);
holes.add(ring);
if(toRemove == -1 && ring.intersects(mouseGeo)){
toRemove = j;
}
}
if(toRemove != -1){
//remove this ring and return geometry
holes.remove(toRemove);
if(geo instanceof Polygon){
return GEOMETRY_FACTORY.createPolygon((LinearRing)subgeo.getExteriorRing(),
holes.toArray(new LinearRing[holes.size()]));
}else if(geo instanceof MultiPolygon){
//modify the subgeometry
final Polygon poly = GEOMETRY_FACTORY.createPolygon((LinearRing)subgeo.getExteriorRing(),
holes.toArray(new LinearRing[holes.size()]));
subGeometries.set(i, poly);
//recreate the multipolygon
return createMultiPolygon(subGeometries);
}else{
throw new IllegalStateException("Should not happen, expecting "
+ "Polygon or MultiPolygon but was "+ geo.getClass());
}
}
}
}catch(final Exception ex){
LOGGER.log(Level.WARNING, ex.getLocalizedMessage(),ex);
}
//nothing to remove return original geometry
return geo;
}
public Geometry toObjectiveCRS(final Feature sf){
final Object obj = FeatureExt.getDefaultGeometryAttributeValue(sf);
if (obj instanceof Geometry) {
return toObjectiveCRS((Geometry)obj);
}
return null;
}
public Geometry toObjectiveCRS(Geometry geom){
try{
final MathTransform trs = CRS.findOperation(
FeatureExt.getCRS(editedLayer.getCollection().getFeatureType()),
map.getCanvas().getObjectiveCRS2D(), null).getMathTransform();
geom = JTS.transform(geom, trs);
JTS.setCRS(geom, map.getCanvas().getObjectiveCRS2D());
return geom;
}catch(final Exception ex){
LOGGER.log(Level.WARNING, ex.getLocalizedMessage(),ex);
}
return null;
}
/**
*
* @param poly : in canvas objective CRS
* @param fl : target layer filter
* @return geometry filter
*/
public Filter toFilter(final Geometry poly, final FeatureMapLayer fl) throws FactoryException, MismatchedDimensionException, TransformException{
final String geoStr = FeatureExt.getDefaultGeometryAttribute(fl.getCollection().getFeatureType()).getName().toString();
final Expression geomField = FF.property(geoStr);
final Geometry dataPoly = poly;
JTS.setCRS(dataPoly, map.getCanvas().getObjectiveCRS2D());
final Expression geomData = FF.literal(dataPoly);
final Filter f = FF.intersects(geomData,geomField);
return f;
}
//manipulating the feature source, transaction -----------------------------
public Feature sourceAddGeometry(Geometry geom) {
if (editedLayer != null && geom != null) {
final FeatureType featureType = (FeatureType) editedLayer.getCollection().getFeatureType();
final CoordinateReferenceSystem dataCrs = FeatureExt.getCRS(featureType);
final Feature feature = featureType.newInstance();
try {
geom = JTS.transform(geom, CRS.findOperation(map.getCanvas().getObjectiveCRS2D(), dataCrs, null).getMathTransform());
} catch (Exception ex) {
LOGGER.log(Level.WARNING, null, ex);
}
feature.setPropertyValue(AttributeConvention.GEOMETRY_PROPERTY.toString(), geom);
if(showAtributeditor){
JFeatureOutLine.show(map,feature);
}
if(editedLayer.getCollection().isWritable()){
try {
((FeatureCollection)editedLayer.getCollection()).add(feature);
} catch (Exception ex) {
LOGGER.log(Level.WARNING, ex.getLocalizedMessage(),ex);
}
}
map.getCanvas().repaint();
return feature;
}
return null;
}
public void sourceModifyFeature(final Feature feature, final Geometry geo, boolean reprojectToDataCRS){
if(feature == null || geo == null){
//nothing to do
return;
}
final String ID = FeatureExt.getId(feature).getID();
ensureNonNull("geometry", geo);
ensureNonNull("id", ID);
if (editedLayer != null && editedLayer.getCollection().isWritable()) {
final Filter filter = FF.id(Collections.singleton(FF.featureId(ID)));
final FeatureType featureType = editedLayer.getCollection().getFeatureType();
final AttributeType geomAttribut = FeatureExt.getDefaultGeometryAttribute(featureType);
final CoordinateReferenceSystem dataCrs = FeatureExt.getCRS(featureType);
try {
final Geometry geom;
if(reprojectToDataCRS){
geom = JTS.transform(geo,
CRS.findOperation(map.getCanvas().getObjectiveCRS(), dataCrs, null).getMathTransform());
JTS.setCRS(geom, dataCrs);
}else{
geom = geo;
}
editedLayer.getCollection().update(filter, Collections.singletonMap(geomAttribut.getName().toString(), geom));
} catch (final Exception ex) {
LOGGER.log(Level.WARNING, ex.getLocalizedMessage(),ex);
} finally {
map.getCanvas().repaint();
}
}
}
public void sourceRemoveFeature(final Feature feature){
sourceRemoveFeature(FeatureExt.getId(feature).getID());
}
public void sourceRemoveFeature(final String ID) {
ensureNonNull("id", ID);
if (editedLayer != null && editedLayer.getCollection().isWritable()) {
Filter filter = FF.id(Collections.singleton(FF.featureId(ID)));
try {
editedLayer.getCollection().remove(filter);
} catch (final Exception ex) {
LOGGER.log(Level.WARNING, ex.getLocalizedMessage(),ex);
}
map.getCanvas().repaint();
}
}
//staic helper methods -----------------------------------------------------
public static Geometry createGeometry(final List<Coordinate> coords) {
int size = coords.size();
switch (size) {
case 0:
return null;
case 1:
return createPoint(coords.get(0));
case 2:
return createLine(coords);
default:
return createLine(coords);
}
}
public static Point createPoint(final Coordinate coord) {
return GEOMETRY_FACTORY.createPoint(coord);
}
public static MultiPoint createMultiPoint(final List<? extends Geometry> geoms) {
List<Point> lst = new ArrayList<Point>();
for (Geometry go : geoms) {
if (go instanceof Point) {
lst.add((Point) go);
}
}
return GEOMETRY_FACTORY.createMultiPoint(lst.toArray(new Point[lst.size()]));
}
public static LineString createLine(final Coordinate ... coords) {
return GEOMETRY_FACTORY.createLineString(coords);
}
public static LineString createLine(final List<Coordinate> coords) {
return GEOMETRY_FACTORY.createLineString(coords.toArray(EMPTY_COORDINATE_ARRAY));
}
public static LinearRing createLinearRing(List<Coordinate> coords) {
coords = new ArrayList<Coordinate>(coords);
if (!(coords.get(0).equals2D(coords.get(coords.size() - 1)))) {
Coordinate coo = new Coordinate(coords.get(0));
coords.add(coo);
}
if(coords.size() == 3){
Coordinate coo = new Coordinate(coords.get(0));
coords.add(coo);
}
return GEOMETRY_FACTORY.createLinearRing(coords.toArray(EMPTY_COORDINATE_ARRAY));
}
public static Polygon createPolygon(final List<Coordinate> coords, LinearRing ... holes) {
LinearRing ring = createLinearRing(coords);
return GEOMETRY_FACTORY.createPolygon(ring, holes);
}
public static MultiPolygon createMultiPolygon(final List<? extends Geometry> geoms) {
List<Polygon> lst = new ArrayList<Polygon>();
for (Geometry go : geoms) {
if (go instanceof Polygon) {
lst.add((Polygon) go);
}else{
throw new IllegalArgumentException("Found an unexpected geometry type while building multipolygon : " + go.getClass());
}
}
return GEOMETRY_FACTORY.createMultiPolygon(lst.toArray(new Polygon[lst.size()]));
}
public static MultiLineString createMultiLine(final List<? extends Geometry> geoms) {
List<LineString> lst = new ArrayList<LineString>();
for (Geometry go : geoms) {
if (go instanceof LineString) {
lst.add((LineString) go);
}
}
return GEOMETRY_FACTORY.createMultiLineString(lst.toArray(new LineString[lst.size()]));
}
}