mirror of
https://github.com/mapnik/mapnik.git
synced 2025-07-20 16:34:26 +00:00
PMTile input plugin (WIP)
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -21,3 +21,6 @@
|
||||
[submodule "deps/mapbox/polylabel"]
|
||||
path = deps/mapbox/polylabel
|
||||
url = https://github.com/mapbox/polylabel.git
|
||||
[submodule "deps/mapbox/mapnik-vector-tile"]
|
||||
path = deps/mapbox/mapnik-vector-tile
|
||||
url = https://github.com/mapnik/mapnik-vector-tile.git
|
||||
|
1
deps/mapbox/mapnik-vector-tile
vendored
Submodule
1
deps/mapbox/mapnik-vector-tile
vendored
Submodule
Submodule deps/mapbox/mapnik-vector-tile added at fde802a7ff
@ -24,20 +24,26 @@ Import ('env')
|
||||
|
||||
PLUGIN_NAME = 'pmtiles'
|
||||
|
||||
MAPNIK_VECTOR_TILE = '../../../deps/mapbox/mapnik-vector-tile/src'
|
||||
|
||||
plugin_env = plugin_base.Clone()
|
||||
|
||||
plugin_env.Prepend(CPPPATH = '#deps/mapbox/mapnik-vector-tile/src')
|
||||
plugin_env.Append(CPPDEFINES = 'MAPNIK_VECTOR_TILE_LIBRARY=1')
|
||||
|
||||
plugin_sources = Split(
|
||||
"""
|
||||
%(PLUGIN_NAME)s_datasource.cpp
|
||||
%(PLUGIN_NAME)s_featureset.cpp
|
||||
%(MAPNIK_VECTOR_TILE)s/vector_tile_compression.cpp
|
||||
%(MAPNIK_VECTOR_TILE)s/vector_tile_geometry_decoder.cpp
|
||||
mvt_io.cpp
|
||||
vector_tile_compression.cpp
|
||||
vector_tile_geometry_decoder.cpp
|
||||
""" % locals()
|
||||
)
|
||||
|
||||
# Link Library to Dependencies
|
||||
libraries = [ 'sqlite3', 'boost_iostreams', 'boost_json' ]
|
||||
libraries = [ 'sqlite3', 'boost_iostreams', 'boost_json' ] #FIXME!
|
||||
libraries = [ 'boost_json' ]
|
||||
|
||||
linkflags = []
|
||||
if env['SQLITE_LINKFLAGS']:
|
||||
|
@ -160,7 +160,7 @@ mapnik::feature_ptr mvt_layer::next_feature()
|
||||
}
|
||||
mapnik::vector_tile_impl::GeometryPBF geoms(geom_itr);
|
||||
mapnik::geometry::geometry<double> geom =
|
||||
mapnik::vector_tile_impl::decode_geometry<double>(geoms, geometry_type,
|
||||
mapnik::vector_tile_impl::decode_geometry<double>(geoms, (std::int32_t)geometry_type,
|
||||
1, tile_x_, tile_y_, scale_, -1.0 * scale_);
|
||||
if (geom.is<mapnik::geometry::geometry_empty>())
|
||||
{
|
||||
@ -297,4 +297,3 @@ mvt_io::mvt_io(std::string&& data, mapnik::context_ptr const& ctx, const uint32_
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/*****************************************************************************
|
||||
*
|
||||
* This file is part of Mapnik (c++ mapping toolkit)
|
||||
@ -23,6 +22,7 @@
|
||||
|
||||
#include "pmtiles_datasource.hpp"
|
||||
#include "pmtiles_featureset.hpp"
|
||||
#include "pmtiles_file.hpp"
|
||||
#include "vector_tile_projection.hpp"
|
||||
#include <mapnik/geom_util.hpp>
|
||||
#include <mapnik/util/fs.hpp>
|
||||
@ -30,6 +30,7 @@
|
||||
#include <mapnik/datasource_plugin.hpp>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
|
||||
DATASOURCE_PLUGIN_IMPL(pmtiles_datasource_plugin, pmtiles_datasource);
|
||||
DATASOURCE_PLUGIN_EXPORT(pmtiles_datasource_plugin);
|
||||
@ -212,7 +213,7 @@ namespace {
|
||||
{
|
||||
for (std::int64_t zoom = 0; zoom < 19; ++zoom)
|
||||
{
|
||||
if (scale > scales[zoom]) return minzoom;
|
||||
if (scale > scales[zoom]) return std::min(zoom, minzoom);
|
||||
else if (scale < scales[zoom] && scale > scales[zoom + 1])
|
||||
{
|
||||
return std::min(zoom, maxzoom);
|
||||
@ -221,12 +222,23 @@ namespace {
|
||||
return maxzoom;
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::string> & pmtiles_datasource::tile_cache()
|
||||
{
|
||||
static thread_local std::unordered_map<std::string, std::string> vector_tile_cache;
|
||||
return vector_tile_cache;
|
||||
}
|
||||
|
||||
mapnik::featureset_ptr pmtiles_datasource::features(mapnik::query const& q) const
|
||||
{
|
||||
#ifdef MAPNIK_STATS
|
||||
mapnik::progress_timer __stats__(std::clog, "pmtiles_datasource::features");
|
||||
#endif
|
||||
|
||||
auto & vector_tile_cache = tile_cache();
|
||||
if (vector_tile_cache.size() > 32) vector_tile_cache.clear();
|
||||
std::cerr << "Address of Datasource:" << std::addressof(*this) << " cache size:" << vector_tile_cache.size() << std::endl;
|
||||
auto datasource_hash = std::hash<std::string>{}(database_path_);
|
||||
std::cerr << "vector_tile_cache address:" << std::addressof(vector_tile_cache) << std::endl;
|
||||
mapnik::box2d<double> const& box = q.get_bbox();
|
||||
std::cerr << "scale_denominator:" << q.scale_denominator() << std::endl;
|
||||
auto zoom = scale_to_zoom(q.scale_denominator(), minzoom_, maxzoom_);
|
||||
@ -237,7 +249,7 @@ mapnik::featureset_ptr pmtiles_datasource::features(mapnik::query const& q) cons
|
||||
{
|
||||
throw mapnik::datasource_exception("Failed to create memory mapping for " + database_path_);
|
||||
}
|
||||
return mapnik::featureset_ptr(new pmtiles_featureset(file_ptr_, context, zoom, box, layer_));
|
||||
return mapnik::featureset_ptr(new pmtiles_featureset(file_ptr_, context, zoom, box, layer_, vector_tile_cache, datasource_hash));
|
||||
}
|
||||
|
||||
mapnik::featureset_ptr pmtiles_datasource::features_at_point(mapnik::coord2d const& pt, double tol) const
|
||||
@ -248,5 +260,6 @@ mapnik::featureset_ptr pmtiles_datasource::features_at_point(mapnik::coord2d con
|
||||
|
||||
mapnik::filter_at_point filter(pt, tol);
|
||||
mapnik::context_ptr context = get_context_with_attributes();
|
||||
return mapnik::featureset_ptr(new pmtiles_featureset(file_ptr_, context, zoom_, filter.box_, layer_));
|
||||
auto datasource_hash = std::hash<std::string>{}(database_path_);
|
||||
return mapnik::featureset_ptr(new pmtiles_featureset(file_ptr_, context, zoom_, filter.box_, layer_, tile_cache(), datasource_hash));
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/*****************************************************************************
|
||||
*
|
||||
* This file is part of Mapnik (c++ mapping toolkit)
|
||||
@ -25,8 +24,6 @@
|
||||
#define PMTILES_DATASOURCE_HPP_
|
||||
|
||||
#include <mapnik/datasource.hpp>
|
||||
//#include <mapnik/featureset.hpp>
|
||||
#include "sqlite_connection.hpp"
|
||||
#include <mapnik/params.hpp>
|
||||
#include <mapnik/query.hpp>
|
||||
#include <mapnik/feature.hpp>
|
||||
@ -37,10 +34,18 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "pmtiles_file.hpp"
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
|
||||
DATASOURCE_PLUGIN_DEF(pmtiles_datasource_plugin, pmtiles);
|
||||
|
||||
namespace mapnik {
|
||||
|
||||
using zxy_type = std::tuple<std::uint8_t, std::uint32_t, std::uint32_t>;
|
||||
|
||||
class pmtiles_file; //fwd decl
|
||||
}
|
||||
|
||||
class pmtiles_datasource : public mapnik::datasource
|
||||
{
|
||||
public:
|
||||
@ -60,6 +65,7 @@ private:
|
||||
mapnik::context_ptr get_query_context(mapnik::query const& q) const;
|
||||
std::string database_path_;
|
||||
std::shared_ptr<mapnik::pmtiles_file> file_ptr_;
|
||||
static std::unordered_map<std::string, std::string> & tile_cache();
|
||||
public:
|
||||
mapnik::box2d<double> extent_;
|
||||
std::int64_t minzoom_ = 0;
|
||||
|
@ -1,9 +1,8 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/*****************************************************************************
|
||||
*
|
||||
* This file is part of Mapnik Vector Tile Plugin
|
||||
* This file is part of Mapnik (c++ mapping toolkit)
|
||||
*
|
||||
* Copyright (C) 2023 Geofabrik GmbH
|
||||
* Copyright (C) 2025 Artem Pavlenko
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@ -21,8 +20,10 @@
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#include "pmtiles_featureset.hpp"
|
||||
#include "vector_tile_compression.hpp"
|
||||
#include "pmtiles_file.hpp"
|
||||
#include <boost/format.hpp>
|
||||
#include <math.h>
|
||||
#if 0
|
||||
@ -35,13 +36,18 @@
|
||||
|
||||
pmtiles_featureset::pmtiles_featureset(std::shared_ptr<mapnik::pmtiles_file> file_ptr,
|
||||
mapnik::context_ptr const& ctx, const int zoom,
|
||||
mapnik::box2d<double> const& extent, std::string const& layer) :
|
||||
mapnik::box2d<double> const& extent, std::string const& layer,
|
||||
std::unordered_map<std::string, std::string> & vector_tile_cache,
|
||||
std::size_t datasource_hash)
|
||||
:
|
||||
file_ptr_(file_ptr),
|
||||
context_(ctx),
|
||||
zoom_(zoom),
|
||||
extent_(extent),
|
||||
layer_(layer),
|
||||
vector_tile_(nullptr)
|
||||
vector_tile_(nullptr),
|
||||
vector_tile_cache_(vector_tile_cache),
|
||||
datasource_hash_(datasource_hash)
|
||||
{
|
||||
int tile_count = 1 << zoom;
|
||||
constexpr double width = 2.0 * 6378137 * M_PI;
|
||||
@ -104,33 +110,46 @@ bool pmtiles_featureset::next_tile()
|
||||
bool pmtiles_featureset::open_tile()
|
||||
{
|
||||
auto tile = file_ptr_->get_tile(zoom_, x_, y_);
|
||||
std::cerr << layer_ << ":" << zoom_ << ":" << x_ << ":" << y_ << std::endl;
|
||||
|
||||
auto datasource_key = (boost::format("%1%-%2%-%3%-%4%") % datasource_hash_ % zoom_ % x_ % y_).str();
|
||||
auto itr = vector_tile_cache_.find(datasource_key);
|
||||
if (itr == vector_tile_cache_.end())
|
||||
{
|
||||
#if 0
|
||||
using namespace boost::iostreams;
|
||||
namespace io = boost::iostreams;
|
||||
filtering_istream in;
|
||||
if (mapnik::vector_tile_impl::is_gzip_compressed(file_ptr_->data() + tile.first, tile.second))
|
||||
{
|
||||
in.push(gzip_decompressor());
|
||||
}
|
||||
else if (mapnik::vector_tile_impl::is_zlib_compressed(file_ptr_->data() + tile.first, tile.second))
|
||||
{
|
||||
in.push(zlib_decompressor());
|
||||
}
|
||||
in.push(array_source(file_ptr_->data() + tile.first, tile.second));
|
||||
std::string buffer;
|
||||
io::copy(in, io::back_inserter(buffer));
|
||||
vector_tile_.reset(new mvt_io(std::move(buffer), context_, x_, y_, zoom_, layer_));
|
||||
using namespace boost::iostreams;
|
||||
namespace io = boost::iostreams;
|
||||
filtering_istream in;
|
||||
if (mapnik::vector_tile_impl::is_gzip_compressed(file_ptr_->data() + tile.first, tile.second))
|
||||
{
|
||||
in.push(gzip_decompressor());
|
||||
}
|
||||
else if (mapnik::vector_tile_impl::is_zlib_compressed(file_ptr_->data() + tile.first, tile.second))
|
||||
{
|
||||
in.push(zlib_decompressor());
|
||||
}
|
||||
in.push(array_source(file_ptr_->data() + tile.first, tile.second));
|
||||
std::string buffer;
|
||||
io::copy(in, io::back_inserter(buffer));
|
||||
vector_tile_.reset(new mvt_io(std::move(buffer), context_, x_, y_, zoom_, layer_));
|
||||
#else
|
||||
if (mapnik::vector_tile_impl::is_gzip_compressed(file_ptr_->data() + tile.first, tile.second) ||
|
||||
mapnik::vector_tile_impl::is_zlib_compressed(file_ptr_->data() + tile.first, tile.second))
|
||||
{
|
||||
std::string decompressed;
|
||||
mapnik::vector_tile_impl::zlib_decompress(file_ptr_->data() + tile.first, tile.second, decompressed);
|
||||
vector_tile_.reset(new mvt_io(std::move(decompressed), context_, x_, y_, zoom_, layer_));
|
||||
} else {
|
||||
vector_tile_.reset(new mvt_io(std::string(file_ptr_->data() + tile.first, tile.second), context_, x_, y_, zoom_, layer_));
|
||||
}
|
||||
std::cerr << "\e[41m" << layer_ << " - " << datasource_key << "\e[0m" << std::endl;
|
||||
if (mapnik::vector_tile_impl::is_gzip_compressed(file_ptr_->data() + tile.first, tile.second) ||
|
||||
mapnik::vector_tile_impl::is_zlib_compressed(file_ptr_->data() + tile.first, tile.second))
|
||||
{
|
||||
std::string decompressed;
|
||||
mapnik::vector_tile_impl::zlib_decompress(file_ptr_->data() + tile.first, tile.second, decompressed);
|
||||
vector_tile_cache_.emplace(datasource_key, decompressed);
|
||||
vector_tile_.reset(new mvt_io(std::move(decompressed), context_, x_, y_, zoom_, layer_));
|
||||
} else {
|
||||
vector_tile_.reset(new mvt_io(std::string(file_ptr_->data() + tile.first, tile.second), context_, x_, y_, zoom_, layer_));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "\e[42m" << layer_ << " - " << datasource_key << "\e[0m" << std::endl;
|
||||
std::string tile = itr->second;
|
||||
vector_tile_.reset(new mvt_io(std::move(tile), context_, x_, y_, zoom_, layer_));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/*****************************************************************************
|
||||
*
|
||||
* This file is part of Mapnik Vector Tile Plugin
|
||||
* This file is part of Mapnik (c++ mapping toolkit)
|
||||
*
|
||||
* Copyright (C) 2023 Geofabrik GmbH
|
||||
* Copyright (C) 2025 Artem Pavlenko
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@ -21,6 +20,7 @@
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef PMTILES_FEATURESET_HPP_
|
||||
#define PMTILES_FEATURESET_HPP_
|
||||
|
||||
@ -28,19 +28,24 @@
|
||||
// mapnik
|
||||
#include <mapnik/feature.hpp>
|
||||
#include <mapnik/datasource.hpp>
|
||||
// sqlite
|
||||
#include "sqlite_connection.hpp"
|
||||
#include "mvt_io.hpp"
|
||||
#include "pmtiles_file.hpp"
|
||||
|
||||
|
||||
namespace mapnik {
|
||||
class pmtiles_file; //fwd decl
|
||||
}
|
||||
|
||||
class pmtiles_featureset : public mapnik::Featureset
|
||||
{
|
||||
public:
|
||||
pmtiles_featureset(std::shared_ptr<mapnik::pmtiles_file> file_ptr,
|
||||
mapnik::context_ptr const& ctx,
|
||||
const int zoom,
|
||||
int const zoom,
|
||||
mapnik::box2d<double> const& extent,
|
||||
const std::string & layer);
|
||||
std::string const& layer,
|
||||
std::unordered_map<std::string,
|
||||
std::string> & vector_tile_cache,
|
||||
std::size_t datasource_hash);
|
||||
|
||||
virtual ~pmtiles_featureset();
|
||||
mapnik::feature_ptr next();
|
||||
@ -51,8 +56,9 @@ private:
|
||||
mapnik::context_ptr context_;
|
||||
int zoom_;
|
||||
mapnik::box2d<double> const& extent_;
|
||||
const std::string& layer_;
|
||||
std::string const& layer_;
|
||||
std::unique_ptr<mvt_io> vector_tile_;
|
||||
std::unordered_map<std::string, std::string> & vector_tile_cache_;
|
||||
int xmin_;
|
||||
int xmax_;
|
||||
int ymin_;
|
||||
@ -61,6 +67,7 @@ private:
|
||||
int x_ = 0;
|
||||
/// y index of the currently accessed tile
|
||||
int y_ = 0;
|
||||
std::size_t datasource_hash_;
|
||||
bool next_tile();
|
||||
bool open_tile();
|
||||
};
|
||||
|
@ -19,8 +19,9 @@
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*****************************************************************************/
|
||||
#ifndef PMTILES_FILE_HPP
|
||||
#define PMTILES_FILE_HPP
|
||||
|
||||
#ifndef MAPNIK_PMTILES_FILE_HPP
|
||||
#define MAPNIK_PMTILES_FILE_HPP
|
||||
|
||||
#include <mapnik/global.hpp>
|
||||
#define MAPNIK_MEMORY_MAPPED_FILE
|
||||
@ -33,9 +34,9 @@
|
||||
#include <fstream>
|
||||
|
||||
// boost
|
||||
#include <boost/iostreams/filtering_stream.hpp>
|
||||
#include <boost/iostreams/copy.hpp>
|
||||
#include <boost/iostreams/filter/gzip.hpp>
|
||||
//#include <boost/iostreams/filtering_stream.hpp>
|
||||
//#include <boost/iostreams/copy.hpp>
|
||||
//#include <boost/iostreams/filter/gzip.hpp>
|
||||
#include <boost/json.hpp>
|
||||
//#include <boost/format.hpp>
|
||||
#include "vector_tile_compression.hpp"
|
||||
@ -348,8 +349,7 @@ class pmtiles_file : public mapnik::util::mapped_memory_file
|
||||
char const* data_;
|
||||
bool check_valid() const
|
||||
{
|
||||
return (std::string(data_, data_ + 7) == "PMTiles" &&
|
||||
data_[7] == 0x3);
|
||||
return (std::string(data_, data_ + 7) == "PMTiles");
|
||||
}
|
||||
int version() const { return data_[7]; }
|
||||
std::uint64_t root_dir_offset() const
|
||||
@ -434,9 +434,7 @@ public:
|
||||
: mapped_memory_file(file_name)
|
||||
//: file_(file_name.c_str(), boost::interprocess::read_only),
|
||||
//region_(file_, boost::interprocess::read_only)
|
||||
{
|
||||
|
||||
}
|
||||
{}
|
||||
|
||||
~pmtiles_file() {}
|
||||
|
||||
@ -445,10 +443,9 @@ public:
|
||||
{
|
||||
header h(data());
|
||||
if (!h.check_valid())
|
||||
std::cerr << "FAIL" << std::endl;
|
||||
std::cerr << "PMTiles: invalid magic number" << std::endl;
|
||||
else
|
||||
{
|
||||
std::cerr << "Gotcha!" << std::endl;
|
||||
std::cerr << "Version:" << h.version() << std::endl;
|
||||
std::cerr << "Min zoom:" << h.min_zoom() << std::endl;
|
||||
std::cerr << "Max zoom:" << h.max_zoom() << std::endl;
|
||||
@ -472,16 +469,25 @@ public:
|
||||
header h(data());
|
||||
auto metadata_offset = h.metadata_offset();
|
||||
auto metadata_length = h.metadata_length();
|
||||
using namespace boost::iostreams;
|
||||
namespace io = boost::iostreams;
|
||||
//using namespace boost::iostreams;
|
||||
//namespace io = boost::iostreams;
|
||||
std::string metadata;
|
||||
filtering_istream in;
|
||||
|
||||
//filtering_istream in;
|
||||
//if (h.internal_compression() == compression_type::GZIP)
|
||||
//{
|
||||
// in.push(gzip_decompressor());
|
||||
//}
|
||||
//in.push(array_source(data() + metadata_offset, metadata_length));
|
||||
//io::copy(in, io::back_inserter(metadata));
|
||||
if (h.internal_compression() == compression_type::GZIP)
|
||||
{
|
||||
in.push(gzip_decompressor());
|
||||
mapnik::vector_tile_impl::zlib_decompress(data() + metadata_offset, metadata_length, metadata);
|
||||
}
|
||||
else
|
||||
{
|
||||
metadata = {data() + metadata_offset, metadata_length};
|
||||
}
|
||||
in.push(array_source(data() + metadata_offset, metadata_length));
|
||||
io::copy(in, io::back_inserter(metadata));
|
||||
boost::json::value json_value;
|
||||
try
|
||||
{
|
||||
@ -554,4 +560,4 @@ public:
|
||||
};
|
||||
}
|
||||
|
||||
#endif //PMTILES_FILE_HPP
|
||||
#endif //MAPNIK_PMTILES_FILE_HPP
|
||||
|
@ -1,69 +0,0 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
/**
|
||||
* Copyright (c) MapBox
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice, this
|
||||
* list of conditions and the following disclaimer in the documentation and/or
|
||||
* other materials provided with the distribution.
|
||||
* - Neither the name "MapBox" nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "vector_tile_compression.hpp"
|
||||
|
||||
// zlib
|
||||
#include <zlib.h>
|
||||
|
||||
// std
|
||||
#include <stdexcept>
|
||||
|
||||
// decodes both zlib and gzip
|
||||
// http://stackoverflow.com/a/1838702/2333354
|
||||
void mapnik::vector_tile_impl::zlib_decompress(const char * data, std::size_t size, std::string & output)
|
||||
{
|
||||
z_stream inflate_s;
|
||||
inflate_s.zalloc = Z_NULL;
|
||||
inflate_s.zfree = Z_NULL;
|
||||
inflate_s.opaque = Z_NULL;
|
||||
inflate_s.avail_in = 0;
|
||||
inflate_s.next_in = Z_NULL;
|
||||
inflateInit2(&inflate_s, 32 + 15);
|
||||
inflate_s.next_in = (Bytef *)data;
|
||||
inflate_s.avail_in = size;
|
||||
size_t length = 0;
|
||||
do {
|
||||
output.resize(length + 2 * size);
|
||||
inflate_s.avail_out = 2 * size;
|
||||
inflate_s.next_out = (Bytef *)(output.data() + length);
|
||||
int ret = inflate(&inflate_s, Z_FINISH);
|
||||
if (ret != Z_STREAM_END && ret != Z_OK && ret != Z_BUF_ERROR)
|
||||
{
|
||||
std::string error_msg = inflate_s.msg;
|
||||
inflateEnd(&inflate_s);
|
||||
throw std::runtime_error(error_msg);
|
||||
}
|
||||
|
||||
length += (2 * size - inflate_s.avail_out);
|
||||
} while (inflate_s.avail_out == 0);
|
||||
inflateEnd(&inflate_s);
|
||||
output.resize(length);
|
||||
}
|
||||
|
@ -1,69 +0,0 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
/**
|
||||
* Copyright (c) MapBox
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice, this
|
||||
* list of conditions and the following disclaimer in the documentation and/or
|
||||
* other materials provided with the distribution.
|
||||
* - Neither the name "MapBox" nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef VECTOR_TILE_COMPRESSION_HPP_
|
||||
#define VECTOR_TILE_COMPRESSION_HPP_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
// zlib
|
||||
#include <zlib.h>
|
||||
|
||||
namespace mapnik
|
||||
{
|
||||
|
||||
namespace vector_tile_impl
|
||||
{
|
||||
|
||||
inline bool is_zlib_compressed(const char * data, std::size_t size)
|
||||
{
|
||||
return size > 2 &&
|
||||
static_cast<uint8_t>(data[0]) == 0x78 &&
|
||||
(
|
||||
static_cast<uint8_t>(data[1]) == 0x9C ||
|
||||
static_cast<uint8_t>(data[1]) == 0x01 ||
|
||||
static_cast<uint8_t>(data[1]) == 0xDA ||
|
||||
static_cast<uint8_t>(data[1]) == 0x5E
|
||||
);
|
||||
}
|
||||
|
||||
inline bool is_gzip_compressed(const char * data, std::size_t size)
|
||||
{
|
||||
return size > 2 && static_cast<uint8_t>(data[0]) == 0x1F && static_cast<uint8_t>(data[1]) == 0x8B;
|
||||
}
|
||||
|
||||
// decodes both zlib and gzip
|
||||
// http://stackoverflow.com/a/1838702/2333354
|
||||
void zlib_decompress(const char * data, std::size_t size, std::string & output);
|
||||
|
||||
} // end ns vector_tile_impl
|
||||
|
||||
} // end ns mapnik
|
||||
|
||||
#endif /* VECTOR_TILE_COMPRESSION_HPP_ */
|
@ -1,67 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) MapBox
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice, this
|
||||
* list of conditions and the following disclaimer in the documentation and/or
|
||||
* other materials provided with the distribution.
|
||||
* - Neither the name "MapBox" nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// zlib
|
||||
#include <zlib.h>
|
||||
|
||||
// std
|
||||
#include <stdexcept>
|
||||
|
||||
#include "vector_tile_compression.hpp"
|
||||
|
||||
// decodes both zlib and gzip
|
||||
// http://stackoverflow.com/a/1838702/2333354
|
||||
void mapnik::vector_tile_impl::zlib_decompress(const char * data, std::size_t size, std::string & output)
|
||||
{
|
||||
z_stream inflate_s;
|
||||
inflate_s.zalloc = Z_NULL;
|
||||
inflate_s.zfree = Z_NULL;
|
||||
inflate_s.opaque = Z_NULL;
|
||||
inflate_s.avail_in = 0;
|
||||
inflate_s.next_in = Z_NULL;
|
||||
inflateInit2(&inflate_s, 32 + 15);
|
||||
inflate_s.next_in = (Bytef *)data;
|
||||
inflate_s.avail_in = size;
|
||||
size_t length = 0;
|
||||
do {
|
||||
output.resize(length + 2 * size);
|
||||
inflate_s.avail_out = 2 * size;
|
||||
inflate_s.next_out = (Bytef *)(output.data() + length);
|
||||
int ret = inflate(&inflate_s, Z_FINISH);
|
||||
if (ret != Z_STREAM_END && ret != Z_OK && ret != Z_BUF_ERROR)
|
||||
{
|
||||
std::string error_msg = inflate_s.msg;
|
||||
inflateEnd(&inflate_s);
|
||||
throw std::runtime_error(error_msg);
|
||||
}
|
||||
|
||||
length += (2 * size - inflate_s.avail_out);
|
||||
} while (inflate_s.avail_out == 0);
|
||||
inflateEnd(&inflate_s);
|
||||
output.resize(length);
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
/**
|
||||
* Copyright (c) MapBox
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice, this
|
||||
* list of conditions and the following disclaimer in the documentation and/or
|
||||
* other materials provided with the distribution.
|
||||
* - Neither the name "MapBox" nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "vector_tile_geometry_decoder.hpp"
|
||||
#include "vector_tile_geometry_decoder.ipp"
|
||||
|
||||
namespace mapnik
|
||||
{
|
||||
|
||||
namespace vector_tile_impl
|
||||
{
|
||||
|
||||
// decode geometry
|
||||
template mapnik::geometry::geometry<double> decode_geometry<double>(GeometryPBF & paths,
|
||||
mvt_message::geom_type geom_type,
|
||||
unsigned version,
|
||||
double tile_x,
|
||||
double tile_y,
|
||||
double scale_x,
|
||||
double scale_y);
|
||||
template mapnik::geometry::geometry<std::int64_t> decode_geometry<std::int64_t>(GeometryPBF & paths,
|
||||
mvt_message::geom_type geom_type,
|
||||
unsigned version,
|
||||
std::int64_t tile_x,
|
||||
std::int64_t tile_y,
|
||||
double scale_x,
|
||||
double scale_y);
|
||||
template mapnik::geometry::geometry<double> decode_geometry<double>(GeometryPBF & paths,
|
||||
mvt_message::geom_type geom_type,
|
||||
unsigned version,
|
||||
double tile_x,
|
||||
double tile_y,
|
||||
double scale_x,
|
||||
double scale_y,
|
||||
mapnik::box2d<double> const& bbox);
|
||||
template mapnik::geometry::geometry<std::int64_t> decode_geometry<std::int64_t>(GeometryPBF & paths,
|
||||
mvt_message::geom_type geom_type,
|
||||
unsigned version,
|
||||
std::int64_t tile_x,
|
||||
std::int64_t tile_y,
|
||||
double scale_x,
|
||||
double scale_y,
|
||||
mapnik::box2d<double> const& bbox);
|
||||
|
||||
} // end ns vector_tile_impl
|
||||
|
||||
} // end ns mapnik
|
@ -1,119 +0,0 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
/**
|
||||
* Copyright (c) MapBox
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice, this
|
||||
* list of conditions and the following disclaimer in the documentation and/or
|
||||
* other materials provided with the distribution.
|
||||
* - Neither the name "MapBox" nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef VECTOR_TILE_GEOMETRY_DECODER_HPP_
|
||||
#define VECTOR_TILE_GEOMETRY_DECODER_HPP_
|
||||
|
||||
//protozero
|
||||
#include <protozero/pbf_reader.hpp>
|
||||
|
||||
//mapnik
|
||||
#include <mapnik/geometry/box2d.hpp>
|
||||
#include <mapnik/geometry.hpp>
|
||||
#if defined(DEBUG)
|
||||
#include <mapnik/debug.hpp>
|
||||
#endif
|
||||
#include "mvt_message.hpp"
|
||||
|
||||
//std
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace mapnik
|
||||
{
|
||||
|
||||
namespace vector_tile_impl
|
||||
{
|
||||
|
||||
// NOTE: this object is for one-time use. Once you've progressed to the end
|
||||
// by calling next(), to re-iterate, you must construct a new object
|
||||
class GeometryPBF
|
||||
{
|
||||
public:
|
||||
using value_type = std::int64_t;
|
||||
using iterator_type = protozero::pbf_reader::const_uint32_iterator;
|
||||
using pbf_itr = protozero::iterator_range<iterator_type>;
|
||||
|
||||
explicit GeometryPBF(pbf_itr const& geo_iterator);
|
||||
|
||||
enum command : uint8_t
|
||||
{
|
||||
end = 0,
|
||||
move_to = 1,
|
||||
line_to = 2,
|
||||
close = 7
|
||||
};
|
||||
|
||||
uint32_t get_length() const
|
||||
{
|
||||
return length;
|
||||
}
|
||||
|
||||
command point_next(value_type & rx, value_type & ry);
|
||||
command line_next(value_type & rx, value_type & ry, bool skip_lineto_zero);
|
||||
command ring_next(value_type & rx, value_type & ry, bool skip_lineto_zero);
|
||||
|
||||
private:
|
||||
iterator_type geo_itr_;
|
||||
iterator_type geo_end_itr_;
|
||||
value_type x, y;
|
||||
value_type ox, oy;
|
||||
uint32_t length;
|
||||
uint8_t cmd;
|
||||
#if defined(DEBUG)
|
||||
public:
|
||||
bool already_had_error;
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename value_type>
|
||||
mapnik::geometry::geometry<value_type> decode_geometry(GeometryPBF & paths,
|
||||
mvt_message::geom_type geom_type,
|
||||
unsigned version,
|
||||
value_type tile_x,
|
||||
value_type tile_y,
|
||||
double scale_x,
|
||||
double scale_y,
|
||||
mapnik::box2d<double> const& bbox);
|
||||
|
||||
template <typename value_type>
|
||||
mapnik::geometry::geometry<value_type> decode_geometry(GeometryPBF & paths,
|
||||
mvt_message::geom_type geom_type,
|
||||
unsigned version,
|
||||
value_type tile_x,
|
||||
value_type tile_y,
|
||||
double scale_x,
|
||||
double scale_y);
|
||||
|
||||
} // end ns vector_tile_impl
|
||||
|
||||
} // end ns mapnik
|
||||
|
||||
#endif /* VECTOR_TILE_GEOMETRY_DECODER_HPP_ */
|
@ -1,877 +0,0 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
/**
|
||||
* Copyright (c) MapBox
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice, this
|
||||
* list of conditions and the following disclaimer in the documentation and/or
|
||||
* other materials provided with the distribution.
|
||||
* - Neither the name "MapBox" nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
//protozero
|
||||
#include <protozero/pbf_reader.hpp>
|
||||
|
||||
//mapnik
|
||||
#include <mapnik/geometry/box2d.hpp>
|
||||
#include <mapnik/geometry.hpp>
|
||||
#if defined(DEBUG)
|
||||
#include <mapnik/debug.hpp>
|
||||
#endif
|
||||
|
||||
//std
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace mapnik
|
||||
{
|
||||
|
||||
namespace vector_tile_impl
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename value_type>
|
||||
inline double calculate_segment_area(value_type const x0, value_type const y0, value_type const x1, value_type const y1)
|
||||
{
|
||||
return (static_cast<double>(x0) * static_cast<double>(y1)) - (static_cast<double>(y0) * static_cast<double>(x1));
|
||||
}
|
||||
|
||||
inline bool area_is_clockwise(double area)
|
||||
{
|
||||
return (area < 0.0);
|
||||
}
|
||||
|
||||
template <typename value_type>
|
||||
inline bool scaling_reversed_orientation(value_type const scale_x_, value_type const scale_y_)
|
||||
{
|
||||
return (scale_x_ * scale_y_) < 0;
|
||||
}
|
||||
|
||||
template <typename value_type>
|
||||
inline void move_cursor(value_type & x, value_type & y, std::int32_t dx, std::int32_t dy)
|
||||
{
|
||||
x += static_cast<value_type>(dx);
|
||||
y += static_cast<value_type>(dy);
|
||||
}
|
||||
|
||||
|
||||
template <typename value_type>
|
||||
inline value_type get_point_value(value_type const val,
|
||||
double const scale_val,
|
||||
double const tile_loc)
|
||||
{
|
||||
return (tile_loc + static_cast<value_type>(std::round(static_cast<double>(val) / scale_val)));
|
||||
}
|
||||
|
||||
template <>
|
||||
inline double get_point_value<double>(double const val,
|
||||
double const scale_val,
|
||||
double const tile_loc)
|
||||
{
|
||||
return tile_loc + (val / scale_val);
|
||||
}
|
||||
|
||||
constexpr std::size_t max_reserve()
|
||||
{
|
||||
// Based on int64_t geometry being 16 bytes in size and
|
||||
// maximum allocation size of 1 MB.
|
||||
return (1024 * 1024) / 16;
|
||||
}
|
||||
|
||||
template <typename geom_value_type>
|
||||
void decode_point(mapnik::geometry::geometry<geom_value_type> & geom,
|
||||
GeometryPBF & paths,
|
||||
geom_value_type const tile_x,
|
||||
geom_value_type const tile_y,
|
||||
double const scale_x,
|
||||
double const scale_y,
|
||||
mapnik::box2d<double> const& bbox)
|
||||
{
|
||||
typename GeometryPBF::command cmd;
|
||||
using pbf_value_type = GeometryPBF::value_type;
|
||||
pbf_value_type x1, y1;
|
||||
mapnik::geometry::multi_point<geom_value_type> mp;
|
||||
#if defined(DEBUG)
|
||||
std::uint32_t previous_len = 0;
|
||||
#endif
|
||||
// Find first moveto inside bbox and then reserve points from size of geometry.
|
||||
while (true)
|
||||
{
|
||||
cmd = paths.point_next(x1, y1);
|
||||
geom_value_type x1_ = get_point_value<geom_value_type>(x1, scale_x, tile_x);
|
||||
geom_value_type y1_ = get_point_value<geom_value_type>(y1, scale_y, tile_y);
|
||||
if (cmd == GeometryPBF::end)
|
||||
{
|
||||
geom = mapnik::geometry::geometry_empty();
|
||||
return;
|
||||
}
|
||||
else if (bbox.intersects(x1_, y1_))
|
||||
{
|
||||
#if defined(DEBUG)
|
||||
if (previous_len <= paths.get_length() && !paths.already_had_error)
|
||||
{
|
||||
MAPNIK_LOG_WARN(decode_point) << "warning: encountered POINT geometry that might have MOVETO commands repeated that could be fewer commands";
|
||||
paths.already_had_error = true;
|
||||
}
|
||||
previous_len = paths.get_length();
|
||||
#endif
|
||||
constexpr std::size_t max_size = max_reserve();
|
||||
if (paths.get_length() + 1 < max_size)
|
||||
{
|
||||
mp.reserve(paths.get_length() + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
mp.reserve(max_size);
|
||||
}
|
||||
mp.emplace_back(x1_, y1_);
|
||||
break;
|
||||
}
|
||||
}
|
||||
while ((cmd = paths.point_next(x1, y1)) != GeometryPBF::end)
|
||||
{
|
||||
#if defined(DEBUG)
|
||||
if (previous_len <= paths.get_length() && !paths.already_had_error)
|
||||
{
|
||||
MAPNIK_LOG_WARN(decode_point) << "warning: encountered POINT geometry that might have MOVETO commands repeated that could be fewer commands";
|
||||
paths.already_had_error = true;
|
||||
}
|
||||
previous_len = paths.get_length();
|
||||
#endif
|
||||
// TODO: consider profiling and trying to optimize this further
|
||||
// when all points are within the bbox filter then the `mp.reserve` should be
|
||||
// perfect, but when some points are thrown out we will allocate more than needed
|
||||
// the "all points intersect" case I think is going to be more common/important
|
||||
// however worth a future look to see if the "some or few points intersect" can be optimized
|
||||
geom_value_type x1_ = get_point_value<geom_value_type>(x1, scale_x, tile_x);
|
||||
geom_value_type y1_ = get_point_value<geom_value_type>(y1, scale_y, tile_y);
|
||||
if (!bbox.intersects(x1_, y1_))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
mp.emplace_back(x1_, y1_);
|
||||
}
|
||||
std::size_t num_points = mp.size();
|
||||
if (num_points == 0)
|
||||
{
|
||||
geom = mapnik::geometry::geometry_empty();
|
||||
}
|
||||
else if (num_points == 1)
|
||||
{
|
||||
geom = std::move(mp[0]);
|
||||
}
|
||||
else if (num_points > 1)
|
||||
{
|
||||
// return multipoint
|
||||
geom = std::move(mp);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename geom_value_type>
|
||||
void decode_linestring(mapnik::geometry::geometry<geom_value_type> & geom,
|
||||
GeometryPBF & paths,
|
||||
geom_value_type const tile_x,
|
||||
geom_value_type const tile_y,
|
||||
double scale_x,
|
||||
double scale_y,
|
||||
mapnik::box2d<double> const& bbox,
|
||||
unsigned version)
|
||||
{
|
||||
using pbf_value_type = GeometryPBF::value_type;
|
||||
typename GeometryPBF::command cmd;
|
||||
pbf_value_type x0, y0;
|
||||
pbf_value_type x1, y1;
|
||||
geom_value_type x0_, y0_;
|
||||
geom_value_type x1_, y1_;
|
||||
mapnik::geometry::multi_line_string<geom_value_type> multi_line;
|
||||
#if defined(DEBUG)
|
||||
std::uint32_t previous_len = 0;
|
||||
#endif
|
||||
mapnik::box2d<double> part_env;
|
||||
cmd = paths.line_next(x0, y0, false);
|
||||
if (cmd == GeometryPBF::end)
|
||||
{
|
||||
geom = mapnik::geometry::geometry_empty();
|
||||
return;
|
||||
}
|
||||
else if (cmd != GeometryPBF::move_to)
|
||||
{
|
||||
throw std::runtime_error("Vector Tile has LINESTRING type geometry where the first command is not MOVETO.");
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
cmd = paths.line_next(x1, y1, true);
|
||||
if (cmd != GeometryPBF::line_to)
|
||||
{
|
||||
if (cmd == GeometryPBF::move_to)
|
||||
{
|
||||
if (version == 1)
|
||||
{
|
||||
// Version 1 of the spec wasn't clearly defined and therefore
|
||||
// we shouldn't be strict on the reading of a tile that has two
|
||||
// moveto commands that are repeated, lets ignore the previous moveto.
|
||||
x0 = x1;
|
||||
y0 = y1;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Vector Tile has LINESTRING type geometry with repeated MOVETO commands.");
|
||||
}
|
||||
}
|
||||
else //cmd == GeometryPBF::end
|
||||
{
|
||||
if (version == 1)
|
||||
{
|
||||
// Version 1 of the spec wasn't clearly defined and therefore
|
||||
// we shouldn't be strict on the reading of a tile that has only a moveto
|
||||
// command. So lets just ignore this moveto command.
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Vector Tile has LINESTRING type geometry with a MOVETO command with no LINETO following.");
|
||||
}
|
||||
}
|
||||
}
|
||||
// add fresh line
|
||||
multi_line.emplace_back();
|
||||
auto & line = multi_line.back();
|
||||
// reserve prior
|
||||
constexpr std::size_t max_size = max_reserve();
|
||||
if (paths.get_length() + 2 < max_size)
|
||||
{
|
||||
line.reserve(paths.get_length() + 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
line.reserve(max_size);
|
||||
}
|
||||
// add moveto command position
|
||||
x0_ = get_point_value<geom_value_type>(x0, scale_x, tile_x);
|
||||
y0_ = get_point_value<geom_value_type>(y0, scale_y, tile_y);
|
||||
line.emplace_back(x0_, y0_);
|
||||
part_env.init(x0_, y0_, x0_, y0_);
|
||||
// add first lineto
|
||||
x1_ = get_point_value<geom_value_type>(x1, scale_x, tile_x);
|
||||
y1_ = get_point_value<geom_value_type>(y1, scale_y, tile_y);
|
||||
line.emplace_back(x1_, y1_);
|
||||
part_env.expand_to_include(x1_, y1_);
|
||||
#if defined(DEBUG)
|
||||
previous_len = paths.get_length();
|
||||
#endif
|
||||
while ((cmd = paths.line_next(x1, y1, true)) == GeometryPBF::line_to)
|
||||
{
|
||||
x1_ = get_point_value<geom_value_type>(x1, scale_x, tile_x);
|
||||
y1_ = get_point_value<geom_value_type>(y1, scale_y, tile_y);
|
||||
line.emplace_back(x1_, y1_);
|
||||
part_env.expand_to_include(x1_, y1_);
|
||||
#if defined(DEBUG)
|
||||
if (previous_len <= paths.get_length() && !paths.already_had_error)
|
||||
{
|
||||
MAPNIK_LOG_WARN(decode_linestring) << "warning: encountered LINESTRING geometry that might have LINETO commands repeated that could be fewer commands";
|
||||
paths.already_had_error = true;
|
||||
}
|
||||
previous_len = paths.get_length();
|
||||
#endif
|
||||
}
|
||||
if (!bbox.intersects(part_env))
|
||||
{
|
||||
// remove last linestring
|
||||
multi_line.pop_back();
|
||||
}
|
||||
if (cmd == GeometryPBF::end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
// else we are guaranteed it is a moveto
|
||||
x0 = x1;
|
||||
y0 = y1;
|
||||
}
|
||||
|
||||
std::size_t num_lines = multi_line.size();
|
||||
if (num_lines == 0)
|
||||
{
|
||||
geom = mapnik::geometry::geometry_empty();
|
||||
}
|
||||
else if (num_lines == 1)
|
||||
{
|
||||
auto itr = std::make_move_iterator(multi_line.begin());
|
||||
if (itr->size() > 1)
|
||||
{
|
||||
geom = std::move(*itr);
|
||||
}
|
||||
else
|
||||
{
|
||||
geom = mapnik::geometry::geometry_empty();
|
||||
}
|
||||
}
|
||||
else if (num_lines > 1)
|
||||
{
|
||||
geom = std::move(multi_line);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename geom_value_type>
|
||||
void decode_polygon(mapnik::geometry::geometry<geom_value_type> & geom,
|
||||
GeometryPBF & paths,
|
||||
geom_value_type const tile_x,
|
||||
geom_value_type const tile_y,
|
||||
double scale_x,
|
||||
double scale_y,
|
||||
mapnik::box2d<double> const& bbox,
|
||||
unsigned version)
|
||||
{
|
||||
typename GeometryPBF::command cmd;
|
||||
using pbf_value_type = GeometryPBF::value_type;
|
||||
pbf_value_type x0, y0;
|
||||
pbf_value_type x1, y1;
|
||||
pbf_value_type x2, y2;
|
||||
geom_value_type x0_, y0_;
|
||||
geom_value_type x1_, y1_;
|
||||
geom_value_type x2_, y2_;
|
||||
#if defined(DEBUG)
|
||||
std::uint32_t previous_len;
|
||||
#endif
|
||||
double ring_area = 0.0;
|
||||
bool first_ring = true;
|
||||
bool first_ring_is_clockwise = false;
|
||||
bool last_exterior_not_included = false;
|
||||
std::vector<mapnik::geometry::linear_ring<geom_value_type> > rings;
|
||||
std::vector<bool> rings_exterior;
|
||||
mapnik::box2d<double> part_env;
|
||||
cmd = paths.ring_next(x0, y0, false);
|
||||
if (cmd == GeometryPBF::end)
|
||||
{
|
||||
geom = mapnik::geometry::geometry_empty();
|
||||
return;
|
||||
}
|
||||
else if (cmd != GeometryPBF::move_to)
|
||||
{
|
||||
throw std::runtime_error("Vector Tile has POLYGON type geometry where the first command is not MOVETO.");
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
cmd = paths.ring_next(x1, y1, true);
|
||||
if (cmd != GeometryPBF::line_to)
|
||||
{
|
||||
if (cmd == GeometryPBF::close && version == 1)
|
||||
{
|
||||
// Version 1 of the specification was not clear on the command requirements for polygons
|
||||
// lets just to recover from this situation.
|
||||
cmd = paths.ring_next(x0, y0, false);
|
||||
if (cmd == GeometryPBF::end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (cmd == GeometryPBF::move_to)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (cmd == GeometryPBF::close)
|
||||
{
|
||||
throw std::runtime_error("Vector Tile has POLYGON type geometry where a CLOSE is followed by a CLOSE.");
|
||||
}
|
||||
else // cmd == GeometryPBF::line_to
|
||||
{
|
||||
throw std::runtime_error("Vector Tile has POLYGON type geometry where a CLOSE is followed by a LINETO.");
|
||||
}
|
||||
}
|
||||
else // cmd == end || cmd == move_to
|
||||
{
|
||||
throw std::runtime_error("Vector Tile has POLYGON type geometry with a MOVETO command with out at least two LINETOs and CLOSE following.");
|
||||
}
|
||||
}
|
||||
#if defined(DEBUG)
|
||||
previous_len = paths.get_length();
|
||||
#endif
|
||||
cmd = paths.ring_next(x2, y2, true);
|
||||
if (cmd != GeometryPBF::line_to)
|
||||
{
|
||||
if (cmd == GeometryPBF::close && version == 1)
|
||||
{
|
||||
// Version 1 of the specification was not clear on the command requirements for polygons
|
||||
// lets just to recover from this situation.
|
||||
cmd = paths.ring_next(x0, y0, false);
|
||||
if (cmd == GeometryPBF::end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (cmd == GeometryPBF::move_to)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (cmd == GeometryPBF::close)
|
||||
{
|
||||
throw std::runtime_error("Vector Tile has POLYGON type geometry where a CLOSE is followed by a CLOSE.");
|
||||
}
|
||||
else // cmd == GeometryPBF::line_to
|
||||
{
|
||||
throw std::runtime_error("Vector Tile has POLYGON type geometry where a CLOSE is followed by a LINETO.");
|
||||
}
|
||||
}
|
||||
else // cmd == end || cmd == move_to
|
||||
{
|
||||
throw std::runtime_error("Vector Tile has POLYGON type geometry with a MOVETO command with out at least two LINETOs and CLOSE following.");
|
||||
}
|
||||
}
|
||||
// add new ring to start adding to
|
||||
rings.emplace_back();
|
||||
auto & ring = rings.back();
|
||||
// reserve prior
|
||||
constexpr std::size_t max_size = max_reserve();
|
||||
if (paths.get_length() + 4 < max_size)
|
||||
{
|
||||
ring.reserve(paths.get_length() + 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
ring.reserve(max_size);
|
||||
}
|
||||
// add moveto command position
|
||||
x0_ = get_point_value<geom_value_type>(x0, scale_x, tile_x);
|
||||
y0_ = get_point_value<geom_value_type>(y0, scale_y, tile_y);
|
||||
ring.emplace_back(x0_, y0_);
|
||||
part_env.init(x0_, y0_, x0_, y0_);
|
||||
// add first lineto
|
||||
x1_ = get_point_value<geom_value_type>(x1, scale_x, tile_x);
|
||||
y1_ = get_point_value<geom_value_type>(y1, scale_y, tile_y);
|
||||
ring.emplace_back(x1_, y1_);
|
||||
part_env.expand_to_include(x1_, y1_);
|
||||
ring_area += calculate_segment_area(x0, y0, x1, y1);
|
||||
// add second lineto
|
||||
x2_ = get_point_value<geom_value_type>(x2, scale_x, tile_x);
|
||||
y2_ = get_point_value<geom_value_type>(y2, scale_y, tile_y);
|
||||
ring.emplace_back(x2_, y2_);
|
||||
part_env.expand_to_include(x2_, y2_);
|
||||
ring_area += calculate_segment_area(x1, y1, x2, y2);
|
||||
x1 = x2;
|
||||
y1 = y2;
|
||||
#if defined(DEBUG)
|
||||
if (previous_len <= paths.get_length() && !paths.already_had_error)
|
||||
{
|
||||
MAPNIK_LOG_WARN(read_rings) << "warning: encountered POLYGON geometry that might have LINETO commands repeated that could be fewer commands";
|
||||
paths.already_had_error = true;
|
||||
}
|
||||
previous_len = paths.get_length();
|
||||
#endif
|
||||
while ((cmd = paths.ring_next(x2, y2, true)) == GeometryPBF::line_to)
|
||||
{
|
||||
x2_ = get_point_value<geom_value_type>(x2, scale_x, tile_x);
|
||||
y2_ = get_point_value<geom_value_type>(y2, scale_y, tile_y);
|
||||
ring.emplace_back(x2_, y2_);
|
||||
part_env.expand_to_include(x2_, y2_);
|
||||
ring_area += calculate_segment_area(x1, y1, x2, y2);
|
||||
x1 = x2;
|
||||
y1 = y2;
|
||||
#if defined(DEBUG)
|
||||
if (previous_len <= paths.get_length() && !paths.already_had_error)
|
||||
{
|
||||
MAPNIK_LOG_WARN(read_rings) << "warning: encountered POLYGON geometry that might have LINETO commands repeated that could be fewer commands";
|
||||
paths.already_had_error = true;
|
||||
}
|
||||
previous_len = paths.get_length();
|
||||
#endif
|
||||
}
|
||||
// Make sure we are now on a close command
|
||||
if (cmd != GeometryPBF::close)
|
||||
{
|
||||
throw std::runtime_error("Vector Tile has POLYGON type geometry with a ring not closed by a CLOSE command.");
|
||||
}
|
||||
if (ring.back().x != x0_ || ring.back().y != y0_)
|
||||
{
|
||||
// If the previous lineto didn't already close the polygon (WHICH IT SHOULD NOT)
|
||||
// close out the polygon ring.
|
||||
ring.emplace_back(x0_, y0_);
|
||||
ring_area += calculate_segment_area(x1, y1, x0, y0);
|
||||
}
|
||||
if (ring.size() > 3)
|
||||
{
|
||||
if (first_ring)
|
||||
{
|
||||
first_ring_is_clockwise = area_is_clockwise(ring_area);
|
||||
if (version != 1 && first_ring_is_clockwise)
|
||||
{
|
||||
throw std::runtime_error("Vector Tile has POLYGON with first ring clockwise. It is not valid according to v2 of VT spec.");
|
||||
}
|
||||
first_ring = false;
|
||||
}
|
||||
bool is_exterior = (first_ring_is_clockwise == area_is_clockwise(ring_area));
|
||||
if ((!is_exterior && last_exterior_not_included) || !bbox.intersects(part_env))
|
||||
{
|
||||
// remove last linestring
|
||||
if (is_exterior)
|
||||
{
|
||||
last_exterior_not_included = true;
|
||||
}
|
||||
rings.pop_back();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_exterior)
|
||||
{
|
||||
last_exterior_not_included = false;
|
||||
}
|
||||
rings_exterior.push_back(is_exterior);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rings.pop_back();
|
||||
}
|
||||
ring_area = 0.0;
|
||||
|
||||
cmd = paths.ring_next(x0, y0, false);
|
||||
if (cmd == GeometryPBF::end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (cmd != GeometryPBF::move_to)
|
||||
{
|
||||
if (cmd == GeometryPBF::close)
|
||||
{
|
||||
throw std::runtime_error("Vector Tile has POLYGON type geometry where a CLOSE is followed by a CLOSE.");
|
||||
}
|
||||
else // cmd == GeometryPBF::line_to
|
||||
{
|
||||
throw std::runtime_error("Vector Tile has POLYGON type geometry where a CLOSE is followed by a LINETO.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rings.size() == 0)
|
||||
{
|
||||
geom = mapnik::geometry::geometry_empty();
|
||||
return;
|
||||
}
|
||||
|
||||
bool reverse_rings = (scaling_reversed_orientation(scale_x, scale_y) != first_ring_is_clockwise);
|
||||
auto rings_itr = std::make_move_iterator(rings.begin());
|
||||
auto rings_end = std::make_move_iterator(rings.end());
|
||||
mapnik::geometry::multi_polygon<geom_value_type> multi_poly;
|
||||
for (std::size_t i = 0; rings_itr != rings_end; ++rings_itr,++i)
|
||||
{
|
||||
if (rings_exterior[i]) multi_poly.emplace_back();
|
||||
auto & poly = multi_poly.back();
|
||||
if (reverse_rings)
|
||||
{
|
||||
std::reverse(rings_itr->begin(), rings_itr->end());
|
||||
}
|
||||
poly.push_back(std::move(*rings_itr));
|
||||
}
|
||||
auto num_poly = multi_poly.size();
|
||||
if (num_poly == 1)
|
||||
{
|
||||
auto itr = std::make_move_iterator(multi_poly.begin());
|
||||
geom = std::move(*itr);
|
||||
}
|
||||
else
|
||||
{
|
||||
geom = std::move(multi_poly);
|
||||
}
|
||||
}
|
||||
|
||||
} // end ns detail
|
||||
|
||||
GeometryPBF::GeometryPBF(pbf_itr const& geo_iterator)
|
||||
: geo_itr_(geo_iterator.begin()),
|
||||
geo_end_itr_(geo_iterator.end()),
|
||||
x(0),
|
||||
y(0),
|
||||
ox(0),
|
||||
oy(0),
|
||||
length(0),
|
||||
cmd(move_to)
|
||||
{
|
||||
#if defined(DEBUG)
|
||||
already_had_error = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
typename GeometryPBF::command GeometryPBF::point_next(value_type & rx, value_type & ry)
|
||||
{
|
||||
if (length == 0)
|
||||
{
|
||||
if (geo_itr_ != geo_end_itr_)
|
||||
{
|
||||
uint32_t cmd_length = static_cast<uint32_t>(*geo_itr_++);
|
||||
cmd = cmd_length & 0x7;
|
||||
length = cmd_length >> 3;
|
||||
if (cmd == move_to)
|
||||
{
|
||||
if (length == 0)
|
||||
{
|
||||
throw std::runtime_error("Vector Tile has POINT geometry with a MOVETO command that has a command count of zero");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cmd == line_to)
|
||||
{
|
||||
throw std::runtime_error("Vector Tile has POINT type geometry with a LINETO command.");
|
||||
}
|
||||
else if (cmd == close)
|
||||
{
|
||||
throw std::runtime_error("Vector Tile has POINT type geometry with a CLOSE command.");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Vector Tile has POINT type geometry with an unknown command.");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return end;
|
||||
}
|
||||
}
|
||||
|
||||
--length;
|
||||
// It is possible for the next to lines to throw because we can not check the length
|
||||
// of the buffer to ensure that it is long enough.
|
||||
// If an exception occurs it will likely be a end_of_buffer_exception with the text:
|
||||
// "end of buffer exception"
|
||||
// While this error message is not verbose a try catch here would slow down processing.
|
||||
int32_t dx = protozero::decode_zigzag32(static_cast<uint32_t>(*geo_itr_++));
|
||||
int32_t dy = protozero::decode_zigzag32(static_cast<uint32_t>(*geo_itr_++));
|
||||
detail::move_cursor(x, y, dx, dy);
|
||||
rx = x;
|
||||
ry = y;
|
||||
return move_to;
|
||||
}
|
||||
|
||||
typename GeometryPBF::command GeometryPBF::line_next(value_type & rx,
|
||||
value_type & ry,
|
||||
bool skip_lineto_zero)
|
||||
{
|
||||
if (length == 0)
|
||||
{
|
||||
if (geo_itr_ != geo_end_itr_)
|
||||
{
|
||||
uint32_t cmd_length = static_cast<uint32_t>(*geo_itr_++);
|
||||
cmd = cmd_length & 0x7;
|
||||
length = cmd_length >> 3;
|
||||
if (cmd == move_to)
|
||||
{
|
||||
if (length != 1)
|
||||
{
|
||||
throw std::runtime_error("Vector Tile has LINESTRING with a MOVETO command that is given more then one pair of parameters or not enough parameters are provided");
|
||||
}
|
||||
--length;
|
||||
// It is possible for the next to lines to throw because we can not check the length
|
||||
// of the buffer to ensure that it is long enough.
|
||||
// If an exception occurs it will likely be a end_of_buffer_exception with the text:
|
||||
// "end of buffer exception"
|
||||
// While this error message is not verbose a try catch here would slow down processing.
|
||||
int32_t dx = protozero::decode_zigzag32(static_cast<uint32_t>(*geo_itr_++));
|
||||
int32_t dy = protozero::decode_zigzag32(static_cast<uint32_t>(*geo_itr_++));
|
||||
detail::move_cursor(x, y, dx, dy);
|
||||
rx = x;
|
||||
ry = y;
|
||||
return move_to;
|
||||
}
|
||||
else if (cmd == line_to)
|
||||
{
|
||||
if (length == 0)
|
||||
{
|
||||
throw std::runtime_error("Vector Tile has geometry with LINETO command that is not followed by a proper number of parameters");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cmd == close)
|
||||
{
|
||||
throw std::runtime_error("Vector Tile has LINESTRING type geometry with a CLOSE command.");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Vector Tile has LINESTRING type geometry with an unknown command.");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return end;
|
||||
}
|
||||
}
|
||||
|
||||
--length;
|
||||
// It is possible for the next to lines to throw because we can not check the length
|
||||
// of the buffer to ensure that it is long enough.
|
||||
// If an exception occurs it will likely be a end_of_buffer_exception with the text:
|
||||
// "end of buffer exception"
|
||||
// While this error message is not verbose a try catch here would slow down processing.
|
||||
int32_t dx = protozero::decode_zigzag32(static_cast<uint32_t>(*geo_itr_++));
|
||||
int32_t dy = protozero::decode_zigzag32(static_cast<uint32_t>(*geo_itr_++));
|
||||
if (skip_lineto_zero && dx == 0 && dy == 0)
|
||||
{
|
||||
// We are going to skip this vertex as the point doesn't move call line_next again
|
||||
return line_next(rx, ry, true);
|
||||
}
|
||||
detail::move_cursor(x, y, dx, dy);
|
||||
rx = x;
|
||||
ry = y;
|
||||
return line_to;
|
||||
}
|
||||
|
||||
typename GeometryPBF::command GeometryPBF::ring_next(value_type & rx,
|
||||
value_type & ry,
|
||||
bool skip_lineto_zero)
|
||||
{
|
||||
if (length == 0)
|
||||
{
|
||||
if (geo_itr_ != geo_end_itr_)
|
||||
{
|
||||
uint32_t cmd_length = static_cast<uint32_t>(*geo_itr_++);
|
||||
cmd = cmd_length & 0x7;
|
||||
length = cmd_length >> 3;
|
||||
if (cmd == move_to)
|
||||
{
|
||||
if (length != 1)
|
||||
{
|
||||
throw std::runtime_error("Vector Tile has POLYGON with a MOVETO command that is given more then one pair of parameters or not enough parameters are provided");
|
||||
}
|
||||
--length;
|
||||
// It is possible for the next two lines to throw because we can not check the length
|
||||
// of the buffer to ensure that it is long enough.
|
||||
// If an exception occurs it will likely be a end_of_buffer_exception with the text:
|
||||
// "end of buffer exception"
|
||||
// While this error message is not verbose a try catch here would slow down processing.
|
||||
int32_t dx = protozero::decode_zigzag32(static_cast<uint32_t>(*geo_itr_++));
|
||||
int32_t dy = protozero::decode_zigzag32(static_cast<uint32_t>(*geo_itr_++));
|
||||
detail::move_cursor(x, y, dx, dy);
|
||||
rx = x;
|
||||
ry = y;
|
||||
ox = x;
|
||||
oy = y;
|
||||
return move_to;
|
||||
}
|
||||
else if (cmd == line_to)
|
||||
{
|
||||
if (length == 0)
|
||||
{
|
||||
throw std::runtime_error("Vector Tile has geometry with LINETO command that is not followed by a proper number of parameters");
|
||||
}
|
||||
}
|
||||
else if (cmd == close)
|
||||
{
|
||||
// Just set length in case a close command provides an invalid number here.
|
||||
// While we could throw because V2 of the spec declares it incorrect, this is not
|
||||
// difficult to fix and has no effect on the results.
|
||||
length = 0;
|
||||
rx = ox;
|
||||
ry = oy;
|
||||
return close;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Vector Tile has POLYGON type geometry with an unknown command.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return end;
|
||||
}
|
||||
}
|
||||
|
||||
--length;
|
||||
// It is possible for the next to lines to throw because we can not check the length
|
||||
// of the buffer to ensure that it is long enough.
|
||||
// If an exception occurs it will likely be a end_of_buffer_exception with the text:
|
||||
// "end of buffer exception"
|
||||
// While this error message is not verbose a try catch here would slow down processing.
|
||||
int32_t dx = protozero::decode_zigzag32(static_cast<uint32_t>(*geo_itr_++));
|
||||
int32_t dy = protozero::decode_zigzag32(static_cast<uint32_t>(*geo_itr_++));
|
||||
if (skip_lineto_zero && dx == 0 && dy == 0)
|
||||
{
|
||||
// We are going to skip this vertex as the point doesn't move call ring_next again
|
||||
return ring_next(rx, ry, true);
|
||||
}
|
||||
detail::move_cursor(x, y, dx, dy);
|
||||
rx = x;
|
||||
ry = y;
|
||||
return line_to;
|
||||
}
|
||||
|
||||
template <typename value_type>
|
||||
mapnik::geometry::geometry<value_type> decode_geometry(GeometryPBF & paths,
|
||||
mvt_message::geom_type geom_type,
|
||||
unsigned version,
|
||||
value_type tile_x,
|
||||
value_type tile_y,
|
||||
double scale_x,
|
||||
double scale_y,
|
||||
mapnik::box2d<double> const& bbox)
|
||||
{
|
||||
mapnik::geometry::geometry<value_type> geom; // output geometry
|
||||
switch (geom_type)
|
||||
{
|
||||
case mvt_message::geom_type::point:
|
||||
{
|
||||
detail::decode_point<value_type>(geom, paths, tile_x, tile_y, scale_x, scale_y, bbox);
|
||||
break;
|
||||
}
|
||||
case mvt_message::geom_type::linestring:
|
||||
{
|
||||
detail::decode_linestring<value_type>(geom, paths, tile_x, tile_y, scale_x, scale_y, bbox, version);
|
||||
break;
|
||||
}
|
||||
case mvt_message::geom_type::polygon:
|
||||
{
|
||||
detail::decode_polygon<value_type>(geom, paths, tile_x, tile_y, scale_x, scale_y, bbox, version);
|
||||
break;
|
||||
}
|
||||
case mvt_message::geom_type::unknown:
|
||||
default:
|
||||
{
|
||||
// This was changed to not throw as unknown according to v2 of spec can simply be ignored and doesn't require
|
||||
// it failing the processing
|
||||
geom = mapnik::geometry::geometry_empty();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return geom;
|
||||
}
|
||||
|
||||
template <typename value_type>
|
||||
mapnik::geometry::geometry<value_type> decode_geometry(GeometryPBF & paths,
|
||||
mvt_message::geom_type geom_type,
|
||||
unsigned version,
|
||||
value_type tile_x,
|
||||
value_type tile_y,
|
||||
double scale_x,
|
||||
double scale_y)
|
||||
{
|
||||
mapnik::box2d<double> bbox(std::numeric_limits<double>::lowest(),
|
||||
std::numeric_limits<double>::lowest(),
|
||||
std::numeric_limits<double>::max(),
|
||||
std::numeric_limits<double>::max());
|
||||
return decode_geometry<value_type>(paths, geom_type, version, tile_x, tile_y, scale_x, scale_y, bbox);
|
||||
}
|
||||
|
||||
} // end ns vector_tile_impl
|
||||
|
||||
} // end ns mapnik
|
@ -1,60 +0,0 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
/**
|
||||
* Copyright (c) MapBox
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice, this
|
||||
* list of conditions and the following disclaimer in the documentation and/or
|
||||
* other materials provided with the distribution.
|
||||
* - Neither the name "MapBox" nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __MAPNIK_VECTOR_TILE_PROJECTION_H__
|
||||
#define __MAPNIK_VECTOR_TILE_PROJECTION_H__
|
||||
|
||||
// mapnik
|
||||
#include <mapnik/geometry/box2d.hpp>
|
||||
#include <mapnik/well_known_srs.hpp>
|
||||
|
||||
namespace mapnik
|
||||
{
|
||||
|
||||
namespace vector_tile_impl
|
||||
{
|
||||
|
||||
inline mapnik::box2d<double> tile_mercator_bbox(std::uint64_t x,
|
||||
std::uint64_t y,
|
||||
std::uint64_t z)
|
||||
{
|
||||
const double half_of_equator = M_PI * EARTH_RADIUS;
|
||||
const double tile_size = 2.0 * half_of_equator / (1ull << z);
|
||||
double minx = -half_of_equator + x * tile_size;
|
||||
double miny = half_of_equator - (y + 1.0) * tile_size;
|
||||
double maxx = -half_of_equator + (x + 1.0) * tile_size;
|
||||
double maxy = half_of_equator - y * tile_size;
|
||||
return mapnik::box2d<double>(minx,miny,maxx,maxy);
|
||||
}
|
||||
|
||||
} // end vector_tile_impl ns
|
||||
|
||||
} // end mapnik ns
|
||||
|
||||
#endif // __MAPNIK_VECTOR_TILE_PROJECTION_H__
|
Reference in New Issue
Block a user