0

I am trying to create MultiSelectionTool that will work in multi, and have a problem with extending selection if Ctrl is pressed while selecting, it just deselect the everything else before and start selecting again. Does anyone have a suggestion how to solve this? here is my code for this

import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.HashSet;
import java.util.Set;

import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.FeatureIterator;
import org.geotools.geometry.DirectPosition2D;
import org.geotools.geometry.Envelope2D;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.FeatureLayer;
import org.geotools.map.Layer;
import org.geotools.map.MapContent;
import org.geotools.styling.FeatureTypeStyle;
import org.geotools.styling.Fill;
import org.geotools.styling.Graphic;
import org.geotools.styling.Mark;
import org.geotools.styling.Rule;
import org.geotools.styling.Stroke;
import org.geotools.styling.Style;
import org.geotools.styling.StyleFactory;
import org.geotools.styling.Symbolizer;
import org.geotools.swing.event.MapMouseEvent;
import org.geotools.swing.styling.JSimpleStyleDialog;
import org.geotools.swing.tool.AbstractZoomTool;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.type.FeatureType;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.identity.FeatureId;

import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Polygon;


/**
 * Multi select info tool
 *
 * * For mouse clicks, features in 5x5 bounding box will be selected
 *
 * * The tool also responds to the user drawing a box on the map mapPane with
 * mouse click-and-drag to define the multi select area.
 *
 */

public class MultiSelectTool extends AbstractZoomTool {

    public static final String  TOOL_NAME      = new String("Select tool");
    public static final String  TOOL_TIP       = new String("Click to select features in selected Layers");
    public static final Point   CURSOR_HOTSPOT = new Point(14, 9);
    public static final String  ICON_IMAGE     = "/org/geotools/swing/icons/pointer.png";
    /*
     * Factories koji će biti korišćeni da se kreira stil i upit nad objektima
     */
    private static StyleFactory sf = CommonFactoryFinder.getStyleFactory();
    private static FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();

    /*
     * Prikladne konstante za tip elemenata geometrije u shapefile-u
     */
    private enum GeomType { POINT, LINE, POLYGON };

    /*
     * Podešavanje boja i debljina linija prikaza
     */
    private static final float LINE_WIDTH = 1.0f;
    private static final float POINT_SIZE = 10.0f;


    private Point2D startDragPos;
    private boolean dragged;
    private boolean ctrl;
    private Cursor cursor;
    private static Rule rule;

    /**
     * Constructor
     */
    public MultiSelectTool() {
        startDragPos = new DirectPosition2D();
        dragged = false;
    }

    /**
     * Constructs 4x4 size bounding box around mouse click point
     * and calls for selectedFeaturesIn bounding_box
     *
     * @param e mouse event
     */
    @Override
    public void onMouseClicked(MapMouseEvent e) {
        if ((e.getModifiers() & ActionEvent.CTRL_MASK) != 0) {
            ctrl = true;
        }

        /*
         * Construct a 6x6 pixel rectangle centred on the mouse click position
         */
        Point screenPos = e.getPoint();
        Rectangle screenRect = new Rectangle(screenPos.x-3, screenPos.y-3, 6, 6);

        /*
         * 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 = getMapPane().getScreenToWorldTransform();
        Rectangle2D worldRect = screenToWorld.createTransformedShape(screenRect).getBounds2D();
        ReferencedEnvelope bbox = new ReferencedEnvelope(worldRect, getMapPane().getMapContent().getCoordinateReferenceSystem());

        selectFeaturesIn(bbox);

        ctrl = false;
    }

    /**
     * Records the map position of the mouse event in case this
     * button press is the beginning of a mouse drag
     *
     * @param ev the mouse event
     */
    @Override
    public void onMousePressed(MapMouseEvent ev) {
        startDragPos = new DirectPosition2D();
        startDragPos.setLocation(ev.getWorldPos());
    }

    /**
     * Records that the mouse is being dragged
     *
     * @param ev the mouse event
     */
    @Override
    public void onMouseDragged(MapMouseEvent ev) {
        dragged = true;
    }

    /**
     * If the mouse was dragged, determines the bounds of the
     * box that the user defined and passes this to the mapPane's
     * {@link org.geotools.swing.JMapPane#setDisplayArea(org.opengis.geometry.Envelope) }
     * method
     *
     * @param ev the mouse event
     */
    @Override
    public void onMouseReleased(MapMouseEvent ev) {
        if (dragged && !ev.getPoint().equals(startDragPos)) {
            if ((ev.getModifiers() & ActionEvent.CTRL_MASK) != 0) {
                ctrl = true;
            }

            Envelope2D env = new Envelope2D();
            env.setFrameFromDiagonal(startDragPos, ev.getWorldPos());

            ReferencedEnvelope renv = new ReferencedEnvelope(env, ev.getWorldPos().getCoordinateReferenceSystem());
            selectFeaturesIn(renv);

            dragged = false;
            ctrl = false;
        }
    }

    /**
     * Get the mouse cursor for this tool
     */
    @Override
    public Cursor getCursor() {
        return cursor;
    }

    /**
     * Returns true to indicate that this tool draws a box
     * on the map display when the mouse is being dragged to
     * select multiple features
     */
    @Override
    public boolean drawDragBox() {
        return true;
    }


    /**
     * Queries each MapLayer, filters features in selected area
     * 
     * @param selected - selection area
     */
    private void selectFeaturesIn(ReferencedEnvelope selected) {

        for (Layer layer : mapPane.getMapContent().layers()) {
            if((!layer.isSelected()) || (!layer.isVisible())) continue;

            /*
             * usually "THE_GEOM" for shapefiles
             * 
             */
            FeatureType schema = layer.getFeatureSource().getSchema();
            String geometryPropertyName = schema.getGeometryDescriptor().getLocalName(); // "THE_GEOM"

             /*
             * Create a Filter to select features that intersect with
             * the bounding box
             */

            Filter filter = ff.bbox(ff.property(geometryPropertyName), selected);
            /*
             * Use the filter to identify the selected features
             */
            try {
                SimpleFeatureCollection selectedFeatures = (SimpleFeatureCollection) layer.getFeatureSource().getFeatures(filter);
                SimpleFeatureCollection nonselectedFeatures = (SimpleFeatureCollection) layer.getFeatureSource().getFeatures();

                FeatureIterator<?> iter = selectedFeatures.features();
                FeatureIterator<?> iter1 = nonselectedFeatures.features();
                Set<FeatureId> IDs = new HashSet<FeatureId>();
                Set<FeatureId> IDns = new HashSet<FeatureId>();
                Set<FeatureId> IDarray = new HashSet<FeatureId>();
                try {
                    while (iter1.hasNext()) {
                        SimpleFeature feature = (SimpleFeature) iter1.next();
                        IDns.add(feature.getIdentifier());
                    }

                } finally {
                    iter1.close();
                }
                try {
                    while (iter.hasNext()) {
                        SimpleFeature feature = (SimpleFeature) iter.next();
                        IDs.add(feature.getIdentifier());
                        System.out.println(" * " + feature.getIdentifier());
                    }


                } finally {
                    iter.close();
                }
                IDarray = IDs;
                Object[] array = IDarray.toArray();
                for( int i=0; i < IDs.size(); i++ ){
                    for( int j=0; j < IDns.size(); j++ ){
                        if(IDns!=IDs){
                            IDns.remove(array[i]);
                        }

                    }
                }

                if(ctrl) {
                    mapPane.getMapContent().expandSelection(layer, IDs);
                } else {
                    mapPane.getMapContent().selectFeatures(layer, IDs);
                }
                mapPane.getMapContent().fireSelectionChanged();
                Style style;
                if (IDs.isEmpty()) {
                    style=createDefaultStyle();
                }else{
                    style = createSelectedStyle(IDs);
                }

                ((FeatureLayer) layer).setStyle(style);
                ((Component) mapPane).repaint();
                mapPane.getMapContent().resetStyles();
            } catch (Exception ex) {
                ex.printStackTrace();
                return;
            }
        }
    }
    private Style createDefaultStyle() {
        Rule rule = createRule(JSimpleStyleDialog.lineColor, JSimpleStyleDialog.fillColor, JSimpleStyleDialog.lineWidth );
        FeatureTypeStyle fts = sf.createFeatureTypeStyle();
        fts.rules().add(rule);

        Style style = sf.createStyle();
        style.featureTypeStyles().add(fts);
        return style;
    }

    private Style createSelectedStyle(Set<FeatureId> IDs) {
        Color color = Color.red;

        Rule otherRule = createRule( JSimpleStyleDialog.lineColor, JSimpleStyleDialog.fillColor, JSimpleStyleDialog.lineWidth );
        otherRule.setElseFilter(true);

        Rule selectedRule = createRule(color, color, LINE_WIDTH);
        selectedRule.setFilter(ff.id(IDs));

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

        Style style = sf.createStyle();
        style.featureTypeStyles().add(fts);
        return style;
    }
    /**
     * Helper za createXXXStyle metodu. 
     */
    public static Rule createRule(Color outlineColor, Color fillColor, Float lineWidth) {
        /*
         * usually "THE_GEOM" for shapefiles
         *
         */
        MapContent content = mapPane.getMapContent();
        for (Layer layer : content.layers()) {
            if((!layer.isSelected()) || (!layer.isVisible())) continue;
         FeatureType schema = layer.getFeatureSource().getSchema();
         String geometryPropertyName = schema.getGeometryDescriptor().getLocalName(); // "THE_GEOM"

         /*
         * Geometry type in current layer
         */

         GeomType geometryType;

         Class<?> clazz = schema.getGeometryDescriptor().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;
         }

         Symbolizer symbolizer = null;
         Fill fill = null;
         Stroke stroke = sf.createStroke(ff.literal(outlineColor), ff.literal(lineWidth));

         switch (geometryType) {
             case POLYGON:
                 symbolizer = sf.createPolygonSymbolizer(stroke, fill, geometryPropertyName);
                 break;
             case LINE:
                 symbolizer = sf.createLineSymbolizer(stroke, geometryPropertyName);
                 break;
             case POINT:
                 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, geometryPropertyName);
         }
         rule = sf.createRule();
         rule.symbolizers().add(symbolizer);
        }
         return rule;
}

}

methods that Multi select tool calls are added to mapcontent

    public void resetStyles() {
        for (MapLayer layer : getLayers()) {
            if((!layer.isSelected()) || (!layer.isVisible())) continue;
            Rule selected_rule = MyStyleGenerator.createRule(layer, Color.BLUE, Color.BLUE, 2.0f);
            selected_rule.setFilter(ff.id(selectedFeatures.get(layer)));
            if(layer.getStyle().featureTypeStyles().get(0).rules().size() == 1) {
                layer.getStyle().featureTypeStyles().get(0).rules().add(selected_rule);
            } else {
                layer.getStyle().featureTypeStyles().get(0).rules().set(1, selected_rule);
            }
        }
    }
    public final void resetSelection() {
        Collection c = selectedFeatures.values();
        Iterator itr = c.iterator();

        while(itr.hasNext()) ((HashSet) itr.next()).clear();

        resetStyles();
    }
    public HashMap<Layer, Set<FeatureId>> getSelectedFeatures() {
        return selectedFeatures;
}
    public void fireSelectionChanged() {
        for (FeatureDetailFrame frame : featureDetailFrames) {
            frame.selectSelectedFeatures();
        }
    }
    public MapLayer[] getLayers() {
        MapLayer[] array = new MapLayer[layers().size()];
        int index = 0;
        for (Iterator<Layer> iter = layers().iterator(); iter.hasNext(); index++) {
            Layer layer = iter.next();
            array[index] = new DefaultMapLayer(layer);
        }
        return array;
    }

    public void expandSelection(Layer layer, Set<FeatureId> IDs) {
        Set<FeatureId> selected = selectedFeatures.get(layer);
        Set<FeatureId> intersection = new HashSet<FeatureId>(selected);
        intersection.retainAll(IDs);
        selected.addAll(IDs);
        selected.removeAll(intersection);
    }
6
  • I suppose that you can find code for such tool from the uDig sources.
    – user30184
    Jun 7 '16 at 14:35
  • as I saw there is no possibility to expand selection by pressing ctrl Jun 9 '16 at 18:07
  • It is probably done with Shift instead sites.millersville.edu/cgeiger/GEOG281/MapsText2015/…
    – user30184
    Jun 9 '16 at 19:43
  • I just needed to add a line IDs=MapContent.selected; in class MultiSelectiTool in if statement for checking is ctrl pressed Jun 12 '16 at 15:00
  • Nice. Write that as an answer and I will upvote it.
    – user30184
    Jun 12 '16 at 15:04
1

Solved by adding one line to if statement for checking is ctrl pressed so now it looks like this:

if(ctrl) {
    mapPane.getMapContent().expandSelection(layer, IDs);
    IDs=MapContent.selected;
} else {
    mapPane.getMapContent().selectFeatures(layer, IDs);
}

and also I changed visibility of selected in MapContent to

public static Set<FeatureId> selected;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.