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