Subversion Repositories mkgmap

Rev

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

/*
 * Copyright (C) 2006, 2011.
 *
 * 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.awt.geom.Area;
import java.awt.geom.Path2D;
import java.io.File;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Pattern;

import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.mkgmap.reader.osm.Tags;
import uk.me.parabola.mkgmap.reader.osm.Way;
import uk.me.parabola.util.GpxCreator;
import uk.me.parabola.util.Java2DConverter;

/**
 * Compare two boundary files or two directories with boundary files.
 * Write differences as gpx files.
 *  
 * @author WanMil (initial version), rewritten for BoundaryQuadTree by GerdP
 *
 */

public class BoundaryDiff {
        private final String inputName1;
        private final String inputName2;

        public BoundaryDiff(String boundaryDirName1, String boundaryDirName2) {
                this.inputName1 = boundaryDirName1;
                this.inputName2 = boundaryDirName2;
        }

        /**
         * Return list of file names
         * @param dirName a directory or a zip file containing *.bnd files,
         * or a single *.bnd file
         * @return
         */

        private static List<String> getBoundsFiles(String dirName) {
                File dir = new File(dirName);
                System.out.println(dirName);
                if (dir.isFile() && dir.getName().endsWith(".bnd")) {
                        List<String> boundaryFiles = new ArrayList<>();
                        boundaryFiles.add(dir.getName());
                        return boundaryFiles;
                }
                return BoundaryUtil.getBoundaryDirContent(dirName);
        }

        /**
         * Compare all files in one list with the files in another list.
         * Optionally restrict the comparison to boundaries with the
         * given tag/value combination.
         * @param tag should be admin_level
         * @param value any value appropriate for the tag
         */

        public void compare(String tag, String value) {
                List<String> b1 = getBoundsFiles(inputName1);
                List<String> b2 = getBoundsFiles(inputName2);

                if (b1.size() == 0 && b2.size() == 0)
                        return;
               
                Collections.sort(b1);
                Collections.sort(b2);

                Queue<String> bounds1 = new LinkedList<>(b1);
                Queue<String> bounds2 = new LinkedList<>(b2);
                b1 = null;
                b2 = null;

                Area only1 = new Area();
                Area only2 = new Area();
               
                int bAll = bounds1.size() + bounds2.size();
                long tProgress = System.currentTimeMillis();

                while (bounds1.isEmpty() == false || bounds2.isEmpty() == false) {
                        String f1 = bounds1.peek();
                        String f2 = bounds2.peek();

                        if (f1 == null) {
                                only2.add(loadArea(inputName2, f2, tag, value));
                                bounds2.poll();
                        } else if (f2 == null) {
                                only1.add(loadArea(inputName1, f1, tag, value));
                                bounds1.poll();
                        } else {
                                int cmp = f1.compareTo(f2);
                                if (cmp == 0) {
                                        Area a1 = loadArea(inputName1, f1, tag, value);
                                        Area a2 = loadArea(inputName2, f2, tag, value);
                                        if (a1.isEmpty() == false|| a2.isEmpty() == false){
                                                Area o1 = new Area(a1);
                                                o1.subtract(a2);
                                                if (o1.isEmpty() == false)
                                                        only1.add(o1);
                                                Area o2 = new Area(a2);
                                                o2.subtract(a1);
                                                if (o2.isEmpty() == false)
                                                        only2.add(o2);
                                        }
                                        bounds1.poll();
                                        bounds2.poll();
                                } else if (cmp < 0) {
                                        only1.add(loadArea(inputName1, f1, tag, value));
                                        bounds1.poll();
                                } else {
                                        only2.add(loadArea(inputName2, f2, tag, value));
                                        bounds2.poll();
                                }
                        }
                       
                        long tNow = System.currentTimeMillis();
                        if (tNow - tProgress >= 10*1000L) {
                                int bNow = bounds1.size()+ bounds2.size();
                                System.out.println(tag+"="+value+": "+(bAll-bNow)+"/"+bAll+" files - "+(bAll-bNow)*100/bAll+"%");
                                tProgress = tNow;
                        }
                }

                saveArea(only1, "removed", tag, value);
                saveArea(only2, "new", tag, value);

        }

        /**
         * Calculate the area that is covered by a given tag /value pair, e.g. admin_level=2
         * @param dirName the name of a directory or *.zip file containing *.bnd files, or a single *.bnd file
         * @param fileName the name of the *.bnd file that should be read
         * @param tag the tag key
         * @param value the tag value
         * @return a new Area (which might be empty)
         */

        private static Area loadArea(String dirName, String fileName, String tag, String value) {
                String dir = dirName;
                String bndFileName = fileName;
                if (dir.endsWith(".bnd")){
                        File f = new File(dir);
                        if (f.isFile()){
                                dir = f.getParent();
                                bndFileName = f.getName();
                        }
                        if (dir == null)
                                dir = "."; // the local directory
                }
                BoundaryQuadTree bqt = BoundaryUtil.loadQuadTree(dir, bndFileName);
                if (tag.equals("admin_level"))
                        return (bqt.getCoveredArea(Integer.valueOf(value)));
                Map<String, Tags> bTags = bqt.getTagsMap();
                Map<String, List<Area>> areas = bqt.getAreas();
                Path2D.Double path = new Path2D.Double();
                for (Entry<String, Tags> entry: bTags.entrySet()){
                        if (value.equals(entry.getValue().get(tag))){
                                List<Area> aList = areas.get(entry.getKey());
                                for (Area area : aList){
                                        path.append(area, false);
                                }
                        }
                }
                Area a = new Area(path);
                return a;
        }

        /**
         * Create gpx file(s) for a single area.
         * @param a the Area
         * @param subDirName target sub-directory
         * @param tagKey used to build the gpx file name
         * @param tagValue used to build the gpx file name
         */

        private static void saveArea(Area a, String subDirName, String tagKey, String tagValue) {

                String gpxBasename = "gpx/diff/" + subDirName + "/"
                                + tagKey + "=" + tagValue + "/";

                List<List<Coord>> singlePolys = Java2DConverter.areaToShapes(a);
                Collections.reverse(singlePolys);

                int i = 0;
                for (List<Coord> polyPart : singlePolys) {
                        String attr = Way.clockwise(polyPart) ? "o" : "i";
                        GpxCreator.createGpx(gpxBasename + i + "_" + attr, polyPart);
                        i++;
                }
        }

        /**
         * print usage info
         */

        private static void printUsage(){
                System.err.println("Usage:");
                System.err
                                .println("java -cp mkgmap.jar uk.me.parabola.mkgmap.reader.osm.boundary.BoundaryDiff <boundsdir1> <boundsdir2> [<tag=value> [<tag=value>]]");
                System.err.println(" <boundsdir1> ");
                System.err
                                .println(" <boundsdir2>: defines two directories or zip files containing boundsfiles to be compared ");
                System.err
                .println(" <tag=value>: defines a tag/value combination for which the diff is created");
                System.err
                .println(" sample:");
                System.err
                .println(" java -cp mkgmap.jar uk.me.parabola.mkgmap.reader.osm.boundary.BoundaryDiff world_20120113.zip bounds admin_level=2");

                System.exit(-1);
        }

        public static void main(final String[] args) {
                if (args.length < 2)
                        printUsage();
                File f1 = new File(args[0]);
                if (f1.exists() == false){
                        System.err.println(args[0] + " does not exist");
                        printUsage();
                }
                File f2 = new File(args[1]);
                if (f2.exists() == false){
                        System.err.println(args[1] + " does not exist");
                        printUsage();
                }

                List<Entry<String,String>> tags = new ArrayList<>();
               
                if (args.length > 2) {
                        for (int i = 2; i < args.length; i++) {
                                final String[] parts = args[i].split(Pattern.quote("="));
                                tags.add(new AbstractMap.SimpleImmutableEntry<>(
                                                parts[0], parts[1]));
                        }
                } else {
                        for (int adminlevel = 2; adminlevel <= 11; adminlevel++) {
                        tags.add(new AbstractMap.SimpleImmutableEntry<>(
                                        "admin_level", String.valueOf(adminlevel)));
                        }
                }
               
                       
                int processors = Runtime.getRuntime().availableProcessors();
                ExecutorService excSvc = Executors.newFixedThreadPool(processors);
                ExecutorCompletionService<String> executor = new ExecutorCompletionService<>(excSvc);

               
                for (final Entry<String, String> tag : tags) {
                        executor.submit(new Runnable() {
                                public void run() {
                                        BoundaryDiff bd = new BoundaryDiff(args[0],args[1]);
                                        bd.compare(tag.getKey(), tag.getValue());
                                }
                        }, tag.getKey() + "=" + tag.getValue());
                }
               
                for (int i = 0; i < tags.size(); i++) {
                        try {
                                String tag = executor.take().get();
                                System.out.println(tag + " finished.");
                        } catch (InterruptedException exp) {
                                exp.printStackTrace();
                        } catch (ExecutionException exp) {
                                exp.printStackTrace();
                        }
                }

                excSvc.shutdown();
        }

}