Changeset 15938 in josm for trunk/src/org


Ignore:
Timestamp:
2020-02-26T17:16:35+01:00 (5 years ago)
Author:
GerdP
Message:

see #16707: improve highlighting of overlapping areas and "zoom to error"

  • store the overlapping area with the TestError
  • return also the intersection area in Geometry.polygonIntersectionResult()
Location:
trunk/src/org/openstreetmap/josm
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/validation/TestError.java

    r15640 r15938  
    22package org.openstreetmap.josm.data.validation;
    33
     4import java.awt.geom.Area;
     5import java.awt.geom.PathIterator;
    46import java.text.MessageFormat;
     7import java.util.ArrayList;
    58import java.util.Arrays;
    69import java.util.Collection;
     
    1215
    1316import org.openstreetmap.josm.command.Command;
     17import org.openstreetmap.josm.data.coor.EastNorth;
    1418import org.openstreetmap.josm.data.osm.Node;
    1519import org.openstreetmap.josm.data.osm.OsmPrimitive;
     
    189193
    190194        /**
     195         * Sets an area to highlight when selecting this error.
     196         *
     197         * @param highlighted the area to highlight
     198         * @return {@code this}
     199         */
     200        public Builder highlight(Area highlighted) {
     201            CheckParameterUtil.ensureParameterNotNull(highlighted, "highlighted");
     202            this.highlighted = Collections.singleton(highlighted);
     203            return this;
     204        }
     205
     206        /**
    191207         * Sets a supplier to obtain a command to fix the error.
    192208         *
     
    422438            } else if (o instanceof List<?>) {
    423439                v.visit((List<Node>) o);
     440            } else if (o instanceof Area) {
     441                for (List<Node> l : getHiliteNodesForArea((Area) o)) {
     442                    v.visit(l);
     443                }
    424444            }
    425445        }
    426446    }
     447
     448    /**
     449     * Calculate list of node pairs describing the area.
     450     * @param area the area
     451     * @return list of node pairs describing the area
     452     */
     453    private  static List<List<Node>> getHiliteNodesForArea(Area area) {
     454        List<List<Node>> hilite = new ArrayList<>();
     455        PathIterator pit = area.getPathIterator(null);
     456        double[] res = new double[6];
     457        List<Node> nodes = new ArrayList<>();
     458        while (!pit.isDone()) {
     459            int type = pit.currentSegment(res);
     460            Node n = new Node(new EastNorth(res[0], res[1]));
     461            switch (type) {
     462            case PathIterator.SEG_MOVETO:
     463                if (!nodes.isEmpty()) {
     464                    hilite.add(nodes);
     465                }
     466                nodes = new ArrayList<>();
     467                nodes.add(n);
     468                break;
     469            case PathIterator.SEG_LINETO:
     470                nodes.add(n);
     471                break;
     472            case PathIterator.SEG_CLOSE:
     473                if (!nodes.isEmpty()) {
     474                    nodes.add(nodes.get(0));
     475                    hilite.add(nodes);
     476                    nodes = new ArrayList<>();
     477                }
     478                break;
     479            default:
     480                break;
     481            }
     482            pit.next();
     483        }
     484        if (nodes.size() > 1) {
     485            hilite.add(nodes);
     486        }
     487        return hilite;
     488    }
     489
    427490
    428491    /**
  • trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java

    r15902 r15938  
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
     6import java.awt.geom.Area;
    67import java.io.BufferedReader;
    78import java.io.IOException;
     
    619620                                errorBuilder = errorBuilder.fix(() -> fix);
    620621                            }
     622                            if (env.intersections != null) {
     623                                Area is = env.intersections.get(c);
     624                                if (is != null) {
     625                                    errorBuilder = errorBuilder.highlight(is);
     626                                }
     627                            }
    621628                            res.add(errorBuilder.primitives(p, (OsmPrimitive) c).build());
    622629                        }
     
    743750                        && e.getPrimitives().size() == toAdd.getPrimitives().size()
    744751                        && e.getPrimitives().containsAll(toAdd.getPrimitives())
    745                         && e.getHighlighted().size() == toAdd.getHighlighted().size()
    746                         && e.getHighlighted().containsAll(toAdd.getHighlighted())) {
     752                        && highlightedIsEqual(e.getHighlighted(), toAdd.getHighlighted())) {
    747753                    isDup = true;
    748754                    break;
     
    752758        if (!isDup)
    753759            errors.add(toAdd);
     760    }
     761
     762    private static boolean highlightedIsEqual(Collection<?> highlighted, Collection<?> highlighted2) {
     763        if (highlighted.size() == highlighted2.size()) {
     764            if (!highlighted.isEmpty()) {
     765                Object h1 = highlighted.iterator().next();
     766                Object h2 = highlighted2.iterator().next();
     767                if (h1 instanceof Area && h2 instanceof Area) {
     768                    return ((Area) h1).equals((Area) h2);
     769                }
     770                return highlighted.containsAll(highlighted2);
     771            }
     772            return true;
     773        }
     774        return false;
    754775    }
    755776
  • trunk/src/org/openstreetmap/josm/gui/mappaint/Environment.java

    r15064 r15938  
    22package org.openstreetmap.josm.gui.mappaint;
    33
     4import java.awt.geom.Area;
     5import java.util.HashMap;
    46import java.util.LinkedHashSet;
     7import java.util.Map;
    58import java.util.Set;
    69
     
    6770     */
    6871    public Set<IPrimitive> children;
     72
     73    /**
     74     * Intersection areas (only filled with CrossingFinder if children is not null)
     75     */
     76    public Map<IPrimitive, Area> intersections;
    6977
    7078    /**
     
    118126        this.context = other.getContext();
    119127        this.children = other.children == null ? null : new LinkedHashSet<>(other.children);
     128        this.intersections = other.intersections == null ? null : new HashMap<>(other.intersections);
    120129    }
    121130
     
    284293        count = null;
    285294        children = null;
     295        intersections = null;
    286296    }
    287297
  • trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java

    r15925 r15938  
    44import static org.openstreetmap.josm.data.projection.Ellipsoid.WGS84;
    55
     6import java.awt.geom.Area;
    67import java.text.MessageFormat;
    78import java.util.ArrayList;
    89import java.util.Collection;
    910import java.util.Collections;
     11import java.util.HashMap;
    1012import java.util.LinkedHashSet;
    1113import java.util.List;
     
    3133import org.openstreetmap.josm.tools.CheckParameterUtil;
    3234import org.openstreetmap.josm.tools.Geometry;
     35import org.openstreetmap.josm.tools.Geometry.PolygonIntersection;
    3336import org.openstreetmap.josm.tools.Logging;
     37import org.openstreetmap.josm.tools.Pair;
    3438import org.openstreetmap.josm.tools.Utils;
    3539
     
    298302
    299303            private final String layer;
     304            private Area area;
    300305
    301306            private CrossingFinder(Environment e) {
     
    309314                if (Objects.equals(layer, OsmUtils.getLayer(w))
    310315                    && left.matches(new Environment(w).withParent(e.osm))
    311                     && e.osm instanceof IWay && Geometry.PolygonIntersection.CROSSING.equals(
    312                             Geometry.polygonIntersection(w.getNodes(), ((IWay<?>) e.osm).getNodes()))) {
    313                     addToChildren(e, w);
     316                    && e.osm instanceof IWay) {
     317                    if (area == null) {
     318                        area = Geometry.getAreaEastNorth(e.osm);
     319                    }
     320                    Pair<PolygonIntersection, Area> is = Geometry.polygonIntersectionResult(
     321                            Geometry.getAreaEastNorth(w), area, Geometry.INTERSECTION_EPS_EAST_NORTH);
     322                    if (Geometry.PolygonIntersection.CROSSING == is.a) {
     323                        addToChildren(e, w);
     324                        // store intersection area to improve highlight and zoom to problem
     325                        if (e.intersections == null) {
     326                            e.intersections = new HashMap<>();
     327                        }
     328                        e.intersections.put(w, is.b);
     329                    }
    314330                }
    315331            }
  • trunk/src/org/openstreetmap/josm/tools/Geometry.java

    r15730 r15938  
    3030import org.openstreetmap.josm.data.osm.INode;
    3131import org.openstreetmap.josm.data.osm.IPrimitive;
     32import org.openstreetmap.josm.data.osm.IRelation;
    3233import org.openstreetmap.josm.data.osm.IWay;
    3334import org.openstreetmap.josm.data.osm.MultipolygonBuilder;
     
    553554
    554555    /**
     556     * Calculate area in east/north space for given primitive. Uses {@link MultipolygonCache} for multipolygon relations.
     557     * @param p the primitive
     558     * @return the area in east/north space, might be empty if the primitive is incomplete or not closed or a node
     559     * since 15938
     560     */
     561    public static Area getAreaEastNorth(IPrimitive p) {
     562        if (p instanceof Way && ((Way) p).isClosed()) {
     563            return Geometry.getArea(((Way) p).getNodes());
     564        }
     565        if (p.isMultipolygon() && !p.isIncomplete() && !((IRelation<?>) p).hasIncompleteMembers()) {
     566            Multipolygon mp = MultipolygonCache.getInstance().get((Relation) p);
     567            Path2D path = new Path2D.Double();
     568            path.setWindingRule(Path2D.WIND_EVEN_ODD);
     569            for (PolyData pd : mp.getCombinedPolygons()) {
     570                path.append(pd.get(), false);
     571            }
     572            return new Area(path);
     573        }
     574        return new Area();
     575    }
     576
     577    /**
    555578     * Returns the Area of a polygon, from the multipolygon relation.
    556579     * @param multipolygon the multipolygon relation
     
    601624     */
    602625    public static PolygonIntersection polygonIntersection(Area a1, Area a2, double eps) {
    603 
     626        return polygonIntersectionResult(a1, a2, eps).a;
     627    }
     628
     629    /**
     630     * Calculate intersection area and kind of intersection between two polygons.
     631     * @param a1 Area of first polygon
     632     * @param a2 Area of second polygon
     633     * @param eps an area threshold, everything below is considered an empty intersection
     634     * @return pair with intersection kind and intersection area (never null, but maybe empty)
     635     * @since 15938
     636     */
     637    public static Pair<PolygonIntersection, Area> polygonIntersectionResult(Area a1, Area a2, double eps) {
    604638        Area inter = new Area(a1);
    605639        inter.intersect(a2);
    606640
    607641        if (inter.isEmpty() || !checkIntersection(inter, eps)) {
    608             return PolygonIntersection.OUTSIDE;
     642            return new Pair<>(PolygonIntersection.OUTSIDE, inter);
    609643        } else if (a2.getBounds2D().contains(a1.getBounds2D()) && inter.equals(a1)) {
    610             return PolygonIntersection.FIRST_INSIDE_SECOND;
     644            return new Pair<>(PolygonIntersection.FIRST_INSIDE_SECOND, inter);
    611645        } else if (a1.getBounds2D().contains(a2.getBounds2D()) && inter.equals(a2)) {
    612             return PolygonIntersection.SECOND_INSIDE_FIRST;
     646            return new Pair<>(PolygonIntersection.SECOND_INSIDE_FIRST, inter);
    613647        } else {
    614             return PolygonIntersection.CROSSING;
     648            return new Pair<>(PolygonIntersection.CROSSING, inter);
    615649        }
    616650    }
Note: See TracChangeset for help on using the changeset viewer.