org.openstreetmap.atlas.geography.CompressedPolyLine Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of atlas Show documentation
Show all versions of atlas Show documentation
"Library to load OSM data into an Atlas format"
package org.openstreetmap.atlas.geography;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* A {@link PolyLine} that is compressed using delta encoding. This is efficient when the
* {@link PolyLine} has a lot of points close by.
*
* @author matthieun
*/
public class CompressedPolyLine implements Located, Serializable
{
/**
* @author matthieun
*/
private static class ByteSign
{
private final byte[] bytes;
private final boolean sign;
ByteSign(final byte[] bytes, final boolean sign)
{
this.bytes = bytes;
this.sign = sign;
}
public byte[] getBytes()
{
return this.bytes;
}
public boolean isSign()
{
return this.sign;
}
}
private static final long serialVersionUID = -7813027521625470225L;
private static final int BYTE_FULL_MASK = 0xFF;
private static final int BYTE_SIZE = 8;
private static final int INT_SIGN_MASK = 0x80000000;
private static final int INT_NO_SIGN_MASK = 0x7FFFFFFF;
private final byte[][] positions;
private final boolean[] signs;
public CompressedPolyLine(final byte[][] positions, final boolean[] signs)
{
this.positions = positions;
this.signs = signs;
}
/**
* Create a compressed version of a {@link PolyLine}
*
* @param polyLine
* The {@link PolyLine} to compress.
*/
public CompressedPolyLine(final PolyLine polyLine)
{
final List positions = new ArrayList<>();
final List signs = new ArrayList<>();
int formerLatitude = 0;
int formerLongitude = 0;
for (final Location location : polyLine)
{
final int latitude = (int) location.getLatitude().asDm7();
final int longitude = (int) location.getLongitude().asDm7();
final int deltaLatitude = latitude - formerLatitude;
final int deltaLongitude = longitude - formerLongitude;
formerLatitude = latitude;
formerLongitude = longitude;
final ByteSign latShrink = shrink(deltaLatitude);
final ByteSign lonShrink = shrink(deltaLongitude);
positions.add(latShrink.getBytes());
signs.add(latShrink.isSign());
positions.add(lonShrink.getBytes());
signs.add(lonShrink.isSign());
}
this.positions = new byte[positions.size()][];
for (int i = 0; i < positions.size(); i++)
{
this.positions[i] = positions.get(i);
}
this.signs = new boolean[signs.size()];
for (int i = 0; i < signs.size(); i++)
{
this.signs[i] = signs.get(i);
}
}
/**
* @return An expanded {@link PolyLine}
*/
public PolyLine asPolyLine()
{
boolean lat = true;
int latitude = 0;
int longitude = 0;
final List locations = new ArrayList<>();
for (int index = 0; index < this.positions.length; index++)
{
final byte[] result = this.positions[index];
if (lat)
{
latitude += expand(result, index);
lat = false;
}
else
{
longitude += expand(result, index);
locations.add(new Location(Latitude.dm7(latitude), Longitude.dm7(longitude)));
lat = true;
}
}
return new PolyLine(locations);
}
@Override
public Rectangle bounds()
{
return asPolyLine().bounds();
}
public byte[][] getPositions()
{
return this.positions;
}
public boolean[] getSigns()
{
return this.signs;
}
@Override
public String toString()
{
return asPolyLine().toString();
}
/**
* Transform an array of bytes into an int. If the bytes are 0x4A and 0x0F, with a negative sign
* (from the index in the signs array) the returned int will be 0x80000F4A.
*
* @param result
* The shrunk value
* @param index
* The index of the sign
* @return The expanded value
*/
private int expand(final byte[] result, final int index)
{
int placeholder = 0;
for (int i = 0; i < result.length; i++)
{
final byte byteValue = result[i];
placeholder |= byteValue & BYTE_FULL_MASK;
if (i < result.length - 1)
{
placeholder <<= BYTE_SIZE;
}
}
final boolean negative = this.signs[index];
if (negative)
{
placeholder |= INT_SIGN_MASK;
}
return placeholder;
}
/**
* Browse the value, byte after byte, and keep only the bytes that have significance. So an int
* 0x80000F4A will return an array of two bytes, 0x4A and 0x0F, and a negative sign. All the 0
* bytes will be thrown out.
*
* @param value
* The value to shrink
* @return The shrunk value.
*/
private ByteSign shrink(final int value)
{
// Get rid of the sign
int placeholder = value & INT_NO_SIGN_MASK;
final List bytes = new ArrayList<>();
while (Math.abs(placeholder) > 0)
{
final byte byteValue = (byte) placeholder;
bytes.add(byteValue);
placeholder >>>= BYTE_SIZE;
}
final int size = bytes.size();
final byte[] result = new byte[size];
for (int i = 0; i < size; i++)
{
result[i] = bytes.get(size - 1 - i);
}
return new ByteSign(result, value < 0);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy