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
404 lines
12 KiB
C++
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
|