001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.actions; 003 004import java.awt.event.ActionEvent; 005import java.util.ArrayList; 006import java.util.Collection; 007import java.util.Collections; 008import java.util.Map; 009import java.util.TreeMap; 010 011import org.openstreetmap.josm.Main; 012import org.openstreetmap.josm.data.coor.EastNorth; 013import org.openstreetmap.josm.data.osm.BBox; 014import org.openstreetmap.josm.data.osm.DataSet; 015import org.openstreetmap.josm.data.osm.Node; 016import org.openstreetmap.josm.data.osm.OsmPrimitive; 017import org.openstreetmap.josm.data.osm.Relation; 018import org.openstreetmap.josm.data.osm.RelationMember; 019import org.openstreetmap.josm.data.osm.Way; 020import org.openstreetmap.josm.tools.Geometry; 021 022/** 023 * This allows to select a polygon/multipolygon by an internal point. 024 * @since 7144 025 */ 026public class SelectByInternalPointAction extends JosmAction { 027 028 /** 029 * Returns the surrounding polygons/multipolygons 030 * ordered by their area size (from small to large) 031 * which contain the internal point. 032 * 033 * @param internalPoint the internal point. 034 * @return the surrounding polygons/multipolygons 035 */ 036 public static Collection<OsmPrimitive> getSurroundingObjects(EastNorth internalPoint) { 037 final DataSet ds = getCurrentDataSet(); 038 if (ds == null) { 039 return Collections.emptySet(); 040 } 041 final Node n = new Node(internalPoint); 042 final Map<Double, OsmPrimitive> found = new TreeMap<>(); 043 for (Way w : ds.getWays()) { 044 if (w.isUsable() && w.isClosed() && w.isSelectable()) { 045 if (Geometry.nodeInsidePolygon(n, w.getNodes())) { 046 found.put(Geometry.closedWayArea(w), w); 047 } 048 } 049 } 050 for (Relation r : ds.getRelations()) { 051 if (r.isUsable() && r.isMultipolygon()) { 052 if (Geometry.isNodeInsideMultiPolygon(n, r, null)) { 053 for (RelationMember m : r.getMembers()) { 054 if (m.isWay() && m.getWay().isClosed()) { 055 found.values().remove(m.getWay()); 056 } 057 } 058 // estimate multipolygon size by its bounding box area 059 BBox bBox = r.getBBox(); 060 EastNorth en1 = Main.map.mapView.getProjection().latlon2eastNorth(bBox.getTopLeft()); 061 EastNorth en2 = Main.map.mapView.getProjection().latlon2eastNorth(bBox.getBottomRight()); 062 double s = Math.abs((en1.east() - en2.east()) * (en1.north() - en2.north())); 063 if (s == 0) s = 1e8; 064 found.put(s, r); 065 } 066 } 067 } 068 return found.values(); 069 } 070 071 072 /** 073 * Returns the smallest surrounding polygon/multipolygon which contains the internal point. 074 * 075 * @param internalPoint the internal point. 076 * @return the smallest surrounding polygon/multipolygon 077 */ 078 public static OsmPrimitive getSmallestSurroundingObject(EastNorth internalPoint) { 079 final Collection<OsmPrimitive> surroundingObjects = getSurroundingObjects(internalPoint); 080 return surroundingObjects.isEmpty() ? null : surroundingObjects.iterator().next(); 081 } 082 083 /** 084 * Select a polygon or multipolygon by an internal point. 085 * 086 * @param internalPoint the internal point. 087 * @param doAdd whether to add selected polygon to the current selection. 088 * @param doRemove whether to remove the selected polygon from the current selection. 089 */ 090 public static void performSelection(EastNorth internalPoint, boolean doAdd, boolean doRemove) { 091 final Collection<OsmPrimitive> surroundingObjects = getSurroundingObjects(internalPoint); 092 if (surroundingObjects.isEmpty()) { 093 return; 094 } else if (doRemove) { 095 final Collection<OsmPrimitive> newSelection = new ArrayList<>(getCurrentDataSet().getSelected()); 096 newSelection.removeAll(surroundingObjects); 097 getCurrentDataSet().setSelected(newSelection); 098 } else if (doAdd) { 099 final Collection<OsmPrimitive> newSelection = new ArrayList<>(getCurrentDataSet().getSelected()); 100 newSelection.add(surroundingObjects.iterator().next()); 101 getCurrentDataSet().setSelected(newSelection); 102 } else { 103 getCurrentDataSet().setSelected(surroundingObjects.iterator().next()); 104 } 105 } 106 107 @Override 108 public void actionPerformed(ActionEvent e) { 109 throw new UnsupportedOperationException(); 110 } 111}