featureset_ptr postgis_datasource::features_with_context(query const& q,processor_context_ptr proc_ctx) const { #ifdef MAPNIK_STATS mapnik::progress_timer __stats__(std::clog, "postgis_datasource::features_with_context"); #endif box2d<double> const& box = q.get_bbox(); double scale_denom = q.scale_denominator(); CnxPool_ptr pool = ConnectionManager::instance().getPool(creator_.id()); if (pool) { shared_ptr<Connection> conn; if ( asynchronous_request_ ) { // limit use to num_async_request_ => if reached don't borrow the last connexion object std::shared_ptr<postgis_processor_context> pgis_ctxt = std::static_pointer_cast<postgis_processor_context>(proc_ctx); if ( pgis_ctxt->num_async_requests_ < max_async_connections_ ) { conn = pool->borrowObject(); pgis_ctxt->num_async_requests_++; } } else { // Always get a connection in synchronous mode conn = pool->borrowObject(); if(!conn ) { throw mapnik::datasource_exception("Postgis Plugin: Null connection"); } } if (geometryColumn_.empty()) { std::ostringstream s_error; s_error << "PostGIS: geometry name lookup failed for table '"; if (! schema_.empty()) { s_error << schema_ << "."; } s_error << geometry_table_ << "'. Please manually provide the 'geometry_field' parameter or add an entry " << "in the geometry_columns for '"; if (! schema_.empty()) { s_error << schema_ << "."; } s_error << geometry_table_ << "'."; throw mapnik::datasource_exception(s_error.str()); } std::ostringstream s; const double px_gw = 1.0 / std::get<0>(q.resolution()); const double px_gh = 1.0 / std::get<1>(q.resolution()); s << "SELECT ST_AsBinary("; if (simplify_geometries_) { s << "ST_Simplify("; } s << "\"" << geometryColumn_ << "\""; if (simplify_geometries_) { // 1/20 of pixel seems to be a good compromise to avoid // drop of collapsed polygons. // See https://github.com/mapnik/mapnik/issues/1639 const double tolerance = std::min(px_gw, px_gh) / 20.0; s << ", " << tolerance << ")"; } s << ") AS geom"; mapnik::context_ptr ctx = std::make_shared<mapnik::context_type>(); std::set<std::string> const& props = q.property_names(); std::set<std::string>::const_iterator pos = props.begin(); std::set<std::string>::const_iterator end = props.end(); if (! key_field_.empty()) { mapnik::sql_utils::quote_attr(s, key_field_); ctx->push(key_field_); for (; pos != end; ++pos) { if (*pos != key_field_) { mapnik::sql_utils::quote_attr(s, *pos); ctx->push(*pos); } } } else { for (; pos != end; ++pos) { mapnik::sql_utils::quote_attr(s, *pos); ctx->push(*pos); } } std::string table_with_bbox = populate_tokens(table_, scale_denom, box, px_gw, px_gh, q.variables()); s << " FROM " << table_with_bbox; if (row_limit_ > 0) { s << " LIMIT " << row_limit_; } std::shared_ptr<IResultSet> rs = get_resultset(conn, s.str(), pool, proc_ctx); return std::make_shared<postgis_featureset>(rs, ctx, desc_.get_encoding(), !key_field_.empty()); } return featureset_ptr(); }
featureset_ptr postgis_datasource::features_with_context(query const& q,processor_context_ptr proc_ctx) const { #ifdef MAPNIK_STATS mapnik::progress_timer __stats__(std::clog, "postgis_datasource::features_with_context"); #endif box2d<double> const& box = q.get_bbox(); double scale_denom = q.scale_denominator(); CnxPool_ptr pool = ConnectionManager::instance().getPool(creator_.id()); if (pool) { shared_ptr<Connection> conn; if ( asynchronous_request_ ) { // limit use to num_async_request_ => if reached don't borrow the last connexion object std::shared_ptr<postgis_processor_context> pgis_ctxt = std::static_pointer_cast<postgis_processor_context>(proc_ctx); if ( pgis_ctxt->num_async_requests_ < max_async_connections_ ) { conn = pool->borrowObject(); pgis_ctxt->num_async_requests_++; } } else { // Always get a connection in synchronous mode conn = pool->borrowObject(); if(!conn ) { throw mapnik::datasource_exception("Postgis Plugin: Null connection"); } } if (geometryColumn_.empty()) { std::ostringstream s_error; s_error << "PostGIS: geometry name lookup failed for table '"; if (! schema_.empty()) { s_error << schema_ << "."; } s_error << geometry_table_ << "'. Please manually provide the 'geometry_field' parameter or add an entry " << "in the geometry_columns for '"; if (! schema_.empty()) { s_error << schema_ << "."; } s_error << geometry_table_ << "'."; throw mapnik::datasource_exception(s_error.str()); } std::ostringstream s; const double px_gw = 1.0 / std::get<0>(q.resolution()); const double px_gh = 1.0 / std::get<1>(q.resolution()); const double px_sz = std::min(px_gw, px_gh); if (twkb_encoding_) { // This will only work against PostGIS 2.2, or a back-patched version // that has (a) a ST_Simplify with a "preserve collapsed" flag and // (b) a ST_RemoveRepeatedPoints with a tolerance parameter and // (c) a ST_AsTWKB implementation // What number of decimals of rounding does the pixel size imply? const int twkb_rounding = -1 * std::lround(log10(px_sz) + twkb_rounding_adjustment_) + 1; // And what's that in map units? const double twkb_tolerance = pow(10.0, -1.0 * twkb_rounding); s << "SELECT ST_AsTWKB("; s << "ST_Simplify("; s << "ST_RemoveRepeatedPoints("; if (simplify_clip_resolution_ > 0.0 && simplify_clip_resolution_ > px_sz) { s << "ST_ClipByBox2D("; } s << "\"" << geometryColumn_ << "\""; // ! ST_ClipByBox2D() if (simplify_clip_resolution_ > 0.0 && simplify_clip_resolution_ > px_sz) { s << "," << sql_bbox(box) << ")"; } // ! ST_RemoveRepeatedPoints() s << "," << twkb_tolerance << ")"; // ! ST_Simplify(), with parameter to keep collapsed geometries s << "," << twkb_tolerance << ",true)"; // ! ST_TWKB() s << "," << twkb_rounding << ") AS geom"; } else { s << "SELECT ST_AsBinary("; if (simplify_geometries_) { s << "ST_Simplify("; } if (simplify_clip_resolution_ > 0.0 && simplify_clip_resolution_ > px_sz) { s << "ST_ClipByBox2D("; } if (simplify_geometries_ && simplify_snap_ratio_ > 0.0) { s<< "ST_SnapToGrid("; } // Geometry column! s << "\"" << geometryColumn_ << "\""; // ! ST_SnapToGrid() if (simplify_geometries_ && simplify_snap_ratio_ > 0.0) { const double tolerance = px_sz * simplify_snap_ratio_; s << "," << tolerance << ")"; } // ! ST_ClipByBox2D() if (simplify_clip_resolution_ > 0.0 && simplify_clip_resolution_ > px_sz) { s << "," << sql_bbox(box) << ")"; } // ! ST_Simplify() if (simplify_geometries_) { const double tolerance = px_sz * simplify_dp_ratio_; s << ", " << tolerance; // Add parameter to ST_Simplify to keep collapsed geometries if (simplify_dp_preserve_) { s << ", true"; } s << ")"; } // ! ST_AsBinary() s << ") AS geom"; } mapnik::context_ptr ctx = std::make_shared<mapnik::context_type>(); std::set<std::string> const& props = q.property_names(); std::set<std::string>::const_iterator pos = props.begin(); std::set<std::string>::const_iterator end = props.end(); if (! key_field_.empty()) { mapnik::sql_utils::quote_attr(s, key_field_); if (key_field_as_attribute_) { ctx->push(key_field_); } for (; pos != end; ++pos) { if (*pos != key_field_) { mapnik::sql_utils::quote_attr(s, *pos); ctx->push(*pos); } } } else { for (; pos != end; ++pos) { mapnik::sql_utils::quote_attr(s, *pos); ctx->push(*pos); } } std::string table_with_bbox = populate_tokens(table_, scale_denom, box, px_gw, px_gh, q.variables()); s << " FROM " << table_with_bbox; if (row_limit_ > 0) { s << " LIMIT " << row_limit_; } std::shared_ptr<IResultSet> rs = get_resultset(conn, s.str(), pool, proc_ctx); return std::make_shared<postgis_featureset>(rs, ctx, desc_.get_encoding(), !key_field_.empty(), key_field_as_attribute_, twkb_encoding_); } return mapnik::make_invalid_featureset(); }