/**********************************************************************
*
* PostGIS - Spatial Types for PostgreSQL
* http://postgis.net
*
* PostGIS 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.
*
* PostGIS 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 PostGIS. If not, see .
*
**********************************************************************
*
* Copyright 2014 Kashif Rasul and
* Shoaib Burq
*
**********************************************************************/
#include "stringbuffer.h"
#include "liblwgeom_internal.h"
static lwvarlena_t *lwline_to_encoded_polyline(const LWLINE *, int precision);
static lwvarlena_t *lwmmpoint_to_encoded_polyline(const LWMPOINT *, int precision);
static lwvarlena_t *pointarray_to_encoded_polyline(const POINTARRAY *, int precision);
/* takes a GEOMETRY and returns an Encoded Polyline representation */
extern lwvarlena_t *
lwgeom_to_encoded_polyline(const LWGEOM *geom, int precision)
{
int type = geom->type;
switch (type)
{
case LINETYPE:
return lwline_to_encoded_polyline((LWLINE*)geom, precision);
case MULTIPOINTTYPE:
return lwmmpoint_to_encoded_polyline((LWMPOINT*)geom, precision);
default:
lwerror("lwgeom_to_encoded_polyline: '%s' geometry type not supported",
lwtype_name(type));
return NULL;
}
}
static lwvarlena_t *
lwline_to_encoded_polyline(const LWLINE *line, int precision)
{
return pointarray_to_encoded_polyline(line->points, precision);
}
static lwvarlena_t *
lwmmpoint_to_encoded_polyline(const LWMPOINT *mpoint, int precision)
{
LWLINE* line = lwline_from_lwmpoint(mpoint->srid, mpoint);
lwvarlena_t *encoded_polyline = lwline_to_encoded_polyline(line, precision);
lwline_free(line);
return encoded_polyline;
}
static lwvarlena_t *
pointarray_to_encoded_polyline(const POINTARRAY *pa, int precision)
{
uint32_t i;
const POINT2D* prevPoint;
int* delta;
stringbuffer_t* sb;
double scale = pow(10, precision);
/* Empty input is empty string */
if (pa->npoints == 0)
{
lwvarlena_t *v = lwalloc(LWVARHDRSZ);
LWSIZE_SET(v->size, LWVARHDRSZ);
return v;
}
delta = lwalloc(2 * sizeof(int) * pa->npoints);
/* Take the double value and multiply it by 1x10^precision, rounding the
* result */
prevPoint = getPoint2d_cp(pa, 0);
delta[0] = round(prevPoint->y * scale);
delta[1] = round(prevPoint->x * scale);
/* Points only include the offset from the previous point */
for (i = 1; i < pa->npoints; i++)
{
const POINT2D* point = getPoint2d_cp(pa, i);
delta[2 * i] = round(point->y * scale) - round(prevPoint->y * scale);
delta[(2 * i) + 1] =
round(point->x * scale) - round(prevPoint->x * scale);
prevPoint = point;
}
/* value to binary: a negative value must be calculated using its two's
* complement */
for (i = 0; i < pa->npoints * 2; i++)
{
/* Multiply by 2 for a signed left shift */
delta[i] *= 2;
/* if value is negative, invert this encoding */
if (delta[i] < 0) {
delta[i] = ~(delta[i]);
}
}
sb = stringbuffer_create();
for (i = 0; i < pa->npoints * 2; i++)
{
int numberToEncode = delta[i];
while (numberToEncode >= 0x20)
{
/* Place the 5-bit chunks into reverse order or
each value with 0x20 if another bit chunk follows and add 63*/
int nextValue = (0x20 | (numberToEncode & 0x1f)) + 63;
stringbuffer_aprintf(sb, "%c", (char)nextValue);
/* Break the binary value out into 5-bit chunks */
numberToEncode >>= 5;
}
numberToEncode += 63;
stringbuffer_aprintf(sb, "%c", (char)numberToEncode);
}
lwfree(delta);
lwvarlena_t *v = stringbuffer_getvarlenacopy(sb);
stringbuffer_destroy(sb);
return v;
}