Subversion Repositories splitter

Rev

Rev 344 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * Copyright (c) 2011,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 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.splitter;

import it.unimi.dsi.fastutil.shorts.ShortArrayList;

import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
import java.util.HashSet;

/**
 * Maps a BitSet containing the used writers to a short value.  
 * An OSM element is written to one or more writers. Every used
 * combination of writers is translated to a short.
 * @author GerdP
 *
 */

public class WriterDictionaryShort{
        public final static int DICT_START = -1 * (Short.MIN_VALUE + 1);
        private OSMWriter[] writers;
        private final ArrayList<BitSet> sets;
        private final ArrayList<ShortArrayList> arrays;
        private final int numOfWriters;
        private final HashMap<BitSet, Short> index;
        private final HashSet<Short> simpleNeighbours = new HashSet<Short>();
       
        /**
         * Create a dictionary for a given array of writers
         * @param writers the array of writers
         */

        WriterDictionaryShort (OSMWriter [] writers){
                this.writers = writers;
                this.numOfWriters = writers.length;
                sets = new ArrayList<BitSet>();
                arrays = new ArrayList<ShortArrayList>();
                index = new HashMap<BitSet, Short>();
                init();
        }
       
        /**
         * initialize the dictionary with sets containing a single writer.
         */

        private void init(){
                ArrayList<Rectangle> rectangles = new ArrayList<Rectangle>(numOfWriters);
                ArrayList<BitSet> writerSets = new ArrayList<BitSet>(numOfWriters);
                for (int i=0; i < numOfWriters; i++){
                        BitSet b = new BitSet();
                        b.set(i);
                        translate(b);
                        rectangles.add(Utils.area2Rectangle(writers[i].getBounds(), 0));
                        writerSets.add(b);
                }
                findSimpleNeigbours(rectangles, writerSets);
                System.out.println("cached " + simpleNeighbours.size() + " combinations of areas that form rectangles.");
                return;
        }
       
        /**
         * Calculate the short value for a given BitSet. The BitSet must not
         * contain values higher than numOfWriters.
         * @param writerSet the BitSet
         * @return a short value that identifies this BitSet
         */

        public short translate(final BitSet writerSet){
                Short combiIndex = index.get(writerSet);
                if (combiIndex == null){
                        BitSet bnew = new BitSet();

                        bnew.or(writerSet);
                        ShortArrayList a = new ShortArrayList();
                        for (int i = writerSet.nextSetBit(0); i >= 0; i = writerSet.nextSetBit(i + 1)) {
                                a.add((short) i);
                        }
                        combiIndex = (short) (sets.size() - DICT_START);
                        if (combiIndex == Short.MAX_VALUE){
                                throw new SplitFailedException("writerDictionary is full. Decrease --max-areas value");
                        }
                        sets.add(bnew);
                        arrays.add(a);
                        index.put(bnew, combiIndex);
                }
                return combiIndex;
        }

        /**
         * find those areas that build rectangles when they are
         * added together. A way or relation that lies exactly within
         * such a combination cannot cross other areas.
         */

        private void findSimpleNeigbours(ArrayList<Rectangle> rectangles, ArrayList<BitSet> writerSets){
                ArrayList<Rectangle> newRectangles = new ArrayList<Rectangle>();
                ArrayList<BitSet> newWriterSets = new ArrayList<BitSet>();
               
                for (int i = 0; i < rectangles.size(); i++){
                        Rectangle r1 =  rectangles.get(i);
                        for (int j = i+1; j < rectangles.size(); j++){
                                Rectangle r2 =  rectangles.get(j);
                                boolean isSimple = false;
                                if (r1.y == r2.y && r1.height == r2.height
                                                && (r1.x == r2.getMaxX() || r2.x == r1.getMaxX()))
                                        isSimple = true;
                                else if (r1.x == r2.x && r1.width == r2.width
                                                && (r1.y == r2.getMaxY() || r2.y == r1.getMaxY()))
                                        isSimple = true;
                                if (isSimple){
                                        BitSet simpleNeighbour = new BitSet();
                                        simpleNeighbour.or(writerSets.get(i));
                                        simpleNeighbour.or(writerSets.get(j));
                                        if (simpleNeighbour.cardinality() <= 10){
                                                short idx = translate(simpleNeighbour);
                                                if (simpleNeighbours.contains(idx) == false){
                                                        simpleNeighbours.add(idx);
                                                        //System.out.println("simple neighbor: " + getMapIds(simpleNeighbour));
                                                        Rectangle pair = new Rectangle(r1);
                                                        pair.add(r2);
                                                        newRectangles.add(pair);
                                                        newWriterSets.add(simpleNeighbour);
                                                }
                                        }
                                }
                        }
                }
                if (newRectangles.isEmpty() == false){
                        rectangles.addAll(newRectangles);
                        writerSets.addAll(newWriterSets);
                        newRectangles = null;
                        newWriterSets = null;
                        if (simpleNeighbours.size() < 1000)
                                findSimpleNeigbours(rectangles,writerSets);
                }
        }
        /**
         * Return the BitSet that is related to the short value.
         * The caller must make sure that the short is valid.
         * @param idx a short value that was returned by the translate()
         * method.  
         * @return the BitSet
         */

        public BitSet getBitSet (final short idx){
                return sets.get(idx + DICT_START);
        }
       
        /**
         * Return a list containing the writer ids for the given
         * short value.  
         * @param idx a short value that was returned by the translate()
         * @return a list containing the writer ids
         */

        public ShortArrayList getList (final short idx){
                return arrays.get(DICT_START + idx);
        }
       
        /**
         * return the number of sets in this dictionary
         * @return the number of sets in this dictionary
         */

        public int size(){
                return sets.size();
        }

        public int getNumOfWriters(){
                return numOfWriters;
        }

        public OSMWriter[] getWriters(){
                return writers;
        }
       
        public boolean mayCross(short writerIdx){
                if (writerIdx + DICT_START < numOfWriters)
                        return false;
                if (simpleNeighbours.contains(writerIdx))
                        return false;
                return true;
        }
       
        public String getMapIds(BitSet writerSet){
                StringBuilder sb = new StringBuilder("{");
                for (int k = 0;k<numOfWriters;k++){
                        if (writerSet.get(k)) {
                                sb.append(writers[k].getMapId());
                                sb.append(", ");
                        }
                }
                return sb.substring(0, sb.length()-2) + "}";
        }

        /**
         * return the id of a single writer or
         * @param writerIdx
         * @return
         */

        public boolean isSingleWriterIdx(short writerIdx) {
                return (writerIdx + DICT_START < numOfWriters);
        }
}