My favorites | Sign in
Project Home Source
Checkout   Browse   Changes  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
// -*- C++ -*-
// $Id: dpencode.cpp,v 1.3 2009-09-08 16:06:32 robertl Exp $
//------------------------------------------------------------------------
//
// Original in JavaScript:
// PolylineEncoder.js copyright Mark McClure April/May 2007
//
// Translated to C++
// Copyright (C) 2009 S. Khai Mong <khai@mangrai.com>.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 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, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111
// USA
//
//------------------------------------------------------------------------
#include <stack>
#include <math.h>
#include "dpencode.h"

using std::vector;
using std::stack;
using std::string;



//------------------------------------------------------------------------
PolylineEncoder::PolylineEncoder(int numLevels, double zoomFactor, double vs):
numLevels(numLevels), zoomFactor(zoomFactor), verySmall(vs)
{
if (verySmall <=0.0)
verySmall = 1.0E-5;
for(int i = 0; i < numLevels; i++)
zoomLevelBreaks.push_back(verySmall*pow(zoomFactor, numLevels-i-1));
}

//------------------------------------------------------------------------
class IntervalPair
{
public:
IntervalPair(int i0, int i1): i0(i0), i1(i1) {
}
int i0, i1;
};

//------------------------------------------------------------------------
static double hypotenuse(double a, double b){
return sqrt(a*a + b*b);
}
//------------------------------------------------------------------------
static double hdist(const LatLng &a, const LatLng &b) {
return hypotenuse(a.lat()-b.lat(), a.lng()-b.lng());
}

//------------------------------------------------------------------------
static string encodeNumber(int num)
{
string encodeString = "";
while (num >= 0x20) {
encodeString += char((0x20 | (num & 0x1f)) + 63);
num = num >> 5;
}
encodeString += char(num + 63);
return encodeString;
}

//------------------------------------------------------------------------
// This one is Google's verbatim.
static string encodeSignedNumber (int num)
{
int sgn_num = num << 1;
if (num < 0) {
sgn_num = ~(sgn_num);
}
return(encodeNumber(sgn_num));
}

//------------------------------------------------------------------------
int roundToInt(double x) {
return (x>0.0) ? int(x+0.5) : int(x-0.5);
}
//------------------------------------------------------------------------
void PolylineEncoder::createEncodings(string &encoded_points,
const vector <LatLng> &points,
const vector <double> &dists)
{
encoded_points = "";;
int plat = 0;
int plng = 0;
for(unsigned int i = 0; i < points.size(); i++) {
if(dists[i] >= 0.0 || i == 0 || i == points.size()-1) {
int late5 = roundToInt(points[i].lat() * 1e5);
int lnge5 = roundToInt(points[i].lng() * 1e5);
int dlat = late5 - plat;
int dlng = lnge5 - plng;
encoded_points += encodeSignedNumber(dlat) + encodeSignedNumber(dlng);
plat = late5;
plng = lnge5;
}
}
}

//------------------------------------------------------------------------
// This computes the appropriate zoom level of a point in terms of it's
// distance from the relevant segment in the DP algorithm. Could be done
// in terms of a logarithm, but this approach makes it a bit easier to
// ensure that the level is not too large.
int PolylineEncoder::computeLevel(double dd)
{
int lev = 0;
if(dd > verySmall) {
while(dd < zoomLevelBreaks[lev]) {
lev++;
}
}
return lev;
}
//------------------------------------------------------------------------
// Now we can use the previous function to march down the list
// of points and encode the levels. Like createEncodings, we
// ignore points whose distance (in dists) is undefined.
void PolylineEncoder::encodeLevels(string &encoded_levels, const vector<LatLng> &points, const vector<double>&dists)
{
encoded_levels = "";
encoded_levels += encodeNumber(numLevels-1);
for (unsigned int i=1; i<points.size()-1; i++) {
if(dists[i] >= 0.0) {
encoded_levels += encodeNumber(numLevels-computeLevel(dists[i])-1);
}
}
encoded_levels += encodeNumber(numLevels-1);
}


//------------------------------------------------------------------------
double PolylineEncoder::distance(const LatLng &p0, const LatLng &p1, const LatLng &p2)
{
double out = 0.0;
if (p1.lat() == p2.lat() && p1.lng() == p2.lng()) {
out = hdist(p2, p0);
}
else {
double dlat = (p2.lat()-p1.lat());
double dlng = (p2.lng()-p1.lng());
double u = ((p0.lat()-p1.lat())*dlat+(p0.lng()-p1.lng())*dlng)/(dlat*dlat + dlng*dlng);

if (u <= 0) {
out = hdist(p0, p1);
}
else if(u >= 1) {
out = hdist(p0, p2);
}
else {
out = hdist(p0, LatLng(p1.lat() + u*dlat, p1.lng() + u*dlng));
}
}
return out;
}

//------------------------------------------------------------------------
void PolylineEncoder::dpEncode(string &encPts, string &encLevels, const vector<LatLng> &points)
{
if (points.size() < 2) {
encPts = encLevels = ""; // no solution here.
return;

}
stack <IntervalPair> stk;
vector <double> dists(points.size(), -1.0);

stk.push(IntervalPair(0, int(points.size())-1));
while (!stk.empty()) {

IntervalPair current = stk.top();
stk.pop();

double maxDist = -1.0;
int maxLoc = -1;
for (int i=current.i0+1; i<current.i1; i++) {
double dist = this->distance(points[i], points[current.i0], points[current.i1]);
if(dist > maxDist) {
maxDist = dist;
maxLoc = i;
}
}
if(maxDist > this->verySmall) {
dists[maxLoc] = maxDist;
stk.push(IntervalPair(current.i0, maxLoc));
stk.push(IntervalPair(maxLoc, current.i1));
}
}
createEncodings(encPts, points, dists);
encodeLevels(encLevels, points, dists);
}

Change log

r4161 by robertlipe on Mar 4, 2012   Diff
More svn mode fixes.
Go to: 

Older revisions

r3791 by robertl on Sep 8, 2009   Diff
Help invocation per page, uses desktop
browser for h elp.  Fix German
translation on Windows.  Warn for no
valid options.
r3767 by robertl on Aug 28, 2009   Diff
Remove trailing whitespace in source.
r3690 by robertl on Jul 5, 2009   Diff
Khai Mong contributes new cross-
platform GUI in Qt4.
All revisions of this file

File info

Size: 6364 bytes, 204 lines

File properties

cvs2svn:cvs-rev
1.3
Powered by Google Project Hosting