Subversion Repositories splitter

Rev

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

/*
 * Copyright (c) 2009, Chris Miller
 *
 * 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;

/**
 * Utility methods for rounding numbers and areas
 *
 * @author Chris Miller
 */

public class RoundingUtils {
        /**
         * Rounds an integer down to the nearest multiple of {@code 2^shift}.
         * Works with both positive and negative integers.
         * @param val the integer to round down.
         * @param shift the power of two to round down to.
         * @return the rounded integer.
         */

        public static int roundDown(int val, int shift) {
                return val >>> shift << shift;
        }

        /**
         * Rounds an integer up to the nearest multiple of {@code 2^shift}.
         * Works with both positive and negative integers.
         * @param val the integer to round up.
         * @param shift the power of two to round up to.
         * @return the rounded integer.
         */

        public static int roundUp(int val, int shift) {
                return (val + (1 << shift) - 1) >>> shift << shift;
        }

        /**
         * Rounds an integer up or down to the nearest multiple of {@code 2^shift}.
         * Works with both positive and negative integers.
         * @param val the integer to round.
         * @param shift the power of two to round to.
         * @return the rounded integer.
         */

        public static int round(int val, int shift) {
                return (val + (1 << (shift - 1))) >>> shift << shift;
        }

        /**
         * Rounds an area's borders to suit the supplied resolution. This
         * means edges are aligned at 2 ^ (24 - resolution) boundaries
         *
         * @param b the area to round
         * @param resolution the map resolution to align the borders at
         * @return the rounded area
         */

        public static Area round(Area b, int resolution) {
                int shift = 24 - resolution;
                int alignment = 1 << shift;

                // Avoid pathological behaviour near the poles by discarding anything
                // greater than +/-85 degrees latitude.
                int minLat = Math.max(b.getMinLat(), Utils.toMapUnit(-85.0d));
                int maxLat = Math.min(b.getMaxLat(), Utils.toMapUnit(85.0d));

                int roundedMinLat = roundDown(minLat, shift);
                int roundedMaxLat = roundUp(maxLat, shift);
                assert roundedMinLat % alignment == 0 : "The area's min latitude is not aligned to a multiple of " + alignment;
                assert roundedMaxLat % alignment == 0 : "The area's max latitude is not aligned to a multiple of " + alignment;

                int roundedMinLon = roundDown(b.getMinLong(), shift);
                int roundedMaxLon = roundUp(b.getMaxLong(), shift);
                // don't produce illegal values
                if (roundedMinLon < -0x800000)
                        roundedMinLon = -0x800000;
                if (roundedMaxLon > 0x800000)
                        roundedMaxLon = 0x800000;
                assert roundedMinLon % alignment == 0 : "The area's min longitude is not aligned to a multiple of " + alignment;
                assert roundedMaxLon % alignment == 0 : "The area's max longitude is not aligned to a multiple of " + alignment;

                return new Area(roundedMinLat, roundedMinLon, roundedMaxLat, roundedMaxLon);
        }
}