From 9470285d3fb1091d22180dda37db58360de1f62c Mon Sep 17 00:00:00 2001 From: Pavol Zibrita Date: Mon, 26 Sep 2011 21:08:29 +0200 Subject: [PATCH] Trying to find better street-city binding according existing boundaries. Removing incomplete boundaries. Returned the algorithm to search nearest city/village for streets with same name that are near different districts. --- .../src/net/osmand/data/Boundary.java | 54 ++++++++++++ .../src/net/osmand/data/MapObject.java | 25 ++++++ .../data/preparation/IndexAddressCreator.java | 88 ++++++++++++++++--- .../osmand/data/preparation/IndexCreator.java | 3 + 4 files changed, 159 insertions(+), 11 deletions(-) diff --git a/DataExtractionOSM/src/net/osmand/data/Boundary.java b/DataExtractionOSM/src/net/osmand/data/Boundary.java index af07281e79..ff0cd7d792 100644 --- a/DataExtractionOSM/src/net/osmand/data/Boundary.java +++ b/DataExtractionOSM/src/net/osmand/data/Boundary.java @@ -1,6 +1,7 @@ package net.osmand.data; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import net.osmand.osm.LatLon; @@ -28,6 +29,59 @@ public class Boundary { return closedWay; } + public boolean computeIsClosedWay() + { + //now we try to merge the ways until we have only one + int oldSize = 0; + while (getOuterWays().size() != oldSize) { + oldSize = getOuterWays().size(); + mergeOuterWays(); + } + //there is one way and last element is equal to the first... + return getOuterWays().size() == 1 && getOuterWays().get(0).getNodes().get(0).getId() == getOuterWays().get(0).getNodes().get(getOuterWays().get(0).getNodes().size()-1).getId(); + } + + + private void mergeOuterWays() { + Way way = getOuterWays().get(0); + List nodes = way.getNodes(); + if (!nodes.isEmpty()) { + int nodesSize = nodes.size(); + Node first = nodes.get(0); + Node last = nodes.get(nodesSize-1); + int size = getOuterWays().size(); + for (int i = size-1; i >= 1; i--) { + //try to find way, that matches the one ... + Way anotherWay = getOuterWays().get(i); + if (anotherWay.getNodes().isEmpty()) { + //remove empty one... + getOuterWays().remove(i); + } else { + if (anotherWay.getNodes().get(0).getId() == first.getId()) { + //reverese this way and add it to the actual + Collections.reverse(anotherWay.getNodes()); + way.getNodes().addAll(0,anotherWay.getNodes()); + getOuterWays().remove(i); + } else if (anotherWay.getNodes().get(0).getId() == last.getId()) { + way.getNodes().addAll(anotherWay.getNodes()); + getOuterWays().remove(i); + } else if (anotherWay.getNodes().get(anotherWay.getNodes().size()-1).getId() == first.getId()) { + //add at begging + way.getNodes().addAll(0,anotherWay.getNodes()); + getOuterWays().remove(i); + } else if (anotherWay.getNodes().get(anotherWay.getNodes().size()-1).getId() == last.getId()) { + Collections.reverse(anotherWay.getNodes()); + way.getNodes().addAll(anotherWay.getNodes()); + getOuterWays().remove(i); + } + } + } + } else { + //remove way with no nodes! + getOuterWays().remove(0); + } + } + public boolean containsPoint(LatLon point) { return containsPoint(point.getLatitude(), point.getLongitude()); } diff --git a/DataExtractionOSM/src/net/osmand/data/MapObject.java b/DataExtractionOSM/src/net/osmand/data/MapObject.java index 95e1100b8c..329209fd16 100644 --- a/DataExtractionOSM/src/net/osmand/data/MapObject.java +++ b/DataExtractionOSM/src/net/osmand/data/MapObject.java @@ -119,4 +119,29 @@ public abstract class MapObject implements Comparable { return getClass().getSimpleName() + " " + name +"("+id+")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((id == null) ? 0 : id.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + MapObject other = (MapObject) obj; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + return true; + } + } diff --git a/DataExtractionOSM/src/net/osmand/data/preparation/IndexAddressCreator.java b/DataExtractionOSM/src/net/osmand/data/preparation/IndexAddressCreator.java index fe51708339..0715cf82bf 100644 --- a/DataExtractionOSM/src/net/osmand/data/preparation/IndexAddressCreator.java +++ b/DataExtractionOSM/src/net/osmand/data/preparation/IndexAddressCreator.java @@ -16,6 +16,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; @@ -70,6 +71,7 @@ public class IndexAddressCreator extends AbstractIndexPartCreator{ private DataTileManager cityManager = new DataTileManager(10); private List postalCodeRelations = new ArrayList(); private Map cityBoundaries = new HashMap(); + private Set allBoundaries = new HashSet(); private TLongHashSet visitedBoundaryWays = new TLongHashSet(); private boolean normalizeStreets; @@ -82,6 +84,8 @@ public class IndexAddressCreator extends AbstractIndexPartCreator{ Connection mapConnection; DBStreetDAO streetDAO; + + public class DBStreetDAO { protected void writeStreetWayNodes(Set streetIds, Way way) throws SQLException { @@ -267,12 +271,13 @@ public class IndexAddressCreator extends AbstractIndexPartCreator{ } } } + //We should not look for similarities, this can be 'very' wrong.... if (cityFound.isEmpty()) { // try to find same name in the middle for (City c : citiesToSearch) { if (boundary.containsPoint(c.getLocation())) { String lower = c.getName().toLowerCase(); - if (boundaryName.startsWith(lower) || boundaryName.endsWith(lower) || boundaryName.contains(" " + lower + " ")) { + if (boundaryName.startsWith(lower + " ") || boundaryName.endsWith(lower + " ") || boundaryName.contains(" " + lower + " ")) { cityFound.add(c); break; } @@ -299,9 +304,43 @@ public class IndexAddressCreator extends AbstractIndexPartCreator{ //we can have more cities in one boundary!! (suburbs...) putCityBoundary(boundary, cityFound); } + allBoundaries.add(boundary); } } + public void bindCitiesWithBoundaries() { + Iterator iter = allBoundaries.iterator(); + while (iter.hasNext()) { + Boundary b = iter.next(); + if (!b.computeIsClosedWay()) { + System.out.println("Removing not closed boundary: " + b.getName() + " alevel:" + b.getAdminLevel()); + iter.remove(); + } + } + + //for cities without boundaries, try to find the right one + for (City c : cities.values()) { + Boundary cityB = cityBoundaries.get(c); + int smallestAdminLevel = 8; //TODO start at level 8 for now... + if (cityB == null) { + LatLon location = c.getLocation(); + Boundary smallestoundary = null; + //try to found boundary + for (Boundary b : allBoundaries) { + if (b.containsPoint(location.getLatitude(), location.getLongitude())) { + //the bigger the admin level, the smaller the boundary :-) + if (b.getAdminLevel() > smallestAdminLevel) { + smallestAdminLevel = b.getAdminLevel(); + smallestoundary = b; + } + } + } + if (smallestoundary != null) { + putCityBoundary(smallestoundary, Collections.singletonList(c)); + } + } + } + } private int extractBoundaryAdminLevel(Entity e) { int adminLevel = -1; @@ -605,12 +644,6 @@ public class IndexAddressCreator extends AbstractIndexPartCreator{ result.add(city); } } -// for (Entry cb : cityBoundaries.entrySet()) { -// if (cb.getValue().containsPoint(location)) { -// result.add(cb.getKey()); -// } -// } - return registerStreetInCities(name, nameEn, location, result); } @@ -626,15 +659,23 @@ public class IndexAddressCreator extends AbstractIndexPartCreator{ Set values = new TreeSet(); for (City city : result) { String cityPart = city.getName(); + boolean found = false; for(City subpart : result){ if(subpart != city){ Boundary subBoundary = cityBoundaries.get(subpart); Boundary b = cityBoundaries.get(city); - if(b != null && subBoundary != null && subBoundary.getAdminLevel() > b.getAdminLevel()){ - cityPart = subpart.getName(); + if(b != null && subBoundary != null && subBoundary.getAdminLevel() > b.getAdminLevel()){ + cityPart = findNearestCityOrSuburb(subBoundary, location); //subpart.getName(); + found = true; } } } + if (!found) { + Boundary b = cityBoundaries.get(city); + if (b != null) { + cityPart = findNearestCityOrSuburb(b, location); + } + } Long foundId = streetDAO.findStreet(name, city, cityPart); if (foundId == null) { long streetId = insertStreetData(addressStreetStat, name, nameEn, location.getLatitude(), location.getLongitude(), city.getId(), @@ -647,6 +688,24 @@ public class IndexAddressCreator extends AbstractIndexPartCreator{ return values; } + private String findNearestCityOrSuburb(Boundary greatestBoundary, + LatLon location) { + String result = greatestBoundary.getName(); + List nearestObjects = new ArrayList(); + nearestObjects.addAll(cityManager.getClosestObjects(location.getLatitude(),location.getLongitude())); + nearestObjects.addAll(cityVillageManager.getClosestObjects(location.getLatitude(),location.getLongitude())); + double dist = Double.MAX_VALUE; + for (City c : nearestObjects) { + if (greatestBoundary.containsPoint(c.getLocation())) { + double actualDistance = MapUtils.getDistance(location, c.getLocation()); + if (actualDistance < dist) { + result = c.getName(); + dist = actualDistance; + } + } + } + return result; + } public City getClosestCity(LatLon point, Set isInNames) { @@ -764,11 +823,12 @@ public class IndexAddressCreator extends AbstractIndexPartCreator{ private long streetIdSequence = 0; + private boolean debugFullNames = false; //true to see atached cityPart and boundaries to the street names + private long insertStreetData(PreparedStatement addressStreetStat, //long id, String name, String nameEn, double latitude, double longitude, Long cityId, String cityPart) throws SQLException { long streetId = streetIdSequence++; -// addressStreetStat.setLong(1, id); addressStreetStat.setLong(1, streetId); addressStreetStat.setString(4, name); addressStreetStat.setString(5, nameEn); @@ -1092,7 +1152,7 @@ public class IndexAddressCreator extends AbstractIndexPartCreator{ private String identifyBestDistrict(final Street street, final String streetName, final String district, final Map> uniqueNames, Map> streetNodes) { - String result = ""; + String result = debugFullNames ? district : ""; //TODO make it an option List sameStreets = uniqueNames.get(streetName); if (sameStreets == null) { sameStreets = new ArrayList(1); @@ -1137,6 +1197,12 @@ public class IndexAddressCreator extends AbstractIndexPartCreator{ city.setId(set.getLong(1)); cities.add(city); + if (debugFullNames) { //TODO make it an option... + Boundary cityB = cityBoundaries.get(city); + if (cityB != null) { + city.setName(city.getName() + " " + cityB.getAdminLevel() + ":" + cityB.getName()); + } + } } set.close(); stat.close(); diff --git a/DataExtractionOSM/src/net/osmand/data/preparation/IndexCreator.java b/DataExtractionOSM/src/net/osmand/data/preparation/IndexCreator.java index ac2365e971..4fea2b1d57 100644 --- a/DataExtractionOSM/src/net/osmand/data/preparation/IndexCreator.java +++ b/DataExtractionOSM/src/net/osmand/data/preparation/IndexCreator.java @@ -460,6 +460,9 @@ public class IndexCreator { } }); + //finish up the boundaries and cities + indexAddressCreator.bindCitiesWithBoundaries(); + progress.setGeneralProgress("[45 / 100]"); //$NON-NLS-1$ progress.startTask(Messages.getString("IndexCreator.PREINDEX_ADRESS_MAP"), accessor.getAllRelations()); //$NON-NLS-1$ accessor.iterateOverEntities(progress, EntityType.RELATION, new OsmDbVisitor() {