Add proj_transform caching to minimise expensive initialisations calls in libproj >= 6 [WIP]

This commit is contained in:
Artem Pavlenko 2021-01-27 09:40:28 +00:00
parent db9829d702
commit 5c086b0cd5
5 changed files with 94 additions and 45 deletions

View file

@ -255,8 +255,18 @@ void feature_style_processor<Processor>::prepare_layer(layer_rendering_material
} }
processor_context_ptr current_ctx = ds->get_context(ctx_map); processor_context_ptr current_ctx = ds->get_context(ctx_map);
proj_transform prj_trans(mat.proj0_,mat.proj1_); std::string key = mat.proj0_.params() + mat.proj1_.params();
auto itr = m_.proj_cache().find(key);
proj_transform * proj_trans_ptr;
if (itr == m_.proj_cache().end())
{
proj_trans_ptr = m_.proj_cache().emplace(key,
std::make_unique<proj_transform>(mat.proj0_, mat.proj1_)).first->second.get();
}
else
{
proj_trans_ptr = itr->second.get();
}
box2d<double> query_ext = extent; // unbuffered box2d<double> query_ext = extent; // unbuffered
box2d<double> buffered_query_ext(query_ext); // buffered box2d<double> buffered_query_ext(query_ext); // buffered
@ -286,22 +296,22 @@ void feature_style_processor<Processor>::prepare_layer(layer_rendering_material
bool early_return = false; bool early_return = false;
// first, try intersection of map extent forward projected into layer srs // first, try intersection of map extent forward projected into layer srs
if (prj_trans.forward(buffered_query_ext, PROJ_ENVELOPE_POINTS) && buffered_query_ext.intersects(layer_ext)) if (proj_trans_ptr->forward(buffered_query_ext, PROJ_ENVELOPE_POINTS) && buffered_query_ext.intersects(layer_ext))
{ {
fw_success = true; fw_success = true;
layer_ext.clip(buffered_query_ext); layer_ext.clip(buffered_query_ext);
} }
// if no intersection and projections are also equal, early return // if no intersection and projections are also equal, early return
else if (prj_trans.equal()) else if (proj_trans_ptr->equal())
{ {
early_return = true; early_return = true;
} }
// next try intersection of layer extent back projected into map srs // next try intersection of layer extent back projected into map srs
else if (prj_trans.backward(layer_ext, PROJ_ENVELOPE_POINTS) && buffered_query_ext_map_srs.intersects(layer_ext)) else if (proj_trans_ptr->backward(layer_ext, PROJ_ENVELOPE_POINTS) && buffered_query_ext_map_srs.intersects(layer_ext))
{ {
layer_ext.clip(buffered_query_ext_map_srs); layer_ext.clip(buffered_query_ext_map_srs);
// forward project layer extent back into native projection // forward project layer extent back into native projection
if (! prj_trans.forward(layer_ext, PROJ_ENVELOPE_POINTS)) if (! proj_trans_ptr->forward(layer_ext, PROJ_ENVELOPE_POINTS))
{ {
MAPNIK_LOG_ERROR(feature_style_processor) MAPNIK_LOG_ERROR(feature_style_processor)
<< "feature_style_processor: Layer=" << lay.name() << "feature_style_processor: Layer=" << lay.name()
@ -353,17 +363,17 @@ void feature_style_processor<Processor>::prepare_layer(layer_rendering_material
layer_ext2 = lay.envelope(); layer_ext2 = lay.envelope();
if (fw_success) if (fw_success)
{ {
if (prj_trans.forward(query_ext, PROJ_ENVELOPE_POINTS)) if (proj_trans_ptr->forward(query_ext, PROJ_ENVELOPE_POINTS))
{ {
layer_ext2.clip(query_ext); layer_ext2.clip(query_ext);
} }
} }
else else
{ {
if (prj_trans.backward(layer_ext2, PROJ_ENVELOPE_POINTS)) if (proj_trans_ptr->backward(layer_ext2, PROJ_ENVELOPE_POINTS))
{ {
layer_ext2.clip(query_ext); layer_ext2.clip(query_ext);
prj_trans.forward(layer_ext2, PROJ_ENVELOPE_POINTS); proj_trans_ptr->forward(layer_ext2, PROJ_ENVELOPE_POINTS);
} }
} }
@ -496,7 +506,18 @@ void feature_style_processor<Processor>::render_material(layer_rendering_materia
std::vector<rule_cache> const & rule_caches = mat.rule_caches_; std::vector<rule_cache> const & rule_caches = mat.rule_caches_;
proj_transform prj_trans(mat.proj0_,mat.proj1_); std::string key = mat.proj0_.params() + mat.proj1_.params();
auto itr = m_.proj_cache().find(key);
proj_transform * proj_trans_ptr;
if (itr == m_.proj_cache().end())
{
proj_trans_ptr = m_.proj_cache().emplace(key,
std::make_unique<proj_transform>(mat.proj0_, mat.proj1_)).first->second.get();
}
else
{
proj_trans_ptr = itr->second.get();
}
bool cache_features = lay.cache_features() && active_styles.size() > 1; bool cache_features = lay.cache_features() && active_styles.size() > 1;
@ -525,10 +546,9 @@ void feature_style_processor<Processor>::render_material(layer_rendering_materia
cache->prepare(); cache->prepare();
render_style(p, style, render_style(p, style,
rule_caches[i], rule_caches[i++],
cache, cache,
prj_trans); *proj_trans_ptr);
++i;
} }
cache->clear(); cache->clear();
} }
@ -540,8 +560,7 @@ void feature_style_processor<Processor>::render_material(layer_rendering_materia
for (feature_type_style const* style : active_styles) for (feature_type_style const* style : active_styles)
{ {
cache->prepare(); cache->prepare();
render_style(p, style, rule_caches[i], cache, prj_trans); render_style(p, style, rule_caches[i++], cache, *proj_trans_ptr);
++i;
} }
cache->clear(); cache->clear();
} }
@ -565,9 +584,8 @@ void feature_style_processor<Processor>::render_material(layer_rendering_materia
{ {
cache->prepare(); cache->prepare();
render_style(p, style, render_style(p, style,
rule_caches[i], rule_caches[i++],
cache, prj_trans); cache, *proj_trans_ptr);
++i;
} }
} }
// We only have a single style and no grouping. // We only have a single style and no grouping.
@ -579,10 +597,9 @@ void feature_style_processor<Processor>::render_material(layer_rendering_materia
{ {
featureset_ptr features = *featuresets++; featureset_ptr features = *featuresets++;
render_style(p, style, render_style(p, style,
rule_caches[i], rule_caches[i++],
features, features,
prj_trans); *proj_trans_ptr);
++i;
} }
} }
} }

View file

@ -33,7 +33,7 @@
#include <mapnik/well_known_srs.hpp> #include <mapnik/well_known_srs.hpp>
#include <mapnik/image_compositing.hpp> #include <mapnik/image_compositing.hpp>
#include <mapnik/font_engine_freetype.hpp> #include <mapnik/font_engine_freetype.hpp>
#include <mapnik/proj_transform.hpp>
#include <mapnik/warning.hpp> #include <mapnik/warning.hpp>
MAPNIK_DISABLE_WARNING_PUSH MAPNIK_DISABLE_WARNING_PUSH
#include <mapnik/warning_ignore.hpp> #include <mapnik/warning_ignore.hpp>
@ -104,7 +104,7 @@ private:
boost::optional<std::string> font_directory_; boost::optional<std::string> font_directory_;
freetype_engine::font_file_mapping_type font_file_mapping_; freetype_engine::font_file_mapping_type font_file_mapping_;
freetype_engine::font_memory_cache_type font_memory_cache_; freetype_engine::font_memory_cache_type font_memory_cache_;
mutable std::map<std::string, std::unique_ptr<proj_transform>> proj_cache_;
public: public:
using const_style_iterator = std::map<std::string,feature_type_style>::const_iterator; using const_style_iterator = std::map<std::string,feature_type_style>::const_iterator;
@ -502,6 +502,10 @@ public:
{ {
return font_memory_cache_; return font_memory_cache_;
} }
std::map<std::string, std::unique_ptr<proj_transform>> & proj_cache() const
{
return proj_cache_;
}
private: private:
friend void swap(Map & rhs, Map & lhs); friend void swap(Map & rhs, Map & lhs);

View file

@ -517,7 +517,6 @@ void Map::zoom_all()
{ {
return; return;
} }
projection proj0(srs_);
box2d<double> ext; box2d<double> ext;
bool success = false; bool success = false;
bool first = true; bool first = true;
@ -526,10 +525,24 @@ void Map::zoom_all()
if (layer.active()) if (layer.active())
{ {
std::string const& layer_srs = layer.srs(); std::string const& layer_srs = layer.srs();
projection proj1(layer_srs);
proj_transform prj_trans(proj0,proj1); std::string key = srs_ + layer_srs;
auto itr = proj_cache_.find(key);
proj_transform * proj_trans_ptr;
if (itr == proj_cache_.end())
{
projection proj0(srs_, true);
projection proj1(layer_srs, true);
proj_trans_ptr = proj_cache_.emplace(key,
std::make_unique<proj_transform>(proj0, proj1)).first->second.get();
}
else
{
proj_trans_ptr = itr->second.get();
}
box2d<double> layer_ext = layer.envelope(); box2d<double> layer_ext = layer.envelope();
if (prj_trans.backward(layer_ext, PROJ_ENVELOPE_POINTS)) if (proj_trans_ptr->backward(layer_ext, PROJ_ENVELOPE_POINTS))
{ {
success = true; success = true;
MAPNIK_LOG_DEBUG(map) << "map: Layer " << layer.name() << " original ext=" << layer.envelope(); MAPNIK_LOG_DEBUG(map) << "map: Layer " << layer.name() << " original ext=" << layer.envelope();
@ -707,11 +720,23 @@ featureset_ptr Map::query_point(unsigned index, double x, double y) const
mapnik::datasource_ptr ds = layer.datasource(); mapnik::datasource_ptr ds = layer.datasource();
if (ds) if (ds)
{ {
mapnik::projection dest(srs_); std::string key = srs_ + layer.srs();
mapnik::projection source(layer.srs()); auto itr = proj_cache_.find(key);
proj_transform prj_trans(source,dest); proj_transform * proj_trans_ptr;
if (itr == proj_cache_.end())
{
mapnik::projection dest(srs_, true);
mapnik::projection source(layer.srs(), true);
proj_trans_ptr = proj_cache_.emplace(key,
std::make_unique<proj_transform>(source, dest)).first->second.get();
}
else
{
proj_trans_ptr = itr->second.get();
}
double z = 0; double z = 0;
if (!prj_trans.equal() && !prj_trans.backward(x,y,z)) if (!proj_trans_ptr->equal() && !proj_trans_ptr->backward(x,y,z))
{ {
throw std::runtime_error("query_point: could not project x,y into layer srs"); throw std::runtime_error("query_point: could not project x,y into layer srs");
} }
@ -721,7 +746,7 @@ featureset_ptr Map::query_point(unsigned index, double x, double y) const
{ {
map_ex.clip(*maximum_extent_); map_ex.clip(*maximum_extent_);
} }
if (!prj_trans.backward(map_ex,PROJ_ENVELOPE_POINTS)) if (!proj_trans_ptr->backward(map_ex,PROJ_ENVELOPE_POINTS))
{ {
std::ostringstream s; std::ostringstream s;
s << "query_point: could not project map extent '" << map_ex s << "query_point: could not project map extent '" << map_ex

View file

@ -131,17 +131,17 @@ proj_transform::proj_transform(projection const& source,
dest.params().c_str(), nullptr); dest.params().c_str(), nullptr);
if (transform_ == nullptr) if (transform_ == nullptr)
{ {
throw std::runtime_error(std::string("Cannot initialize proj_transform for given projections without proj4 support (-DMAPNIK_USE_PROJ): '") + source.params() + "'->'" + dest.params() + "'"); throw std::runtime_error(std::string("Cannot initialize proj_transform for given projections: '") + source.params() + "'->'" + dest.params() + "'");
} }
PJ* transform_gis = proj_normalize_for_visualization(ctx_, transform_); PJ* transform_gis = proj_normalize_for_visualization(ctx_, transform_);
if (transform_gis == nullptr) if (transform_gis == nullptr)
{ {
throw std::runtime_error(std::string("Cannot initialize proj_transform for given projections without proj4 support (-DMAPNIK_USE_PROJ): '") + source.params() + "'->'" + dest.params() + "'"); throw std::runtime_error(std::string("Cannot initialize proj_transform for given projections: '") + source.params() + "'->'" + dest.params() + "'");
} }
proj_destroy(transform_); proj_destroy(transform_);
transform_ = transform_gis; transform_ = transform_gis;
#else #else
throw std::runtime_error(std::string("Cannot initialize proj_transform for given projections without proj4 support (-DMAPNIK_USE_PROJ): '") + source.params() + "'->'" + dest.params() + "'"); throw std::runtime_error(std::string("Cannot initialize proj_transform for given projections without proj support (-DMAPNIK_USE_PROJ): '") + source.params() + "'->'" + dest.params() + "'");
#endif #endif
} }
} }

View file

@ -94,7 +94,8 @@ void projection::init_proj() const
if (!proj_) if (!proj_)
{ {
proj_ctx_ = proj_context_create(); proj_ctx_ = proj_context_create();
proj_ = proj_create_crs_to_crs(proj_ctx_, "epsg:4326", params_.c_str(), nullptr); //proj_ = proj_create_crs_to_crs(proj_ctx_, "epsg:4326", params_.c_str(), nullptr);
proj_ = proj_create(proj_ctx_, params_.c_str());
if (!proj_ || !proj_ctx_) if (!proj_ || !proj_ctx_)
{ {
if (proj_ctx_) { if (proj_ctx_) {
@ -108,15 +109,17 @@ void projection::init_proj() const
throw proj_init_error(params_); throw proj_init_error(params_);
} }
// determine the type of CRS // determine the type of CRS
PJ* crs = proj_create(proj_ctx_, params_.c_str()); //PJ* crs = proj_create(proj_ctx_, params_.c_str());
if (crs) //if (crs)
{ //if (proj_)
PJ_TYPE type = proj_get_type(crs); //{
is_geographic_ = (type == PJ_TYPE_GEOGRAPHIC_2D_CRS //PJ_TYPE type = proj_get_type(crs);
|| PJ_TYPE type = proj_get_type(proj_);
type == PJ_TYPE_GEOGRAPHIC_3D_CRS) ? true : false; is_geographic_ = (type == PJ_TYPE_GEOGRAPHIC_2D_CRS
} ||
proj_destroy(crs); type == PJ_TYPE_GEOGRAPHIC_3D_CRS) ? true : false;
//}
//proj_destroy(crs);
} }
#endif #endif
} }