// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.plugins.fixAddresses; import java.util.List; import java.util.Locale; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.data.Bounds; import org.openstreetmap.josm.data.coor.LatLon; import org.openstreetmap.josm.data.osm.Node; import org.openstreetmap.josm.data.osm.OsmPrimitive; import org.openstreetmap.josm.data.osm.Relation; import org.openstreetmap.josm.data.osm.RelationMember; import org.openstreetmap.josm.data.osm.Way; import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; import org.openstreetmap.josm.tools.CheckParameterUtil; import org.openstreetmap.josm.tools.Pair; /** * The Class OsmUtils provides some utilities not provided by the OSM data framework. */ public final class OsmUtils { /** * Instantiates a new osm utils. */ private OsmUtils() {} /** The cached locale. */ private static String cachedLocale = null; /** * Gets the minimum distance of single coordinate to a way. * * @param coor the coordinate to get the minimum distance for. * @param w the w the way to compare against * @return the minimum distance between the given coordinate and the way */ public static double getMinimumDistanceToWay(LatLon coor, Way w) { if (coor == null || w == null) return Double.POSITIVE_INFINITY; double minDist = Double.MAX_VALUE; List<Pair<Node, Node>> x = w.getNodePairs(true); for (Pair<Node, Node> pair : x) { LatLon ap = pair.a.getCoor(); LatLon bp = pair.b.getCoor(); double dist = findMinimum(ap, bp, coor); if (dist < minDist) { minDist = dist; } } return minDist; } /** * Find the minimum distance between a point and two way coordinates recursively. * * @param a the a the first way point coordinate * @param b the b the second way point coordinate * @param c the c the node coordinate * @return the double the minimum distance in m of the way and the node */ private static double findMinimum(LatLon a, LatLon b, LatLon c) { CheckParameterUtil.ensureParameterNotNull(c, "c"); CheckParameterUtil.ensureParameterNotNull(b, "b"); CheckParameterUtil.ensureParameterNotNull(a, "a"); LatLon mid = new LatLon((a.lat() + b.lat()) / 2, (a.lon() + b.lon()) / 2); double ac = a.greatCircleDistance(c); double bc = b.greatCircleDistance(c); double mc = mid.greatCircleDistance(c); double min = Math.min(Math.min(ac, mc), bc); if (min < 5.0) { // close enough? return min; } if (mc < ac && mc < bc) { // mid point has lower distance than a and b if (ac > bc) { // recurse return findMinimum(b, mid, c); } else { return findMinimum(a, mid, c); } } else { // mid point is not closer than a or b return Math.min(ac, bc); } } /** * Checks, if the given address has a relation hosting the address values. This method looks * for a relation of type 'associatedStreet' and checks the members for address values, if present. * If the member has address values, this methods sets the derived properties of the address * node accordingly. * * @param address The address to check. * @return true, if an associated relation has been found. */ public static boolean getValuesFromRelation(OSMAddress address) { if (address == null) { return false; } boolean hasValuesFromRel = false; /* true, if we applied some address props from the relation */ OsmPrimitive addrNode = address.getOsmObject(); // check all referrers of the node for (OsmPrimitive osm : addrNode.getReferrers()) { if (osm instanceof Relation) { Relation r = (Relation) osm; // Relation has the right type? if (!TagUtils.isAssociatedStreetRelation(r)) continue; // check for 'street' members for (RelationMember rm : r.getMembers()) { if (TagUtils.isStreetMember(rm)) { OsmPrimitive street = rm.getMember(); if (TagUtils.hasHighwayTag(street)) { String streetName = TagUtils.getNameValue(street); if (!StringUtils.isNullOrEmpty(streetName)) { // street name found -> set property address.setDerivedValue(TagConstants.ADDR_STREET_TAG, streetName); hasValuesFromRel = true; break; } // else: Street has no name: Ooops } // else: Street member, but no highway tag: Ooops } } // Check for other address properties if (TagUtils.hasAddrCityTag(r)) { // city address.setDerivedValue(TagConstants.ADDR_CITY_TAG, TagUtils.getAddrCityValue(r)); hasValuesFromRel = true; } if (TagUtils.hasAddrCountryTag(r)) { // country address.setDerivedValue(TagConstants.ADDR_COUNTRY_TAG, TagUtils.getAddrCountryValue(r)); hasValuesFromRel = true; } if (TagUtils.hasAddrPostcodeTag(r)) { // postcode address.setDerivedValue(TagConstants.ADDR_POSTCODE_TAG, TagUtils.getAddrPostcodeValue(r)); hasValuesFromRel = true; } } } return hasValuesFromRel; } /** * Gets the tag values from an address interpolation ref, if present. * * @param address The address * @return true, if house numbers are given via address interpolation; otherwise false. */ public static boolean getValuesFromAddressInterpolation(OSMAddress address) { if (address == null) return false; OsmPrimitive osmAddr = address.getOsmObject(); for (OsmPrimitive osm : osmAddr.getReferrers()) { if (osm instanceof Way) { Way w = (Way) osm; if (TagUtils.hasAddrInterpolationTag(w)) { applyDerivedValue(address, w, TagConstants.ADDR_POSTCODE_TAG); applyDerivedValue(address, w, TagConstants.ADDR_CITY_TAG); applyDerivedValue(address, w, TagConstants.ADDR_COUNTRY_TAG); applyDerivedValue(address, w, TagConstants.ADDR_STREET_TAG); applyDerivedValue(address, w, TagConstants.ADDR_STATE_TAG); return true; } } } return false; } /** * Gets the local code as string. * * @return the string representation of the local. */ public static String getLocale() { // Check if user could prefer imperial system if (cachedLocale == null) { Locale l = Locale.getDefault(); cachedLocale = l.toString(); } return cachedLocale; } /** * Zooms to the given addresses. * * @param addressList the address list */ public static void zoomAddresses(List<OSMAddress> addressList) { CheckParameterUtil.ensureParameterNotNull(addressList, "addressList"); if (Main.map == null && Main.map.mapView == null) return; // nothing to do if (addressList.size() == 0) return; // dto. // compute bounding box BoundingXYVisitor bbox = new BoundingXYVisitor(); for (OSMAddress source : addressList) { OsmPrimitive osm = source.getOsmObject(); Bounds b = new Bounds(osm.getBBox().getTopLeft(), osm.getBBox().getBottomRight()); bbox.visit(b); } if (bbox.getBounds() != null) { // zoom to calculated bounding box Main.map.mapView.zoomTo(bbox.getBounds()); } } /** * Helper method to set a derived value of an address node. * * @param address The address to change * @param w the way containing the tag for the derived value. * @param tag the tag to set as applied value. */ private static void applyDerivedValue(OSMAddress address, Way w, String tag) { CheckParameterUtil.ensureParameterNotNull(address, "address"); CheckParameterUtil.ensureParameterNotNull(w, "way"); if (!address.hasTag(tag) && TagUtils.hasTag(w, tag)) { address.setDerivedValue(tag, w.get(tag)); } } }