/* * 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> (); if (boundaries != null){ 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 static 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; } } }