package me.osm.gazetter.addresses.impl;
import gnu.trove.set.TLongSet;
import gnu.trove.set.hash.TLongHashSet;
import java.util.List;
import java.util.Map;
import me.osm.gazetter.addresses.AddrLevelsComparator;
import me.osm.gazetter.addresses.AddressesLevelsMatcher;
import me.osm.gazetter.addresses.AddressesUtils;
import me.osm.gazetter.addresses.NamesMatcher;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Default implementation for
* {@link AddressesLevelsMatcher}
* */
public class AddressesLevelsMatcherImpl implements AddressesLevelsMatcher {
private static final Logger log = LoggerFactory.getLogger(AddressesLevelsMatcherImpl.class);
/**
* Create matcher
*
* @param lelvelsComparator
* @param namesMatcher
* @param placeBoundaries
* */
public AddressesLevelsMatcherImpl(AddrLevelsComparator lelvelsComparator,
NamesMatcher namesMatcher, List<String> placeBoundaries) {
this.lelvelsComparator = lelvelsComparator;
this.namesMatcher = namesMatcher;
FORSE_ADDR_CITY_MATCH = false;
this.placeBoundaries = placeBoundaries;
}
protected AddrLevelsComparator lelvelsComparator;
protected NamesMatcher namesMatcher;
protected boolean FORSE_ADDR_CITY_MATCH;
protected List<String> placeBoundaries;
@Override
public JSONObject hnAsJSON(JSONObject addrPoint, JSONObject addrRow) {
JSONObject hnAddrPart = new JSONObject();
String hn = addrRow.optString("addr:housenumber");
hnAddrPart.put(ADDR_NAME, hn);
hnAddrPart.put(ADDR_LVL, "hn");
hnAddrPart.put(ADDR_LVL_SIZE, lelvelsComparator.getLVLSize("hn"));
hnAddrPart.put("lnk", addrPoint.optString("id"));
JSONObject names = new JSONObject();
hnAddrPart.put(ADDR_NAMES, names);
if(addrRow.has("addr:housename")) {
names.put("addr:housename", addrRow.getString("addr:housename"));
}
if(addrRow.has("addr:hn-orig")) {
names.put("addr:hn-orig", addrRow.getString("addr:hn-orig"));
}
return hnAddrPart;
}
@Override
public JSONObject streetAsJSON(JSONObject addrPoint, JSONObject addrRow,
JSONObject associatedStreet, List<JSONObject> nearbyStreets, int boundariesHash) {
if(associatedStreet != null) {
TLongSet waysSet = new TLongHashSet();
JSONArray jsonArray = associatedStreet.getJSONArray("associatedWays");
for(int i = 0; i < jsonArray.length(); i++) {
waysSet.add(jsonArray.getLong(i));
}
if(nearbyStreets != null)
{
for(JSONObject ls : nearbyStreets) {
long wayId = Long.parseLong(StringUtils.split(ls.getString("id"), '-')[2].substring(1));
if(waysSet.contains(wayId)) {
JSONObject streetAddrPart = new JSONObject();
Map<String, String> nameTags = AddressesUtils.filterNameTags(ls);
nameTags.putAll(AddressesUtils.filterNameTags(associatedStreet));
if(nameTags.get("name") != null) {
streetAddrPart.put(ADDR_NAME, nameTags.get("name"));
streetAddrPart.put(ADDR_LVL, "street");
streetAddrPart.put(ADDR_NAMES, new JSONObject(nameTags));
streetAddrPart.put("lnk", ls.optString("id"));
streetAddrPart.put(ADDR_LVL_SIZE, lelvelsComparator.getLVLSize("street"));
streetAddrPart.put("strtUID", getStreetUUID(ls, boundariesHash));
return streetAddrPart;
}
else {
log.warn("Can't find name for associated street.\nStreet:\n{}\nRelation\n{}",
ls.toString(), associatedStreet.toString());
}
}
}
}
}
if(!addrRow.has("addr:street")) {
return null;
}
JSONObject matchedStreet = null;
String street = addrRow.getString("addr:street");
if(nearbyStreets != null) {
for(JSONObject ls : nearbyStreets) {
if(namesMatcher.isStreetNameMatch(street, AddressesUtils.filterNameTags(ls))) {
matchedStreet = ls;
break;
}
}
}
JSONObject streetAddrPart = new JSONObject();
streetAddrPart.put(ADDR_NAME, street);
streetAddrPart.put(ADDR_LVL, "street");
streetAddrPart.put(ADDR_LVL_SIZE, lelvelsComparator.getLVLSize("street"));
if(matchedStreet != null) {
streetAddrPart.put(ADDR_NAMES, new JSONObject(AddressesUtils.filterNameTags(matchedStreet)));
streetAddrPart.put("lnk", matchedStreet.optString("id"));
streetAddrPart.put("strtUID", getStreetUUID(matchedStreet, boundariesHash));
}
return streetAddrPart;
}
private int getStreetUUID(JSONObject street, int boundariesHash) {
JSONArray streetBoundaries = street.optJSONArray("boundaries");
if(streetBoundaries == null || streetBoundaries.length() == 0) {
return 0;
}
for(int i = 0; i < streetBoundaries.length(); i++) {
int hash = streetBoundaries.getJSONObject(i).optInt("boundariesHash");
if(boundariesHash == hash) {
return hash;
}
}
return streetBoundaries.getJSONObject(0).optInt("boundariesHash");
}
@Override
public JSONObject quarterAsJSON(JSONObject addrPoint, JSONObject addrRow,
Map<String, JSONObject> level2Boundary, JSONObject nearestNeighbour) {
if(addrRow.has("addr:quarter")) {
JSONObject quarterJSON = new JSONObject();
String name = addrRow.getString("addr:quarter");
quarterJSON.put(ADDR_NAME, name);
quarterJSON.put(ADDR_LVL, "place:quarter");
quarterJSON.put(ADDR_LVL_SIZE, lelvelsComparator.getLVLSize("place:quarter"));
JSONObject obj = null;
if(obj == null) {
obj = level2Boundary.get("place:quarter");
}
if(obj == null || !namesMatcher.isPlaceNameMatch(name, AddressesUtils.filterNameTags(obj))) {
obj = level2Boundary.get("place:neighbour");
}
if(obj != null) {
quarterJSON.put("lnk", obj.getString("id"));
quarterJSON.put(ADDR_NAMES, AddressesUtils.filterNameTags(obj));
}
return quarterJSON;
}
return null;
}
private static class Cortage {
public String name;
public JSONObject obj;
}
@Override
public JSONObject cityAsJSON(JSONObject addrPoint, JSONObject addrRow,
Map<String, JSONObject> level2Boundary, JSONObject nearestPlace,
String nearestPlaceLvl) {
String tagCityName = null;
String name = null;
String lvl = null;
if(addrRow.has("addr:city")) {
tagCityName = addrRow.getString("addr:city");
name = tagCityName;
lvl = "place:city";
}
JSONObject obj = null;
//Search for boundary
for(String addrKey : placeBoundaries)
{
JSONObject hamlet = level2Boundary.get(addrKey);
Cortage cortage = checkPlace(tagCityName, hamlet);
if(cortage != null) {
obj = cortage.obj;
lvl = addrKey;
name = cortage.name;
break;
}
}
//Didn't found in boundaries - check nearest
//but only if we have addr:city tag
if(obj == null && tagCityName != null && nearestPlace != null) {
Map<String, String> nearestPlaceTags = AddressesUtils.filterNameTags(nearestPlace);
if(lelvelsComparator.supports(nearestPlaceLvl) &&
namesMatcher.isPlaceNameMatch(tagCityName, nearestPlaceTags)) {
obj = nearestPlace;
lvl = nearestPlaceLvl;
name = nearestPlaceTags.get("name");
if(name == null) {
name = tagCityName;
}
}
}
if(obj == null && tagCityName == null) {
return null;
}
//at least we have addr:city tag
JSONObject cityJSON = new JSONObject();
if(tagCityName != null) {
cityJSON.put(ADDR_NAME, tagCityName);
cityJSON.put(ADDR_LVL, "place:city");
cityJSON.put(ADDR_LVL_SIZE, lelvelsComparator.getLVLSize("place:city"));
}
//override min values by obj
if(obj != null) {
cityJSON.put(ADDR_NAME, name);
cityJSON.put(ADDR_LVL, lvl);
cityJSON.put("lnk", obj.getString("id"));
cityJSON.put(ADDR_NAMES, AddressesUtils.filterNameTags(obj));
cityJSON.put(ADDR_LVL_SIZE, lelvelsComparator.getLVLSize(lvl));
}
if(cityJSON.optString("name", null) == null) {
return null;
}
return cityJSON;
}
private Cortage checkPlace(String tagCityName, JSONObject place) {
if(place != null) {
Map<String, String> hamletNameTags = AddressesUtils.filterNameTags(place);
if(tagCityName != null && FORSE_ADDR_CITY_MATCH) {
if(namesMatcher.isPlaceNameMatch(tagCityName, hamletNameTags)) {
Cortage r = new Cortage();
r.obj = place;
r.name = hamletNameTags.get("name");
if(r.name == null) {
r.name = tagCityName;
}
return r;
}
// with FORSE_ADDR_CITY_MATCH we should keep looking
// until matches place will be founded
return null;
}
Cortage r = new Cortage();
r.obj = place;
r.name = hamletNameTags.get("name");
if(r.name == null) {
r.name = tagCityName;
}
return r;
}
return null;
}
@Override
public JSONObject postCodeAsJSON(JSONObject addrPoint, JSONObject addrRow) {
String name = addrRow.optString("addr:postcode");
if(name != null) {
JSONObject postCodeJSON = new JSONObject();
postCodeJSON.put(ADDR_NAME, name);
postCodeJSON.put(ADDR_LVL, "postcode");
postCodeJSON.put(ADDR_LVL_SIZE, lelvelsComparator.getLVLSize("postcode"));
return postCodeJSON;
}
return null;
}
}