001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.tools;
003
004import java.awt.Rectangle;
005import java.awt.geom.Area;
006import java.awt.geom.Path2D;
007import java.util.Collection;
008
009import org.openstreetmap.josm.data.coor.LatLon;
010import org.openstreetmap.josm.data.osm.BBox;
011import org.openstreetmap.josm.data.osm.Relation;
012import org.openstreetmap.josm.data.osm.Way;
013
014/**
015 * Implementation of simple boolean {@link GeoProperty}.
016 */
017public class DefaultGeoProperty implements GeoProperty<Boolean> {
018
019    private final Area area;
020    private LatLon random;
021
022    /**
023     * Create DefaultGeoProperty based on a collection of closed ways.
024     *
025     * @param ways the ways forming the area
026     */
027    public DefaultGeoProperty(Collection<Way> ways) {
028        Path2D path = new Path2D.Double();
029        path.setWindingRule(Path2D.WIND_EVEN_ODD);
030        for (Way w : ways) {
031            Geometry.buildPath2DLatLon(w.getNodes(), path);
032        }
033        this.area = new Area(path);
034    }
035
036    /**
037     * Create DefaultGeoProperty based on a multipolygon relation.
038     *
039     * @param multipolygon the multipolygon
040     */
041    public DefaultGeoProperty(Relation multipolygon) {
042        this.area = Geometry.getAreaLatLon(multipolygon);
043    }
044
045    @Override
046    public Boolean get(LatLon ll) {
047        return area.contains(ll.lon(), ll.lat());
048    }
049
050    @Override
051    public Boolean get(BBox box) {
052        Area abox = new Area(box.toRectangle());
053        Geometry.PolygonIntersection is = Geometry.polygonIntersection(abox, area, 1e-10 /* using deg and not meters */);
054        switch (is) {
055            case FIRST_INSIDE_SECOND:
056                return Boolean.TRUE;
057            case OUTSIDE:
058                return Boolean.FALSE;
059            default:
060                return null;
061        }
062    }
063
064    /**
065     * Returns the area.
066     * @return the area
067     * @since 14484
068     */
069    public final Area getArea() {
070        return area;
071    }
072
073    /**
074     * Returns a random lat/lon in the area.
075     * @return a random lat/lon in the area
076     * @since 15359
077     */
078    public final synchronized LatLon getRandomLatLon() {
079        if (random == null) {
080            Rectangle r = area.getBounds();
081            double x, y;
082            do {
083                x = r.getX() + r.getWidth() * Math.random();
084                y = r.getY() + r.getHeight() * Math.random();
085            } while (!area.contains(x, y));
086
087            random = new LatLon(y, x);
088        }
089        return random;
090    }
091}