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 033 * ordered by their area size (from small to large) 034 * which contain the internal point. 035 * 036 * @param internalPoint the internal point. 037 * @return the surrounding polygons/multipolygons 038 */ 039 public static Collection<OsmPrimitive> getSurroundingObjects(EastNorth internalPoint) { 040 final DataSet ds = Main.getLayerManager().getEditDataSet(); 041 if (ds == null) { 042 return Collections.emptySet(); 043 } 044 final Node n = new Node(internalPoint); 045 final Map<Double, OsmPrimitive> found = new TreeMap<>(); 046 for (Way w : ds.getWays()) { 047 if (w.isUsable() && w.isClosed() && w.isSelectable() && Geometry.nodeInsidePolygon(n, w.getNodes())) { 048 found.put(Geometry.closedWayArea(w), w); 049 } 050 } 051 for (Relation r : ds.getRelations()) { 052 if (r.isUsable() && r.isMultipolygon() && r.isSelectable() && 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 found.put(s <= 0 ? 1e8 : s, r); 064 } 065 } 066 return found.values(); 067 } 068 069 /** 070 * Returns the smallest surrounding polygon/multipolygon which contains the internal point. 071 * 072 * @param internalPoint the internal point. 073 * @return the smallest surrounding polygon/multipolygon 074 */ 075 public static OsmPrimitive getSmallestSurroundingObject(EastNorth internalPoint) { 076 final Collection<OsmPrimitive> surroundingObjects = getSurroundingObjects(internalPoint); 077 return surroundingObjects.isEmpty() ? null : surroundingObjects.iterator().next(); 078 } 079 080 /** 081 * Select a polygon or multipolygon by an internal point. 082 * 083 * @param internalPoint the internal point. 084 * @param doAdd whether to add selected polygon to the current selection. 085 * @param doRemove whether to remove the selected polygon from the current selection. 086 */ 087 public static void performSelection(EastNorth internalPoint, boolean doAdd, boolean doRemove) { 088 final Collection<OsmPrimitive> surroundingObjects = getSurroundingObjects(internalPoint); 089 final DataSet ds = Main.getLayerManager().getEditDataSet(); 090 if (surroundingObjects.isEmpty()) { 091 return; 092 } else if (doRemove) { 093 final Collection<OsmPrimitive> newSelection = new ArrayList<>(ds.getSelected()); 094 newSelection.removeAll(surroundingObjects); 095 ds.setSelected(newSelection); 096 } else if (doAdd) { 097 final Collection<OsmPrimitive> newSelection = new ArrayList<>(ds.getSelected()); 098 newSelection.add(surroundingObjects.iterator().next()); 099 ds.setSelected(newSelection); 100 } else { 101 ds.setSelected(surroundingObjects.iterator().next()); 102 } 103 } 104}