Package org.opentripplanner.common.geometry

Source Code of org.opentripplanner.common.geometry.CompactLineString

/* This program is free software: you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.

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.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>. */

package org.opentripplanner.common.geometry;

import java.io.Serializable;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;

/**
* Compact line string. To optimize storage, we use the following tricks:
* <ul>
* <li>Store only intermediate points (end-points are given by the external context, ie from/to
* vertices)</li>
* <li>For straight-line geometries (sometimes around half of the street geometries), re-use the
* same static object (since there is nothing to store)</li>
* <li>Store intermediate point in fixed floating points with fixed precision, using delta coding
* from the previous point, and variable length coding (most of the delta coordinates will thus fits
* in 1 or 2 bytes).</li>
* </ul>
*
* This trick alone saves around 20% of memory compared to the bulky JTS LineString, which is split
* on many objects (Coordinates, cached Envelope, Geometry itself). Performance hit should be low as
* we do not need the geometry during a path search.
*
* @author laurent
*/
public final class CompactLineString implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * Multiplier for fixed-float representation. For lat/lon CRS, 1e6 leads to a precision of 0.11
     * meter at a minimum (at the equator).
     */
    private static final double FIXED_FLOAT_MULT = 1.0e6;

    /**
     * Constant to check that line string end points are sticking to given points. 0.000001 is
     * around 1 meter at the equator. Do not use a too low value, ShapeFile builder has some
     * rounding issues and do not ensure perfect equality between endpoints and geometry.
     */
    private static final double EPS = 0.000001;

    /**
     * Singleton representation of a straight-line (where nothing has to be stored), to be re-used.
     */
    protected static final int[] STRAIGHT_LINE = new int[0];

    protected static final byte[] STRAIGHT_LINE_PACKED = new byte[0];

    /**
     * Geometry factory. TODO - Do we need to make this parametrable?
     */
    private static GeometryFactory geometryFactory = new GeometryFactory();

    /**
     * Public factory to create a compact line string. Optimize for straight-line only line string
     * (w/o intermediate end-points).
     *
     * @param xa X coordinate of end point A
     * @param ya Y coordinate of end point A
     * @param xb X coordinate of end point B
     * @param yb Y coordinate of end point B
     * @param lineString The geometry to compact. Please be aware that we ignore first and last
     *        coordinate in the line string, they need to be exactly the same as A and B.
     * @param reverse True if A and B are inverted (B is start, A is end).
     * @return
     */
    public static int[] compactLineString(double xa, double ya, double xb, double yb,
            LineString lineString, boolean reverse) {
        if (lineString == null)
            return null;
        if (lineString.getCoordinates().length == 2)
            return STRAIGHT_LINE;
        double x0 = reverse ? xb : xa;
        double y0 = reverse ? yb : ya;
        double x1 = reverse ? xa : xb;
        double y1 = reverse ? ya : yb;
        Coordinate[] c = lineString.getCoordinates();
        /*
         * Check if the lineString is really sticking to the given end-points. TODO: If this is not
         * guaranteed, store all delta (from 0 to n-1) -- but how to mark it? A prefix?
         */
        if (Math.abs(x0 - c[0].x) > EPS || Math.abs(y0 - c[0].y) > EPS
                || Math.abs(x1 - c[c.length - 1].x) > EPS || Math.abs(y1 - c[c.length - 1].y) > EPS)
            throw new IllegalArgumentException(
                    "CompactLineString geometry must stick to given end points. If you need to relax this, please read source code.");
        int oix = (int) Math.round(x0 * FIXED_FLOAT_MULT);
        int oiy = (int) Math.round(y0 * FIXED_FLOAT_MULT);
        int[] coords = new int[(c.length - 2) * 2];
        for (int i = 1; i < c.length - 1; i++) {
            /*
             * Note: We should do the rounding *before* the delta to prevent rounding errors from
             * accumulating on long line strings.
             */
            int ix = (int) Math.round(c[i].x * FIXED_FLOAT_MULT);
            int iy = (int) Math.round(c[i].y * FIXED_FLOAT_MULT);
            int dix = ix - oix;
            int diy = iy - oiy;
            coords[(i - 1) * 2] = dix;
            coords[(i - 1) * 2 + 1] = diy;
            oix = ix;
            oiy = iy;
        }
        return coords;
    }

    /**
     * Same as the other version, but in a var-len int packed form (Dlugosz coding).
     *
     * @param x0
     * @param y0
     * @param x1
     * @param y1
     * @param lineString
     * @return
     */
    public static byte[] compackLineString(double x0, double y0, double x1, double y1,
            LineString lineString, boolean reverse) {
        int[] coords = compactLineString(x0, y0, x1, y1, lineString, reverse);
        if (coords == STRAIGHT_LINE)
            return STRAIGHT_LINE_PACKED;
        return DlugoszVarLenIntPacker.pack(coords);
    }

    /**
     * Construct a LineString based on external end-points and compacted int version.
     *
     * @param xa
     * @param ya
     * @param xb
     * @param yb
     * @param coords Compact version of coordinates
     * @param reverse True if A and B and the compacted geometry is reversed.
     * @return
     */
    public static LineString uncompactLineString(double xa, double ya, double xb, double yb,
            int[] coords, boolean reverse) {
        int size = coords == null ? 2 : (coords.length / 2) + 2;
        Coordinate[] c = new Coordinate[size];
        double x0 = reverse ? xb : xa;
        double y0 = reverse ? yb : ya;
        double x1 = reverse ? xa : xb;
        double y1 = reverse ? ya : yb;
        c[0] = new Coordinate(x0, y0);
        if (coords != null) {
            int oix = (int) Math.round(x0 * FIXED_FLOAT_MULT);
            int oiy = (int) Math.round(y0 * FIXED_FLOAT_MULT);
            for (int i = 1; i < c.length - 1; i++) {
                int ix = oix + coords[(i - 1) * 2];
                int iy = oiy + coords[(i - 1) * 2 + 1];
                c[i] = new Coordinate(ix / FIXED_FLOAT_MULT, iy / FIXED_FLOAT_MULT);
                oix = ix;
                oiy = iy;
            }
        }
        c[c.length - 1] = new Coordinate(x1, y1);
        LineString out = geometryFactory.createLineString(c);
        if (reverse)
            out = (LineString) out.reverse();
        return out;
    }

    /**
     * Same as the other version, but in a var-len int packed form (Dlugosz coding).
     *
     * @param x0
     * @param y0
     * @param x1
     * @param y1
     * @param coords
     * @return
     */
    public static LineString uncompackLineString(double x0, double y0, double x1, double y1,
            byte[] packedCoords, boolean reverse) {
        return uncompactLineString(x0, y0, x1, y1, DlugoszVarLenIntPacker.unpack(packedCoords), reverse);
    }
}
TOP

Related Classes of org.opentripplanner.common.geometry.CompactLineString

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.