mapnik/src/twkb.cpp
Mathis Logemann e7c3d04309 format dir include and src
format all files

Revert "format all files"

This reverts commit 95d5812e49e7f916b68e786596f5a8eb5bcac414.

Revert "format some files"

This reverts commit ed3c8762d4d828b2b28e7b18809fc33f4f8ccaf5.

format all files

fix formatting in dir include

fix formatting of debug macro
2022-01-27 00:12:08 +01:00

404 lines
12 KiB
C++

/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2021 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/geometry.hpp>
#include <mapnik/geometry/correct.hpp>
#include <mapnik/util/noncopyable.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;
poly.reserve(num_rings);
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);
}
poly.push_back(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