Subversion Repositories mkgmap

Rev

Rev 2380 | View as "text/plain" | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * 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;
                }
        }
}