Skip to content

Commit

Permalink
wrote decode_points & decode_levels for use in testing
Browse files Browse the repository at this point in the history
finished off test for RT #46337, passing.
  • Loading branch information
spurkis committed Apr 2, 2010
1 parent 6b43578 commit 23dda38
Show file tree
Hide file tree
Showing 7 changed files with 312 additions and 137 deletions.
6 changes: 5 additions & 1 deletion Changes
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ All changes by Steve Purkis, unless otherwise noted.
0.05
+ Use sprintf() as a round() function, floor() removed.
+ RT #49327: applied patch to handle small negative numbers [Slaven Rezić]
+ RT #46337: added test, not yet fixed. [Reported by Joe Navratil]
+ RT #46337: added tests, seems to have been fixed by other fixes.
[Reported by Joe Navratil]
+ RT #49323, #36181: Geo::Gpx is now a build_reccomends & correct version
* Fixed a bug where signed numbers were being treated as unsigned for
bitwise ops.
+ Introduced decode_points() & decode_levels(), mainly for testing.

0.04
+ Profiled, benchmarked & optimized. Now almost twice as fast on an AMD
Expand Down
94 changes: 79 additions & 15 deletions lib/Geo/Google/PolylineEncoder.pm
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,6 @@ sub reset_encoder {
sub set_points {
my ($self, $points) = @_;

$points->[0]->{first} = 1;
$points->[-1]->{last} = 1;

# For the moment, just stick the points we were given into $self->points:
return $self->points( $points );

Expand Down Expand Up @@ -455,19 +452,24 @@ sub encode_signed_number {
# RT 49327: the signedness has to be determined *after* rounding
my $is_negative = $num < 0;

# 3. Convert the decimal value to binary. Note that a negative value must
# be calculated using its two's complement by inverting the binary value
# and adding one to the result.
{
# 3. Convert the decimal value to binary. Note that a negative value
# must be calculated using its two's complement by inverting the
# binary value and adding one to the result.

# (perl ints are already manipulatable in binary, so do nothing)
# Note: perl ints are already manipulatable in binary, but not always
# treated as signed ints - this can cause weirdness with bitwise
# operators... `perldoc integer` for more info.
use integer; # treat bitwise operands as signed

# 4. Left-shift the binary value one bit:
$num = $num << 1;
# 4. Left-shift the binary value one bit:
$num = $num << 1;

# 5. If the original decimal value is negative, invert this encoding:
# (see note on RT 49327 above)
if ($is_negative) {
$num = ~$num;
# 5. If the original decimal value is negative, invert this encoding:
# (see note on RT 49327 above)
if ($is_negative) {
$num = ~$num;
}
}

return $self->encode_number($num);
Expand All @@ -477,6 +479,7 @@ sub encode_signed_number {
# http://code.google.com/apis/maps/documentation/include/polyline.js
sub encode_number {
my ($self, $num) = @_;
no integer; # treat bitwise operands as unsigned

# 6. Break the binary value out into 5-bit chunks (starting from the right hand side):
# 7. Place the 5-bit chunks into reverse order:
Expand All @@ -487,17 +490,78 @@ sub encode_number {
my $encodeString = "";
while ($num >= 0x20) {
my $nextValue = (0x20 | ($num & 0x1f)) + 63;
$encodeString .= chr($nextValue);
$encodeString .= chr( $nextValue );
$num >>= 5;
}

my $finalValue = $num + 63;
$encodeString .= chr($finalValue);
$encodeString .= chr( $finalValue );

return $encodeString;
}


# Decode an encoded polyline into a list of lat/lng tuples.
# adapted from http://code.google.com/apis/maps/documentation/include/polyline.js
sub decode_points {
my ($class, $encoded) = @_;

my $len = length( $encoded );
my @array;

my $index = 0;
my $lat = 0;
my $lon = 0;

while ($index < $len) {
{
use integer; # treat bitwise operands as signed
my $b;
my $shift = 0;
my $result = 0;
do {
$b = ord( substr( $encoded, $index++, 1 ) ) - 63;
$result |= ($b & 0x1f) << $shift;
$shift += 5;
} while ($b >= 0x20);
my $dlat = (($result & 1) ? ~($result >> 1) : ($result >> 1));
$lat += $dlat;

# cut-n-paste to improve performance?
$shift = 0;
$result = 0;
do {
$b = ord( substr( $encoded, $index++, 1 ) ) - 63;
$result |= ($b & 0x1f) << $shift;
$shift += 5;
} while ($b >= 0x20);
my $dlon = (($result & 1) ? ~($result >> 1) : ($result >> 1));
$lon += $dlon;
}

push @array, { lat => $lat * 1e-5, lon => $lon * 1e-5 };
}

return \@array;
}

# Decode an encoded levels string into a list of levels.
# adapted from http://code.google.com/apis/maps/documentation/include/polyline.js
sub decode_levels {
my ($class, $encoded) = @_;

my $len = length( $encoded );
my @levels;

for (my $index = 0; $index < $len; $index++) {
my $level = ord( substr( $encoded, $index, 1 ) ) - 63;
push @levels, $level;
}

return \@levels;
}


1;

__END__
Expand Down
10 changes: 8 additions & 2 deletions t/01_basic.t
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,23 @@ use_ok( 'Geo::Google::PolylineEncoder' );
# Test 1 - basic polyline with 3 points
# example from http://code.google.com/apis/maps/documentation/polylinealgorithm.html
{
my @points = [
my $points = [
{ lat => 38.5, lon => -120.2 }, # lvl 17
{ lat => 40.7, lon => -120.95 }, # lvl 16
{ lat => 43.252, lon => -126.453 }, # lvl 17
];
my $encoder = Geo::Google::PolylineEncoder->new(zoom_factor => 2, num_levels => 18);
my $eline = $encoder->encode( @points );
my $eline = $encoder->encode( $points );
is( $eline->{num_levels}, 18, 'ex1 num_levels' );
is( $eline->{zoom_factor}, 2, 'ex1 zoom_factor' );
is( $eline->{points}, '_p~iF~ps|U_ulLnnqC_mqNvxq`@', 'ex1 points' );
is( $eline->{levels}, 'POP', 'ex1 levels' );

my $d_points = $encoder->decode_points( $eline->{points} );
my $d_levels = $encoder->decode_levels( $eline->{levels} );
is( scalar @$d_points, scalar @$d_levels, 'decode: num levels == num points' );
is_deeply( $d_points, $points, 'decode_points' );
is_deeply( $d_levels, [ 17, 16, 17 ], 'decode_levels' );
}

# Test 1a - polyline with only 2 points
Expand Down
Loading

0 comments on commit 23dda38

Please sign in to comment.