/*
* Copyright (C) 2012.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 or
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
package uk.me.parabola.mkgmap.reader.osm.boundary;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Pattern;
import uk.me.parabola.log.Logger;
import uk.me.parabola.mkgmap.build.Locator;
import uk.me.parabola.mkgmap.build.LocatorUtil;
import uk.me.parabola.mkgmap.reader.osm.Tags;
import uk.me.parabola.mkgmap.reader.osm.boundary.Boundary;
import uk.me.parabola.util.EnhancedProperties;
/**
* Allows to extract boundary tags into BoundaryLocationInfo.
* Uses a locator if possible, else defaults.
* The locator is only needed when used with the LocationHook, utilities like the
* BoundaryPreparer will work without it.
* @author GerdP
*
*/
public class BoundaryLocationPreparer
{
private static final Logger log =
Logger.
getLogger(BoundaryLocationPreparer.
class);
private Locator locator
;
private static final Pattern COMMA_OR_SEMICOLON_PATTERN =
Pattern.
compile("[,;]+");
// tag keys for name resolution
private final List<String> nameList
;
/**
* Create a preparer.
* @param props The program properties or null.
*/
public BoundaryLocationPreparer
(EnhancedProperties props
) {
if (props ==
null){
this.
locator =
null;
this.
nameList =
new ArrayList<String>();
for (String name: BoundaryLocationPreparer.
LEVEL2_NAMES){
this.
nameList.
add(name
);
}
}
else{
this.
locator =
new Locator(props
);
this.
nameList = LocatorUtil.
getNameTags(props
);
}
}
/**
* Extract location relevant information from tags
* @param tags the Tags of a boundary
* @return a new BoundaryLocationInfo instance
*/
public BoundaryLocationInfo parseTags
(Tags tags
){
String zip = getZip
(tags
);
int admLevel = getAdminLevel
(tags
);
boolean isISO =
false;
String name = getName
(tags
);
if (locator
!=
null){
if (admLevel ==
2) {
String isoCode = locator.
addCountry(tags
);
if (isoCode
!=
null) {
isISO =
true;
name = isoCode
;
} else {
log.
warn("Country name",name,
"not in locator config. Country may not be assigned correctly.");
}
log.
debug("Coded:",name
);
}
}
return new BoundaryLocationInfo
(admLevel, name, zip, isISO
);
}
/**
* Extract and prepare tag infos from BoundaryList
* @param boundaries list of boundaries
* @return A Map that maps boundary Ids to the location relevant tags
*/
public HashMap<String, BoundaryLocationInfo
> getPreparedLocationInfo
(
List<Boundary
> boundaries
) {
HashMap<String, BoundaryLocationInfo
> preparedLocationInfo =
new HashMap<String, BoundaryLocationInfo
> ();
for (Boundary b :boundaries
){
preparedLocationInfo.
put(b.
getId(), parseTags
(b.
getTags()));
}
return preparedLocationInfo
;
}
/**
* These tags are used to retrieve the name of admin_level=2 boundaries. They need to
* be handled special because their name is changed to the 3 letter ISO code using
* the Locator class and the LocatorConfig.xml file.
*/
private static final String[] LEVEL2_NAMES =
new String[]{"name",
"name:en",
"int_name"};
/**
* Try to extract the name of the boundary.
* @param tags the boundary tags
* @return a name or null if no usable name tag was found
*/
private String getName
(Tags tags
) {
if ("2".
equals(tags.
get("admin_level"))) {
for (String enNameTag : LEVEL2_NAMES
)
{
String nameTagValue = tags.
get(enNameTag
);
if (nameTagValue ==
null) {
continue;
}
String[] nameParts = COMMA_OR_SEMICOLON_PATTERN.
split(nameTagValue
);
if (nameParts.
length ==
0) {
continue;
}
return nameParts
[0].
trim().
intern();
}
}
for (String nameTag : nameList
) {
String nameTagValue = tags.
get(nameTag
);
if (nameTagValue ==
null) {
continue;
}
String[] nameParts = COMMA_OR_SEMICOLON_PATTERN.
split(nameTagValue
);
if (nameParts.
length ==
0) {
continue;
}
return nameParts
[0].
trim().
intern();
}
return null;
}
/**
* Try to extract a zip code from the the tags of a boundary.
* @param tags the boundary tags
* @return null if no zip code was found, else a String that should be a zip code.
*/
private String getZip
(Tags tags
) {
String zip = tags.
get("postal_code");
if (zip ==
null) {
if ("postal_code".
equals(tags.
get("boundary"))){
String name = tags.
get("name");
if (name ==
null) {
name = getName
(tags
);
}
if (name
!=
null) {
String[] nameParts = name.
split(Pattern.
quote(" "));
if (nameParts.
length > 0) {
zip = nameParts
[0].
trim();
}
}
}
}
return zip
;
}
public static final int UNSET_ADMIN_LEVEL =
100; // must be higher than real levels
/**
* translate the admin_level tag to an integer.
* @param tags the boundary tags
* @return the admin_level value. The value is UNSET_ADMIN_LEVEL if
* the conversion failed.
*/
private int getAdminLevel
(Tags tags
) {
String level = tags.
get("admin_level");
if (level ==
null) {
return UNSET_ADMIN_LEVEL
;
}
try {
Integer res =
Integer.
valueOf(level
);
if (res
< 2 || res
> 11)
return UNSET_ADMIN_LEVEL
;
else
return res
;
} catch (NumberFormatException nfe
) {
return UNSET_ADMIN_LEVEL
;
}
}
}