TWKB - port CartoDB implementation to mapnik 3.x.x
ref https://github.com/CartoDB/mapnik/blob/2.3.x.cartodb/plugins/input/postgis/postgis_utils.cpp ref https://github.com/CartoDB/mapnik/tree/2.3.x.cartodb/plugins/input/postgis#23xcartodb
This commit is contained in:
parent
f9397e7e66
commit
1f61aa3981
3 changed files with 393 additions and 3 deletions
|
@ -60,9 +60,11 @@ class MAPNIK_DECL geometry_utils : private util::noncopyable
|
|||
{
|
||||
public:
|
||||
|
||||
static mapnik::geometry::geometry<double> from_wkb(const char* wkb,
|
||||
std::size_t size,
|
||||
wkbFormat format = wkbGeneric);
|
||||
static geometry::geometry<double> from_wkb(char const* wkb,
|
||||
std::size_t size,
|
||||
wkbFormat format = wkbGeneric);
|
||||
|
||||
static geometry::geometry<double> from_twkb(char const* twkb, std::size_t size);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -205,6 +205,7 @@ source = Split(
|
|||
rule.cpp
|
||||
save_map.cpp
|
||||
wkb.cpp
|
||||
twkb.cpp
|
||||
projection.cpp
|
||||
proj_transform.cpp
|
||||
scale_denominator.cpp
|
||||
|
|
387
src/twkb.cpp
Normal file
387
src/twkb.cpp
Normal file
|
@ -0,0 +1,387 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* This file is part of Mapnik (c++ mapping toolkit)
|
||||
*
|
||||
* Copyright (C) 2016 Artem Pavlenko
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
// mapnik
|
||||
#include <mapnik/wkb.hpp>
|
||||
#include <mapnik/feature.hpp>
|
||||
#include <mapnik/geom_util.hpp>
|
||||
#include <mapnik/util/noncopyable.hpp>
|
||||
#include <mapnik/geometry_correct.hpp>
|
||||
#include <cmath>
|
||||
|
||||
namespace mapnik { namespace detail {
|
||||
|
||||
struct twkb_reader : mapnik::util::noncopyable
|
||||
{
|
||||
private:
|
||||
const char *twkb_;
|
||||
size_t size_;
|
||||
unsigned int pos_;
|
||||
// Metadata on the geometry we are parsing
|
||||
uint8_t twkb_type_;
|
||||
uint8_t has_bbox_;
|
||||
uint8_t has_size_;
|
||||
uint8_t has_idlist_;
|
||||
uint8_t has_z_;
|
||||
uint8_t has_m_;
|
||||
uint8_t is_empty_;
|
||||
// Precision factors to convert ints to double
|
||||
double factor_xy_;
|
||||
double factor_z_;
|
||||
double factor_m_;
|
||||
// An array to keep delta values from 4 dimensions
|
||||
int64_t coord_x_;
|
||||
int64_t coord_y_;
|
||||
int64_t coord_z_;
|
||||
int64_t coord_m_;
|
||||
|
||||
public:
|
||||
enum twkbGeometryType : std::uint8_t
|
||||
{
|
||||
twkbPoint = 1,
|
||||
twkbLineString = 2,
|
||||
twkbPolygon = 3,
|
||||
twkbMultiPoint = 4,
|
||||
twkbMultiLineString = 5,
|
||||
twkbMultiPolygon = 6,
|
||||
twkbGeometryCollection = 7
|
||||
};
|
||||
|
||||
twkb_reader(char const* twkb, size_t size)
|
||||
: twkb_(twkb), size_(size), pos_(0), twkb_type_(0), // Geometry type
|
||||
has_bbox_(0), // Bounding box?
|
||||
has_size_(0), // Size attribute?
|
||||
has_idlist_(0), // Presence of X/Y
|
||||
has_z_(0), // Presence of Z
|
||||
has_m_(0), // Presence of M
|
||||
is_empty_(0), // Empty?
|
||||
factor_xy_(0.0), // Expansion factor for X/Y
|
||||
factor_z_(0.0), // Expansion factor for Z
|
||||
factor_m_(0.0) // Expansion factor for M
|
||||
{}
|
||||
|
||||
mapnik::geometry::geometry<double> read()
|
||||
{
|
||||
mapnik::geometry::geometry<double> geom = mapnik::geometry::geometry_empty();
|
||||
// Read the metadata bytes, populating all the
|
||||
// information about optional fields, extended (z/m) dimensions
|
||||
// expansion factors and so on
|
||||
read_header();
|
||||
|
||||
// Each new read call has to reset the coordinate accumulators
|
||||
coord_x_ = 0; // Accumulation register (x)
|
||||
coord_y_ = 0; // Accumulation register (y)
|
||||
coord_z_ = 0; // Accumulation register (z)
|
||||
coord_m_ = 0; // Accumulation register (m)
|
||||
|
||||
// If the geometry is empty, add nothing to the paths array
|
||||
if (is_empty_)
|
||||
return geom;
|
||||
|
||||
// Read the [optional] size information
|
||||
if (has_size_)
|
||||
size_ = read_unsigned_integer();
|
||||
|
||||
// Read the [optional] bounding box information
|
||||
if (has_bbox_) read_bbox();
|
||||
|
||||
switch (twkb_type_)
|
||||
{
|
||||
case twkbPoint:
|
||||
geom = read_point();
|
||||
break;
|
||||
case twkbLineString:
|
||||
geom = read_linestring();
|
||||
break;
|
||||
case twkbPolygon:
|
||||
geom = read_polygon();
|
||||
break;
|
||||
case twkbMultiPoint:
|
||||
geom = read_multipoint();
|
||||
break;
|
||||
case twkbMultiLineString:
|
||||
geom = read_multilinestring();
|
||||
break;
|
||||
case twkbMultiPolygon:
|
||||
geom = read_multipolygon();
|
||||
break;
|
||||
case twkbGeometryCollection:
|
||||
geom = read_collection();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return geom;
|
||||
}
|
||||
|
||||
private:
|
||||
int64_t unzigzag64(uint64_t val)
|
||||
{
|
||||
if (val & 0x01)
|
||||
return -1 * (int64_t)((val + 1) >> 1);
|
||||
else
|
||||
return (int64_t)(val >> 1);
|
||||
}
|
||||
|
||||
int32_t unzigzag32(uint32_t val)
|
||||
{
|
||||
if (val & 0x01) return -1 * (int32_t)((val + 1) >> 1);
|
||||
else return (int32_t)(val >> 1);
|
||||
}
|
||||
|
||||
int8_t unzigzag8(uint8_t val)
|
||||
{
|
||||
if (val & 0x01) return -1 * (int8_t)((val + 1) >> 1);
|
||||
else return (int8_t)(val >> 1);
|
||||
}
|
||||
|
||||
// Read from signed 64bit varint
|
||||
int64_t read_signed_integer() { return unzigzag64(read_unsigned_integer()); }
|
||||
|
||||
// Read from unsigned 64bit varint
|
||||
uint64_t read_unsigned_integer()
|
||||
{
|
||||
uint64_t nVal = 0;
|
||||
int nShift = 0;
|
||||
uint8_t nByte;
|
||||
|
||||
// Check so we don't read beyond the twkb
|
||||
while (pos_ < size_)
|
||||
{
|
||||
nByte = twkb_[pos_];
|
||||
// We get here when there is more to read in the input varInt
|
||||
// Here we take the least significant 7 bits of the read
|
||||
// byte and put it in the most significant place in the result variable.
|
||||
nVal |= ((uint64_t)(nByte & 0x7f)) << nShift;
|
||||
// move the "cursor" of the input buffer step (8 bits)
|
||||
pos_++;
|
||||
// move the cursor in the resulting variable (7 bits)
|
||||
nShift += 7;
|
||||
// Hibit isn't set, so this is the last byte
|
||||
if (!(nByte & 0x80)) {
|
||||
return nVal;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Every TWKB geometry starts with a metadata header
|
||||
//
|
||||
// type_and_dims byte
|
||||
// metadata_header byte
|
||||
// [extended_dims] byte
|
||||
// [size] uvarint
|
||||
// [bounds] bbox
|
||||
//
|
||||
void read_header()
|
||||
{
|
||||
uint8_t type_precision = twkb_[pos_++];
|
||||
uint8_t metadata = twkb_[pos_++];
|
||||
twkb_type_ = type_precision & 0x0F;
|
||||
int8_t precision = unzigzag8((type_precision & 0xF0) >> 4);
|
||||
factor_xy_ = std::pow(10, static_cast<double>(precision));
|
||||
has_bbox_ = metadata & 0x01;
|
||||
has_size_ = (metadata & 0x02) >> 1;
|
||||
has_idlist_ = (metadata & 0x04) >> 2;
|
||||
uint8_t zm = (metadata & 0x08) >> 3;
|
||||
is_empty_ = (metadata & 0x10) >> 4;
|
||||
|
||||
// Flag for higher dimensions means read a third byte
|
||||
// of extended dimension information
|
||||
if (zm)
|
||||
{
|
||||
zm = twkb_[pos_++];
|
||||
// Strip Z/M presence and precision from ext byte
|
||||
has_z_ = (zm & 0x01);
|
||||
has_m_ = (zm & 0x02) >> 1;
|
||||
// Convert the precision into factor
|
||||
int8_t precision_z = (zm & 0x1C) >> 2;
|
||||
int8_t precision_m = (zm & 0xE0) >> 5;
|
||||
factor_z_ = pow(10, (double)precision_z);
|
||||
factor_m_ = pow(10, (double)precision_m);
|
||||
}
|
||||
}
|
||||
|
||||
void read_bbox()
|
||||
{
|
||||
// we have nowhere to store this box information
|
||||
// for now, so we'll just move the read head forward
|
||||
// an appropriate number of times
|
||||
if (has_bbox_)
|
||||
{
|
||||
read_signed_integer(); // uint64_t xmin
|
||||
read_signed_integer(); // uint64_t xdelta
|
||||
read_signed_integer(); // uint64_t ymin
|
||||
read_signed_integer(); // uint64_t ydelta
|
||||
if (has_z_)
|
||||
{
|
||||
read_signed_integer(); // uint64_t zmin
|
||||
read_signed_integer(); // uint64_t zdelta
|
||||
}
|
||||
if (has_m_)
|
||||
{
|
||||
read_signed_integer(); // uint64_t mmin
|
||||
read_signed_integer(); // uint64_t mdelta
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void read_idlist(unsigned int num_ids)
|
||||
{
|
||||
// we have nowhere to store this id information
|
||||
// for now, so we'll just move the read head
|
||||
// forward an appropriate number of times
|
||||
if (has_idlist_)
|
||||
{
|
||||
for (unsigned int i = 0; i < num_ids; ++i)
|
||||
{
|
||||
read_signed_integer(); // uint64_t id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Ring>
|
||||
void read_coords(Ring & ring, std::size_t num_points)
|
||||
{
|
||||
for (std::size_t i = 0; i < num_points; ++i)
|
||||
{
|
||||
coord_x_ += read_signed_integer();
|
||||
coord_y_ += read_signed_integer();
|
||||
ring.emplace_back( coord_x_ / factor_xy_, coord_y_ / factor_xy_);
|
||||
// Skip Z and M
|
||||
if (has_z_) coord_z_ += read_signed_integer();
|
||||
if (has_m_) coord_m_ += read_signed_integer();
|
||||
}
|
||||
}
|
||||
|
||||
mapnik::geometry::point<double> read_point()
|
||||
{
|
||||
coord_x_ += read_signed_integer();
|
||||
coord_y_ += read_signed_integer();
|
||||
double x = coord_x_ / factor_xy_;
|
||||
double y = coord_y_ / factor_xy_;
|
||||
return mapnik::geometry::point<double>(x, y);
|
||||
}
|
||||
|
||||
mapnik::geometry::multi_point<double> read_multipoint()
|
||||
{
|
||||
mapnik::geometry::multi_point<double> multi_point;
|
||||
unsigned int num_points = read_unsigned_integer();
|
||||
if (has_idlist_) read_idlist(num_points);
|
||||
|
||||
if (num_points > 0)
|
||||
{
|
||||
multi_point.reserve(num_points);
|
||||
for (unsigned int i = 0; i < num_points; ++i)
|
||||
{
|
||||
multi_point.emplace_back(read_point());
|
||||
}
|
||||
}
|
||||
return multi_point;
|
||||
}
|
||||
|
||||
mapnik::geometry::line_string<double> read_linestring()
|
||||
{
|
||||
mapnik::geometry::line_string<double> line;
|
||||
unsigned int num_points = read_unsigned_integer();
|
||||
if (num_points > 0)
|
||||
{
|
||||
line.reserve(num_points);
|
||||
read_coords<mapnik::geometry::line_string<double>>(line, num_points);
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
mapnik::geometry::multi_line_string<double> read_multilinestring()
|
||||
{
|
||||
mapnik::geometry::multi_line_string<double> multi_line;
|
||||
unsigned int num_lines = read_unsigned_integer();
|
||||
if (has_idlist_) read_idlist(num_lines);
|
||||
multi_line.reserve(num_lines);
|
||||
for (unsigned int i = 0; i < num_lines; ++i)
|
||||
{
|
||||
multi_line.push_back(read_linestring());
|
||||
}
|
||||
return multi_line;
|
||||
}
|
||||
|
||||
mapnik::geometry::polygon<double> read_polygon()
|
||||
{
|
||||
unsigned int num_rings = read_unsigned_integer();
|
||||
mapnik::geometry::polygon<double> poly;
|
||||
if (num_rings > 1)
|
||||
{
|
||||
poly.interior_rings.reserve(num_rings - 1);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < num_rings; ++i)
|
||||
{
|
||||
mapnik::geometry::linear_ring<double> ring;
|
||||
unsigned int num_points = read_unsigned_integer();
|
||||
if (num_points > 0)
|
||||
{
|
||||
ring.reserve(num_points);
|
||||
read_coords<mapnik::geometry::linear_ring<double>>(ring, num_points);
|
||||
}
|
||||
if ( i == 0) poly.set_exterior_ring(std::move(ring));
|
||||
else poly.add_hole(std::move(ring));
|
||||
}
|
||||
return poly;
|
||||
}
|
||||
|
||||
mapnik::geometry::multi_polygon<double> read_multipolygon()
|
||||
{
|
||||
mapnik::geometry::multi_polygon<double> multi_poly;
|
||||
unsigned int num_polys = read_unsigned_integer();
|
||||
if (has_idlist_) read_idlist(num_polys);
|
||||
for (unsigned int i = 0; i < num_polys; ++i)
|
||||
{
|
||||
multi_poly.push_back(read_polygon());
|
||||
}
|
||||
return multi_poly;
|
||||
}
|
||||
|
||||
mapnik::geometry::geometry_collection<double> read_collection()
|
||||
{
|
||||
unsigned int num_geometries = read_unsigned_integer();
|
||||
mapnik::geometry::geometry_collection<double> collection;
|
||||
if (has_idlist_) read_idlist(num_geometries);
|
||||
for (unsigned int i = 0; i < num_geometries; ++i)
|
||||
{
|
||||
collection.push_back(read());
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
mapnik::geometry::geometry<double> geometry_utils::from_twkb(const char* wkb, std::size_t size)
|
||||
{
|
||||
detail::twkb_reader reader(wkb, size);
|
||||
mapnik::geometry::geometry<double> geom(reader.read());
|
||||
// note: this will only be applied to polygons
|
||||
mapnik::geometry::correct(geom);
|
||||
return geom;
|
||||
}
|
||||
|
||||
} // namespace mapnik
|
Loading…
Reference in a new issue