diff --git a/DataExtractionOSM/src/net/osmand/data/Boundary.java b/DataExtractionOSM/src/net/osmand/data/Boundary.java index 094caea149..22d3cf68d0 100644 --- a/DataExtractionOSM/src/net/osmand/data/Boundary.java +++ b/DataExtractionOSM/src/net/osmand/data/Boundary.java @@ -13,7 +13,7 @@ public class Boundary { private long boundaryId; private String name; - private String adminLevel; + private int adminLevel; // not necessary ready rings @@ -82,25 +82,25 @@ public class Boundary { this.name = name; } - public String getAdminLevel() { + public int getAdminLevel() { return adminLevel; } - public void setAdminLevel(String adminLevel) { + public void setAdminLevel(int adminLevel) { this.adminLevel = adminLevel; } - public List getSubboundaries() { + private List getSubboundaries() { return subboundaries; } - public void addSubBoundary(Boundary subBoundary) { + private void addSubBoundary(Boundary subBoundary) { if (subBoundary != null) { subboundaries.add(subBoundary); } } - public void addSubBoundaries(Collection subBoundaries) { + private void addSubBoundaries(Collection subBoundaries) { subboundaries.addAll(subBoundaries); } diff --git a/DataExtractionOSM/src/net/osmand/data/preparation/IndexAddressCreator.java b/DataExtractionOSM/src/net/osmand/data/preparation/IndexAddressCreator.java index f3b14e094a..89db3e29a5 100644 --- a/DataExtractionOSM/src/net/osmand/data/preparation/IndexAddressCreator.java +++ b/DataExtractionOSM/src/net/osmand/data/preparation/IndexAddressCreator.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; +import java.util.TreeSet; import net.osmand.Algoritms; import net.osmand.IProgress; @@ -75,7 +76,6 @@ public class IndexAddressCreator extends AbstractIndexPartCreator{ private String[] normalizeDefaultSuffixes; private String[] normalizeSuffixes; - private String cityAdminLevel; private boolean saveAddressWays; // TODO @@ -84,30 +84,33 @@ public class IndexAddressCreator extends AbstractIndexPartCreator{ public class DBStreetDAO { - protected void writeStreetWayNodes(Long streetId, Way way) - throws SQLException { - for (Node n : way.getNodes()) { - if (n == null) { - continue; + protected void writeStreetWayNodes(Set streetIds, Way way) throws SQLException { + for (Long streetId : streetIds) { + for (Node n : way.getNodes()) { + if (n == null) { + continue; + } + addressStreetNodeStat.setLong(1, n.getId()); + addressStreetNodeStat.setDouble(2, n.getLatitude()); + addressStreetNodeStat.setDouble(3, n.getLongitude()); + addressStreetNodeStat.setLong(5, way.getId()); + addressStreetNodeStat.setLong(4, streetId); + addBatch(addressStreetNodeStat); } - addressStreetNodeStat.setLong(1, n.getId()); - addressStreetNodeStat.setDouble(2, n.getLatitude()); - addressStreetNodeStat.setDouble(3, n.getLongitude()); - addressStreetNodeStat.setLong(5, way.getId()); - addressStreetNodeStat.setLong(4, streetId); - addBatch(addressStreetNodeStat); } } - protected void writeBuilding(Long streetId, Building building) throws SQLException { - addressBuildingStat.setLong(1, building.getId()); - addressBuildingStat.setDouble(2, building.getLocation().getLatitude()); - addressBuildingStat.setDouble(3, building.getLocation().getLongitude()); - addressBuildingStat.setString(4, building.getName()); - addressBuildingStat.setString(5, building.getEnName()); - addressBuildingStat.setLong(6, streetId); - addressBuildingStat.setString(7, building.getPostcode() == null ? null : building.getPostcode().toUpperCase()); - addBatch(addressBuildingStat); + protected void writeBuilding(Set streetIds, Building building) throws SQLException { + for (Long streetId : streetIds) { + addressBuildingStat.setLong(1, building.getId()); + addressBuildingStat.setDouble(2, building.getLocation().getLatitude()); + addressBuildingStat.setDouble(3, building.getLocation().getLongitude()); + addressBuildingStat.setString(4, building.getName()); + addressBuildingStat.setString(5, building.getEnName()); + addressBuildingStat.setLong(6, streetId); + addressBuildingStat.setString(7, building.getPostcode() == null ? null : building.getPostcode().toUpperCase()); + addBatch(addressBuildingStat); + } } public Long findStreet(String name, City city, String cityPart) throws SQLException { @@ -165,14 +168,14 @@ public class IndexAddressCreator extends AbstractIndexPartCreator{ } @Override - protected void writeStreetWayNodes(Long streetId, Way way) + protected void writeStreetWayNodes(Set streetId, Way way) throws SQLException { super.writeStreetWayNodes(streetId, way); addressStreetNodeLocalSet.add(way.getId()); } @Override - protected void writeBuilding(Long streetId, Building building) + protected void writeBuilding(Set streetId, Building building) throws SQLException { super.writeBuilding(streetId, building); addressBuildingLocalSet.add(building.getId()); @@ -229,7 +232,6 @@ public class IndexAddressCreator extends AbstractIndexPartCreator{ this.normalizeStreets = normalizeStreets; this.normalizeDefaultSuffixes = normalizeDefaultSuffixes; this.normalizeSuffixes = normalizeSuffixes; - this.cityAdminLevel = cityAdminLevel; this.saveAddressWays = saveAddressWays; } @@ -248,19 +250,28 @@ public class IndexAddressCreator extends AbstractIndexPartCreator{ } public void indexBoundariesRelation(Entity e, OsmDbAccessorContext ctx) throws SQLException { - if (isBoundary(e) && hasNeededCityAdminLevel(e)) { + if (isBoundary(e)) { + int adminLevel = 8; + try { + String tag = e.getTag(OSMTagKey.ADMIN_LEVEL); + if(tag == null){ + return; + } + adminLevel = Integer.parseInt(tag); + } catch (NumberFormatException ex) { + return; + } + Boundary boundary = extractBoundary(e, ctx); - - if (boundary != null && boundary.getCenterPoint() != null) { + if (boundary != null && boundary.getCenterPoint() != null && !Algoritms.isEmpty(boundary.getName())) { + boundary.setAdminLevel(adminLevel); LatLon point = boundary.getCenterPoint(); boolean cityFound = false; - boolean containsCityInside = false; for (City c : cityManager.getClosestObjects(point.getLatitude(), point.getLongitude(), 3)) { if (boundary.containsPoint(c.getLocation())) { - if (boundary.getName() == null || boundary.getName().equalsIgnoreCase(c.getName())) { + if (boundary.getName().equalsIgnoreCase(c.getName())) { putCityBoundary(boundary, c); cityFound = true; - containsCityInside = true; } } } @@ -268,45 +279,13 @@ public class IndexAddressCreator extends AbstractIndexPartCreator{ if (!cityFound) { for (City c : cityVillageManager.getClosestObjects(point.getLatitude(), point.getLongitude(), 3)) { if (boundary.containsPoint(c.getLocation())) { - if (boundary.getName() == null || boundary.getName().equalsIgnoreCase(c.getName())) { + if (boundary.getName().equalsIgnoreCase(c.getName())) { putCityBoundary(boundary, c); cityFound = true; } } } } - if (!cityFound && boundary.getName() != null) { - // / create new city for named boundary very rare case that's why do not proper generate id - // however it could be a problem - City nCity = new City(containsCityInside ? CityType.CITY : CityType.SUBURB); - nCity.setLocation(point.getLatitude(), point.getLongitude()); - nCity.setId(-boundary.getBoundaryId()); - nCity.setName(boundary.getName()); - putCityBoundary(boundary, nCity); - cityVillageManager.registerObject(point.getLatitude(), point.getLongitude(), nCity); - - writeCity(nCity); - // commit to put all cities - if (pStatements.get(addressCityStat) > 0) { - addressCityStat.executeBatch(); - pStatements.put(addressCityStat, 0); - } - } - } - } else if (isBoundary(e) && hasGreaterCityAdminLevel(cityAdminLevel,e)) { - //Any lower admin_level boundary is attached to the nearest city - Boundary boundary = extractBoundary(e, ctx); - if (boundary != null && boundary.getCenterPoint() != null) { - LatLon point = boundary.getCenterPoint(); - for (City c : cityManager.getClosestObjects(point.getLatitude(), point.getLongitude(), 3)) { - Boundary cityB = cityBoundaries.get(c); - if (cityB == null) { - cityB = new Boundary(); //create empty boundary that is replaced with the real one for the city (if found) - putCityBoundary(cityB, c); - } - cityB.addSubBoundary(boundary); - break; - } } } } @@ -314,49 +293,27 @@ public class IndexAddressCreator extends AbstractIndexPartCreator{ private void putCityBoundary(Boundary boundary, City c) { final Boundary oldBoundary = cityBoundaries.get(c); if (oldBoundary != null) { - boundary.addSubBoundaries(oldBoundary.getSubboundaries()); + if (oldBoundary.getAdminLevel() < boundary.getAdminLevel()) { + cityBoundaries.put(c, boundary); + } + } else { + cityBoundaries.put(c, boundary); } - cityBoundaries.put(c, boundary); } - private boolean isBoundary(Entity e) - { + private boolean isBoundary(Entity e) { return "administrative".equals(e.getTag(OSMTagKey.BOUNDARY)) && (e instanceof Relation || e instanceof Way); } - private boolean hasNeededCityAdminLevel(Entity e) - { - return cityAdminLevel.equals(e.getTag(OSMTagKey.ADMIN_LEVEL)); - } - private boolean hasGreaterCityAdminLevel(String admin_level, Entity e) - { - try { - return Integer.parseInt(admin_level) < Integer.parseInt(e.getTag(OSMTagKey.ADMIN_LEVEL)); - } catch (NumberFormatException ex) { - return false; - } - } - - private boolean hasGreaterCityAdminLevel(int admin_level, Boundary b) - { - try { - return admin_level < Integer.parseInt(b.getAdminLevel()); - } catch (NumberFormatException ex) { - return false; - } - } - - private Boundary extractBoundary(Entity e, OsmDbAccessorContext ctx) - throws SQLException { + private Boundary extractBoundary(Entity e, OsmDbAccessorContext ctx) throws SQLException { if (isBoundary(e)) { Boundary boundary = null; if (e instanceof Relation) { Relation i = (Relation) e; ctx.loadEntityData(i, true); boundary = new Boundary(); - boundary.setAdminLevel(e.getTag(OSMTagKey.ADMIN_LEVEL)); if (i.getTag(OSMTagKey.NAME) != null) { boundary.setName(i.getTag(OSMTagKey.NAME)); } @@ -375,15 +332,11 @@ public class IndexAddressCreator extends AbstractIndexPartCreator{ } boundary.getOuterWays().add((Way) es); } - } else if (isBoundary(es)) { - //add any sub boundaries... - boundary.addSubBoundary(extractBoundary(es, ctx)); } } } else if (e instanceof Way) { if (!visitedBoundaryWays.contains(e.getId())) { boundary = new Boundary(); - boundary.setAdminLevel(e.getTag(OSMTagKey.ADMIN_LEVEL)); if (e.getTag(OSMTagKey.NAME) != null) { boundary.setName(e.getTag(OSMTagKey.NAME)); } @@ -460,7 +413,7 @@ public class IndexAddressCreator extends AbstractIndexPartCreator{ } } - Long streetId = getStreetInCity(c, name, location, (a6.getId() << 2) | 2); + Set streetId = registerStreetInCity(name, null, location, (a6.getId() << 2) | 2, Collections.singletonList(c)); if (streetId == null) { return; } @@ -590,78 +543,68 @@ public class IndexAddressCreator extends AbstractIndexPartCreator{ return newName.trim(); } - public Long getStreetInCity(City city, String name, LatLon location, long initId) throws SQLException { - if (name == null || city == null) { - return null; + public Set getStreetInCity(Set isInNames, String name, String nameEn, LatLon location, long initId) throws SQLException { + if (name == null || location == null) { + return Collections.emptySet(); + } - Long foundId = null; - name = normalizeStreetName(name); - String cityPart = findCityPart(city,location); - foundId = streetDAO.findStreet(name,city,cityPart); - - if (foundId == null) { - insertStreetData(addressStreetStat, initId, name, Junidecode.unidecode(name), - location.getLatitude(), location.getLongitude(), city.getId(), cityPart); - streetDAO.insertStreet(addressStreetStat, name, city, cityPart, initId); - foundId = initId; - } - return foundId; - } - - private String findCityPart(City city, LatLon location) { - final Boundary cityBoundary = cityBoundaries.get(city); - int greatestBoudnaryLevel = getParsedCityAdminLevel(); - Boundary greatestBoundary = cityBoundary; - if (cityBoundary != null) { - for (Boundary subB : allSubBoundaries(cityBoundary)) { - if (subB.containsPoint(location) && hasGreaterCityAdminLevel(greatestBoudnaryLevel, subB)) { - greatestBoudnaryLevel = Integer.parseInt(subB.getAdminLevel()); - greatestBoundary = subB; - } - } - } - return greatestBoundary != cityBoundary ? findNearestCityOrSuburb(greatestBoundary, location) : city.getName(); - } - - - private int getParsedCityAdminLevel() { - try { - return Integer.parseInt(cityAdminLevel); - } catch (NumberFormatException ex) { - return IndexCreator.DEFAULT_CITY_ADMIN_LEVEL; - } - } - - private String findNearestCityOrSuburb(Boundary greatestBoundary, - LatLon location) { - String result = greatestBoundary.getName(); + List result = new ArrayList(); 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; - } + Boundary boundary = cityBoundaries.get(c); + if (isInNames.contains(c.getName()) || boundary.containsPoint(location)) { + result.add(c); } } - return result; + if (result.isEmpty()) { + City city = getClosestCity(location, isInNames); + if (city != null) { + result.add(city); + } + } + + return registerStreetInCity(name, nameEn, location, initId, result); } - //TODO this is done on each city always, maybe we can just compute it once... - private Collection allSubBoundaries(Boundary cityBoundary) { - List result = new ArrayList(); - for (Boundary subB : cityBoundary.getSubboundaries()) { - result.add(subB); - result.addAll(allSubBoundaries(subB)); + + private Set registerStreetInCity(String name, String nameEn, LatLon location, long initId, + List result) throws SQLException { + if (result.isEmpty()) { + return null; } - return result; + if (Algoritms.isEmpty(nameEn)) { + nameEn = Junidecode.unidecode(name); + } + + Set values = new TreeSet(); + for (City city : result) { + String cityPart = city.getName(); + 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(); + } + } + } + Long foundId = streetDAO.findStreet(name, result.get(0), cityPart); + if (foundId == null) { + insertStreetData(addressStreetStat, initId, name, nameEn, location.getLatitude(), location.getLongitude(), city.getId(), + cityPart); + streetDAO.insertStreet(addressStreetStat, name, city, cityPart, initId); + foundId = initId; + } + values.add(foundId); + } + return values; } + + public City getClosestCity(LatLon point, Set isInNames) { if (point == null) { return null; @@ -744,9 +687,8 @@ public class IndexAddressCreator extends AbstractIndexPartCreator{ if (!exist) { ctx.loadEntityData(e, false); LatLon l = e.getLatLon(); - City city = getClosestCity(l, getIsINames(e)); - Long idStreet = getStreetInCity(city, e.getTag(OSMTagKey.ADDR_STREET), l, (e.getId() << 2)); - if (idStreet != null) { + Set idStreet = getStreetInCity(getIsINames(e), e.getTag(OSMTagKey.ADDR_STREET), null, l, (e.getId() << 2)); + if (!idStreet.isEmpty()) { Building building = new Building(e); building.setName(e.getTag(OSMTagKey.ADDR_HOUSE_NUMBER)); streetDAO.writeBuilding(idStreet, building); @@ -767,9 +709,8 @@ public class IndexAddressCreator extends AbstractIndexPartCreator{ if (!exist) { ctx.loadEntityData(e, false); LatLon l = e.getLatLon(); - City city = getClosestCity(l, getIsINames(e)); - Long idStreet = getStreetInCity(city, e.getTag(OSMTagKey.NAME), l, (e.getId() << 2) | 1); - if (idStreet != null && saveAddressWays) { + Set idStreet = getStreetInCity(getIsINames(e), e.getTag(OSMTagKey.NAME), e.getTag(OSMTagKey.NAME_EN), l, (e.getId() << 2) | 1); + if (!idStreet.isEmpty() && saveAddressWays) { streetDAO.writeStreetWayNodes(idStreet, (Way) e); } }