-
Notifications
You must be signed in to change notification settings - Fork 80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
StringCompressedPolyLine now supports WKB for incompressible polylines #200
Changes from 1 commit
0cb5485
0d00ccc
7f723e8
5df39aa
6292fa4
4f9faa3
8cb9948
9aea50f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
…lgo fails
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,11 +5,20 @@ | |
import java.util.List; | ||
|
||
import org.openstreetmap.atlas.exception.CoreException; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
/** | ||
* Derived from MapQuest's developer portal: | ||
* https://developer.mapquest.com/documentation/common/encode-decode/ | ||
* Encode a {@link PolyLine} using an algorithm derived from the MapQuest variant of the Google | ||
* Polyline Encoding Format. The encoding scheme falls back on WKB in edge cases when the algorithm | ||
* fails (such as when two consecutive points in the polyline have a longitude difference of greater | ||
* than 180 degrees). | ||
* | ||
* @see <a href="https://developer.mapquest.com/documentation/common/encode-decode">The MapQuest | ||
* algorithm</a> | ||
* @see <a href= | ||
* "https://developers.google.com/maps/documentation/utilities/polylinealgorithm?csw=1">Google | ||
* Polyline Encoding Format</a> | ||
* @author matthieun | ||
*/ | ||
public class StringCompressedPolyLine implements Serializable | ||
|
@@ -42,7 +51,13 @@ public PolyLineCompressionException(final String message, final Object... items) | |
private static final long MAXIMUM_DELTA_LONGITUDE = (long) (MAXIMUM_DELTA_LONGITUDE_IN_DEGREES | ||
* Math.pow(10, PRECISION)); | ||
|
||
private final byte[] encoding; | ||
// if the first byte of the encoding array is this sentinel value, then the following encoding | ||
// is WKB and not string compressed | ||
private static final byte WKB_SENTINEL = 0; | ||
|
||
private static final Logger logger = LoggerFactory.getLogger(StringCompressedPolyLine.class); | ||
|
||
private byte[] encoding; | ||
|
||
public StringCompressedPolyLine(final byte[] encoding) | ||
{ | ||
|
@@ -55,25 +70,34 @@ public StringCompressedPolyLine(final PolyLine polyLine) | |
{ | ||
this.encoding = compress(polyLine, PRECISION).getBytes(ENCODING_NAME); | ||
} | ||
catch (final PolyLineCompressionException e) | ||
catch (final PolyLineCompressionException exception) | ||
{ | ||
throw e; | ||
this.encoding = getWkbFallback(polyLine); | ||
} | ||
catch (final Exception e) | ||
catch (final Exception exception) | ||
{ | ||
throw new CoreException("Could not compress polyline.", e); | ||
throw new CoreException("Could not compress polyline.", exception); | ||
} | ||
} | ||
|
||
public PolyLine asPolyLine() | ||
{ | ||
try | ||
if (this.encoding[0] == WKB_SENTINEL) | ||
{ | ||
return asPolyLine(new String(this.encoding, ENCODING_NAME), PRECISION); | ||
final byte[] strippedEncoding = new byte[this.encoding.length - 1]; | ||
System.arraycopy(this.encoding, 1, strippedEncoding, 0, strippedEncoding.length); | ||
return PolyLine.wkb(strippedEncoding); | ||
} | ||
catch (final Exception e) | ||
else | ||
{ | ||
throw new CoreException("Could not decompress polyline.", e); | ||
try | ||
{ | ||
return asPolyLine(new String(this.encoding, ENCODING_NAME), PRECISION); | ||
} | ||
catch (final Exception exception) | ||
{ | ||
throw new CoreException("Could not decompress polyline.", exception); | ||
} | ||
} | ||
} | ||
|
||
|
@@ -85,13 +109,22 @@ public byte[] getEncoding() | |
@Override | ||
public String toString() | ||
{ | ||
try | ||
if (this.encoding[0] == WKB_SENTINEL) | ||
{ | ||
return new String(this.encoding, ENCODING_NAME); | ||
final byte[] strippedEncoding = new byte[this.encoding.length - 1]; | ||
System.arraycopy(this.encoding, 1, strippedEncoding, 0, strippedEncoding.length); | ||
return PolyLine.wkb(strippedEncoding).toWkt(); | ||
} | ||
catch (final Exception e) | ||
else | ||
{ | ||
throw new CoreException("Could not stringify byte array.", e); | ||
try | ||
{ | ||
return new String(this.encoding, ENCODING_NAME); | ||
} | ||
catch (final Exception e) | ||
{ | ||
throw new CoreException("Could not stringify byte array.", e); | ||
} | ||
} | ||
} | ||
|
||
|
@@ -152,6 +185,7 @@ private String compress(final PolyLine points, final int precision0) | |
final long deltaLongitude = longitude - oldLongitude; | ||
if (Math.abs(deltaLongitude) > MAXIMUM_DELTA_LONGITUDE) | ||
{ | ||
logger.warn("Unable to use string compression for {}", points.toWkt()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't that logging statement redundant if the below exception is thrown? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Normally it would be but this specific exception is always caught on line 77 of Please correct me if I am wrong, and I can remove the log statement. |
||
throw new PolyLineCompressionException( | ||
"Unable to compress the polyLine, two consecutive points ({} and {}) are too far apart in longitude: {} degrees.", | ||
last, location, deltaLongitude / precision); | ||
|
@@ -182,4 +216,13 @@ private String encodeNumber(final long number0) | |
encoded += String.valueOf(Character.toChars((int) number + ENCODING_OFFSET_MINUS_ONE)); | ||
return encoded; | ||
} | ||
|
||
private byte[] getWkbFallback(final PolyLine polyLine) | ||
{ | ||
final byte[] wkbEncoding = polyLine.toWkb(); | ||
final byte[] finalEncoding = new byte[1 + wkbEncoding.length]; | ||
finalEncoding[0] = WKB_SENTINEL; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Such a waste of bytes!! ;p |
||
System.arraycopy(wkbEncoding, 0, finalEncoding, 1, wkbEncoding.length); | ||
return finalEncoding; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this would not handle the single location polylines outlined below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it not already? This method is just a carbon copy of the already existing
wkt
method. TheWkbPolyLineConverter.backwardConvert
method used bywkb
will just end up creating a singleton list to initialize the polyline.If you take a look at the
toWkb
method I added (line 1126), you'll notice it does handle the case of a polyline with a single point (following the example set bytoWkt
). In fact I was actually wondering why this is necessary. Both of those functions callWkXPolyLineConverter.convert
, and it looks like that method also handles the special case of a single point PolyLine. So is this check redundant, or am I missing something?