Changeset 13968 in josm
- Timestamp:
- 2018-06-23T01:24:18+02:00 (6 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/validation/tests/Addresses.java
r12846 r13968 6 6 7 7 import java.util.ArrayList; 8 import java.util.Arrays; 8 9 import java.util.Collection; 9 10 import java.util.HashMap; … … 13 14 import java.util.Map; 14 15 import java.util.Map.Entry; 16 import java.util.Objects; 15 17 import java.util.Set; 18 import java.util.stream.Collectors; 19 import java.util.stream.Stream; 16 20 17 21 import org.openstreetmap.josm.data.coor.EastNorth; 22 import org.openstreetmap.josm.data.coor.LatLon; 18 23 import org.openstreetmap.josm.data.osm.Node; 19 24 import org.openstreetmap.josm.data.osm.OsmPrimitive; … … 21 26 import org.openstreetmap.josm.data.osm.RelationMember; 22 27 import org.openstreetmap.josm.data.osm.Way; 28 import org.openstreetmap.josm.data.preferences.DoubleProperty; 23 29 import org.openstreetmap.josm.data.validation.Severity; 24 30 import org.openstreetmap.josm.data.validation.Test; 25 31 import org.openstreetmap.josm.data.validation.TestError; 26 import org.openstreetmap.josm.spi.preferences.Config;27 32 import org.openstreetmap.josm.tools.Geometry; 28 33 import org.openstreetmap.josm.tools.Logging; 29 34 import org.openstreetmap.josm.tools.Pair; 30 35 import org.openstreetmap.josm.tools.SubclassFilteredCollection; 36 import org.openstreetmap.josm.tools.Utils; 31 37 32 38 /** … … 41 47 protected static final int MULTIPLE_STREET_RELATIONS = 2604; 42 48 protected static final int HOUSE_NUMBER_TOO_FAR = 2605; 49 50 protected static final DoubleProperty MAX_DUPLICATE_DISTANCE = new DoubleProperty("validator.addresses.max_duplicate_distance", 200.0); 51 protected static final DoubleProperty MAX_STREET_DISTANCE = new DoubleProperty("validator.addresses.max_street_distance", 200.0); 43 52 44 53 // CHECKSTYLE.OFF: SingleSpaceSeparator … … 48 57 protected static final String ADDR_PLACE = "addr:place"; 49 58 protected static final String ADDR_STREET = "addr:street"; 59 protected static final String ADDR_CITY = "addr:city"; 60 protected static final String ADDR_UNIT = "addr:unit"; 61 protected static final String ADDR_FLATS = "addr:flats"; 62 protected static final String ADDR_HOUSE_NAME = "addr:housename"; 63 protected static final String ADDR_POSTCODE = "addr:postcode"; 50 64 protected static final String ASSOCIATED_STREET = "associatedStreet"; 51 65 // CHECKSTYLE.ON: SingleSpaceSeparator 66 67 private Map<String, Collection<OsmPrimitive>> addresses = null; 68 private Set<String> ignoredAddresses = null; 52 69 53 70 /** … … 103 120 } 104 121 122 static boolean isPOI(OsmPrimitive p) { 123 return p.hasKey("shop", "amenity", "tourism", "leisure", "emergency", "craft", "office", "name"); 124 } 125 126 private boolean hasAddress(OsmPrimitive p) { 127 return p.hasKey(ADDR_HOUSE_NUMBER) && p.hasKey(ADDR_STREET, ADDR_PLACE); 128 } 129 130 /** 131 * adds the OsmPrimitive to the address map if it complies to the restrictions 132 * @param p OsmPrimitive that has an address 133 */ 134 private void collectAddress(OsmPrimitive p) { 135 if (!isPOI(p)) { 136 String simplifiedAddress = getSimplifiedAddress(p); 137 if (!ignoredAddresses.contains(simplifiedAddress)) { 138 addresses.computeIfAbsent(simplifiedAddress, x -> new ArrayList<>()).add(p); 139 } 140 } 141 } 142 143 protected void initAddressMap(OsmPrimitive primitive) { 144 addresses = new HashMap<>(); 145 ignoredAddresses = new HashSet<>(); 146 for (OsmPrimitive p : primitive.getDataSet().allNonDeletedPrimitives()) { 147 if (p instanceof Node && p.hasKey(ADDR_UNIT, ADDR_FLATS)) { 148 for (OsmPrimitive r : p.getReferrers()) { 149 if (hasAddress(r)) { 150 // ignore addresses of buildings that are connected to addr:unit nodes 151 // it's quite reasonable that there are more buildings with this address 152 String simplifiedAddress = getSimplifiedAddress(r); 153 if (!ignoredAddresses.contains(simplifiedAddress)) { 154 ignoredAddresses.add(simplifiedAddress); 155 } else if (addresses.containsKey(simplifiedAddress)) { 156 addresses.remove(simplifiedAddress); 157 } 158 } 159 } 160 } 161 if (hasAddress(p)) { 162 collectAddress(p); 163 } 164 } 165 } 166 167 @Override 168 public void endTest() { 169 addresses = null; 170 ignoredAddresses = null; 171 super.endTest(); 172 } 173 174 protected void checkForDuplicate(OsmPrimitive p) { 175 if (addresses == null) { 176 initAddressMap(p); 177 } 178 if (!isPOI(p) && hasAddress(p)) { 179 String simplifiedAddress = getSimplifiedAddress(p); 180 if (ignoredAddresses.contains(simplifiedAddress)) { 181 return; 182 } 183 if (addresses.containsKey(simplifiedAddress)) { 184 double maxDistance = MAX_DUPLICATE_DISTANCE.get(); 185 for (OsmPrimitive p2 : addresses.get(simplifiedAddress)) { 186 if (p == p2) { 187 continue; 188 } 189 Severity severityLevel = Severity.WARNING; 190 String city1 = p.get(ADDR_CITY); 191 String city2 = p2.get(ADDR_CITY); 192 double distance = getDistance(p, p2); 193 if (city1 != null && city2 != null) { 194 if (city1.equals(city2)) { 195 if (!p.hasKey(ADDR_POSTCODE) || !p2.hasKey(ADDR_POSTCODE) || p.get(ADDR_POSTCODE).equals(p2.get(ADDR_POSTCODE))) { 196 severityLevel = Severity.WARNING; 197 } else { 198 // address including city identical but postcode differs 199 // most likely perfectly fine 200 severityLevel = Severity.OTHER; 201 } 202 } else { 203 // address differs only by city - notify if very close, otherwise ignore 204 if (distance < maxDistance) { 205 severityLevel = Severity.OTHER; 206 } else { 207 continue; 208 } 209 } 210 } else { 211 // at least one address has no city specified 212 if (p.hasKey(ADDR_POSTCODE) && p2.hasKey(ADDR_POSTCODE) && p.get(ADDR_POSTCODE).equals(p2.get(ADDR_POSTCODE))) { 213 // address including postcode identical 214 severityLevel = Severity.WARNING; 215 } else { 216 // city/postcode unclear - warn if very close, otherwise only notify 217 // TODO: get city from surrounding boundaries? 218 if (distance < maxDistance) { 219 severityLevel = Severity.WARNING; 220 } else { 221 severityLevel = Severity.OTHER; 222 } 223 } 224 } 225 errors.add(TestError.builder(this, severityLevel, DUPLICATE_HOUSE_NUMBER) 226 .message(tr("Duplicate house numbers"), marktr("''{0}'' ({1}m)"), simplifiedAddress, (int) distance) 227 .primitives(Arrays.asList(p, p2)).build()); 228 } 229 addresses.get(simplifiedAddress).remove(p); // otherwise we would get every warning two times 230 } 231 } 232 } 233 234 static String getSimplifiedAddress(OsmPrimitive p) { 235 String simplifiedStreetName = p.hasKey(ADDR_STREET) ? p.get(ADDR_STREET) : p.get(ADDR_PLACE); 236 // ignore whitespaces and dashes in street name, so that "Mozart-Gasse", "Mozart Gasse" and "Mozartgasse" are all seen as equal 237 return Utils.strip(Stream.of( 238 simplifiedStreetName.replaceAll("[ -]", ""), 239 p.get(ADDR_HOUSE_NUMBER), 240 p.get(ADDR_HOUSE_NAME), 241 p.get(ADDR_UNIT), 242 p.get(ADDR_FLATS)) 243 .filter(Objects::nonNull) 244 .collect(Collectors.joining(" "))) 245 .toUpperCase(Locale.ENGLISH); 246 } 247 105 248 @Override 106 249 public void visit(Node n) { 107 250 checkHouseNumbersWithoutStreet(n); 251 checkForDuplicate(n); 108 252 } 109 253 … … 111 255 public void visit(Way w) { 112 256 checkHouseNumbersWithoutStreet(w); 257 checkForDuplicate(w); 113 258 } 114 259 … … 116 261 public void visit(Relation r) { 117 262 checkHouseNumbersWithoutStreet(r); 263 checkForDuplicate(r); 118 264 if (r.hasTag("type", ASSOCIATED_STREET)) { 119 265 // Used to count occurences of each house number in order to find duplicates … … 186 332 } 187 333 334 /** 335 * returns rough distance between two OsmPrimitives 336 * @param a primitive a 337 * @param b primitive b 338 * @return distance of center of bounding boxes in meters 339 */ 340 static double getDistance(OsmPrimitive a, OsmPrimitive b) { 341 LatLon centerA = a.getBBox().getCenter(); 342 LatLon centerB = b.getBBox().getCenter(); 343 return (centerA.greatCircleDistance(centerB)); 344 } 345 188 346 protected void checkDistance(OsmPrimitive house, Collection<Way> street) { 189 347 EastNorth centroid; … … 205 363 } 206 364 if (centroid == null) return; // fix #8305 207 double maxDistance = Config.getPref().getDouble("validator.addresses.max_street_distance", 200.0);365 double maxDistance = MAX_STREET_DISTANCE.get(); 208 366 boolean hasIncompleteWays = false; 209 367 for (Way streetPart : street) {
Note:
See TracChangeset
for help on using the changeset viewer.