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