Subversion Repositories splitter

Rev

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

/*
 * Copyright (C) 2014, Gerd Petermann
 *
 * 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.splitter.solver;

import java.awt.Rectangle;
import java.util.BitSet;

import uk.me.parabola.splitter.Area;
import uk.me.parabola.splitter.Utils;

/**
 * Contains info that is needed by the {@link Tile} class. For a given
 * DensityMap we calculate some extra info to allow faster access to row sums
 * and column sums.
 *
 * @author GerdP
 *
 */

public class EnhancedDensityMap {
        private final DensityMap densityMap;
        private int[][] xyMap;
        private int[][] yxMap;
        private BitSet xyOutsidePolygon = new BitSet();
        private double[] aspectRatioFactor;
        private int minAspectRatioFactorPos;
        private int maxNodesInDensityMapGridElement = Integer.MIN_VALUE;
        private int maxNodesInDensityMapGridElementInPoly = Integer.MIN_VALUE;
        private java.awt.geom.Area polygonArea;

        public EnhancedDensityMap(DensityMap densities, java.awt.geom.Area polygonArea) {
                this.densityMap = densities;
                this.polygonArea = polygonArea;
                prepare();
        }

        /**
         * If a polygon is given, filter the density data Compute once complex
         * trigonometric results for needed for proper aspect ratio calculations.
         *
         */

        private void prepare() {
                // performance: calculate only once the needed complex math results
                aspectRatioFactor = new double[densityMap.getHeight() + 1];
                int minLat = densityMap.getBounds().getMinLat();
                int maxLat = densityMap.getBounds().getMaxLat();
                int lat = 0;
                double maxAspectRatioFactor = Double.MIN_VALUE;
                int minPos = Integer.MAX_VALUE;
                for (int i = 0; i < aspectRatioFactor.length; i++) {
                        lat = minLat + i * (1 << densityMap.getShift());
                        assert lat <= maxLat;
                        aspectRatioFactor[i] = Math.cos(Math.toRadians(Utils.toDegrees(lat)));
                        if (maxAspectRatioFactor < aspectRatioFactor[i]) {
                                maxAspectRatioFactor = aspectRatioFactor[i];
                                minPos = i;
                        }
                }
                minAspectRatioFactorPos = minPos;
                assert lat == maxLat;

                // filter the density map and populate xyMap
                int width = densityMap.getWidth();
                int height = densityMap.getHeight();
                xyMap = new int[width][];
                int shift = densityMap.getShift();
                for (int x = 0; x < width; x++) {
                        int polyXPos = densityMap.getBounds().getMinLong() + (x << shift);
                        int[] xCol = null;
                        xCol = new int[height];
                        boolean colNeeded = false;
                        for (int y = 0; y < height; y++) {
                                int count = densityMap.getNodeCount(x, y);
                                if (polygonArea != null) {
                                        int polyYPos = densityMap.getBounds().getMinLat() + (y << shift);
                                        if (polygonArea.intersects(polyXPos, polyYPos, 1 << shift, 1 << shift)) {
                                                maxNodesInDensityMapGridElementInPoly = Math.max(count, maxNodesInDensityMapGridElementInPoly);
                                        } else {
                                                xyOutsidePolygon.set(x * height + y);
                                        }
                                }
                                if (count > 0) {
                                        maxNodesInDensityMapGridElement = Math.max(count, maxNodesInDensityMapGridElement);
                                        xCol[y] = count;
                                        colNeeded = true;
                                }
                        }
                        if (colNeeded)
                                xyMap[x] = xCol;
                }
               
                // create and populate yxMap, this helps to speed up row access
                yxMap = new int[height][];
                for (int y = 0; y < height; y++) {
                        boolean rowNeeded = false;
                        int[] yRow = new int[width];
                        for (int x = 0; x < width; x++) {
                                int count = 0;
                                if (xyMap[x] != null)
                                        count = xyMap[x][y];
                                if (count > 0) {
                                        rowNeeded = true;
                                        yRow[x] = count;
                                }
                        }
                        if (rowNeeded)
                                yxMap[y] = yRow;
                }
        }

        public boolean isGridElemInPolygon(int x, int y) {
                if (polygonArea == null || xyOutsidePolygon.isEmpty())
                        return true;
                return !xyOutsidePolygon.get(x * densityMap.getHeight() + y);
        }

        // calculate aspect ratio of a tile which is a view on the densityMap
        public double getAspectRatio(Rectangle r) {
                double ratio;
                double maxWidth;
                if (r.y < minAspectRatioFactorPos && r.y + r.height > minAspectRatioFactorPos) {
                        maxWidth = r.width; // tile crosses equator
                } else {
                        double width1 = r.width * aspectRatioFactor[r.y];
                        double width2 = r.width * aspectRatioFactor[r.y + r.height];
                        maxWidth = Math.max(width1, width2);
                }
                ratio = maxWidth / r.height;
                return ratio;
        }

        public Area getBounds() {
                return densityMap.getBounds();
        }

        public DensityMap getDensityMap() {
                return densityMap;
        }

        public long getNodeCount() {
                return densityMap.getNodeCount();
        }

        public int[] getMapRow(int mapRow) {
                return yxMap[mapRow];
        }

        public int[] getMapCol(int mapCol) {
                return xyMap[mapCol];
        }

        public double[] getAspectRatioFactor() {
                return aspectRatioFactor;
        }

        public int getMinAspectRatioFactorPos() {
                return minAspectRatioFactorPos;
        }

        public int getMaxNodesInDensityMapGridElement() {
                return maxNodesInDensityMapGridElement;
        }

        public int getMaxNodesInDensityMapGridElementInPoly() {
                return maxNodesInDensityMapGridElementInPoly;
        }

        public java.awt.geom.Area getPolygonArea() {
                return polygonArea;
        }

        public boolean allInsidePolygon() {
                return polygonArea == null || xyOutsidePolygon.isEmpty();
        }
       
}