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