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